diff --git a/vendor/github.com/Chain-Zhang/pinyin/.gitignore b/vendor/github.com/Chain-Zhang/pinyin/.gitignore new file mode 100644 index 0000000..9ea998a --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/.gitignore @@ -0,0 +1,29 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# For Goland IDE +.idea/ + +# For Mac OS +*~ +.DS_Store + +# For auto build +bin/ +pkg/ +build/ +_publish_dir + +# For temp +temp/ +tmp/ \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/.travis.yml b/vendor/github.com/Chain-Zhang/pinyin/.travis.yml new file mode 100644 index 0000000..bc0a922 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/.travis.yml @@ -0,0 +1,18 @@ +language: go + +go: + - 1.11.x + +os: + - linux + - osx + - windows + +before_install: + - go get -v ./... + +script: + - go test ./... -race -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/LICENSE b/vendor/github.com/Chain-Zhang/pinyin/LICENSE new file mode 100644 index 0000000..c98d6a4 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Chain Zhang + +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. diff --git a/vendor/github.com/Chain-Zhang/pinyin/Makefile b/vendor/github.com/Chain-Zhang/pinyin/Makefile new file mode 100644 index 0000000..bd9e94e --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/Makefile @@ -0,0 +1,4 @@ +PKG_LIST := $(shell go list ./... | grep -v /vendor/) + +test: + @go test -short -cover ${PKG_LIST} \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/README.md b/vendor/github.com/Chain-Zhang/pinyin/README.md new file mode 100644 index 0000000..9e61663 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/README.md @@ -0,0 +1,66 @@ +# pinyin + +[![Build Status](https://travis-ci.com/Chain-Zhang/pinyin.svg?branch=master)](https://travis-ci.com/Chain-Zhang/pinyin) +[![codecov](https://codecov.io/gh/Chain-Zhang/pinyin/branch/master/graph/badge.svg)](https://codecov.io/gh/Chain-Zhang/pinyin) + +golang实现中文汉字转拼音 + +demo + +```go +package main + +import( + "fmt" + "github.com/chain-zhang/pinyin" +) + +func main() { + str, err := pinyin.New("我是中国人").Split("").Mode(InitialsInCapitals).Convert() + if err != nil { + // 错误处理 + }else{ + fmt.Println(str) + } + + str, err = pinyin.New("我是中国人").Split(" ").Mode(pinyin.WithoutTone).Convert() + if err != nil { + // 错误处理 + }else{ + fmt.Println(str) + } + + str, err = pinyin.New("我是中国人").Split("-").Mode(pinyin.Tone).Convert() + if err != nil { + // 错误处理 + }else{ + fmt.Println(str) + } + + str, err = pinyin.New("我是中国人").Convert() + if err != nil { + // 错误处理 + }else{ + fmt.Println(str) + } +} +``` + +输出 + +```bash +WoShiZhongGuoRen +wo shi zhong guo ren +wǒ-shì-zhōng-guó-rén +wo shi zhong guo ren +``` + +Mode 介绍 + +* `InitialsInCapitals`: 首字母大写, 不带音调 +* `WithoutTone`: 全小写,不带音调 +* `Tone`: 全小写带音调 + +Split 介绍 + +split 方法是两个汉字之间的分隔符. \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/codecov.yml b/vendor/github.com/Chain-Zhang/pinyin/codecov.yml new file mode 100644 index 0000000..4044a50 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/codecov.yml @@ -0,0 +1,2 @@ +codecov: + token: f3bed817-d998-44c1-b474-168d03f438bd \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/error.go b/vendor/github.com/Chain-Zhang/pinyin/error.go new file mode 100644 index 0000000..1023039 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/error.go @@ -0,0 +1,7 @@ +package pinyin + +import "errors" + +var ( + ErrInitialize = errors.New("not yet initialized") +) diff --git a/vendor/github.com/Chain-Zhang/pinyin/go.mod b/vendor/github.com/Chain-Zhang/pinyin/go.mod new file mode 100644 index 0000000..d95ea0c --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/go.mod @@ -0,0 +1,3 @@ +module github.com/Chain-Zhang/pinyin + +go 1.13 diff --git a/vendor/github.com/Chain-Zhang/pinyin/pinyin.go b/vendor/github.com/Chain-Zhang/pinyin/pinyin.go new file mode 100644 index 0000000..6fa4c89 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/pinyin.go @@ -0,0 +1,173 @@ +package pinyin + +import ( + "strconv" + "strings" + "unicode/utf8" +) + +var ( + tones = [][]rune{ + {'ā', 'ē', 'ī', 'ō', 'ū', 'ǖ', 'Ā', 'Ē', 'Ī', 'Ō', 'Ū', 'Ǖ'}, + {'á', 'é', 'í', 'ó', 'ú', 'ǘ', 'Á', 'É', 'Í', 'Ó', 'Ú', 'Ǘ'}, + {'ǎ', 'ě', 'ǐ', 'ǒ', 'ǔ', 'ǚ', 'Ǎ', 'Ě', 'Ǐ', 'Ǒ', 'Ǔ', 'Ǚ'}, + {'à', 'è', 'ì', 'ò', 'ù', 'ǜ', 'À', 'È', 'Ì', 'Ò', 'Ù', 'Ǜ'}, + } + neutrals = []rune{'a', 'e', 'i', 'o', 'u', 'v', 'A', 'E', 'I', 'O', 'U', 'V'} +) + +var ( + // 从带声调的声母到对应的英文字符的映射 + tonesMap map[rune]rune + + // 从汉字到声调的映射 + numericTonesMap map[rune]int + + // 从汉字到拼音的映射(带声调) + pinyinMap map[rune]string + + initialized bool +) + +type Mode int + +const ( + WithoutTone Mode = iota + 1 // 默认模式,例如:guo + Tone // 带声调的拼音 例如:guó + InitialsInCapitals // 首字母大写不带声调,例如:Guo +) + +type pinyin struct { + origin string + split string + mode Mode +} + +func init() { + tonesMap = make(map[rune]rune) + numericTonesMap = make(map[rune]int) + pinyinMap = make(map[rune]string) + for i, runes := range tones { + for j, tone := range runes { + tonesMap[tone] = neutrals[j] + numericTonesMap[tone] = i + 1 + } + } + + for k, v := range resource { + i, err := strconv.ParseInt(k, 16, 32) + if err != nil { + continue + } + pinyinMap[rune(i)] = v + } + initialized = true +} + +func New(origin string) *pinyin { + return &pinyin{ + origin: origin, + split: " ", + mode: WithoutTone, + } +} + +func (py *pinyin) Split(split string) *pinyin { + py.split = split + return py +} + +func (py *pinyin) Mode(mode Mode) *pinyin { + py.mode = mode + return py +} + +func (py *pinyin) Convert() (string, error) { + if !initialized { + return "", ErrInitialize + } + + sr := []rune(py.origin) + words := make([]string, 0) + var temp string + for i, s := range sr { + _, ok := pinyinMap[s] + if !ok { + // 非中文处理 + temp += string(s) + if i == len(sr)-1 { + words = append(words, temp) + } + continue + } + word, err := getPinyin(s, py.mode) + if err != nil { + return "", err + } + if len(temp) > 0 { + + words = append(words, temp) + temp = "" + } + if len(word) > 0 { + words = append(words, word) + } + } + result := strings.Join(words, py.split) + result = strings.Replace(result, " ", " ", -1) + result = strings.Replace(result, " ", " ", -1) + return result, nil +} + +func getPinyin(hanzi rune, mode Mode) (string, error) { + if !initialized { + return "", ErrInitialize + } + + switch mode { + case Tone: + return getTone(hanzi), nil + case InitialsInCapitals: + return getInitialsInCapitals(hanzi), nil + default: + return getDefault(hanzi), nil + } +} + +func getTone(hanzi rune) string { + return pinyinMap[hanzi] +} + +func getDefault(hanzi rune) string { + tone := getTone(hanzi) + + if tone == "" { + return tone + } + + output := make([]rune, utf8.RuneCountInString(tone)) + + count := 0 + for _, t := range tone { + neutral, found := tonesMap[t] + if found { + output[count] = neutral + } else { + output[count] = t + } + count++ + } + return string(output) +} + +func getInitialsInCapitals(hanzi rune) string { + def := getDefault(hanzi) + if def == "" { + return def + } + sr := []rune(def) + if sr[0] > 32 { + sr[0] = sr[0] - 32 + } + return string(sr) +} diff --git a/vendor/github.com/Chain-Zhang/pinyin/pinyin.txt b/vendor/github.com/Chain-Zhang/pinyin/pinyin.txt new file mode 100644 index 0000000..82d18fa --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/pinyin.txt @@ -0,0 +1,41208 @@ +3400=>qiū +3401=>tiàn +3404=>kuà +3405=>wǔ +3406=>yǐn +340C=>yí +3416=>xié +341C=>chóu +3421=>nuò +3424=>dān +3428=>xù +3429=>xíng +342B=>xiōng +342C=>liú +342D=>lǐn +342E=>xiāng +342F=>yōng +3430=>xìn +3431=>zhěn +3432=>dài +3433=>wù +3434=>pān +3437=>mǎ +3438=>qiàn +3439=>yì +343A=>yín +343B=>nèi +343C=>chèng +343D=>fēng +3441=>zhuō +3442=>fǎng +3443=>ǎo +3444=>wǔ +3445=>zuò +3447=>zhòu +3448=>dòng +3449=>sù +344A=>yì +344B=>qióng +344C=>kuāng +344D=>lèi +344E=>nǎo +344F=>zhù +3450=>shū +3454=>xǔ +3457=>shēn +3458=>jiè +3459=>dié +345A=>nuó +345B=>sù +345C=>yì +345D=>lòng +345E=>yìng +345F=>běng +3463=>lán +3464=>miáo +3465=>yì +3466=>lì +3467=>jì +3468=>yǔ +3469=>luó +346A=>chái +346E=>hún +346F=>xǔ +3470=>huì +3471=>rǎo +3473=>zhòu +3475=>hàn +3476=>xì +3477=>tài +3478=>yáo +3479=>huì +347A=>jùn +347B=>mà +347C=>lüè +347D=>táng +347E=>yáo +347F=>zhào +3480=>zhāi +3481=>yǔ +3482=>zhuó +3483=>èr +3484=>rǎn +3485=>qǐ +3486=>chì +3487=>wǔ +3488=>hàn +3489=>tǎng +348A=>sè +348C=>qióng +348D=>léi +348E=>sà +3491=>kuǐ +3492=>pú +3493=>tà +3494=>shú +3495=>yāng +3496=>ǒu +3497=>tái +3499=>mián +349A=>yìn +349B=>diào +349C=>yǔ +349D=>miè +349E=>jùn +349F=>niǎo +34A0=>xiè +34A1=>yóu +34A4=>chè +34A5=>fēng +34A6=>lěi +34A7=>lì +34A9=>luǒ +34AB=>jì +34B0=>quán +34B2=>cái +34B3=>liǎng +34B4=>gǔ +34B5=>mào +34B7=>guǎ +34B8=>suì +34BB=>mào +34BC=>mán +34BD=>quān +34BE=>shì +34BF=>lí +34C1=>wǎng +34C2=>kòu +34C3=>dù +34C4=>zhèn +34C5=>tīng +34C8=>bìng +34C9=>huò +34CA=>dòng +34CB=>gòng +34CC=>chēng +34CE=>qīn +34CF=>jiǒng +34D0=>lù +34D1=>xìng +34D3=>nán +34D4=>xiè +34D6=>bì +34D7=>jié +34D8=>sù +34DA=>gōng +34DC=>yòu +34DD=>xíng +34DE=>qià +34DF=>pí +34E0=>diàn +34E1=>fǔ +34E2=>luò +34E3=>qià +34E4=>qià +34E5=>tāng +34E6=>bāi +34E7=>gān +34E8=>cí +34E9=>xuān +34EA=>lǎng +34ED=>shé +34EF=>lí +34F0=>huà +34F1=>tóu +34F2=>piān +34F3=>dī +34F4=>ruǎn +34F5=>è +34F6=>qiè +34F7=>yì +34F8=>zhuō +34F9=>ruì +34FA=>jiān +34FC=>chì +34FD=>chóng +34FE=>xī +3500=>lüè +3501=>dēng +3502=>lín +3503=>jué +3504=>sù +3505=>xiào +3506=>zàn +3509=>zhǔ +350A=>zhǎn +350B=>jiān +350C=>zòu +350D=>chuā +350E=>xiè +350F=>lì +3511=>chì +3512=>xí +3513=>jiǎn +3515=>jí +3517=>fèi +3518=>chù +3519=>bēng +351A=>jié +351C=>bá +351D=>liǎng +351E=>kuài +3520=>xiā +3521=>biē +3522=>jué +3523=>léi +3524=>xìn +3525=>bài +3526=>yǎng +3527=>lǜ +3528=>bèi +3529=>è +352A=>lǔ +352D=>chè +352E=>nuó +352F=>xuán +3530=>héng +3531=>yǔ +3533=>guǐ +3534=>yì +3535=>xuǎn +3536=>gòng +3537=>lòu +3538=>tī +3539=>lè +353A=>shì +353C=>sǔn +353D=>yào +353E=>xiān +353F=>zòu +3541=>què +3542=>yín +3543=>xī +3544=>zhǐ +3545=>jiá +3546=>hù +3547=>lā +3548=>yǐ +3549=>kè +354A=>fū +354B=>qín +354C=>ài +354E=>kè +354F=>chú +3550=>xiě +3551=>chú +3552=>wēi +3555=>huàn +3556=>sù +3557=>yòu +3559=>jùn +355A=>zhǎo +355B=>xù +355C=>shǐ +355E=>shuā +355F=>kuì +3560=>shuāng +3561=>hé +3562=>gài +3563=>yǎn +3564=>qiú +3565=>shēn +3566=>huà +3567=>xī +3568=>fàn +3569=>pàng +356A=>dǎn +356B=>fǎng +356C=>gōng +356D=>āo +356E=>fǔ +356F=>nè +3570=>xuè +3571=>yóu +3572=>huá +3574=>chén +3575=>guó +3576=>ň +3577=>huà +3578=>lì +3579=>fá +357A=>xiāo +357B=>pǒu +357D=>sì +3580=>lè +3581=>lìn +3582=>yì +3583=>hǒu +3585=>xù +3586=>qú +3587=>ér +358A=>xún +358F=>niè +3590=>wěi +3591=>xiè +3592=>tí +3593=>hóng +3594=>tǔn +3595=>niè +3596=>niè +3597=>yín +3598=>zhēn +359E=>wāi +359F=>shòu +35A0=>nuò +35A1=>yè +35A2=>qí +35A3=>tòu +35A4=>hán +35A5=>jùn +35A6=>dǒng +35A7=>hūn +35A8=>lù +35A9=>jū +35AA=>huò +35AB=>líng +35AD=>tiǎn +35AE=>lún +35B5=>gé +35B6=>yān +35B7=>shí +35B8=>xué +35B9=>pēn +35BA=>chǔn +35BB=>niú +35BC=>duǒ +35BD=>zé +35BE=>è +35BF=>xié +35C0=>yōu +35C1=>è +35C2=>shěng +35C3=>wěn +35C4=>kū +35C5=>hú +35C6=>gé +35C7=>xiá +35C8=>màn +35C9=>lüè +35CA=>jí +35CB=>hóu +35CC=>zhì +35CF=>wāi +35D1=>bai +35D2=>ài +35D3=>zhuī +35D4=>qiān +35D5=>gòu +35D6=>dàn +35D7=>bēi +35D8=>bó +35D9=>chū +35DA=>lì +35DB=>xiào +35DC=>xiù +35E2=>hóng +35E3=>tì +35E4=>cù +35E5=>kuò +35E6=>láo +35E7=>zhì +35E8=>xiē +35E9=>xī +35EB=>qiè +35EC=>zhā +35ED=>xī +35F0=>cóng +35F1=>jí +35F2=>huò +35F3=>tǎ +35F4=>yán +35F5=>xù +35F6=>pō +35F7=>sǎi +35FB=>guō +35FC=>yè +35FD=>xiǎng +35FE=>xuē +35FF=>hé +3600=>zuò +3601=>yì +3602=>cí +3604=>lēng +3605=>xián +3606=>tǎi +3607=>róng +3608=>yì +3609=>zhì +360A=>xī +360B=>xián +360C=>jù +360D=>jí +360E=>hǎn +3610=>pào +3611=>lì +3613=>lán +3614=>sǎi +3615=>hǎn +3616=>yán +3617=>qū +3619=>yán +361A=>hǎn +361B=>kān +361C=>chǐ +361D=>niè +361E=>huò +3620=>bì +3621=>xiá +3622=>wěng +3623=>xuán +3624=>wān +3625=>yóu +3626=>qín +3627=>xù +3628=>niè +3629=>bì +362A=>hào +362B=>jǐng +362C=>ào +362D=>ào +3630=>zhēn +3631=>tān +3632=>jú +3634=>zuò +3635=>bù +3636=>jié +3637=>ài +3638=>zàng +3639=>cí +363A=>fá +363F=>niè +3640=>liù +3641=>méi +3642=>duì +3643=>bāng +3644=>bì +3645=>bǎo +3647=>chù +3648=>xià +3649=>tiǎn +364A=>cháng +364D=>duō +364E=>wēi +364F=>fù +3650=>duǒ +3651=>yǔ +3652=>yě +3653=>kuí +3654=>wěi +3655=>kuài +3657=>wēi +3658=>yāo +3659=>lǒng +365A=>xīng +365B=>zhuān +365C=>chí +365D=>xié +365E=>niè +365F=>lǎng +3660=>yī +3661=>zōng +3662=>mán +3663=>zhàng +3664=>xià +3665=>gùn +3666=>xié +3668=>jì +3669=>liáo +366A=>yì +366B=>jí +366C=>yín +366E=>dā +366F=>yì +3670=>xiè +3671=>hào +3672=>yǒng +3673=>kǎn +3674=>chàn +3675=>tái +3676=>táng +3677=>zhí +3678=>bào +3679=>méng +367A=>kuí +367B=>chán +367C=>lěi +367E=>xì +3680=>xī +3681=>qiào +3682=>nàng +3683=>yūn +3685=>lóng +3686=>fù +3687=>zōng +3689=>gǔ +368A=>kāi +368B=>diāo +368C=>huà +368D=>kuǐ +368F=>gǎo +3690=>tào +3692=>shǎn +3693=>lǎi +3694=>niè +3695=>fú +3696=>gǎo +3697=>qié +3698=>bàn +3699=>jiā +369A=>kōng +369B=>xì +369C=>yù +369D=>zhuī +369E=>shěn +369F=>chuò +36A0=>xiāo +36A1=>jǐ +36A2=>nú +36A3=>xiáo +36A4=>yì +36A5=>yú +36A6=>yí +36A7=>yǎn +36A8=>shěn +36A9=>rǎn +36AA=>hào +36AB=>sà +36AC=>jūn +36AD=>yóu +36AF=>xín +36B0=>pēi +36B1=>qiū +36B2=>chān +36B4=>bù +36B5=>dōng +36B6=>sì +36B7=>ěr +36B9=>mǎo +36BA=>yùn +36BB=>jī +36BD=>qiǎo +36BE=>xiōng +36BF=>páo +36C0=>chú +36C1=>pēng +36C2=>nuǒ +36C3=>jié +36C4=>yī +36C5=>èr +36C6=>duò +36CA=>duǒ +36CD=>qiè +36CE=>lǚ +36CF=>qiú +36D0=>sǒu +36D1=>càn +36D2=>dòu +36D3=>xī +36D4=>fēng +36D5=>yì +36D6=>suō +36D7=>qiē +36D8=>pò +36D9=>xīn +36DA=>tǒng +36DB=>xìn +36DC=>yóu +36DD=>bèi +36DE=>lòng +36E3=>yún +36E4=>lí +36E5=>tà +36E6=>lǎn +36E7=>mǎn +36E8=>qiǎng +36E9=>zhóu +36EA=>yàn +36EB=>xī +36EC=>lù +36ED=>xī +36EE=>sǎo +36EF=>fàn +36F1=>wěi +36F2=>fà +36F3=>yì +36F4=>nǎo +36F5=>chēng +36F6=>tàn +36F7=>jī +36F8=>shù +36F9=>pián +36FA=>ān +36FB=>kuā +36FC=>chā +36FE=>xián +36FF=>zhì +3702=>fēng +3703=>liàn +3704=>xún +3705=>xù +3706=>mì +3707=>huì +3708=>mù +3709=>yōng +370A=>zhǎn +370B=>yì +370C=>nǒu +370D=>táng +370E=>xī +370F=>yún +3710=>shù +3711=>fú +3712=>yì +3713=>dá +3715=>lián +3716=>cáo +3717=>cān +3718=>jù +3719=>lù +371A=>sù +371B=>nèn +371C=>ào +371D=>ǎn +371E=>qiàn +3720=>cuī +3721=>cōng +3723=>rán +3724=>niǎn +3725=>mái +3726=>xín +3727=>yuè +3728=>nái +3729=>ào +372A=>shēn +372B=>mà +372E=>làn +372F=>xī +3730=>yuè +3731=>zhì +3732=>wěng +3733=>huái +3734=>mèng +3735=>niǎo +3736=>wǎn +3737=>mí +3738=>niè +3739=>qú +373A=>zàn +373B=>liàn +373C=>zhí +373D=>zǐ +373E=>hái +373F=>xù +3740=>hào +3741=>xuān +3742=>zhì +3743=>miǎn +3744=>chún +3745=>gòu +3747=>chún +3748=>luán +3749=>zhù +374A=>shǒu +374B=>liǎo +374C=>jiù +374D=>xiě +374E=>dìng +374F=>jiè +3750=>róng +3751=>máng +3753=>kè +3754=>yǎo +3755=>níng +3756=>yí +3757=>láng +3758=>yóng +3759=>yín +375A=>yán +375B=>sù +375D=>lín +375E=>yā +375F=>máo +3760=>míng +3761=>zuì +3762=>yǔ +3763=>yì +3764=>gòu +3765=>mǐ +3766=>jùn +3767=>wěn +3769=>kāng +376A=>diàn +376B=>lóng +376D=>xǐng +376E=>cuì +376F=>qiáo +3770=>mián +3771=>mèng +3772=>qǐn +3774=>wán +3775=>dé +3776=>ài +3778=>biàn +3779=>nóu +377A=>lián +377B=>jǐn +377C=>yū +377D=>chuí +377E=>zuǒ +377F=>bǒ +3780=>huī +3781=>yào +3782=>tuǐ +3783=>jì +3784=>ān +3785=>luò +3786=>jǐ +3787=>wěi +3788=>bō +3789=>zā +378A=>xù +378B=>niǎn +378C=>yùn +378E=>bǎ +378F=>zhé +3790=>jū +3791=>wěi +3792=>xiè +3793=>qì +3794=>yí +3795=>xiè +3796=>cí +3797=>qiú +3798=>dū +3799=>niào +379A=>qì +379B=>jǐ +379C=>tuī +379E=>sóng +379F=>diàn +37A0=>láo +37A1=>zhǎn +37A4=>yín +37A5=>cén +37A6=>jǐ +37A7=>huì +37A8=>zǐ +37A9=>lán +37AA=>náo +37AB=>jù +37AC=>qìn +37AD=>dài +37AF=>jié +37B0=>xǔ +37B1=>cōng +37B2=>yòng +37B3=>dǒu +37B4=>chí +37B6=>mǐn +37B7=>huáng +37B8=>suì +37B9=>kě +37BA=>zú +37BB=>hào +37BC=>chéng +37BD=>xuè +37BE=>ní +37BF=>chì +37C0=>lián +37C1=>àn +37C2=>mǔ +37C3=>sī +37C4=>xiáng +37C5=>yáng +37C6=>huá +37C7=>cuò +37C8=>qiú +37C9=>láo +37CA=>fú +37CB=>duì +37CC=>máng +37CD=>láng +37CE=>tuǒ +37CF=>hán +37D0=>mǎng +37D1=>bó +37D2=>qūn +37D3=>qí +37D4=>hán +37D6=>lòng +37D8=>tiáo +37D9=>zé +37DA=>qí +37DB=>zàn +37DC=>mí +37DD=>péi +37DE=>zhàn +37DF=>xiàng +37E0=>gǎng +37E2=>qí +37E4=>lù +37E6=>yùn +37E7=>è +37E8=>duān +37E9=>mín +37EA=>wēi +37EB=>quán +37EC=>sǒu +37ED=>mín +37EE=>tū +37F0=>mǐng +37F1=>yǎo +37F2=>jué +37F3=>lì +37F4=>kuài +37F5=>gǎng +37F6=>yuán +37F7=>da +37F9=>láo +37FA=>lóu +37FB=>qiàn +37FC=>áo +37FD=>biǎo +37FE=>yōng +37FF=>mǎng +3800=>dǎo +3802=>áo +3804=>xí +3805=>fú +3806=>dān +3807=>jiù +3808=>rùn +3809=>tóng +380A=>qū +380B=>è +380C=>qī +380D=>jí +380E=>jí +380F=>huá +3810=>jiào +3811=>zuì +3812=>biǎo +3813=>méng +3814=>bài +3815=>wěi +3816=>yǐ +3817=>ào +3818=>yǔ +3819=>háo +381A=>duì +381B=>wò +381C=>nì +381D=>cuán +381F=>lí +3820=>lú +3821=>niǎo +3822=>huái +3823=>lì +3825=>lǜ +3826=>fēng +3827=>mǐ +3828=>yù +382A=>jù +382D=>zhǎn +382E=>pēng +382F=>yǐ +3831=>jì +3832=>bǐ +3834=>rèn +3835=>huāng +3836=>fán +3837=>gé +3838=>kù +3839=>jiè +383A=>shā +383C=>sī +383D=>tóng +383E=>yuān +383F=>zī +3840=>bì +3841=>kuǎ +3842=>lì +3843=>huāng +3844=>xún +3845=>nuǒ +3847=>zhé +3848=>wèn +3849=>xián +384A=>qià +384B=>yé +384C=>mào +384F=>shù +3851=>qiāo +3852=>zhūn +3853=>kūn +3854=>wù +3855=>yīng +3856=>chuáng +3857=>tí +3858=>lián +3859=>bī +385A=>gōu +385B=>máng +385C=>xiè +385D=>fèng +385E=>lóu +385F=>zāo +3860=>zhèng +3861=>chú +3862=>màn +3863=>lóng +3865=>yìn +3866=>pīn +3867=>zhèng +3868=>jiān +3869=>luán +386A=>nié +386B=>yì +386D=>jì +386E=>jí +386F=>zhái +3870=>yǔ +3871=>jiǔ +3872=>huán +3873=>zhǐ +3874=>lā +3875=>líng +3876=>zhǐ +3877=>běn +3878=>zhà +3879=>jū +387A=>dàn +387B=>liào +387C=>yì +387D=>zhào +387E=>xiàn +387F=>chì +3880=>cì +3881=>chǐ +3882=>yǎn +3883=>láng +3884=>dòu +3885=>lòng +3886=>chán +3888=>tuí +3889=>chá +388A=>ǎi +388B=>chǐ +388D=>yǐng +388E=>zhé +388F=>tóu +3891=>tuí +3892=>chá +3893=>yǎo +3894=>zǒng +3896=>pān +3897=>qiào +3898=>lián +3899=>qín +389A=>lǔ +389B=>yàn +389C=>kàng +389D=>sū +389E=>yì +389F=>chān +38A0=>jiǒng +38A1=>jiǎng +38A3=>jìng +38A5=>dòng +38A7=>juàn +38A8=>hàn +38A9=>dì +38AC=>hóng +38AE=>chí +38AF=>diāo +38B0=>bì +38B2=>xùn +38B3=>lú +38B5=>xié +38B6=>bì +38B8=>bì +38BA=>xián +38BB=>ruì +38BC=>biè +38BD=>ěr +38BE=>juàn +38C0=>zhèn +38C1=>bèi +38C2=>è +38C3=>yǔ +38C4=>qú +38C5=>zàn +38C6=>mí +38C7=>yì +38C8=>sì +38CC=>shàn +38CD=>tái +38CE=>mù +38CF=>jìng +38D0=>biàn +38D1=>róng +38D2=>cèng +38D3=>càn +38D4=>dīng +38D9=>dí +38DA=>tǒng +38DB=>tà +38DC=>xíng +38DD=>sōng +38DE=>duó +38DF=>xì +38E0=>tāo +38E2=>tí +38E3=>shàn +38E4=>jiàn +38E5=>zhì +38E6=>wēi +38E7=>yìn +38EA=>huǎn +38EB=>zhǒng +38EC=>qì +38ED=>zōng +38EF=>xiè +38F0=>xiè +38F1=>zé +38F2=>wéi +38F5=>tà +38F6=>zhān +38F7=>nìng +38FB=>yì +38FC=>rěn +38FD=>shù +38FE=>chà +38FF=>zhuó +3901=>miǎn +3902=>jí +3903=>fáng +3904=>pèi +3905=>ài +3906=>fàn +3907=>ǎo +3908=>qìn +3909=>qiā +390A=>xiào +390B=>fēn +390C=>gān +390D=>qiāo +390E=>gē +390F=>tóng +3910=>chān +3911=>yòu +3912=>gāo +3913=>bèn +3914=>fù +3915=>chù +3916=>zhù +3918=>zhòu +391A=>háng +391B=>nín +391C=>jué +391D=>chōng +391E=>chà +391F=>kǒng +3920=>liè +3921=>lì +3922=>yù +3924=>yú +3925=>hài +3926=>lì +3927=>hóu +3928=>gǒng +3929=>kè +392A=>yuàn +392B=>dé +392C=>huì +392E=>guàng +392F=>jiǒng +3930=>zuò +3931=>fù +3932=>qiè +3933=>běi +3934=>chè +3935=>cí +3936=>máng +3937=>hān +3938=>xì +3939=>qiú +393A=>huǎng +393D=>chóu +393E=>sàn +393F=>yān +3940=>zhí +3941=>dé +3942=>tè +3943=>mèn +3944=>líng +3945=>shòu +3946=>tuì +3947=>cán +3948=>dié +3949=>chè +394A=>péng +394B=>yī +394C=>jú +394D=>jì +394E=>lái +394F=>tiǎn +3950=>yuàn +3952=>cǎi +3953=>qī +3954=>yù +3955=>lián +3956=>cōng +395A=>yú +395B=>jí +395C=>wèi +395D=>mǐ +395E=>suì +395F=>xié +3960=>xū +3961=>chì +3962=>qiú +3963=>huì +3965=>yú +3966=>qiè +3967=>shùn +3968=>shuì +3969=>duǒ +396A=>lóu +396C=>páng +396D=>tài +396E=>zhòu +396F=>yǐn +3970=>sāo +3971=>fěi +3972=>chēn +3973=>yuán +3974=>yí +3975=>hùn +3976=>sè +3977=>yè +3978=>mǐn +3979=>fěn +397A=>hé +397C=>yìn +397D=>cè +397E=>nì +397F=>ào +3980=>féng +3981=>lián +3982=>cháng +3983=>chǎn +3984=>má +3985=>diē +3986=>hū +3987=>lù +3989=>yì +398A=>huá +398B=>zhā +398C=>hū +398D=>è +398E=>huò +398F=>sǔn +3990=>nì +3991=>xiàn +3992=>lí +3993=>xiàn +3994=>yàn +3995=>lóng +3996=>mèn +3997=>jīn +3998=>jī +399A=>biǎn +399B=>yǔ +399C=>huò +399D=>miǎo +399E=>chóu +399F=>mái +39A1=>lè +39A2=>jié +39A3=>wèi +39A4=>yì +39A5=>xuān +39A6=>xì +39A7=>cǎn +39A8=>lán +39A9=>yǐn +39AA=>xiè +39AB=>zā +39AC=>luǒ +39AD=>líng +39AE=>qián +39AF=>huò +39B0=>jiān +39B1=>wǒ +39B4=>gé +39B5=>zhū +39B6=>dié +39B7=>yǒng +39B8=>jǐ +39B9=>yáng +39BA=>rù +39BB=>xí +39BC=>shuàng +39BD=>yù +39BE=>yí +39BF=>qiǎn +39C0=>jí +39C1=>qù +39C2=>tián +39C3=>shōu +39C4=>qiǎn +39C5=>mù +39C6=>jīn +39C7=>mǎo +39C8=>yǐn +39C9=>gài +39CA=>pō +39CB=>xuǎn +39CC=>mào +39CD=>fǎng +39CE=>yá +39CF=>gāng +39D0=>sǒng +39D1=>huī +39D2=>yù +39D3=>guā +39D4=>guài +39D5=>liǔ +39D6=>è +39D7=>zǐ +39D8=>zì +39D9=>bì +39DA=>wǎ +39DC=>liè +39DF=>kuǎi +39E1=>hài +39E2=>yīn +39E3=>zhū +39E4=>chòng +39E5=>xiǎn +39E6=>xuàn +39E8=>qiú +39E9=>pèi +39EA=>guǐ +39EB=>ér +39EC=>gǒng +39ED=>qióng +39EE=>hū +39EF=>lǎo +39F0=>lì +39F1=>chèn +39F2=>sǎn +39F3=>zhuò +39F4=>wǒ +39F5=>póu +39F6=>kēng +39F7=>tùn +39F8=>pēng +39F9=>tè +39FA=>tà +39FB=>zhuó +39FC=>biào +39FD=>gù +39FE=>hū +3A00=>bǐng +3A01=>zhì +3A02=>dǒng +3A03=>duǐ +3A04=>zhōu +3A05=>nèi +3A06=>lǐn +3A07=>pó +3A08=>jǐ +3A09=>mín +3A0A=>wěi +3A0B=>chě +3A0C=>gòu +3A0D=>bāng +3A0E=>rú +3A0F=>tān +3A10=>bǔ +3A11=>zōng +3A12=>kuī +3A13=>láo +3A14=>hàn +3A15=>yíng +3A16=>zhì +3A17=>jié +3A18=>xǐng +3A19=>xié +3A1A=>xún +3A1B=>shǎn +3A1C=>qián +3A1D=>xiē +3A1E=>sù +3A1F=>hāi +3A20=>mì +3A21=>hún +3A22=>pī +3A24=>huì +3A25=>nà +3A26=>sǒng +3A27=>bèn +3A28=>chōu +3A29=>jié +3A2A=>huàng +3A2B=>lǎn +3A2D=>hù +3A2E=>dōu +3A2F=>huò +3A30=>gǔn +3A31=>yáo +3A32=>cè +3A33=>guǐ +3A34=>jiàn +3A35=>jiǎn +3A36=>dǎo +3A37=>jìn +3A38=>mà +3A39=>huì +3A3A=>miǎn +3A3B=>cán +3A3C=>lüè +3A3D=>pì +3A3E=>yàng +3A3F=>jù +3A40=>jù +3A41=>què +3A43=>qiān +3A44=>shāi +3A46=>jiù +3A47=>huò +3A48=>yǔn +3A49=>dá +3A4A=>xuān +3A4B=>xiāo +3A4C=>fèi +3A4D=>cè +3A4E=>yè +3A50=>dèn +3A52=>qín +3A53=>huǐ +3A54=>tún +3A56=>qiáng +3A57=>xí +3A58=>nǐ +3A59=>sāi +3A5A=>méng +3A5B=>tuán +3A5C=>lǎn +3A5D=>háo +3A5E=>cì +3A5F=>zhài +3A60=>āo +3A61=>luǒ +3A62=>miè +3A64=>fū +3A66=>xié +3A67=>bó +3A68=>huì +3A69=>qǐng +3A6A=>xié +3A6D=>bó +3A6E=>qián +3A6F=>pó +3A70=>jiǎo +3A71=>jué +3A72=>kǔn +3A73=>sǒng +3A74=>jú +3A75=>è +3A76=>niè +3A77=>qiān +3A78=>dié +3A79=>dié +3A7B=>qī +3A7C=>zhī +3A7D=>qí +3A7E=>zhuì +3A7F=>kū +3A80=>yú +3A81=>qín +3A82=>kū +3A83=>hé +3A84=>fú +3A86=>dǐ +3A87=>xiàn +3A88=>guì +3A89=>hé +3A8A=>qún +3A8B=>hàn +3A8C=>tǒng +3A8D=>bó +3A8E=>shǎn +3A8F=>bǐ +3A90=>lù +3A91=>yè +3A92=>ní +3A93=>chuái +3A94=>sàn +3A95=>diào +3A96=>lù +3A97=>tǒu +3A98=>liǎn +3A99=>kě +3A9A=>sàn +3A9B=>zhěn +3A9C=>chuǎi +3A9D=>liàn +3A9E=>mào +3AA0=>qiān +3AA1=>kài +3AA2=>shǎo +3AA3=>xiāo +3AA4=>bì +3AA5=>zhā +3AA6=>yìn +3AA7=>xī +3AA8=>shàn +3AA9=>sù +3AAA=>sà +3AAB=>ruì +3AAC=>chuō +3AAD=>lú +3AAE=>líng +3AAF=>chá +3AB1=>huàn +3AB4=>jiá +3AB5=>bàn +3AB6=>hú +3AB7=>dǒu +3AB9=>lǒu +3ABA=>jū +3ABB=>juàn +3ABC=>kě +3ABD=>suǒ +3ABE=>luò +3ABF=>zhé +3AC0=>dǐng +3AC1=>duàn +3AC2=>zhù +3AC3=>yǎn +3AC4=>páng +3AC5=>chá +3ACA=>yǐ +3ACD=>yóu +3ACE=>huī +3ACF=>yǎo +3AD0=>yǎo +3AD1=>zhǐ +3AD2=>gǒng +3AD3=>qǐ +3AD4=>gèn +3AD7=>hòu +3AD8=>mì +3AD9=>fú +3ADA=>hū +3ADB=>guàng +3ADC=>tǎn +3ADD=>dī +3ADF=>yán +3AE2=>qù +3AE4=>chǎng +3AE5=>mǐng +3AE6=>tāo +3AE7=>bào +3AE8=>ān +3AEB=>xiǎn +3AEF=>mào +3AF0=>làng +3AF1=>nǎn +3AF2=>bèi +3AF3=>chén +3AF5=>fēi +3AF6=>zhǒu +3AF7=>jī +3AF8=>jiē +3AF9=>shù +3AFB=>kùn +3AFC=>dié +3AFD=>lù +3B02=>yú +3B03=>tái +3B04=>chàn +3B05=>màn +3B06=>mǐn +3B07=>huàn +3B08=>wēn +3B09=>nuǎn +3B0A=>huàn +3B0B=>hóu +3B0C=>jìng +3B0D=>bó +3B0E=>xiǎn +3B0F=>lì +3B10=>jìn +3B12=>mǎng +3B13=>piào +3B14=>háo +3B15=>yáng +3B17=>xiàn +3B18=>sù +3B19=>wěi +3B1A=>chè +3B1B=>xī +3B1C=>jìn +3B1D=>céng +3B1E=>hè +3B1F=>fēn +3B20=>shài +3B21=>líng +3B23=>duì +3B24=>qī +3B25=>pù +3B26=>yuè +3B27=>bó +3B29=>huì +3B2A=>dié +3B2B=>yàn +3B2C=>jù +3B2D=>jiào +3B2E=>nàn +3B2F=>liè +3B30=>yú +3B31=>tì +3B32=>tiān +3B33=>wǔ +3B34=>hǒng +3B35=>xiáo +3B36=>hào +3B38=>tiāo +3B39=>zhēng +3B3B=>huāng +3B3C=>fù +3B3F=>tūn +3B41=>réng +3B42=>jiǎo +3B44=>xìn +3B47=>yuàn +3B48=>jué +3B49=>huá +3B4B=>bàng +3B4C=>móu +3B4E=>gāng +3B4F=>wěi +3B51=>mèi +3B52=>sì +3B53=>biàn +3B54=>lú +3B55=>qū +3B58=>gé +3B59=>zhé +3B5A=>lǚ +3B5B=>pài +3B5C=>róng +3B5D=>qiú +3B5E=>liè +3B5F=>gǒng +3B60=>xiǎn +3B61=>xì +3B62=>xīn +3B64=>niǎo +3B68=>xié +3B69=>liè +3B6A=>fū +3B6B=>cuó +3B6C=>zhuó +3B6D=>bā +3B6E=>zuò +3B6F=>zhé +3B70=>zuī +3B71=>hé +3B72=>jí +3B74=>jiān +3B78=>tú +3B79=>xián +3B7A=>yǎn +3B7B=>táng +3B7C=>tà +3B7D=>dǐ +3B7E=>jué +3B7F=>áng +3B80=>hán +3B81=>xiáo +3B82=>jú +3B83=>wēi +3B84=>bǎng +3B85=>zhuī +3B86=>niè +3B87=>tiàn +3B88=>nài +3B8B=>yǒu +3B8C=>mián +3B8F=>nài +3B90=>shěng +3B91=>chā +3B92=>yān +3B93=>gèn +3B94=>chòng +3B95=>ruǎn +3B96=>jiá +3B97=>qín +3B98=>máo +3B99=>è +3B9A=>lì +3B9B=>chí +3B9C=>zāng +3B9D=>hé +3B9E=>jié +3B9F=>niǎn +3BA1=>guàn +3BA2=>hóu +3BA3=>gài +3BA5=>bèn +3BA6=>suǒ +3BA7=>wū +3BA8=>jì +3BA9=>xī +3BAA=>qióng +3BAB=>hé +3BAC=>wēng +3BAD=>xián +3BAE=>jié +3BAF=>hún +3BB0=>pí +3BB1=>shēn +3BB2=>chōu +3BB3=>zhèn +3BB5=>zhān +3BB6=>shuò +3BB7=>jī +3BB8=>sòng +3BB9=>zhǐ +3BBA=>běn +3BBE=>lǎng +3BBF=>bì +3BC0=>xuàn +3BC1=>péi +3BC2=>dài +3BC4=>zhī +3BC5=>pí +3BC6=>chǎn +3BC7=>bì +3BC8=>sù +3BC9=>huò +3BCA=>hén +3BCB=>jiǒng +3BCC=>chuán +3BCD=>jiǎng +3BCE=>nèn +3BCF=>gǔ +3BD0=>fǎng +3BD3=>tà +3BD4=>cuì +3BD5=>xī +3BD6=>dé +3BD7=>xián +3BD8=>kuǎn +3BD9=>zhé +3BDA=>tā +3BDB=>hú +3BDC=>cuì +3BDD=>lù +3BDE=>juàn +3BDF=>lù +3BE0=>qiàn +3BE1=>pào +3BE2=>zhèn +3BE4=>lì +3BE5=>cáo +3BE6=>qí +3BE9=>tì +3BEA=>líng +3BEB=>qú +3BEC=>liǎn +3BED=>lǔ +3BEE=>shú +3BEF=>gòng +3BF0=>zhé +3BF1=>pāo +3BF2=>jìn +3BF3=>qíng +3BF6=>zōng +3BF7=>pú +3BF8=>jǐn +3BF9=>biǎo +3BFA=>jiàn +3BFB=>gǔn +3BFE=>zāo +3BFF=>liè +3C00=>lí +3C01=>luǒ +3C02=>shěn +3C03=>mián +3C04=>jiàn +3C05=>dí +3C06=>bèi +3C08=>liǎn +3C0A=>xián +3C0B=>pín +3C0C=>què +3C0D=>lóng +3C0E=>zuì +3C10=>jué +3C11=>shān +3C12=>xué +3C14=>xiè +3C16=>lǎn +3C17=>qí +3C18=>yí +3C19=>nuó +3C1A=>lí +3C1B=>yuè +3C1D=>yǐ +3C1E=>chī +3C1F=>jì +3C20=>hāng +3C21=>xiè +3C22=>kēng +3C23=>zī +3C24=>hē +3C25=>xì +3C26=>qù +3C27=>hāi +3C28=>xiā +3C29=>hāi +3C2A=>guī +3C2B=>chān +3C2C=>xún +3C2D=>xū +3C2E=>shèn +3C2F=>kòu +3C30=>xiā +3C31=>shà +3C32=>yū +3C33=>yà +3C34=>pǒu +3C35=>zú +3C36=>yǒu +3C37=>zì +3C38=>liǎn +3C39=>xiān +3C3A=>xià +3C3B=>yǐ +3C3C=>shà +3C3D=>yàn +3C3E=>jiào +3C3F=>xī +3C40=>chǐ +3C41=>shì +3C42=>kāng +3C43=>yǐn +3C44=>hēi +3C45=>yì +3C46=>xī +3C47=>sè +3C48=>jìn +3C49=>yè +3C4A=>yōu +3C4B=>què +3C4C=>yé +3C4D=>luán +3C4E=>kūn +3C4F=>zhèng +3C54=>xiē +3C56=>cuì +3C57=>xiū +3C58=>àn +3C59=>xiǔ +3C5A=>cán +3C5B=>chuǎn +3C5C=>zhá +3C5E=>yì +3C5F=>pī +3C60=>kū +3C61=>shēng +3C62=>láng +3C63=>tuǐ +3C64=>xī +3C65=>líng +3C66=>qī +3C67=>wò +3C68=>liàn +3C69=>dú +3C6A=>mèn +3C6B=>làn +3C6C=>wěi +3C6D=>duàn +3C6E=>kuài +3C6F=>ái +3C70=>zǎi +3C71=>huì +3C72=>yì +3C73=>mò +3C74=>zì +3C75=>fèn +3C76=>péng +3C78=>bì +3C79=>lì +3C7A=>lú +3C7B=>luò +3C7C=>hāi +3C7D=>zhěn +3C7E=>gāi +3C7F=>què +3C80=>zhēn +3C81=>kōng +3C82=>chéng +3C83=>jiù +3C84=>jué +3C85=>jì +3C86=>líng +3C88=>sháo +3C89=>què +3C8A=>ruì +3C8B=>chuò +3C8C=>nèng +3C8D=>zhī +3C8E=>lóu +3C8F=>pāo +3C92=>bào +3C93=>róng +3C94=>xiān +3C95=>lèi +3C96=>xiāo +3C97=>fū +3C98=>qú +3C9A=>shā +3C9B=>zhǐ +3C9C=>tán +3C9D=>rǒng +3C9E=>sū +3C9F=>yǐng +3CA0=>máo +3CA1=>nài +3CA2=>biàn +3CA4=>shuāi +3CA5=>táng +3CA6=>hàn +3CA7=>sào +3CA8=>róng +3CAA=>dēng +3CAB=>pú +3CAC=>jiāo +3CAD=>tǎn +3CAF=>rán +3CB0=>níng +3CB1=>liè +3CB2=>dié +3CB3=>dié +3CB4=>zhòng +3CB6=>lǜ +3CB7=>dàn +3CB8=>xī +3CB9=>guǐ +3CBA=>jí +3CBB=>nì +3CBC=>yì +3CBD=>niàn +3CBE=>yǔ +3CBF=>wǎng +3CC0=>guò +3CC1=>zè +3CC2=>yán +3CC3=>cuì +3CC4=>xián +3CC5=>jiǎo +3CC6=>tǒu +3CC7=>fù +3CC8=>pèi +3CCA=>yōu +3CCB=>qiū +3CCC=>yā +3CCD=>bù +3CCE=>biàn +3CCF=>shì +3CD0=>zhá +3CD1=>yì +3CD2=>biàn +3CD4=>duì +3CD5=>lán +3CD6=>yī +3CD7=>chài +3CD8=>chōng +3CD9=>xuàn +3CDA=>xù +3CDB=>yú +3CDC=>xiū +3CE0=>tà +3CE1=>guō +3CE5=>lòng +3CE6=>xiè +3CE7=>chè +3CE8=>jiǎn +3CE9=>tān +3CEA=>pì +3CEB=>zǎn +3CEC=>xuán +3CED=>xián +3CEE=>niào +3CF4=>mì +3CF5=>jì +3CF6=>nǒu +3CF7=>hū +3CF8=>huā +3CF9=>wǎng +3CFA=>yóu +3CFB=>zé +3CFC=>bì +3CFD=>mǐ +3CFE=>qiāng +3CFF=>xiè +3D00=>fàn +3D01=>yì +3D02=>tān +3D03=>lèi +3D04=>yǒng +3D06=>jìn +3D07=>shè +3D08=>yìn +3D09=>jǐ +3D0B=>sù +3D0F=>wǎng +3D10=>miàn +3D11=>sù +3D12=>yì +3D13=>shāi +3D14=>xī +3D15=>jí +3D16=>luò +3D17=>yōu +3D18=>mào +3D19=>zhǎ +3D1A=>suì +3D1B=>zhì +3D1C=>biàn +3D1D=>lí +3D25=>qiào +3D26=>guàn +3D27=>xī +3D28=>zhèn +3D29=>yōng +3D2A=>niè +3D2B=>jùn +3D2C=>xiè +3D2D=>yǎo +3D2E=>xiè +3D2F=>zhī +3D30=>néng +3D32=>sī +3D33=>lǒng +3D34=>chén +3D35=>mì +3D36=>què +3D37=>dān +3D38=>shǎn +3D3C=>sù +3D3D=>xiè +3D3E=>bó +3D3F=>dǐng +3D40=>zú +3D42=>shù +3D43=>shé +3D44=>hàn +3D45=>tān +3D46=>gǎo +3D4A=>nà +3D4B=>mì +3D4C=>xún +3D4D=>mèn +3D4E=>jiàn +3D4F=>cuǐ +3D50=>jué +3D51=>hè +3D52=>fèi +3D53=>shí +3D54=>chě +3D55=>shèn +3D56=>nǜ +3D57=>píng +3D58=>màn +3D5D=>yì +3D5E=>chóu +3D60=>kū +3D61=>báo +3D62=>léi +3D63=>kě +3D64=>shà +3D65=>bì +3D66=>suí +3D67=>gé +3D68=>pì +3D69=>yì +3D6A=>xián +3D6B=>nì +3D6C=>yíng +3D6D=>zhǔ +3D6E=>chún +3D6F=>féng +3D70=>xù +3D71=>piǎo +3D72=>wǔ +3D73=>liáo +3D74=>cáng +3D75=>zòu +3D76=>zuō +3D77=>biàn +3D78=>yào +3D79=>huán +3D7A=>pài +3D7B=>xiū +3D7D=>lěi +3D7E=>qìng +3D7F=>xiào +3D80=>jiāo +3D81=>guó +3D84=>yán +3D85=>xué +3D86=>zhū +3D87=>héng +3D88=>yíng +3D89=>xī +3D8C=>lián +3D8D=>xiǎn +3D8E=>huán +3D8F=>yīn +3D91=>liàn +3D92=>shǎn +3D93=>cáng +3D94=>bèi +3D95=>jiǎn +3D96=>shù +3D97=>fàn +3D98=>diàn +3D9A=>bà +3D9B=>yú +3D9E=>nǎng +3D9F=>lěi +3DA0=>yì +3DA1=>dài +3DA3=>chán +3DA4=>chǎo +3DA5=>gān +3DA6=>jìn +3DA7=>nèn +3DAB=>liǎo +3DAC=>mò +3DAD=>yǒu +3DAF=>liù +3DB0=>hán +3DB2=>yòng +3DB3=>jìn +3DB4=>chǐ +3DB5=>rèn +3DB6=>nóng +3DB9=>hòng +3DBA=>tiàn +3DBC=>āi +3DBD=>guā +3DBE=>biāo +3DBF=>bó +3DC0=>qióng +3DC2=>shù +3DC3=>chuǐ +3DC4=>huǐ +3DC5=>chǎo +3DC6=>fù +3DC7=>huī +3DC8=>è +3DC9=>wèi +3DCA=>fén +3DCB=>tán +3DCD=>lún +3DCE=>hè +3DCF=>yǒng +3DD0=>huǐ +3DD2=>yú +3DD3=>zǒng +3DD4=>yàn +3DD5=>qiú +3DD6=>zhào +3DD7=>jiǒng +3DD8=>tái +3DDF=>tuì +3DE0=>lín +3DE1=>jiǒng +3DE2=>zhǎ +3DE3=>xīng +3DE4=>hù +3DE6=>xù +3DEA=>cuì +3DEB=>qǐng +3DEC=>mò +3DEE=>zāo +3DEF=>bèng +3DF0=>chī +3DF3=>yàn +3DF4=>gé +3DF5=>mò +3DF6=>bèi +3DF7=>juǎn +3DF8=>dié +3DF9=>zhào +3DFB=>wú +3DFC=>yàn +3DFE=>jué +3DFF=>xiān +3E00=>tái +3E01=>hǎn +3E03=>diǎn +3E04=>jì +3E05=>jié +3E07=>zuǎn +3E09=>xiè +3E0A=>lài +3E0B=>fán +3E0C=>huò +3E0D=>xì +3E0E=>niè +3E0F=>mí +3E10=>rán +3E11=>cuàn +3E12=>yín +3E13=>mì +3E15=>jué +3E16=>qū +3E17=>tóng +3E18=>wàn +3E19=>zhē +3E1A=>lǐ +3E1B=>sháo +3E1C=>kòng +3E1D=>xiān +3E1E=>zhé +3E1F=>zhī +3E20=>tiǎo +3E21=>shū +3E22=>bèi +3E23=>yè +3E24=>piàn +3E25=>chàn +3E26=>hù +3E27=>kèn +3E28=>jiū +3E29=>ān +3E2A=>chún +3E2B=>qián +3E2C=>bèi +3E2D=>bā +3E2E=>fén +3E2F=>kē +3E30=>tuó +3E31=>tuó +3E32=>zuó +3E33=>líng +3E35=>guǐ +3E36=>yān +3E37=>shì +3E38=>hǒu +3E39=>liè +3E3A=>shā +3E3B=>sì +3E3D=>bèi +3E3E=>rèn +3E3F=>dú +3E40=>bó +3E41=>liáng +3E42=>qiǎn +3E43=>fèi +3E44=>jì +3E45=>zǒng +3E46=>huī +3E47=>hé +3E48=>lí +3E49=>yuán +3E4A=>yuè +3E4B=>xiū +3E4C=>chǎn +3E4D=>dí +3E4E=>léi +3E4F=>jǐn +3E50=>chóng +3E51=>sì +3E52=>pǔ +3E53=>yǎo +3E54=>jiāng +3E55=>huān +3E56=>huàn +3E57=>tāo +3E58=>rù +3E59=>wěng +3E5A=>yíng +3E5B=>ráo +3E5C=>yín +3E5D=>shì +3E5E=>yín +3E5F=>jué +3E60=>tún +3E61=>xuán +3E62=>jiā +3E63=>zhōng +3E64=>qiè +3E65=>zhù +3E66=>diāo +3E68=>yòu +3E6B=>yí +3E6C=>shǐ +3E6D=>yì +3E6E=>mò +3E71=>què +3E72=>xiāo +3E73=>wú +3E74=>gēng +3E75=>yǐng +3E76=>tíng +3E77=>shǐ +3E78=>ní +3E79=>gēng +3E7A=>tà +3E7B=>wō +3E7C=>jú +3E7D=>chǎn +3E7E=>piǎo +3E7F=>zhuó +3E80=>hū +3E81=>nǎo +3E82=>yán +3E83=>gǒu +3E84=>yǔ +3E85=>hóu +3E87=>sī +3E88=>chī +3E89=>hù +3E8A=>yàng +3E8B=>wēng +3E8C=>xiàn +3E8D=>pín +3E8E=>róng +3E8F=>lóu +3E90=>lǎo +3E91=>shān +3E92=>xiāo +3E93=>zé +3E94=>hài +3E95=>fán +3E96=>hǎn +3E97=>chān +3E98=>zhàn +3E9A=>tǎ +3E9B=>zhù +3E9C=>nóng +3E9D=>hàn +3E9E=>yú +3E9F=>zhuó +3EA0=>yòu +3EA1=>lì +3EA2=>huò +3EA3=>xī +3EA4=>xiān +3EA5=>chán +3EA6=>lián +3EA8=>sī +3EA9=>jiù +3EAA=>pú +3EAB=>qiú +3EAC=>gǒng +3EAD=>zǐ +3EAE=>yú +3EB1=>réng +3EB2=>niǔ +3EB3=>méi +3EB4=>bā +3EB5=>jiú +3EB7=>xù +3EB8=>píng +3EB9=>biàn +3EBA=>mào +3EBF=>yí +3EC0=>yú +3EC2=>píng +3EC3=>qū +3EC4=>bǎo +3EC5=>huì +3EC9=>bù +3ECA=>máng +3ECB=>là +3ECC=>tú +3ECD=>wú +3ECE=>lì +3ECF=>líng +3ED1=>jì +3ED2=>jùn +3ED3=>zōu +3ED4=>duǒ +3ED5=>jué +3ED6=>dài +3ED7=>bèi +3EDD=>là +3EDE=>bīn +3EDF=>suí +3EE0=>tú +3EE1=>xuē +3EE7=>duò +3EEA=>suì +3EEB=>bì +3EEC=>tū +3EED=>sè +3EEE=>càn +3EEF=>tú +3EF0=>miǎn +3EF1=>jīn +3EF2=>lǚ +3EF5=>zhàn +3EF6=>bǐ +3EF7=>jí +3EF8=>zēn +3EF9=>xuān +3EFA=>lì +3EFD=>suì +3EFE=>yōng +3EFF=>shǔ +3F02=>é +3F07=>qióng +3F08=>luó +3F09=>zhèn +3F0A=>tún +3F0B=>gū +3F0C=>yǔ +3F0D=>lěi +3F0E=>bó +3F0F=>něi +3F10=>pián +3F11=>liàn +3F12=>tǎng +3F13=>lián +3F14=>wēn +3F15=>dāng +3F16=>lì +3F17=>tíng +3F18=>wǎ +3F19=>zhòu +3F1A=>gāng +3F1B=>xíng +3F1C=>àng +3F1D=>fàn +3F1E=>pèng +3F1F=>bó +3F20=>tuó +3F21=>shū +3F22=>yí +3F23=>bó +3F24=>qiè +3F25=>tǒu +3F26=>gǒng +3F27=>tóng +3F28=>hán +3F29=>chéng +3F2A=>jié +3F2B=>huàn +3F2C=>xìng +3F2D=>diàn +3F2E=>chāi +3F2F=>dòng +3F30=>pí +3F31=>ruǎn +3F32=>liè +3F33=>shěng +3F34=>ǒu +3F35=>dì +3F36=>yú +3F37=>chuán +3F38=>róng +3F39=>kāng +3F3A=>táng +3F3B=>cóng +3F3C=>piáo +3F3D=>chuǎng +3F3E=>lù +3F3F=>tóng +3F40=>zhèng +3F41=>lì +3F42=>sà +3F43=>pān +3F44=>sī +3F46=>dāng +3F47=>hú +3F48=>yì +3F49=>xiàn +3F4A=>xiè +3F4B=>luó +3F4C=>liù +3F4E=>tán +3F4F=>gàn +3F51=>tán +3F55=>yóu +3F56=>nán +3F58=>gǎng +3F59=>jùn +3F5A=>chì +3F5B=>gōu +3F5C=>wǎn +3F5D=>lì +3F5E=>liú +3F5F=>liè +3F60=>xiá +3F61=>bēi +3F62=>ǎn +3F63=>yù +3F64=>jú +3F65=>róu +3F66=>xún +3F67=>zī +3F68=>cuó +3F69=>càn +3F6A=>zěng +3F6B=>yōng +3F6C=>fù +3F6D=>ruǎn +3F6F=>xí +3F70=>shù +3F71=>jiǎo +3F72=>jiǎo +3F73=>xū +3F74=>zhàng +3F77=>shuì +3F78=>chén +3F79=>fǎn +3F7A=>jí +3F7B=>zhī +3F7D=>gù +3F7E=>wù +3F80=>qiè +3F81=>shù +3F82=>hāi +3F83=>tuó +3F84=>dú +3F85=>zǐ +3F86=>rán +3F87=>mù +3F88=>fù +3F89=>líng +3F8A=>jí +3F8B=>xiū +3F8C=>xuǎn +3F8D=>nái +3F8E=>yā +3F8F=>jiè +3F90=>lì +3F91=>dá +3F92=>rú +3F93=>yuān +3F94=>lǚ +3F95=>shěn +3F96=>lǐ +3F97=>liàng +3F98=>gěng +3F99=>xìn +3F9A=>xiē +3F9B=>qǐn +3F9C=>qiè +3F9D=>chè +3F9E=>yóu +3F9F=>bù +3FA0=>kuáng +3FA1=>què +3FA2=>ài +3FA3=>qīn +3FA4=>qiāng +3FA5=>chù +3FA6=>pèi +3FA7=>kuò +3FA8=>yī +3FA9=>guāi +3FAA=>shěng +3FAB=>piān +3FAD=>zhòu +3FAE=>huáng +3FAF=>huī +3FB0=>hú +3FB1=>bèi +3FB4=>zhā +3FB5=>jì +3FB6=>gǔ +3FB7=>xī +3FB8=>gǎo +3FB9=>chái +3FBA=>mà +3FBB=>zhù +3FBC=>tuǐ +3FBD=>zhuì +3FBE=>xiān +3FBF=>láng +3FC3=>zhì +3FC4=>ài +3FC5=>xiǎn +3FC6=>guō +3FC7=>xí +3FC9=>tuǐ +3FCA=>cǎn +3FCB=>sào +3FCC=>xiān +3FCD=>jiè +3FCE=>fèn +3FCF=>qún +3FD1=>yào +3FD2=>dǎo +3FD3=>jiá +3FD4=>lěi +3FD5=>yán +3FD6=>lú +3FD7=>tuí +3FD8=>yíng +3FD9=>pì +3FDA=>luò +3FDB=>lì +3FDC=>biě +3FDE=>mào +3FDF=>bái +3FE0=>huàng +3FE2=>yào +3FE3=>hē +3FE4=>chǔn +3FE5=>hé +3FE6=>nìng +3FE7=>chóu +3FE8=>lì +3FE9=>tǎng +3FEA=>huán +3FEB=>bì +3FEC=>bā +3FED=>chè +3FEE=>yàng +3FEF=>dá +3FF0=>áo +3FF1=>xué +3FF3=>zī +3FF4=>dā +3FF5=>rǎn +3FF6=>bāng +3FF7=>cuó +3FF8=>wǎn +3FF9=>tà +3FFA=>báo +3FFB=>gān +3FFC=>yán +3FFD=>xī +3FFE=>zhù +3FFF=>yǎ +4000=>fàn +4001=>yòu +4002=>ān +4003=>tuí +4004=>méng +4005=>shè +4006=>jìn +4007=>gǔ +4008=>jì +4009=>qiáo +400A=>jiǎo +400B=>yán +400C=>xì +400D=>kàn +400E=>miǎn +400F=>xuàn +4010=>shān +4011=>wò +4012=>qiān +4013=>huàn +4014=>rèn +4015=>zhèn +4016=>tiān +4017=>jué +4018=>xié +4019=>qì +401A=>áng +401B=>mèi +401C=>gǔ +401E=>tāo +401F=>fán +4020=>jù +4021=>chàn +4022=>shùn +4023=>bì +4024=>mào +4025=>shuò +4026=>gǔ +4027=>hǒng +4028=>huà +4029=>luò +402A=>háng +402B=>jiá +402C=>quán +402D=>gāi +402E=>huāng +402F=>bǔ +4030=>gǔ +4031=>fēng +4032=>mù +4033=>ài +4034=>yǐng +4035=>shùn +4036=>liàng +4037=>jié +4038=>chì +4039=>jié +403A=>chōu +403B=>pìng +403C=>chēn +403D=>yán +403E=>dǔ +403F=>dì +4041=>liàng +4042=>xiàn +4043=>biāo +4044=>xìng +4045=>měng +4046=>yè +4047=>mì +4048=>qì +4049=>qì +404A=>wò +404B=>xiè +404C=>yù +404D=>qià +404E=>chéng +404F=>yǎo +4050=>yīng +4051=>yáng +4052=>jí +4053=>zōng +4054=>xuān +4055=>mín +4056=>lōu +4057=>kǎi +4058=>yǎo +4059=>yǎn +405A=>sǔn +405B=>guì +405C=>huàng +405D=>yíng +405E=>shěng +405F=>chá +4060=>lián +4062=>xuán +4063=>chuán +4064=>chè +4065=>nì +4066=>qù +4067=>miáo +4068=>huò +4069=>yú +406A=>zhǎn +406B=>hú +406C=>céng +406D=>biāo +406E=>qián +406F=>xī +4070=>jiǎng +4071=>kōu +4072=>mái +4073=>mǎng +4074=>zhǎn +4075=>biǎn +4076=>jī +4077=>jué +4078=>náng +4079=>bì +407A=>shì +407B=>shuò +407C=>mò +407D=>liè +407E=>miè +407F=>mò +4080=>xī +4081=>chán +4082=>qú +4083=>jiào +4084=>huò +4085=>xiān +4086=>xù +4087=>niǔ +4088=>tóng +4089=>hóu +408A=>yù +408C=>chōng +408D=>bó +408E=>zuǎn +408F=>diāo +4090=>zhuō +4091=>jī +4092=>qià +4094=>xìng +4095=>huì +4096=>shí +4097=>kū +4099=>duī +409A=>yáo +409B=>yú +409C=>bàng +409D=>jié +409E=>zhè +409F=>jiā +40A0=>shǐ +40A1=>dǐ +40A2=>dǒng +40A3=>cí +40A4=>fù +40A5=>mín +40A6=>zhēn +40A7=>zhěn +40A9=>yàn +40AA=>qiǎo +40AB=>hāng +40AC=>gǒng +40AD=>qiāo +40AE=>lüè +40AF=>guài +40B0=>là +40B1=>ruì +40B2=>fǎ +40B3=>cuǒ +40B4=>yán +40B5=>gōng +40B6=>jié +40B7=>guāi +40B8=>guó +40B9=>suǒ +40BA=>wǒ +40BB=>zhèng +40BC=>niè +40BD=>diào +40BE=>lǎi +40BF=>tà +40C0=>cuì +40C1=>yā +40C2=>gǔn +40C5=>dī +40C7=>mián +40C8=>jiē +40C9=>mín +40CA=>jǔ +40CB=>yú +40CC=>zhēn +40CD=>zhào +40CE=>zhà +40CF=>xīng +40D1=>bān +40D2=>hé +40D3=>gòu +40D4=>hóng +40D5=>láo +40D6=>wù +40D7=>bō +40D8=>kēng +40D9=>lù +40DA=>cù +40DB=>lián +40DC=>yī +40DD=>qiào +40DE=>shú +40E0=>xuàn +40E1=>jīn +40E2=>qīn +40E3=>huǐ +40E4=>sù +40E5=>chuáng +40E6=>dūn +40E7=>lóng +40E9=>náo +40EA=>tán +40EB=>dǎn +40EC=>wěi +40ED=>gǎn +40EE=>dá +40EF=>lì +40F0=>cā +40F1=>xiàn +40F2=>pán +40F3=>là +40F4=>zhū +40F5=>niǎo +40F6=>huái +40F7=>yíng +40F8=>xiàn +40F9=>làn +40FA=>mó +40FB=>bà +40FD=>guǐ +40FE=>bǐ +40FF=>fū +4100=>huò +4101=>yì +4102=>liù +4104=>yīn +4105=>juàn +4106=>huó +4107=>chéng +4108=>dòu +4109=>é +410B=>yǎn +410C=>zhuì +410D=>zhà +410E=>qǐ +410F=>yú +4110=>quàn +4111=>huó +4112=>niè +4113=>huáng +4114=>jǔ +4115=>shè +4118=>péng +4119=>míng +411A=>cáo +411B=>lóu +411C=>lí +411D=>chuāng +411F=>cuī +4120=>shàn +4121=>dān +4122=>qí +4124=>lài +4125=>líng +4126=>liǎo +4127=>réng +4128=>yú +4129=>yì +412A=>diǎo +412B=>qǐ +412C=>yí +412D=>nián +412E=>fū +412F=>jiǎn +4130=>yá +4131=>fāng +4132=>ruì +4133=>xiān +4136=>bì +4137=>shí +4138=>pò +4139=>nián +413A=>zhì +413B=>táo +413C=>tiǎn +413D=>tiǎn +413E=>rù +413F=>yì +4140=>liè +4141=>àn +4142=>hé +4143=>qióng +4144=>lì +4145=>guī +4146=>zì +4147=>sù +4148=>yuàn +4149=>yà +414A=>chá +414B=>wǎn +414C=>juān +414D=>tǐng +414E=>yǒu +414F=>huì +4150=>jiǎn +4151=>ruí +4152=>máng +4153=>jǔ +4154=>zī +4155=>jū +4156=>ān +4157=>suì +4158=>lái +4159=>hùn +415A=>quǎn +415B=>chāng +415C=>duò +415D=>kōng +415E=>nè +415F=>cǎn +4160=>tí +4161=>xǔ +4162=>jiù +4163=>huáng +4164=>qì +4165=>jié +4166=>máo +4167=>yān +4169=>zhǐ +416A=>tuí +416C=>ài +416D=>páng +416E=>càng +416F=>táng +4170=>ěn +4171=>hùn +4172=>qí +4173=>chú +4174=>suǒ +4175=>zhuó +4176=>nòu +4177=>tú +4178=>shēn +4179=>lǒu +417A=>biāo +417B=>lí +417C=>mán +417D=>xīn +417E=>cén +417F=>huáng +4180=>měi +4181=>gāo +4182=>lián +4183=>dào +4184=>zhǎn +4185=>zī +4188=>zhì +4189=>bà +418A=>cuì +418B=>qiū +418D=>lóng +418E=>xiān +418F=>fèi +4190=>guó +4191=>chéng +4192=>jiù +4193=>è +4194=>chōng +4195=>yuè +4196=>hóng +4197=>yǎo +4198=>yā +4199=>yáo +419A=>tóng +419B=>zhà +419C=>yòu +419D=>xuè +419E=>yǎo +419F=>kè +41A0=>huàn +41A1=>láng +41A2=>yuè +41A3=>chén +41A6=>shèn +41A8=>níng +41A9=>míng +41AA=>hōng +41AB=>chuāng +41AC=>yǔn +41AD=>xuān +41AE=>jìn +41AF=>zhuó +41B0=>yū +41B1=>tān +41B2=>kāng +41B3=>qióng +41B5=>chéng +41B6=>jiū +41B7=>xuè +41B8=>zhēng +41B9=>chōng +41BA=>pān +41BB=>qiào +41BD=>qú +41BE=>lán +41BF=>yì +41C0=>róng +41C1=>sī +41C2=>qiān +41C3=>sì +41C5=>fá +41C7=>méng +41C8=>huà +41CB=>hài +41CC=>qiào +41CD=>chù +41CE=>què +41CF=>duì +41D0=>lì +41D1=>bà +41D2=>jiè +41D3=>xū +41D4=>luò +41D6=>yǔn +41D7=>zhōng +41D8=>hù +41D9=>yǐn +41DB=>zhǐ +41DC=>qiǎn +41DE=>gān +41DF=>jiàn +41E0=>zhù +41E1=>zhù +41E2=>kǔ +41E3=>niè +41E4=>ruì +41E5=>zé +41E6=>ǎng +41E7=>zhì +41E8=>gòng +41E9=>yì +41EA=>chī +41EB=>jī +41EC=>zhū +41ED=>lǎo +41EE=>rèn +41EF=>róng +41F0=>zhēng +41F1=>nà +41F2=>cè +41F5=>yí +41F6=>jué +41F7=>bié +41F8=>chéng +41F9=>jùn +41FA=>dòu +41FB=>wěi +41FC=>yì +41FD=>zhé +41FE=>yán +4200=>sān +4201=>lún +4202=>píng +4203=>zhǎo +4204=>hán +4205=>yù +4206=>dài +4207=>zhào +4208=>féi +4209=>shà +420A=>líng +420B=>tà +420C=>qū +420D=>máng +420E=>yè +420F=>báo +4210=>guì +4211=>guǎ +4212=>nǎn +4213=>gé +4215=>shí +4216=>kē +4217=>suǒ +4218=>cí +4219=>zhòu +421A=>tái +421B=>kuài +421C=>qìn +421D=>xū +421E=>dǔ +421F=>cè +4220=>huǎn +4221=>cōng +4222=>sǎi +4223=>zhèng +4224=>qián +4225=>jīn +4226=>zōng +4227=>wěi +422A=>xì +422B=>nà +422C=>pú +422D=>sōu +422E=>jù +422F=>zhēn +4230=>shāo +4231=>tāo +4232=>bān +4233=>tà +4234=>qiàn +4235=>wēng +4236=>róng +4237=>luò +4238=>hú +4239=>sǒu +423A=>zhōng +423B=>pú +423C=>miè +423D=>jīn +423E=>shāo +423F=>mì +4240=>shù +4241=>líng +4242=>lěi +4243=>jiǎng +4244=>léng +4245=>zhì +4246=>diǎo +4248=>sǎn +4249=>gū +424A=>fàn +424B=>mèi +424C=>suì +424D=>jiǎn +424E=>táng +424F=>xiè +4250=>kū +4251=>wú +4252=>fán +4253=>luò +4254=>cān +4255=>céng +4256=>líng +4257=>yī +4258=>cóng +4259=>yún +425A=>méng +425B=>yù +425C=>zhì +425D=>yǐ +425E=>dǎn +425F=>huò +4260=>wéi +4261=>tán +4262=>sè +4263=>xiè +4264=>sǒu +4265=>sǒng +4266=>qiān +4267=>liú +4268=>yì +426A=>lèi +426B=>lí +426C=>fèi +426D=>liè +426E=>lìn +426F=>xiàn +4270=>xiào +4271=>ōu +4272=>mí +4273=>xiān +4274=>ráng +4275=>zhuàn +4276=>shuāng +4277=>yán +4278=>biàn +4279=>líng +427A=>hóng +427B=>qí +427C=>liào +427D=>bǎn +427E=>bì +427F=>hú +4280=>hú +4282=>cè +4283=>pèi +4284=>qióng +4285=>míng +4286=>jiù +4287=>bù +4288=>méi +4289=>sǎn +428A=>wèi +428D=>lí +428E=>quǎn +4290=>hún +4291=>xiǎng +4293=>shì +4294=>yíng +4296=>nǎn +4297=>huáng +4298=>jiù +4299=>yān +429B=>sà +429C=>tuán +429D=>xiè +429E=>zhé +429F=>mén +42A0=>xì +42A1=>mán +42A3=>huáng +42A4=>tán +42A5=>xiào +42A6=>yè +42A7=>bì +42A8=>luó +42A9=>fán +42AA=>lì +42AB=>cuǐ +42AC=>chuā +42AD=>dào +42AE=>dí +42AF=>kuàng +42B0=>chú +42B1=>xiān +42B2=>chàn +42B3=>mí +42B4=>qiàn +42B5=>qiú +42B6=>zhèn +42BA=>hù +42BB=>gān +42BC=>chǐ +42BD=>guài +42BE=>mù +42BF=>bó +42C0=>huà +42C1=>gěng +42C2=>yáo +42C3=>mào +42C4=>wǎng +42C8=>rú +42C9=>xué +42CA=>zhēng +42CB=>mín +42CC=>jiǎng +42CE=>zhàn +42CF=>zuó +42D0=>yuè +42D1=>liè +42D3=>zhòu +42D4=>bì +42D5=>rèn +42D6=>yù +42D8=>chuò +42D9=>ěr +42DA=>yì +42DB=>mǐ +42DC=>qìng +42DE=>wǎng +42DF=>jì +42E0=>bǔ +42E2=>biē +42E3=>fán +42E4=>yuè +42E5=>lí +42E6=>fán +42E7=>qú +42E8=>fǔ +42E9=>ér +42EA=>ē +42EB=>zhēng +42EC=>tiān +42ED=>yù +42EE=>jìn +42EF=>qǐ +42F0=>jú +42F1=>lái +42F2=>chě +42F3=>běi +42F4=>niù +42F5=>yì +42F6=>xǔ +42F7=>móu +42F8=>xún +42F9=>fú +42FB=>nín +42FC=>tīng +42FD=>běng +42FE=>zhǎ +42FF=>wēi +4300=>kē +4301=>yāo +4302=>òu +4303=>xiāo +4304=>gěng +4305=>táng +4306=>guì +4307=>huì +4308=>tā +430A=>yáo +430B=>dā +430C=>qì +430D=>jǐn +430E=>lüè +430F=>mì +4310=>mì +4311=>jiān +4312=>lù +4313=>fán +4314=>ōu +4315=>mí +4316=>jié +4317=>fǔ +4318=>biè +4319=>huàng +431A=>sū +431B=>yáo +431C=>niè +431D=>jīn +431E=>liǎn +431F=>bó +4320=>jiān +4321=>tǐ +4322=>líng +4323=>zuǎn +4324=>shī +4325=>yǐn +4326=>dào +4327=>chóu +4328=>cā +4329=>miè +432A=>yǎn +432B=>lǎn +432C=>chóng +432D=>jiāo +432E=>shuāng +432F=>quān +4330=>niè +4331=>luò +4333=>shī +4334=>luò +4335=>zhú +4337=>chōu +4338=>juàn +4339=>jiǒng +433A=>ěr +433B=>yì +433C=>ruì +433D=>cǎi +433E=>rén +433F=>fú +4340=>lán +4341=>suì +4342=>yú +4343=>yóu +4344=>diǎn +4345=>líng +4346=>zhù +4347=>tà +4348=>píng +4349=>zhǎi +434A=>jiāo +434B=>chuí +434C=>bù +434D=>kòu +434E=>cùn +4350=>hǎn +4351=>hǎn +4352=>mǒu +4353=>hù +4354=>gōng +4355=>dī +4356=>fú +4357=>xuàn +4358=>mí +4359=>méi +435A=>làng +435B=>gù +435C=>zhào +435D=>tà +435E=>yù +435F=>zòng +4360=>lí +4361=>lù +4362=>wú +4363=>léi +4364=>jǐ +4365=>lì +4366=>lí +4368=>pō +4369=>yǎng +436A=>wà +436B=>tuó +436C=>pēng +436E=>zhào +436F=>guǐ +4371=>xú +4372=>nái +4373=>què +4374=>wěi +4375=>zhēng +4376=>dōng +4377=>wěi +4378=>bó +437A=>huàn +437B=>xuàn +437C=>zān +437D=>lì +437E=>yǎn +437F=>huáng +4380=>xuè +4381=>hú +4382=>bǎo +4383=>rǎn +4384=>xiāo +4385=>pò +4386=>liào +4387=>zhōu +4388=>yì +4389=>xù +438A=>luò +438B=>kào +438C=>chù +438E=>nà +438F=>hán +4390=>chǎo +4391=>lù +4392=>zhǎn +4393=>tà +4394=>fū +4395=>hōng +4396=>zēng +4397=>qiáo +4398=>sù +4399=>pīn +439A=>guàn +439C=>hūn +439D=>chú +439F=>ér +43A0=>ér +43A1=>ruǎn +43A2=>qǐ +43A3=>sì +43A4=>jú +43A6=>yǎn +43A7=>bàng +43A8=>yè +43A9=>zī +43AA=>nè +43AB=>chuàng +43AC=>bà +43AD=>cāo +43AE=>tì +43AF=>hàn +43B0=>zuó +43B1=>bà +43B2=>zhé +43B3=>wà +43B4=>gēng +43B5=>bì +43B6=>èr +43B7=>zhù +43B8=>wù +43B9=>wén +43BA=>zhì +43BB=>zhòu +43BC=>lù +43BD=>wén +43BE=>gǔn +43BF=>qiú +43C0=>là +43C1=>zǎi +43C2=>sǒu +43C3=>mián +43C4=>dǐ +43C5=>qì +43C6=>cáo +43C7=>piào +43C8=>lián +43C9=>shī +43CA=>lóng +43CB=>sù +43CC=>qì +43CD=>yuàn +43CE=>féng +43CF=>xū +43D0=>jué +43D1=>dì +43D2=>piàn +43D3=>guǎn +43D4=>niǔ +43D5=>rèn +43D6=>zhèn +43D7=>gài +43D8=>pì +43D9=>tǎn +43DA=>chǎo +43DB=>chǔn +43DC=>hē +43DD=>zhuān +43DE=>mò +43DF=>bié +43E0=>qì +43E1=>shì +43E2=>bǐ +43E3=>jué +43E4=>sì +43E6=>guā +43E7=>nà +43E8=>huǐ +43E9=>xī +43EA=>èr +43EB=>xiū +43EC=>móu +43EE=>xí +43EF=>zhì +43F0=>rùn +43F1=>jú +43F2=>dié +43F3=>zhè +43F4=>shào +43F5=>měng +43F6=>bì +43F7=>hàn +43F8=>yú +43F9=>xiàn +43FA=>pāng +43FB=>néng +43FC=>cán +43FD=>bù +43FF=>qǐ +4400=>jì +4401=>zhuó +4402=>lù +4403=>jùn +4404=>xiàn +4405=>xī +4406=>cǎi +4407=>wěn +4408=>zhí +4409=>zì +440A=>kūn +440B=>cōng +440C=>tiǎn +440D=>chù +440E=>dī +440F=>chǔn +4410=>qiū +4411=>zhé +4412=>zhā +4413=>róu +4414=>bǐn +4415=>jí +4416=>xī +4417=>zhū +4418=>jué +4419=>gé +441A=>jī +441B=>dā +441C=>chēn +441D=>suò +441E=>ruò +441F=>xiǎng +4420=>huǎng +4421=>qí +4422=>zhù +4423=>sǔn +4424=>chāi +4425=>wěng +4426=>kē +4427=>kào +4428=>gǔ +4429=>gāi +442A=>fàn +442B=>cōng +442C=>cáo +442D=>zhì +442E=>chǎn +442F=>léi +4430=>xiū +4431=>zhài +4432=>zhé +4433=>yú +4434=>guì +4435=>gōng +4436=>zān +4437=>dān +4438=>huò +4439=>sōu +443A=>tàn +443B=>gū +443C=>xì +443D=>mán +443E=>duó +443F=>ào +4440=>pì +4441=>wù +4442=>ǎi +4443=>méng +4444=>pì +4445=>méng +4446=>yǎng +4447=>zhì +4448=>bó +4449=>yíng +444A=>wéi +444B=>rǎng +444C=>lán +444D=>yān +444E=>chǎn +444F=>quán +4450=>zhěn +4451=>pú +4453=>tái +4454=>fèi +4455=>shǔ +4457=>dàng +4458=>cuó +4459=>tān +445A=>tián +445B=>chǐ +445C=>tà +445D=>jiǎ +445E=>shùn +445F=>huáng +4460=>liǎo +4463=>chēn +4464=>jìn +4465=>è +4466=>gōu +4467=>fú +4468=>duò +446A=>è +446B=>bēng +446C=>tāo +446D=>dì +446F=>dì +4470=>bù +4471=>wǎn +4472=>zhào +4473=>lún +4474=>qí +4475=>mù +4476=>qiàn +4478=>zōng +4479=>sōu +447B=>yóu +447C=>zhōu +447D=>tà +447F=>sù +4480=>bù +4481=>xí +4482=>jiǎng +4483=>cào +4484=>fù +4485=>téng +4486=>chè +4487=>fù +4488=>fèi +4489=>wǔ +448A=>xī +448B=>yǎng +448C=>mìng +448D=>pǎng +448E=>mǎng +448F=>sēng +4490=>méng +4491=>cǎo +4492=>tiáo +4493=>kǎi +4494=>bài +4495=>xiǎo +4496=>xìn +4497=>qì +449A=>shǎo +449B=>huàn +449C=>niú +449D=>xiáo +449E=>chén +449F=>dān +44A0=>fēng +44A1=>yǐn +44A2=>áng +44A3=>rǎn +44A4=>rì +44A5=>mán +44A6=>fàn +44A7=>qū +44A8=>shǐ +44A9=>hé +44AA=>biàn +44AB=>dài +44AC=>mò +44AD=>děng +44B0=>kuāng +44B2=>chà +44B3=>duǒ +44B4=>yǒu +44B5=>hào +44B7=>guā +44B8=>xuè +44B9=>lèi +44BA=>jǐn +44BB=>qǐ +44BC=>qū +44BD=>wǎng +44BE=>yī +44BF=>liáo +44C2=>yán +44C3=>yì +44C4=>yín +44C5=>qí +44C6=>zhé +44C7=>xì +44C8=>yì +44C9=>yé +44CA=>wú +44CB=>zhī +44CC=>zhì +44CD=>hǎn +44CE=>chuò +44CF=>fū +44D0=>chún +44D1=>píng +44D2=>kuǎi +44D3=>chóu +44D5=>tuǒ +44D6=>qióng +44D7=>cōng +44D8=>gāo +44D9=>kuā +44DA=>qū +44DB=>qū +44DC=>zhī +44DD=>mèng +44DE=>lì +44DF=>zhōu +44E0=>tà +44E1=>zhī +44E2=>gù +44E3=>liǎng +44E4=>hū +44E5=>là +44E6=>diǎn +44E7=>cì +44E8=>yīng +44EB=>qí +44ED=>chà +44EE=>mào +44EF=>dú +44F0=>yīn +44F1=>chái +44F2=>ruì +44F3=>hěn +44F4=>ruǎn +44F5=>fū +44F6=>lài +44F7=>xìng +44F8=>jiān +44F9=>yì +44FA=>měi +44FC=>máng +44FD=>jì +44FE=>suō +44FF=>hàn +4501=>lì +4502=>zǐ +4503=>zǔ +4504=>yáo +4505=>gē +4506=>lí +4507=>qǐ +4508=>gòng +4509=>lì +450A=>bīng +450B=>suō +450E=>sù +450F=>chòu +4510=>jiān +4511=>xié +4512=>bèi +4513=>xǔ +4514=>jìng +4515=>pú +4516=>líng +4517=>xiáng +4518=>zuò +4519=>diào +451A=>chún +451B=>qǐng +451C=>nán +451D=>zhāi +451E=>lǜ +451F=>yí +4520=>shǎo +4521=>yú +4522=>huá +4523=>lí +4524=>pā +4527=>lí +452A=>shuǎng +452C=>yì +452D=>nìng +452E=>sī +452F=>kù +4530=>fù +4531=>yī +4532=>dēng +4533=>rán +4534=>cè +4536=>tí +4537=>qín +4538=>biǎo +4539=>suì +453A=>wéi +453B=>dūn +453C=>sè +453D=>ài +453E=>qì +453F=>zǔn +4540=>kuǎn +4541=>fěi +4543=>yìn +4545=>sǎo +4546=>dòu +4547=>huì +4548=>xiè +4549=>zé +454A=>tán +454B=>táng +454C=>zhì +454D=>yì +454E=>fú +454F=>é +4551=>jùn +4552=>jiā +4553=>chá +4554=>xián +4555=>màn +4557=>bì +4558=>líng +4559=>jié +455A=>kuì +455B=>jiá +455D=>chēng +455E=>làng +455F=>xīng +4560=>fèi +4561=>lǘ +4562=>zhǎ +4563=>hé +4564=>jī +4565=>nǐ +4566=>yíng +4567=>xiào +4568=>téng +4569=>lǎo +456A=>zé +456B=>kuí +456D=>qián +456E=>jú +456F=>piáo +4570=>fán +4571=>tóu +4572=>lǐn +4573=>mí +4574=>zhuó +4575=>xié +4576=>hù +4577=>mí +4578=>jiē +4579=>zá +457A=>cóng +457B=>lì +457C=>rán +457D=>zhú +457E=>yín +457F=>hàn +4581=>yì +4582=>luán +4583=>yuè +4584=>rán +4585=>líng +4586=>niàng +4587=>yù +4588=>nüè +458A=>yì +458B=>nüè +458C=>yì +458D=>qián +458E=>xiá +458F=>chǔ +4590=>yín +4591=>mì +4592=>xī +4593=>nà +4594=>kǎn +4595=>zǔ +4596=>xiá +4597=>yán +4598=>tú +4599=>tī +459A=>wū +459B=>suǒ +459C=>yín +459D=>chóng +459E=>zhǒu +459F=>mǎng +45A0=>yuán +45A1=>nǜ +45A2=>miáo +45A3=>zǎo +45A4=>wǎn +45A5=>lí +45A6=>qū +45A7=>nà +45A8=>shí +45A9=>bì +45AA=>zī +45AB=>bàng +45AD=>juàn +45AE=>xiǎng +45AF=>kuí +45B0=>pài +45B1=>kuāng +45B2=>xún +45B3=>zhà +45B4=>yáo +45B5=>kūn +45B6=>huī +45B7=>xī +45B8=>é +45B9=>yáng +45BA=>tiáo +45BB=>yóu +45BC=>jué +45BD=>lí +45BF=>lí +45C0=>chēng +45C1=>jì +45C2=>hǔ +45C3=>zhàn +45C4=>fǔ +45C5=>cháng +45C6=>guǎn +45C7=>jú +45C8=>méng +45C9=>chāng +45CA=>tàn +45CB=>móu +45CC=>xīng +45CD=>lǐ +45CE=>yān +45CF=>sōu +45D0=>shī +45D1=>yì +45D2=>bìng +45D3=>cōng +45D4=>hóu +45D5=>wǎn +45D6=>dì +45D7=>jī +45D8=>gé +45D9=>hán +45DA=>bó +45DB=>xiū +45DC=>liú +45DD=>cán +45DE=>cán +45DF=>yì +45E0=>xuán +45E1=>yán +45E2=>zǎo +45E3=>hàn +45E4=>yóng +45E5=>zōng +45E7=>kāng +45E8=>yú +45E9=>qī +45EA=>zhè +45EB=>má +45EE=>shuǎng +45EF=>jìn +45F0=>guàn +45F1=>pú +45F2=>lìn +45F4=>tíng +45F5=>jiāng +45F6=>là +45F7=>yì +45F8=>yōng +45F9=>cì +45FA=>yǎn +45FB=>jié +45FC=>xūn +45FD=>wèi +45FE=>xiǎn +45FF=>níng +4600=>fù +4601=>gé +4603=>mò +4604=>zhù +4605=>nái +4606=>xiǎn +4607=>wén +4608=>lì +4609=>cán +460A=>miè +460B=>jiān +460C=>nì +460D=>chài +460E=>wān +460F=>xù +4610=>nǜ +4611=>mài +4612=>zuī +4613=>kàn +4614=>kā +4615=>háng +4618=>yù +4619=>wèi +461A=>zhú +461D=>yì +461F=>diāo +4620=>fú +4621=>bǐ +4622=>zhǔ +4623=>zǐ +4624=>shù +4625=>xiá +4626=>ní +4628=>jiǎo +4629=>xún +462A=>chōng +462B=>nòu +462C=>róng +462D=>zhì +462E=>sāng +4630=>shān +4631=>yù +4633=>jīn +4635=>lù +4636=>hān +4637=>biē +4638=>yì +4639=>zuì +463A=>zhàn +463B=>yù +463C=>wǎn +463D=>ní +463E=>guǎn +463F=>jué +4640=>běng +4641=>cán +4643=>duò +4644=>qì +4645=>yāo +4646=>kuì +4647=>ruán +4648=>hóu +4649=>xún +464A=>xiè +464C=>kuì +464E=>xié +464F=>bó +4650=>kè +4651=>cuī +4652=>xù +4653=>bǎi +4654=>ōu +4655=>zǒng +4657=>tì +4658=>chǔ +4659=>chí +465A=>niǎo +465B=>guàn +465C=>féng +465D=>xiè +465E=>dēng +465F=>wéi +4660=>jué +4661=>kuì +4662=>zèng +4663=>sà +4664=>duǒ +4665=>líng +4666=>méng +4668=>guǒ +4669=>méng +466A=>lóng +466C=>yìng +466E=>guàn +466F=>cù +4670=>lí +4671=>dú +4673=>biāo +4675=>xī +4677=>dé +4678=>dé +4679=>xiàn +467A=>lián +467C=>shào +467D=>xié +467E=>shī +467F=>wèi +4682=>hè +4683=>yóu +4684=>lù +4685=>lài +4686=>yǐng +4687=>shěng +4688=>juàn +4689=>qì +468A=>jiǎn +468B=>yùn +468D=>qì +468F=>lìn +4690=>jí +4691=>mái +4692=>chuáng +4693=>niǎn +4694=>bīn +4695=>lì +4696=>líng +4697=>gāng +4698=>chéng +4699=>xuān +469A=>xiǎn +469B=>hú +469C=>bī +469D=>zú +469E=>dǎi +469F=>dǎi +46A0=>hùn +46A1=>sāi +46A2=>chè +46A3=>tí +46A5=>nuò +46A6=>zhì +46A7=>liú +46A8=>fèi +46A9=>jiǎo +46AA=>guān +46AB=>xí +46AC=>lín +46AD=>xuān +46AE=>réng +46AF=>tǎo +46B0=>pǐ +46B1=>xìn +46B2=>shàn +46B3=>zhì +46B4=>wà +46B5=>tǒu +46B6=>tiān +46B7=>yī +46B8=>xiè +46B9=>pǐ +46BA=>yáo +46BB=>yáo +46BC=>nǜ +46BD=>hào +46BE=>nín +46BF=>yìn +46C0=>fǎn +46C1=>nán +46C2=>yāo +46C3=>wàn +46C4=>yuǎn +46C5=>xiá +46C6=>zhòu +46C7=>yuǎn +46C8=>shì +46C9=>miàn +46CA=>xī +46CB=>jì +46CC=>táo +46CD=>fèi +46CE=>xuè +46CF=>ní +46D0=>cí +46D1=>mì +46D2=>biàn +46D4=>ná +46D5=>yù +46D6=>è +46D7=>zhǐ +46D8=>rén +46D9=>xù +46DA=>lüè +46DB=>huì +46DC=>xùn +46DD=>náo +46DE=>hàn +46DF=>jiá +46E0=>dòu +46E1=>huà +46E2=>tū +46E3=>pīng +46E4=>cù +46E5=>xī +46E6=>sòng +46E7=>mí +46E8=>xìn +46E9=>wù +46EA=>qióng +46EB=>zhāng +46EC=>táo +46ED=>xìng +46EE=>jiù +46EF=>jù +46F0=>hùn +46F1=>tí +46F2=>mán +46F3=>yàn +46F4=>jī +46F5=>shòu +46F6=>lěi +46F7=>wǎn +46F8=>chè +46F9=>càn +46FA=>jiè +46FB=>yòu +46FC=>huǐ +46FD=>zhǎ +46FE=>sù +46FF=>gé +4700=>nǎo +4701=>xì +4703=>duī +4704=>chí +4705=>wéi +4706=>zhé +4707=>gǔn +4708=>chāo +4709=>chī +470A=>zāo +470B=>huì +470C=>luán +470D=>liáo +470E=>láo +470F=>tuō +4710=>huī +4711=>wù +4712=>ào +4713=>shè +4714=>suí +4715=>mài +4716=>tàn +4717=>xìn +4718=>jǐng +4719=>án +471A=>tà +471B=>chán +471C=>wèi +471D=>tuǎn +471E=>jì +471F=>chén +4720=>chè +4721=>yù +4722=>xiǎn +4723=>xīn +4727=>nǎo +4729=>yàn +472A=>qiú +472B=>jiāng +472C=>sǒng +472D=>jùn +472E=>liáo +472F=>jú +4731=>mǎn +4732=>liè +4734=>chù +4735=>chǐ +4736=>xiáng +4737=>qīn +4738=>měi +4739=>shù +473A=>chǎi +473B=>chǐ +473C=>gú +473D=>yú +473E=>yīn +4740=>liú +4741=>láo +4742=>shù +4743=>zhé +4744=>shuāng +4745=>huī +4748=>è +474A=>shà +474B=>zòng +474C=>jué +474D=>jùn +474E=>tuān +474F=>lóu +4750=>wéi +4751=>chōng +4752=>zhù +4753=>liè +4755=>zhé +4756=>zhǎo +4758=>yì +4759=>chū +475A=>ní +475B=>bō +475C=>suān +475D=>yǐ +475E=>hào +475F=>yà +4760=>huán +4761=>màn +4762=>màn +4763=>qú +4764=>lǎo +4765=>háo +4766=>zhōng +4767=>mín +4768=>xián +4769=>zhèn +476A=>shǔ +476B=>zuó +476C=>zhù +476D=>gòu +476E=>xuàn +476F=>yì +4770=>zhì +4771=>xié +4772=>jìn +4773=>cán +4775=>bù +4776=>liáng +4777=>zhī +4778=>jì +4779=>wǎn +477A=>guàn +477B=>jū +477C=>jìng +477D=>ài +477E=>fù +477F=>guì +4780=>hòu +4781=>yàn +4782=>ruǎn +4783=>zhì +4784=>biào +4785=>yí +4786=>suǒ +4787=>dié +4788=>guì +4789=>shèng +478A=>xùn +478B=>chèn +478C=>shé +478D=>qíng +4790=>chǔn +4791=>hóng +4792=>dòng +4793=>chēng +4794=>wěi +4795=>rú +4796=>shǔ +4797=>cāi +4798=>jí +4799=>zá +479A=>qí +479B=>yān +479C=>fù +479D=>yù +479E=>fú +479F=>pò +47A0=>zhī +47A1=>tǎn +47A2=>zuó +47A3=>chě +47A4=>qú +47A5=>yòu +47A6=>hé +47A7=>hòu +47A8=>guǐ +47A9=>è +47AA=>jiàng +47AB=>yǔn +47AC=>tòu +47AD=>cūn +47AE=>tū +47AF=>fù +47B0=>zuó +47B1=>hú +47B3=>bó +47B4=>zhāo +47B5=>juě +47B6=>tāng +47B7=>jué +47B8=>fù +47B9=>huáng +47BA=>chūn +47BB=>yǒng +47BC=>chuǐ +47BD=>suǒ +47BE=>chí +47BF=>qiān +47C0=>cāi +47C1=>xiáo +47C2=>mán +47C3=>cān +47C4=>qì +47C5=>jiàn +47C6=>bì +47C7=>jī +47C8=>zhí +47C9=>zhú +47CA=>qú +47CB=>zhǎn +47CC=>jí +47CD=>biān +47CF=>lì +47D0=>lì +47D1=>yuè +47D2=>quán +47D3=>chēng +47D4=>fù +47D5=>chà +47D6=>tàng +47D7=>shì +47D8=>hàng +47D9=>qiè +47DA=>qí +47DB=>bó +47DC=>nà +47DD=>tòu +47DE=>chú +47DF=>cù +47E0=>yuè +47E1=>zhī +47E2=>chén +47E3=>chù +47E4=>bì +47E5=>méng +47E6=>bá +47E7=>tián +47E8=>mín +47E9=>liě +47EA=>fěng +47EB=>chēng +47EC=>qiù +47ED=>tiáo +47EE=>fú +47EF=>kuò +47F0=>jiǎn +47F4=>zhèn +47F5=>qiú +47F6=>zuò +47F7=>chì +47F8=>kuí +47F9=>liè +47FA=>bèi +47FB=>dù +47FC=>wǔ +47FE=>zhuó +47FF=>lù +4800=>tāng +4802=>chú +4803=>liǎng +4804=>tiǎn +4805=>kǔn +4806=>cháng +4807=>jué +4808=>tú +4809=>huàn +480A=>fèi +480B=>bì +480D=>xiā +480E=>wò +480F=>jì +4810=>qù +4811=>kuǐ +4812=>hú +4813=>qiū +4814=>suì +4815=>cāi +4817=>qiù +4818=>pì +4819=>páng +481A=>wà +481B=>yáo +481C=>róng +481D=>xūn +481E=>cù +481F=>dié +4820=>chì +4821=>cuó +4822=>mèng +4823=>xuǎn +4824=>duǒ +4825=>bié +4826=>zhè +4827=>chú +4828=>chàn +4829=>guì +482A=>duàn +482B=>zòu +482C=>dèng +482D=>lái +482E=>téng +482F=>yuè +4830=>quán +4831=>zhú +4832=>líng +4833=>chēn +4834=>zhěn +4835=>fù +4836=>shè +4837=>tiǎo +4838=>kuā +4839=>ái +483B=>qióng +483C=>shù +483D=>hái +483E=>shǎn +483F=>wài +4840=>zhǎn +4841=>lǒng +4842=>jiū +4843=>lì +4845=>chūn +4846=>róng +4847=>yuè +4848=>jué +4849=>kǎng +484A=>fǎn +484B=>qí +484C=>hóng +484D=>fú +484E=>lú +484F=>hóng +4850=>tuó +4851=>mín +4852=>tián +4853=>juàn +4854=>qǐ +4855=>zhěng +4856=>qìng +4857=>gǒng +4858=>tián +4859=>láng +485A=>mào +485B=>yìn +485C=>lù +485D=>yuān +485E=>jú +485F=>pì +4861=>xié +4862=>biàn +4863=>hūn +4864=>zhū +4865=>róng +4866=>sǎng +4867=>wū +4868=>chà +4869=>kēng +486A=>shàn +486B=>péng +486C=>màn +486D=>xiū +486F=>cōng +4870=>kēng +4871=>zhuǎn +4872=>chán +4873=>sī +4874=>chōng +4875=>suì +4876=>bèi +4877=>kài +4879=>zhì +487A=>wèi +487B=>mín +487C=>líng +487D=>zuān +487E=>niè +487F=>líng +4880=>qì +4881=>yuè +4883=>yì +4884=>xǐ +4885=>chén +4887=>rǒng +4888=>chén +4889=>nóng +488A=>yóu +488B=>jì +488C=>bó +488D=>fǎng +4890=>cú +4891=>dǐ +4892=>jiāo +4893=>yú +4894=>hé +4895=>xù +4896=>yù +4897=>qū +4899=>bài +489A=>gēng +489B=>jiǒng +489D=>yà +489E=>shù +489F=>yóu +48A0=>sòng +48A1=>yè +48A2=>càng +48A3=>yáo +48A4=>shù +48A5=>yán +48A6=>shuài +48A7=>liào +48A8=>cōng +48A9=>yù +48AA=>bó +48AB=>suí +48AD=>yàn +48AE=>lèi +48AF=>lín +48B0=>tī +48B1=>dú +48B2=>yuè +48B3=>jǐ +48B5=>yún +48B8=>jū +48B9=>jǔ +48BA=>chū +48BB=>chén +48BC=>gōng +48BD=>xiàng +48BE=>xiǎn +48BF=>ān +48C0=>guǐ +48C1=>yǔ +48C2=>lěi +48C4=>tú +48C5=>chén +48C6=>xíng +48C7=>qiú +48C8=>hàng +48CA=>dǎng +48CB=>cǎi +48CC=>dǐ +48CD=>yǎn +48CE=>zī +48D0=>yīng +48D1=>chán +48D3=>lí +48D4=>suǒ +48D5=>mǎ +48D6=>mǎ +48D8=>táng +48D9=>péi +48DA=>lóu +48DB=>qī +48DC=>cuó +48DD=>tú +48DE=>è +48DF=>cán +48E0=>jié +48E1=>yí +48E2=>jí +48E3=>dǎng +48E4=>jué +48E5=>bǐ +48E6=>lèi +48E7=>yì +48E8=>chún +48E9=>chún +48EA=>pò +48EB=>lí +48EC=>zǎi +48ED=>tài +48EE=>pò +48EF=>cú +48F0=>jù +48F1=>xù +48F2=>fàn +48F4=>xù +48F5=>èr +48F6=>huó +48F7=>zhū +48F8=>rǎn +48F9=>fá +48FA=>juān +48FB=>hān +48FC=>liáng +48FD=>zhī +48FE=>mì +48FF=>yū +4901=>cén +4902=>méi +4903=>yīn +4904=>miǎn +4905=>tú +4906=>kuí +4909=>mì +490A=>róng +490B=>yù +490C=>qiāng +490D=>mí +490E=>jú +490F=>pǐ +4910=>jǐn +4911=>wàng +4912=>jì +4913=>méng +4914=>jiàn +4915=>xuè +4916=>bào +4917=>gǎn +4918=>chǎn +4919=>lì +491A=>lǐ +491B=>qiú +491C=>dùn +491D=>yìng +491E=>yǔn +491F=>chén +4920=>zhǐ +4921=>rǎn +4923=>lüè +4924=>kāi +4925=>guǐ +4926=>yuè +4927=>huì +4928=>pì +4929=>chá +492A=>duǒ +492B=>chán +492C=>shā +492D=>shì +492E=>shè +492F=>xíng +4930=>yíng +4931=>shì +4932=>chì +4933=>yè +4934=>hán +4935=>fèi +4936=>yè +4937=>yǎn +4938=>zuàn +4939=>sōu +493A=>jīn +493B=>duò +493C=>xiàn +493D=>guān +493E=>tāo +493F=>qiè +4940=>chǎn +4941=>hán +4942=>mèng +4943=>yuè +4944=>cù +4945=>qiàn +4946=>jǐn +4947=>shàn +4948=>mǔ +4949=>yuān +494B=>pēng +494C=>zhèng +494D=>zhì +494E=>chún +494F=>yǔ +4950=>móu +4951=>wàn +4952=>jiàng +4953=>qī +4954=>sù +4955=>piě +4956=>tián +4957=>kuǎn +4958=>cù +4959=>suì +495B=>jiē +495C=>jiàn +495D=>áo +495E=>jiǎo +495F=>yè +4961=>yè +4962=>lóng +4963=>záo +4964=>báo +4965=>lián +4967=>huán +4968=>lǜ +4969=>wéi +496A=>xiǎn +496B=>tiě +496C=>bó +496D=>zhèng +496E=>zhú +496F=>bēi +4970=>méng +4971=>xiě +4972=>ōu +4973=>yōu +4975=>xiǎo +4976=>lì +4977=>zhá +4978=>mí +497A=>yé +497D=>pō +497E=>xiě +4982=>shàn +4983=>zhuō +4985=>shàn +4986=>jué +4987=>jì +4988=>jiē +498A=>niǎo +498B=>áo +498C=>chù +498D=>wù +498E=>guǎn +498F=>xiè +4990=>tǐng +4991=>xuè +4992=>dàng +4993=>zhān +4994=>tǎn +4995=>pēng +4996=>xié +4997=>xù +4998=>xiàn +4999=>sì +499A=>kuà +499B=>zhèng +499C=>wú +499D=>huō +499E=>rùn +499F=>wěn +49A0=>dū +49A1=>huán +49A2=>kuò +49A3=>fù +49A4=>chuài +49A5=>xián +49A6=>qín +49A7=>qié +49A8=>lán +49AA=>yà +49AB=>yīng +49AC=>què +49AD=>hāng +49AE=>chǔn +49AF=>zhì +49B1=>wěi +49B2=>yán +49B3=>xiàng +49B4=>yì +49B5=>nǐ +49B6=>zhèng +49B7=>chuài +49B9=>shí +49BA=>dīng +49BB=>zǐ +49BC=>jué +49BD=>xù +49BE=>yuán +49C1=>xǔ +49C2=>dào +49C3=>tián +49C4=>gè +49C5=>yí +49C6=>hóng +49C7=>yī +49C9=>lǐ +49CA=>kū +49CB=>xiǎn +49CC=>suī +49CD=>xì +49CE=>xuàn +49D1=>dī +49D2=>lái +49D3=>zhōu +49D4=>niàn +49D5=>chéng +49D6=>jiàn +49D7=>bì +49D8=>zhuàn +49D9=>líng +49DA=>hào +49DB=>bàng +49DC=>táng +49DD=>chī +49DE=>mà +49DF=>xiàn +49E0=>shuàn +49E1=>yōng +49E2=>qū +49E4=>pú +49E5=>huì +49E6=>wéi +49E7=>yǐ +49E8=>yè +49EA=>chè +49EB=>háo +49EC=>bīn +49EE=>xiàn +49EF=>chán +49F0=>hùn +49F2=>hàn +49F3=>cí +49F4=>zhī +49F5=>qí +49F6=>kuí +49F7=>róu +49F9=>yīng +49FA=>xióng +49FC=>hú +49FD=>cuǐ +49FF=>què +4A00=>dí +4A01=>wù +4A02=>qiū +4A04=>yàn +4A05=>liáo +4A06=>bí +4A08=>bīn +4A0A=>yuān +4A0B=>nüè +4A0C=>báo +4A0D=>yǐng +4A0E=>hóng +4A0F=>cí +4A10=>qià +4A11=>tí +4A12=>yù +4A13=>léi +4A14=>báo +4A16=>jì +4A17=>fú +4A18=>xiàn +4A19=>cén +4A1A=>hū +4A1B=>sè +4A1C=>bēng +4A1D=>qīng +4A1E=>yǔ +4A1F=>wā +4A20=>ǎi +4A21=>hán +4A22=>dàn +4A23=>gé +4A24=>dí +4A25=>huò +4A26=>pāng +4A28=>zhuī +4A29=>líng +4A2A=>mái +4A2B=>mài +4A2C=>lián +4A2D=>xiāo +4A2E=>xuě +4A2F=>zhèn +4A30=>pò +4A31=>fù +4A32=>nóu +4A33=>xì +4A34=>duì +4A35=>dàn +4A36=>yǔn +4A37=>xiàn +4A38=>yǐn +4A39=>shū +4A3A=>duì +4A3B=>bèng +4A3C=>hù +4A3D=>fěi +4A3E=>fèi +4A3F=>zá +4A40=>bèi +4A41=>fēi +4A42=>xiān +4A43=>shì +4A44=>miǎn +4A45=>zhǎn +4A46=>zhǎn +4A47=>zhān +4A48=>huì +4A49=>fǔ +4A4A=>wǎn +4A4B=>mǒ +4A4C=>qiáo +4A4D=>liǎo +4A4F=>miè +4A50=>hū +4A51=>hóng +4A52=>yú +4A53=>qí +4A54=>duò +4A55=>áng +4A57=>bà +4A58=>dì +4A59=>xuàn +4A5A=>dì +4A5B=>bì +4A5C=>zhòu +4A5D=>páo +4A5E=>tié +4A5F=>yí +4A61=>jiá +4A62=>zhì +4A63=>tú +4A64=>xié +4A65=>dàn +4A66=>tiáo +4A67=>xiè +4A68=>chàng +4A69=>yuǎn +4A6A=>guǎn +4A6B=>liǎng +4A6C=>běng +4A6E=>lù +4A6F=>jí +4A70=>xuàn +4A71=>shù +4A72=>dū +4A73=>sōu +4A74=>hú +4A75=>yùn +4A76=>chǎn +4A77=>bāng +4A78=>róng +4A79=>é +4A7A=>wēng +4A7B=>bà +4A7C=>féng +4A7D=>yū +4A7E=>zhè +4A7F=>fén +4A80=>guǎn +4A81=>bǔ +4A82=>gé +4A83=>dūn +4A84=>huáng +4A85=>dú +4A86=>tǐ +4A87=>bó +4A88=>qiàn +4A89=>liè +4A8A=>lóng +4A8B=>wèi +4A8C=>zhàn +4A8D=>lán +4A8E=>suī +4A8F=>nà +4A90=>bì +4A91=>tuó +4A92=>zhù +4A93=>diē +4A94=>bǔ +4A95=>jú +4A96=>pò +4A97=>xiá +4A98=>wěi +4A99=>pò +4A9A=>dā +4A9B=>fān +4A9C=>chān +4A9D=>hù +4A9E=>zá +4AA4=>fán +4AA5=>xiè +4AA6=>hóng +4AA7=>chí +4AA8=>báo +4AA9=>yín +4AAB=>jīng +4AAC=>bó +4AAD=>ruǎn +4AAE=>chǒu +4AAF=>yīng +4AB0=>yī +4AB1=>gǎi +4AB2=>kūn +4AB3=>yǔn +4AB4=>zhěn +4AB5=>yǎ +4AB6=>jū +4AB7=>hòu +4AB8=>mín +4AB9=>bāi +4ABA=>gé +4ABB=>biàn +4ABC=>zhuō +4ABD=>hào +4ABE=>zhěn +4ABF=>shěng +4AC0=>gěn +4AC1=>bì +4AC2=>duǒ +4AC3=>chún +4AC4=>chuà +4AC5=>sàn +4AC6=>chéng +4AC7=>rán +4AC8=>chěn +4AC9=>mào +4ACA=>péi +4ACB=>wēi +4ACC=>pǐ +4ACD=>fǔ +4ACE=>zhuō +4ACF=>qī +4AD0=>lín +4AD1=>yī +4AD2=>mén +4AD3=>wú +4AD4=>qì +4AD5=>dié +4AD6=>chěn +4AD7=>xiá +4AD8=>hé +4AD9=>sǎng +4ADA=>guā +4ADB=>hóu +4ADC=>āo +4ADD=>fǔ +4ADE=>qiāo +4ADF=>hùn +4AE0=>pī +4AE1=>yán +4AE2=>sī +4AE3=>xí +4AE4=>míng +4AE5=>kuǐ +4AE6=>gé +4AE8=>ào +4AE9=>sǎn +4AEA=>shuǎng +4AEB=>lóu +4AEC=>zhěn +4AED=>huì +4AEE=>chán +4AF0=>lìn +4AF1=>ná +4AF2=>hàn +4AF3=>dú +4AF4=>jìn +4AF5=>mián +4AF6=>fán +4AF7=>è +4AF8=>chāo +4AF9=>hóng +4AFA=>hóng +4AFB=>yù +4AFC=>xuè +4AFD=>pāo +4AFE=>bī +4AFF=>chāo +4B00=>yǒu +4B01=>yí +4B02=>xuè +4B03=>sà +4B04=>xù +4B05=>lì +4B06=>lì +4B07=>yuàn +4B08=>duì +4B09=>huò +4B0A=>shà +4B0B=>léng +4B0C=>pōu +4B0D=>hū +4B0E=>guó +4B0F=>bù +4B10=>ruí +4B11=>wèi +4B12=>sōu +4B13=>àn +4B14=>yú +4B15=>xiāng +4B16=>héng +4B17=>yáng +4B18=>xiāo +4B19=>yáo +4B1B=>bì +4B1D=>héng +4B1E=>táo +4B1F=>liú +4B21=>zhù +4B23=>xì +4B24=>zàn +4B25=>yì +4B26=>dòu +4B27=>yuán +4B28=>jiù +4B2A=>bó +4B2B=>tí +4B2C=>yǐng +4B2E=>yí +4B2F=>nián +4B30=>shào +4B31=>bèn +4B32=>gōu +4B33=>bǎn +4B34=>mò +4B35=>gāi +4B36=>èn +4B37=>shě +4B39=>zhì +4B3A=>yàng +4B3B=>jiàn +4B3C=>yuàn +4B3D=>shuì +4B3E=>tí +4B3F=>wěi +4B40=>xùn +4B41=>zhì +4B42=>yì +4B43=>rěn +4B44=>shì +4B45=>hú +4B46=>nè +4B47=>yē +4B48=>jiàn +4B49=>suǐ +4B4A=>yǐng +4B4B=>bǎo +4B4C=>hú +4B4D=>hú +4B4E=>yè +4B50=>yàng +4B51=>lián +4B52=>xī +4B53=>èn +4B54=>duī +4B55=>zǎn +4B56=>zhù +4B57=>yǐng +4B58=>yǐng +4B59=>jǐn +4B5A=>chuáng +4B5B=>dàn +4B5D=>kuài +4B5E=>yì +4B5F=>yè +4B60=>jiǎn +4B61=>èn +4B62=>níng +4B63=>cí +4B64=>qiǎn +4B65=>xuè +4B66=>bō +4B67=>mǐ +4B68=>shuì +4B69=>mó +4B6A=>liáng +4B6B=>qǐ +4B6C=>qǐ +4B6D=>shǒu +4B6E=>fú +4B6F=>bó +4B70=>bèng +4B71=>bié +4B72=>yǐ +4B73=>wèi +4B74=>huán +4B75=>fán +4B76=>qí +4B77=>máo +4B78=>bǎo +4B79=>áng +4B7A=>ǎng +4B7B=>fù +4B7C=>qí +4B7D=>qún +4B7E=>tuó +4B7F=>yì +4B80=>bó +4B81=>pián +4B82=>bá +4B84=>xuán +4B87=>yù +4B88=>chí +4B89=>lú +4B8A=>yí +4B8B=>lì +4B8D=>niǎo +4B8E=>xì +4B8F=>wú +4B91=>lèi +4B92=>pū +4B93=>zhuō +4B94=>zuī +4B95=>zhuó +4B96=>chāng +4B97=>àn +4B98=>ér +4B99=>yù +4B9A=>lèng +4B9B=>fù +4B9C=>zhá +4B9D=>hún +4B9E=>chǔn +4B9F=>sōu +4BA0=>bī +4BA1=>bì +4BA2=>zhá +4BA4=>hé +4BA5=>lì +4BA7=>hàn +4BA8=>zǎi +4BA9=>gú +4BAA=>chéng +4BAB=>lóu +4BAC=>mò +4BAD=>mì +4BAE=>mài +4BAF=>ào +4BB0=>zhé +4BB1=>zhú +4BB2=>huáng +4BB3=>fán +4BB4=>dèng +4BB5=>tóng +4BB7=>dú +4BB8=>wò +4BB9=>wèi +4BBA=>jì +4BBB=>chì +4BBC=>lín +4BBD=>biāo +4BBE=>lóng +4BBF=>jiǎn +4BC0=>niè +4BC1=>luó +4BC2=>shēn +4BC4=>guā +4BC5=>niè +4BC6=>yì +4BC7=>kū +4BC8=>wán +4BC9=>wā +4BCA=>qià +4BCB=>bó +4BCC=>kāo +4BCD=>líng +4BCE=>gàn +4BCF=>guā +4BD0=>hái +4BD1=>kuāng +4BD2=>héng +4BD3=>kuī +4BD4=>zé +4BD5=>tīng +4BD6=>láng +4BD7=>bì +4BD8=>huàn +4BD9=>pò +4BDA=>yǎo +4BDB=>wàn +4BDC=>tì +4BDD=>suǐ +4BDE=>kuā +4BDF=>duì +4BE0=>ǎo +4BE1=>jiàn +4BE2=>mó +4BE3=>kuì +4BE4=>kuài +4BE5=>àn +4BE6=>mà +4BE7=>qǐng +4BE8=>qiāo +4BEA=>kǎo +4BEB=>hào +4BEC=>duǒ +4BED=>xiān +4BEE=>nái +4BEF=>suō +4BF0=>jiè +4BF1=>pī +4BF2=>pā +4BF3=>sōng +4BF4=>cháng +4BF5=>niè +4BF6=>mán +4BF7=>sōng +4BF8=>cì +4BF9=>xiān +4BFA=>kuò +4BFC=>dí +4BFD=>póu +4BFE=>tiáo +4BFF=>zú +4C00=>wǒ +4C01=>fèi +4C02=>cài +4C03=>péng +4C04=>sāi +4C06=>róu +4C07=>qí +4C08=>cuó +4C09=>pán +4C0A=>bó +4C0B=>mán +4C0C=>zǒng +4C0D=>cì +4C0E=>kuì +4C0F=>jì +4C10=>lán +4C12=>méng +4C13=>mián +4C14=>pán +4C15=>lú +4C16=>zuǎn +4C18=>liú +4C19=>yǐ +4C1A=>wén +4C1B=>lì +4C1C=>lì +4C1D=>zèng +4C1E=>zhǔ +4C1F=>hún +4C20=>shén +4C21=>chì +4C22=>xìng +4C23=>wǎng +4C24=>dōng +4C25=>huò +4C26=>pǐ +4C27=>hū +4C28=>mèi +4C29=>chě +4C2A=>mèi +4C2B=>chāo +4C2C=>jú +4C2D=>nòu +4C2F=>yì +4C30=>rú +4C31=>líng +4C32=>yà +4C34=>qì +4C35=>zī +4C37=>bàng +4C38=>gōng +4C39=>zé +4C3A=>jiè +4C3B=>yú +4C3C=>qín +4C3D=>bèi +4C3E=>bā +4C3F=>tuó +4C40=>yāng +4C41=>qiáo +4C42=>yǒu +4C43=>zhì +4C44=>jiè +4C45=>mò +4C46=>shéng +4C47=>shàn +4C48=>qí +4C49=>shàn +4C4A=>mǐ +4C4B=>gǒng +4C4C=>yí +4C4D=>gèng +4C4E=>gèng +4C4F=>tǒu +4C50=>fū +4C51=>xué +4C52=>yè +4C53=>tíng +4C54=>tiáo +4C55=>móu +4C56=>liú +4C57=>cān +4C58=>lí +4C59=>shū +4C5A=>lù +4C5B=>huò +4C5C=>cuò +4C5D=>pái +4C5E=>liú +4C5F=>jù +4C60=>zhàn +4C61=>jú +4C62=>zhēng +4C63=>zú +4C64=>xiàn +4C65=>zhì +4C68=>là +4C6B=>là +4C6C=>xū +4C6D=>gèng +4C6E=>é +4C6F=>mú +4C70=>zhòng +4C71=>tí +4C72=>yuán +4C73=>zhān +4C74=>gèng +4C75=>wēng +4C76=>láng +4C77=>yú +4C78=>sōu +4C79=>zhǎ +4C7A=>hái +4C7B=>huá +4C7C=>zhǎn +4C7E=>lóu +4C7F=>chàn +4C80=>zhì +4C81=>wèi +4C82=>xuán +4C83=>zǎo +4C84=>mín +4C85=>guī +4C86=>sū +4C89=>sī +4C8A=>duò +4C8B=>cén +4C8C=>kuǎn +4C8D=>téng +4C8E=>něi +4C8F=>láo +4C90=>lǔ +4C91=>yí +4C92=>xiè +4C93=>yǎn +4C94=>qíng +4C95=>pū +4C96=>chóu +4C97=>xián +4C98=>guǎn +4C99=>jié +4C9A=>lài +4C9B=>méng +4C9C=>yè +4C9E=>lì +4C9F=>yìn +4CA0=>chūn +4CA1=>qiū +4CA2=>téng +4CA3=>yú +4CA6=>dài +4CA7=>dù +4CA8=>hóng +4CAA=>xì +4CAC=>qí +4CAE=>yuán +4CAF=>jí +4CB0=>yùn +4CB1=>fǎng +4CB2=>gōng +4CB3=>háng +4CB4=>zhèn +4CB5=>què +4CB8=>jiè +4CB9=>pí +4CBA=>gàn +4CBB=>xuán +4CBC=>shēng +4CBD=>shí +4CBE=>qiǎo +4CBF=>cí +4CC0=>dié +4CC1=>bó +4CC2=>diāo +4CC3=>wǎn +4CC4=>cí +4CC5=>zhǐ +4CC6=>bái +4CC7=>wǔ +4CC8=>bǎo +4CC9=>dàn +4CCA=>bá +4CCB=>tóng +4CCD=>gōng +4CCE=>jiù +4CCF=>guì +4CD0=>cì +4CD1=>yǒu +4CD2=>yuán +4CD3=>lǎo +4CD4=>jú +4CD5=>fú +4CD6=>niè +4CD7=>é +4CD8=>é +4CD9=>xǐng +4CDA=>kàn +4CDB=>yàn +4CDC=>tú +4CDD=>pǒu +4CDE=>běng +4CDF=>míng +4CE0=>shuì +4CE1=>yàn +4CE2=>qí +4CE3=>yuán +4CE4=>biē +4CE6=>xuān +4CE7=>hóu +4CE8=>huáng +4CE9=>yāo +4CEA=>juàn +4CEB=>kuí +4CEC=>è +4CED=>jí +4CEE=>mò +4CEF=>chóng +4CF0=>bǎo +4CF1=>wù +4CF2=>zhèn +4CF3=>xù +4CF4=>tà +4CF5=>chì +4CF6=>xī +4CF7=>cóng +4CF8=>má +4CF9=>kòu +4CFA=>yàn +4CFB=>cán +4CFD=>hè +4CFE=>dēng +4CFF=>rán +4D00=>tóng +4D01=>yù +4D02=>xiàng +4D03=>náo +4D04=>shùn +4D05=>fén +4D06=>pú +4D07=>líng +4D08=>ǎo +4D09=>huán +4D0A=>yí +4D0B=>huán +4D0C=>méng +4D0D=>yīng +4D0E=>lěi +4D0F=>yàn +4D10=>bǎo +4D11=>dié +4D12=>líng +4D13=>shī +4D14=>jiāo +4D15=>liè +4D16=>jīng +4D17=>jú +4D18=>tī +4D19=>pì +4D1A=>gǎng +4D1B=>xiāo +4D1C=>wāi +4D1D=>chuài +4D1E=>dí +4D1F=>huán +4D20=>yǎo +4D21=>lì +4D22=>mí +4D23=>hū +4D24=>shēng +4D25=>jiā +4D26=>yín +4D27=>wēi +4D29=>piáo +4D2A=>lù +4D2B=>líng +4D2C=>yì +4D2D=>cái +4D2E=>shàn +4D2F=>hū +4D30=>shú +4D31=>tuō +4D32=>mò +4D33=>huá +4D34=>tiè +4D35=>bǐng +4D36=>péng +4D37=>hún +4D38=>fū +4D39=>guǒ +4D3A=>bù +4D3B=>lí +4D3C=>chàn +4D3D=>pí +4D3E=>cuó +4D3F=>méng +4D40=>suǒ +4D41=>qiàng +4D42=>zhí +4D43=>kuàng +4D44=>bí +4D45=>áo +4D46=>méng +4D47=>xiàn +4D48=>kù +4D49=>tóu +4D4A=>tuān +4D4B=>wěi +4D4C=>xiān +4D4E=>tuān +4D4F=>lǎo +4D50=>chǎn +4D51=>nì +4D52=>nì +4D53=>lí +4D54=>dǒng +4D55=>jù +4D56=>qiàn +4D57=>bó +4D58=>shài +4D59=>zhā +4D5A=>tǎo +4D5B=>qiàn +4D5C=>nǒng +4D5D=>yì +4D5E=>jìng +4D5F=>gǎn +4D60=>dí +4D61=>jiǎn +4D62=>mèi +4D63=>dá +4D64=>jiǎn +4D65=>yù +4D66=>xiè +4D67=>zài +4D68=>máng +4D69=>lí +4D6A=>gùn +4D6B=>xūn +4D6C=>tà +4D6D=>zhè +4D6E=>yàng +4D6F=>tuǎn +4D70=>shāng +4D71=>xì +4D72=>qiāo +4D73=>wèi +4D74=>yìng +4D75=>chuā +4D76=>qú +4D77=>wā +4D79=>zhī +4D7A=>tǐng +4D7B=>gǔ +4D7C=>shāng +4D7D=>cà +4D7E=>fú +4D7F=>tiè +4D80=>tà +4D81=>tà +4D82=>zhuó +4D83=>hán +4D84=>píng +4D85=>hé +4D86=>zhuī +4D87=>zhòu +4D88=>bó +4D89=>liú +4D8A=>nǜ +4D8B=>xī +4D8C=>pào +4D8D=>dì +4D8E=>hē +4D8F=>tì +4D90=>wài +4D91=>tì +4D92=>qí +4D93=>jì +4D94=>chí +4D95=>bà +4D96=>jìn +4D97=>kè +4D98=>lì +4D99=>jù +4D9A=>qǔ +4D9B=>là +4D9C=>gǔ +4D9D=>qià +4D9E=>qí +4D9F=>xiàn +4DA0=>jiǎn +4DA1=>shí +4DA2=>jiān +4DA3=>ái +4DA4=>huá +4DA5=>zhā +4DA6=>zé +4DA7=>yǎo +4DA8=>zhān +4DA9=>jì +4DAA=>chà +4DAB=>yàn +4DAC=>jiān +4DAE=>yǎn +4DB0=>jiāo +4DB1=>tóng +4DB2=>nán +4DB3=>yuè +4DB5=>chí +4E00=>yī +4E01=>dīng +4E02=>kǎo +4E03=>qī +4E04=>shàng +4E05=>xià +4E06=>hǎn +4E07=>wàn +4E08=>zhàng +4E09=>sān +4E0A=>shàng +4E0B=>xià +4E0C=>jī +4E0D=>bù +4E0E=>yǔ +4E0F=>miǎn +4E10=>gài +4E11=>chǒu +4E12=>chǒu +4E13=>zhuān +4E14=>qiě +4E15=>pī +4E16=>shì +4E17=>shì +4E18=>qiū +4E19=>bǐng +4E1A=>yè +4E1B=>cóng +4E1C=>dōng +4E1D=>sī +4E1E=>chéng +4E1F=>diū +4E20=>qiū +4E21=>liǎng +4E22=>diū +4E23=>yǒu +4E24=>liǎng +4E25=>yán +4E26=>bìng +4E27=>sàng +4E28=>gǔn +4E29=>jiū +4E2A=>gè +4E2B=>yā +4E2C=>qiáng +4E2D=>zhōng +4E2E=>jǐ +4E2F=>jiè +4E30=>fēng +4E31=>guàn +4E32=>chuàn +4E33=>chǎn +4E34=>lín +4E35=>zhuó +4E36=>zhǔ +4E37=>ha +4E38=>wán +4E39=>dān +4E3A=>wèi +4E3B=>zhǔ +4E3C=>jǐng +4E3D=>lì +4E3E=>jǔ +4E3F=>piě +4E40=>fú +4E41=>yí +4E42=>yì +4E43=>nǎi +4E44=>wu +4E45=>jiǔ +4E46=>jiǔ +4E47=>tuō +4E48=>me +4E49=>yì +4E4A=>yī +4E4B=>zhī +4E4C=>wū +4E4D=>zhà +4E4E=>hu +4E4F=>fá +4E50=>lè +4E51=>yín +4E52=>pīng +4E53=>pāng +4E54=>qiáo +4E55=>hǔ +4E56=>guāi +4E57=>chéng +4E58=>chéng +4E59=>yǐ +4E5A=>yǐn +4E5B=>ya +4E5C=>miē +4E5D=>jiǔ +4E5E=>qǐ +4E5F=>yě +4E60=>xí +4E61=>xiāng +4E62=>gài +4E63=>jiǔ +4E64=>xià +4E65=>hù +4E66=>shū +4E67=>dou +4E68=>shǐ +4E69=>jī +4E6A=>náng +4E6B=>jiā +4E6C=>jù +4E6D=>shí +4E6E=>mǎo +4E6F=>hū +4E70=>mǎi +4E71=>luàn +4E72=>zī +4E73=>rǔ +4E74=>xué +4E75=>yǎn +4E76=>fǔ +4E77=>shā +4E78=>nǎ +4E79=>gān +4E7A=>suǒ +4E7B=>yú +4E7C=>cui +4E7D=>zhě +4E7E=>gān +4E7F=>zhì +4E80=>guī +4E81=>gān +4E82=>luàn +4E83=>lǐn +4E84=>yì +4E85=>jué +4E86=>le +4E87=>ma +4E88=>yǔ +4E89=>zhēng +4E8A=>shì +4E8B=>shì +4E8C=>èr +4E8D=>chù +4E8E=>yú +4E8F=>kuī +4E90=>yú +4E91=>yún +4E92=>hù +4E93=>qí +4E94=>wǔ +4E95=>jǐng +4E96=>sì +4E97=>suì +4E98=>gèn +4E99=>gèn +4E9A=>yà +4E9B=>xiē +4E9C=>yà +4E9D=>qí +4E9E=>yà +4E9F=>jí +4EA0=>tóu +4EA1=>wáng +4EA2=>kàng +4EA3=>tà +4EA4=>jiāo +4EA5=>hài +4EA6=>yì +4EA7=>chǎn +4EA8=>hēng +4EA9=>mǔ +4EAA=>ye +4EAB=>xiǎng +4EAC=>jīng +4EAD=>tíng +4EAE=>liàng +4EAF=>xiǎng +4EB0=>jīng +4EB1=>yè +4EB2=>qīn +4EB3=>bó +4EB4=>yòu +4EB5=>xiè +4EB6=>dǎn +4EB7=>lián +4EB8=>duǒ +4EB9=>mén +4EBA=>rén +4EBB=>rén +4EBC=>jí +4EBD=>ji +4EBE=>wáng +4EBF=>yì +4EC0=>shén +4EC1=>rén +4EC2=>lè +4EC3=>dīng +4EC4=>zè +4EC5=>jǐn +4EC6=>pū +4EC7=>chóu +4EC8=>bā +4EC9=>zhǎng +4ECA=>jīn +4ECB=>jiè +4ECC=>bīng +4ECD=>réng +4ECE=>cóng +4ECF=>fó +4ED0=>sǎn +4ED1=>lún +4ED2=>bīng +4ED3=>cāng +4ED4=>zǐ +4ED5=>shì +4ED6=>tā +4ED7=>zhàng +4ED8=>fù +4ED9=>xian +4EDA=>xiān +4EDB=>tuō +4EDC=>hóng +4EDD=>tóng +4EDE=>rèn +4EDF=>qiān +4EE0=>gǎn +4EE1=>gē +4EE2=>bó +4EE3=>dài +4EE4=>lìng +4EE5=>yǐ +4EE6=>chào +4EE7=>cháng +4EE8=>sā +4EE9=>shang +4EEA=>yí +4EEB=>mù +4EEC=>men +4EED=>rèn +4EEE=>jiǎ +4EEF=>chào +4EF0=>yǎng +4EF1=>qián +4EF2=>zhòng +4EF3=>pǐ +4EF4=>wò +4EF5=>wǔ +4EF6=>jiàn +4EF7=>jià +4EF8=>yǎo +4EF9=>fēng +4EFA=>cāng +4EFB=>rèn +4EFC=>wáng +4EFD=>fèn +4EFE=>dī +4EFF=>fǎng +4F00=>zhōng +4F01=>qǐ +4F02=>pèi +4F03=>yú +4F04=>diào +4F05=>dùn +4F06=>wù +4F07=>yì +4F08=>xǐn +4F09=>kàng +4F0A=>yī +4F0B=>jí +4F0C=>ài +4F0D=>wu +4F0E=>jì +4F0F=>fú +4F10=>fá +4F11=>xiū +4F12=>jìn +4F13=>pī +4F14=>dǎn +4F15=>fū +4F16=>tǎng +4F17=>zhòng +4F18=>yōu +4F19=>huǒ +4F1A=>huì +4F1B=>yǔ +4F1C=>cuì +4F1D=>chuán +4F1E=>sǎn +4F1F=>wěi +4F20=>chuán +4F21=>chē +4F22=>yá +4F23=>xiàn +4F24=>shāng +4F25=>chāng +4F26=>lún +4F27=>cāng +4F28=>xùn +4F29=>xìn +4F2A=>wěi +4F2B=>zhù +4F2C=>ze +4F2D=>xián +4F2E=>nǔ +4F2F=>bó +4F30=>gū +4F31=>nǐ +4F32=>nì +4F33=>xiè +4F34=>bàn +4F35=>xù +4F36=>ling +4F37=>zhòu +4F38=>shēn +4F39=>qū +4F3A=>cì +4F3B=>bēng +4F3C=>shì +4F3D=>jiā +4F3E=>pī +4F3F=>yì +4F40=>sì +4F41=>yǐ +4F42=>zhēng +4F43=>diàn +4F44=>hān +4F45=>mài +4F46=>dàn +4F47=>zhù +4F48=>bù +4F49=>qū +4F4A=>bǐ +4F4B=>zhāo +4F4C=>cǐ +4F4D=>wèi +4F4E=>dī +4F4F=>zhù +4F50=>zuǒ +4F51=>yòu +4F52=>yǎng +4F53=>tǐ +4F54=>zhàn +4F55=>hé +4F56=>bì +4F57=>tuó +4F58=>shé +4F59=>yú +4F5A=>yì +4F5B=>fú +4F5C=>zuò +4F5D=>gōu +4F5E=>nìng +4F5F=>tóng +4F60=>nǐ +4F61=>xiān +4F62=>qú +4F63=>yōng +4F64=>wǎ +4F65=>qiān +4F66=>shi +4F67=>kǎ +4F68=>bao +4F69=>pèi +4F6A=>huí +4F6B=>hè +4F6C=>lǎo +4F6D=>xiáng +4F6E=>gé +4F6F=>yáng +4F70=>bǎi +4F71=>fǎ +4F72=>mǐng +4F73=>jiā +4F74=>èr +4F75=>bìng +4F76=>jí +4F77=>hěn +4F78=>huó +4F79=>guǐ +4F7A=>quán +4F7B=>tiāo +4F7C=>jiǎo +4F7D=>cì +4F7E=>yì +4F7F=>shǐ +4F80=>xíng +4F81=>shēn +4F82=>tuō +4F83=>kǎn +4F84=>zhí +4F85=>gāi +4F86=>lái +4F87=>yí +4F88=>chǐ +4F89=>kuǎ +4F8A=>guāng +4F8B=>lì +4F8C=>yīn +4F8D=>shì +4F8E=>mǐ +4F8F=>zhū +4F90=>xù +4F91=>yòu +4F92=>ān +4F93=>lù +4F94=>móu +4F95=>ér +4F96=>lún +4F97=>dòng +4F98=>chà +4F99=>chī +4F9A=>xùn +4F9B=>gōng +4F9C=>zhōu +4F9D=>yī +4F9E=>rú +4F9F=>cún +4FA0=>xiá +4FA1=>sì +4FA2=>zài +4FA3=>lǚ +4FA4=>ta +4FA5=>jiǎo +4FA6=>zhēn +4FA7=>cè +4FA8=>qiáo +4FA9=>kuài +4FAA=>chái +4FAB=>nìng +4FAC=>nóng +4FAD=>jǐn +4FAE=>wǔ +4FAF=>hóu +4FB0=>jiǒng +4FB1=>chěng +4FB2=>zhèn +4FB3=>zuò +4FB4=>chǒu +4FB5=>qīn +4FB6=>lǚ +4FB7=>jú +4FB8=>shù +4FB9=>tǐng +4FBA=>shèn +4FBB=>tuì +4FBC=>bó +4FBD=>nán +4FBE=>xiāo +4FBF=>biàn +4FC0=>tuǐ +4FC1=>yǔ +4FC2=>xì +4FC3=>cù +4FC4=>é +4FC5=>qiú +4FC6=>xú +4FC7=>guàng +4FC8=>kù +4FC9=>wǔ +4FCA=>jùn +4FCB=>yì +4FCC=>fǔ +4FCD=>liáng +4FCE=>zǔ +4FCF=>qiào +4FD0=>lì +4FD1=>yǒng +4FD2=>hùn +4FD3=>jìng +4FD4=>qiàn +4FD5=>sàn +4FD6=>pěi +4FD7=>sú +4FD8=>fú +4FD9=>xī +4FDA=>lǐ +4FDB=>fǔ +4FDC=>pīng +4FDD=>bǎo +4FDE=>yú +4FDF=>qí +4FE0=>xiá +4FE1=>xìn +4FE2=>xiū +4FE3=>yǔ +4FE4=>dì +4FE5=>chē +4FE6=>chóu +4FE7=>zhi +4FE8=>yǎn +4FE9=>liǎ +4FEA=>lì +4FEB=>lái +4FEC=>si +4FED=>jiǎn +4FEE=>xiū +4FEF=>fǔ +4FF0=>huò +4FF1=>jù +4FF2=>xiào +4FF3=>pái +4FF4=>jiàn +4FF5=>biào +4FF6=>chù +4FF7=>fèi +4FF8=>fèng +4FF9=>yà +4FFA=>ǎn +4FFB=>bèi +4FFC=>yù +4FFD=>xīn +4FFE=>bǐ +4FFF=>hǔ +5000=>chāng +5001=>zhī +5002=>bìng +5003=>jiù +5004=>yáo +5005=>cuì +5006=>liǎ +5007=>wǎn +5008=>lái +5009=>cāng +500A=>zòng +500B=>gè +500C=>guān +500D=>bèi +500E=>tiǎn +500F=>shū +5010=>shū +5011=>men +5012=>dào +5013=>tán +5014=>jué +5015=>chuí +5016=>xìng +5017=>péng +5018=>tǎng +5019=>hou +501A=>yǐ +501B=>qī +501C=>tì +501D=>gàn +501E=>jìng +501F=>jiè +5020=>suī +5021=>chàng +5022=>jié +5023=>fǎng +5024=>zhí +5025=>kōng +5026=>juàn +5027=>zōng +5028=>jù +5029=>qiàn +502A=>ní +502B=>lún +502C=>zhuō +502D=>wō +502E=>luǒ +502F=>sōng +5030=>lèng +5031=>hùn +5032=>dōng +5033=>zì +5034=>bèn +5035=>wǔ +5036=>jù +5037=>nǎi +5038=>cǎi +5039=>jiǎn +503A=>zhài +503B=>yē +503C=>zhí +503D=>shà +503E=>qīng +503F=>qie +5040=>yīng +5041=>chēng +5042=>jiān +5043=>yǎn +5044=>ruǎn +5045=>zhòng +5046=>chǔn +5047=>jiǎ +5048=>jì +5049=>wěi +504A=>yǔ +504B=>bìng +504C=>ruò +504D=>tí +504E=>wēi +504F=>piān +5050=>yàn +5051=>fēng +5052=>tǎng +5053=>wò +5054=>è +5055=>xié +5056=>chě +5057=>shěng +5058=>kǎn +5059=>dì +505A=>zuò +505B=>chā +505C=>tíng +505D=>bèi +505E=>xiè +505F=>huáng +5060=>yǎo +5061=>zhàn +5062=>chǒu +5063=>yān +5064=>yóu +5065=>jiàn +5066=>xǔ +5067=>zhā +5068=>cī +5069=>fù +506A=>bī +506B=>zhì +506C=>zǒng +506D=>miǎn +506E=>jí +506F=>yǐ +5070=>xiè +5071=>xún +5072=>cāi +5073=>duān +5074=>cè +5075=>zhēn +5076=>ǒu +5077=>tōu +5078=>tōu +5079=>bèi +507A=>zá +507B=>lóu +507C=>jié +507D=>wěi +507E=>fèn +507F=>cháng +5080=>guī +5081=>sǒu +5082=>zhì +5083=>sù +5084=>xiā +5085=>fu +5086=>yuàn +5087=>rǒng +5088=>lì +5089=>nù +508A=>yùn +508B=>jiǎng +508C=>mà +508D=>bàng +508E=>diān +508F=>táng +5090=>hào +5091=>jié +5092=>xī +5093=>shàn +5094=>qiàn +5095=>jué +5096=>cāng +5097=>chù +5098=>sǎn +5099=>bèi +509A=>xiào +509B=>yǒng +509C=>yáo +509D=>tàn +509E=>suō +509F=>yǎng +50A0=>fā +50A1=>bìng +50A2=>jiā +50A3=>dǎi +50A4=>zài +50A5=>tǎng +50A6=>gu +50A7=>bīn +50A8=>chǔ +50A9=>nuó +50AA=>cān +50AB=>lěi +50AC=>cuī +50AD=>yōng +50AE=>zāo +50AF=>zǒng +50B0=>bēng +50B1=>sǒng +50B2=>ào +50B3=>chuán +50B4=>yǔ +50B5=>zhài +50B6=>zú +50B7=>shāng +50B8=>chuǎng +50B9=>jìng +50BA=>chì +50BB=>shǎ +50BC=>hàn +50BD=>zhāng +50BE=>qīng +50BF=>yàn +50C0=>dì +50C1=>xiè +50C2=>lóu +50C3=>bèi +50C4=>piào +50C5=>jǐn +50C6=>liàn +50C7=>lù +50C8=>mán +50C9=>qiān +50CA=>xiān +50CB=>tàn +50CC=>yíng +50CD=>dòng +50CE=>zhuàn +50CF=>xiàng +50D0=>shàn +50D1=>qiáo +50D2=>jiǒng +50D3=>tuǐ +50D4=>zǔn +50D5=>pú +50D6=>xī +50D7=>láo +50D8=>chǎng +50D9=>guāng +50DA=>liáo +50DB=>qī +50DC=>chēng +50DD=>chán +50DE=>wěi +50DF=>jī +50E0=>bō +50E1=>huì +50E2=>chuǎn +50E3=>tiě +50E4=>dàn +50E5=>jiǎo +50E6=>jiù +50E7=>sēng +50E8=>fèn +50E9=>xiàn +50EA=>jú +50EB=>è +50EC=>jiāo +50ED=>jiàn +50EE=>tóng +50EF=>lìn +50F0=>bó +50F1=>gù +50F2=>xian +50F3=>sù +50F4=>xiàn +50F5=>jiāng +50F6=>mǐn +50F7=>yè +50F8=>jìn +50F9=>jià +50FA=>qiào +50FB=>pì +50FC=>fēng +50FD=>zhòu +50FE=>ài +50FF=>sài +5100=>yí +5101=>jùn +5102=>nóng +5103=>chán +5104=>yì +5105=>dàng +5106=>jǐng +5107=>xuān +5108=>kuài +5109=>jiǎn +510A=>chù +510B=>dān +510C=>jiǎo +510D=>shǎ +510E=>zài +510F=>can +5110=>bīn +5111=>án +5112=>rú +5113=>tái +5114=>chóu +5115=>chái +5116=>lán +5117=>nǐ +5118=>jǐn +5119=>qiàn +511A=>méng +511B=>wǔ +511C=>níng +511D=>qióng +511E=>nǐ +511F=>cháng +5120=>liè +5121=>lěi +5122=>lǚ +5123=>kuǎng +5124=>bào +5125=>yù +5126=>biāo +5127=>zǎn +5128=>zhí +5129=>sì +512A=>yōu +512B=>háo +512C=>chèn +512D=>chèn +512E=>lì +512F=>téng +5130=>wěi +5131=>lǒng +5132=>chǔ +5133=>chán +5134=>ráng +5135=>shū +5136=>huì +5137=>lì +5138=>luó +5139=>zǎn +513A=>nuó +513B=>tǎng +513C=>yǎn +513D=>léi +513E=>nàng +513F=>ér +5140=>wù +5141=>yǔn +5142=>zān +5143=>yuán +5144=>xiōng +5145=>chōng +5146=>zhào +5147=>xiōng +5148=>xiān +5149=>guāng +514A=>duì +514B=>kè +514C=>duì +514D=>miǎn +514E=>tù +514F=>cháng +5150=>ér +5151=>duì +5152=>ér +5153=>jīn +5154=>tù +5155=>sì +5156=>yǎn +5157=>yǎn +5158=>shǐ +515A=>dǎng +515B=>qiān +515C=>dōu +515D=>fēn +515E=>máo +515F=>shēn +5160=>dōu +5162=>jīng +5163=>lǐ +5164=>huáng +5165=>rù +5166=>wáng +5167=>nèi +5168=>quán +5169=>liǎng +516A=>yú +516B=>bā +516C=>gōng +516D=>liù +516E=>xī +516F=>han +5170=>lán +5171=>gòng +5172=>tiān +5173=>guān +5174=>xìng +5175=>bīng +5176=>qí +5177=>jù +5178=>diǎn +5179=>zī +517A=>fēn +517B=>yǎng +517C=>jiān +517D=>shòu +517E=>jì +517F=>yì +5180=>jì +5181=>chǎn +5182=>jiōng +5183=>mào +5184=>rǎn +5185=>nèi +5186=>yuán +5187=>mǎo +5188=>gāng +5189=>rǎn +518A=>cè +518B=>jiōng +518C=>cè +518D=>zài +518E=>guǎ +518F=>jiǒng +5190=>mào +5191=>zhòu +5192=>mào +5193=>gòu +5194=>xǔ +5195=>miǎn +5196=>mì +5197=>rǒng +5198=>yín +5199=>xiě +519A=>kǎn +519B=>jūn +519C=>nóng +519D=>yí +519E=>mí +519F=>shì +51A0=>guān +51A1=>méng +51A2=>zhǒng +51A3=>jù +51A4=>yuān +51A5=>míng +51A6=>kòu +51A7=>lín +51A8=>fù +51A9=>xiě +51AA=>mì +51AB=>bīng +51AC=>dōng +51AD=>tái +51AE=>gāng +51AF=>féng +51B0=>bīng +51B1=>hù +51B2=>chōng +51B3=>jué +51B4=>hù +51B5=>kuàng +51B6=>yě +51B7=>lěng +51B8=>pàn +51B9=>fú +51BA=>mǐn +51BB=>dòng +51BC=>xiǎn +51BD=>liè +51BE=>qià +51BF=>jiān +51C0=>jìng +51C1=>shù +51C2=>měi +51C3=>tú +51C4=>qī +51C5=>gù +51C6=>zhǔn +51C7=>sōng +51C8=>jìng +51C9=>liáng +51CA=>qìng +51CB=>diāo +51CC=>líng +51CD=>dòng +51CE=>gàn +51CF=>jiǎn +51D0=>yīn +51D1=>còu +51D2=>yí +51D3=>lì +51D4=>chuàng +51D5=>mǐng +51D6=>zhun +51D7=>cuī +51D8=>sī +51D9=>duó +51DA=>jìn +51DB=>lǐn +51DC=>lǐn +51DD=>níng +51DE=>xī +51DF=>dú +51E0=>jǐ +51E1=>fán +51E2=>fán +51E3=>fán +51E4=>fèng +51E5=>jū +51E6=>chǔ +51E7=>zheng +51E8=>fēng +51E9=>mu +51EA=>zhi +51EB=>fú +51EC=>fēng +51ED=>píng +51EE=>fēng +51EF=>kǎi +51F0=>huáng +51F1=>kǎi +51F2=>gān +51F3=>dèng +51F4=>píng +51F5=>qiǎn +51F6=>xiōng +51F7=>kuài +51F8=>tū +51F9=>āo +51FA=>chū +51FB=>jī +51FC=>dàng +51FD=>hán +51FE=>hán +51FF=>záo +5200=>dāo +5201=>diāo +5202=>dāo +5203=>rèn +5204=>rèn +5205=>chuāng +5206=>fēn +5207=>qiè +5208=>yì +5209=>jī +520A=>kān +520B=>qiàn +520C=>cǔn +520D=>chú +520E=>wěn +520F=>jī +5210=>dǎn +5211=>xíng +5212=>huà +5213=>wán +5214=>jué +5215=>lí +5216=>yuè +5217=>liè +5218=>liú +5219=>zé +521A=>gāng +521B=>chuàng +521C=>fú +521D=>chū +521E=>qù +521F=>jū +5220=>shān +5221=>mǐn +5222=>líng +5223=>zhōng +5224=>pàn +5225=>bié +5226=>jié +5227=>jié +5228=>páo +5229=>lì +522A=>shān +522B=>bié +522C=>chǎn +522D=>jǐng +522E=>guā +522F=>gēng +5230=>dào +5231=>chuàng +5232=>kuī +5233=>kū +5234=>duò +5235=>èr +5236=>zhì +5237=>shuā +5238=>quàn +5239=>shā +523A=>cì +523B=>kè +523C=>jié +523D=>guì +523E=>cì +523F=>guì +5240=>kǎi +5241=>duò +5242=>jì +5243=>tì +5244=>jǐng +5245=>lóu +5246=>luǒ +5247=>zé +5248=>yuān +5249=>cuò +524A=>xuē +524B=>kè +524C=>lá +524D=>qián +524E=>shā +524F=>chuàng +5250=>guǎ +5251=>jiàn +5252=>cuò +5253=>lí +5254=>tī +5255=>fèi +5256=>pōu +5257=>chǎn +5258=>qí +5259=>chuàng +525A=>zì +525B=>gāng +525C=>wān +525D=>bō +525E=>jī +525F=>duō +5260=>qíng +5261=>shàn +5262=>dū +5263=>jiàn +5264=>jì +5265=>bō +5266=>yān +5267=>jù +5268=>huō +5269=>shèng +526A=>jiǎn +526B=>duó +526C=>duān +526D=>wū +526E=>guǎ +526F=>fù +5270=>shèng +5271=>jiàn +5272=>gē +5273=>dá +5274=>kǎi +5275=>chuàng +5276=>chuān +5277=>chǎn +5278=>tuán +5279=>lù +527A=>lí +527B=>pěng +527C=>shān +527D=>piāo +527E=>kōu +527F=>jiǎo +5280=>guā +5281=>qiāo +5282=>jué +5283=>huà +5284=>zhā +5285=>zhuò +5286=>lián +5287=>jù +5288=>pī +5289=>liú +528A=>guì +528B=>jiǎo +528C=>guì +528D=>jiàn +528E=>jiàn +528F=>tāng +5290=>huō +5291=>jì +5292=>jiàn +5293=>yì +5294=>jiàn +5295=>zhì +5296=>chán +5297=>jiǎn +5298=>mó +5299=>lí +529A=>zhǔ +529B=>lì +529C=>yà +529D=>quàn +529E=>bàn +529F=>gōng +52A0=>jiā +52A1=>wu +52A2=>mài +52A3=>liè +52A4=>jìn +52A5=>kēng +52A6=>xié +52A7=>zhǐ +52A8=>dòng +52A9=>zhù +52AA=>nǔ +52AB=>jié +52AC=>qú +52AD=>shào +52AE=>yì +52AF=>zhū +52B0=>mò +52B1=>lì +52B2=>jìn +52B3=>láo +52B4=>láo +52B5=>juàn +52B6=>kǒu +52B7=>yáng +52B8=>wā +52B9=>xiào +52BA=>móu +52BB=>kuāng +52BC=>jié +52BD=>liè +52BE=>hé +52BF=>shì +52C0=>kè +52C1=>jìn +52C2=>gào +52C3=>bó +52C4=>mǐn +52C5=>chì +52C6=>láng +52C7=>yǒng +52C8=>yǒng +52C9=>miǎn +52CA=>kè +52CB=>xūn +52CC=>juàn +52CD=>qíng +52CE=>lù +52CF=>bù +52D0=>měng +52D1=>chì +52D2=>lēi +52D3=>kài +52D4=>miǎn +52D5=>dòng +52D6=>xù +52D7=>xù +52D8=>kān +52D9=>wu +52DA=>yì +52DB=>xūn +52DC=>wěng +52DD=>shèng +52DE=>láo +52DF=>mù +52E0=>lù +52E1=>piào +52E2=>shì +52E3=>jī +52E4=>qín +52E5=>jiàng +52E6=>chāo +52E7=>quàn +52E8=>xiàng +52E9=>yì +52EA=>jué +52EB=>fān +52EC=>juān +52ED=>tóng +52EE=>jù +52EF=>dān +52F0=>xié +52F1=>mài +52F2=>xūn +52F3=>xūn +52F4=>lǜ +52F5=>lì +52F6=>chè +52F7=>ráng +52F8=>quàn +52F9=>bāo +52FA=>sháo +52FB=>yún +52FC=>jiū +52FD=>bào +52FE=>gōu +52FF=>wù +5300=>yún +5301=>wén +5302=>bi +5303=>gài +5304=>gài +5305=>bāo +5306=>cōng +5307=>yi +5308=>xiōng +5309=>pēng +530A=>jū +530B=>táo +530C=>gé +530D=>pú +530E=>è +530F=>páo +5310=>fú +5311=>gōng +5312=>dá +5313=>jiù +5314=>qiōng +5315=>bǐ +5316=>huà +5317=>běi +5318=>nǎo +5319=>shi +531A=>fāng +531B=>jiù +531C=>yí +531D=>zā +531E=>jiàng +531F=>kàng +5320=>jiang +5321=>kuāng +5322=>hū +5323=>xiá +5324=>qū +5325=>biàn +5326=>guǐ +5327=>qiè +5328=>zāng +5329=>kuāng +532A=>fěi +532B=>hū +532C=>yǔ +532D=>guǐ +532E=>kuì +532F=>huì +5330=>dān +5331=>guì +5332=>lián +5333=>lián +5334=>suǎn +5335=>dú +5336=>jiù +5337=>jué +5338=>xì +5339=>pǐ +533A=>qū +533B=>yī +533C=>kē +533D=>yǎn +533E=>biǎn +533F=>nì +5340=>qū +5341=>shí +5342=>xùn +5343=>qiān +5344=>niàn +5345=>sà +5346=>zú +5347=>shēng +5348=>wǔ +5349=>huì +534A=>bàn +534B=>shì +534C=>xì +534D=>wàn +534E=>huá +534F=>xié +5350=>wàn +5351=>bēi +5352=>zú +5353=>zhuō +5354=>xié +5355=>dān +5356=>mài +5357=>nán +5358=>dān +5359=>jí +535A=>bó +535B=>shuài +535C=>bo +535D=>kuàng +535E=>biàn +535F=>bǔ +5360=>zhàn +5361=>kǎ +5362=>lú +5363=>yǒu +5364=>lǔ +5365=>xī +5366=>guà +5367=>wò +5368=>xiè +5369=>jié +536A=>jié +536B=>wèi +536C=>áng +536D=>qióng +536E=>zhī +536F=>mǎo +5370=>yìn +5371=>wēi +5372=>shào +5373=>jí +5374=>què +5375=>luǎn +5376=>chǐ +5377=>juǎn +5378=>xiè +5379=>xù +537A=>jǐn +537B=>què +537C=>wù +537D=>jí +537E=>è +537F=>qīng +5380=>xī +5381=>san +5382=>chǎng +5383=>wěi +5384=>è +5385=>tīng +5386=>lì +5387=>zhé +5388=>hǎn +5389=>lì +538A=>yǎ +538B=>yā +538C=>yàn +538D=>shè +538E=>dǐ +538F=>zhǎ +5390=>páng +5391=>yá +5392=>hé +5393=>yá +5394=>zhì +5395=>cè +5396=>páng +5397=>tí +5398=>lí +5399=>shè +539A=>hòu +539B=>tīng +539C=>zuī +539D=>cuò +539E=>fèi +539F=>yuán +53A0=>cè +53A1=>yuán +53A2=>xiāng +53A3=>yǎn +53A4=>lì +53A5=>jué +53A6=>shà +53A7=>diān +53A8=>chú +53A9=>jiù +53AA=>jǐn +53AB=>áo +53AC=>guǐ +53AD=>yàn +53AE=>sī +53AF=>lì +53B0=>chǎng +53B1=>lán +53B2=>lì +53B3=>yán +53B4=>yǎn +53B5=>yuán +53B6=>sī +53B7=>gōng +53B8=>lín +53B9=>róu +53BA=>qù +53BB=>qù +53BC=>ěr +53BD=>lěi +53BE=>dū +53BF=>xiàn +53C0=>zhuān +53C1=>sān +53C2=>cān +53C3=>cān +53C4=>cān +53C5=>cān +53C6=>ài +53C7=>dài +53C8=>yòu +53C9=>chā +53CA=>jí +53CB=>you +53CC=>shuāng +53CD=>fǎn +53CE=>shōu +53CF=>guài +53D0=>bá +53D1=>fā +53D2=>ruò +53D3=>shì +53D4=>shū +53D5=>zhuó +53D6=>qǔ +53D7=>shòu +53D8=>biàn +53D9=>xù +53DA=>jiǎ +53DB=>pàn +53DC=>sǒu +53DD=>gào +53DE=>wèi +53DF=>sǒu +53E0=>dié +53E1=>ruì +53E2=>cóng +53E3=>kǒu +53E4=>gǔ +53E5=>jù +53E6=>lìng +53E7=>guǎ +53E8=>dāo +53E9=>kòu +53EA=>zhǐ +53EB=>jiào +53EC=>zhào +53ED=>ba +53EE=>dīng +53EF=>kě +53F0=>tái +53F1=>chì +53F2=>shǐ +53F3=>yòu +53F4=>qiú +53F5=>pǒ +53F6=>yè +53F7=>hào +53F8=>sī +53F9=>tàn +53FA=>chǐ +53FB=>lè +53FC=>diāo +53FD=>jī +53FE=>liǎo +53FF=>hōng +5400=>miē +5401=>xū +5402=>máng +5403=>chī +5404=>gè +5405=>xuān +5406=>yāo +5407=>zǐ +5408=>hé +5409=>jí +540A=>diào +540B=>cùn +540C=>tóng +540D=>míng +540E=>hòu +540F=>lì +5410=>tǔ +5411=>xiàng +5412=>zhā +5413=>xià +5414=>yě +5415=>lǚ +5416=>yā +5417=>ma +5418=>ǒu +5419=>huō +541A=>yī +541B=>jūn +541C=>chǒu +541D=>lìn +541E=>tūn +541F=>yín +5420=>fèi +5421=>bǐ +5422=>qìn +5423=>qìn +5424=>jiè +5425=>bù +5426=>fǒu +5427=>ba +5428=>dūn +5429=>fēn +542A=>é +542B=>hán +542C=>tīng +542D=>kēng +542E=>shǔn +542F=>qǐ +5430=>hóng +5431=>zhī +5432=>yǐn +5433=>wú +5434=>wú +5435=>chǎo +5436=>ne +5437=>xuè +5438=>xī +5439=>chuī +543A=>dōu +543B=>wěn +543C=>hǒu +543D=>hōng +543E=>wú +543F=>gào +5440=>ya +5441=>jùn +5442=>lǚ +5443=>è +5444=>gé +5445=>méi +5446=>dāi +5447=>qǐ +5448=>chéng +5449=>wú +544A=>gào +544B=>fū +544C=>jiào +544D=>hōng +544E=>chǐ +544F=>shēng +5450=>ne +5451=>tūn +5452=>fǔ +5453=>yì +5454=>dāi +5455=>ǒu +5456=>lì +5457=>bei +5458=>yuán +5459=>guō +545A=>wen +545B=>qiāng +545C=>wū +545D=>è +545E=>shī +545F=>juǎn +5460=>pěn +5461=>wěn +5462=>ne +5463=>ḿ +5464=>lìng +5465=>rán +5466=>yōu +5467=>dǐ +5468=>zhōu +5469=>shì +546A=>zhòu +546B=>tiè +546C=>xì +546D=>yì +546E=>qì +546F=>píng +5470=>zǐ +5471=>gū +5472=>cī +5473=>wèi +5474=>xǔ +5475=>ā +5476=>náo +5477=>gā +5478=>pēi +5479=>yì +547A=>xiāo +547B=>shēn +547C=>hū +547D=>mìng +547E=>dá +547F=>qù +5480=>jǔ +5481=>hán +5482=>zā +5483=>tuō +5484=>duō +5485=>pǒu +5486=>páo +5487=>bié +5488=>fú +5489=>yāng +548A=>hé +548B=>zǎ +548C=>hé +548D=>hāi +548E=>jiù +548F=>yǒng +5490=>fu +5491=>dā +5492=>zhòu +5493=>wǎ +5494=>kā +5495=>gu +5496=>kā +5497=>zuo +5498=>bù +5499=>lóng +549A=>dōng +549B=>níng +549C=>ta +549D=>sī +549E=>xiàn +549F=>huò +54A0=>qì +54A1=>èr +54A2=>è +54A3=>guāng +54A4=>zhà +54A5=>xì +54A6=>yí +54A7=>lie +54A8=>zī +54A9=>miē +54AA=>mī +54AB=>zhǐ +54AC=>yǎo +54AD=>jī +54AE=>zhòu +54AF=>gē +54B0=>shù +54B1=>zán +54B2=>xiào +54B3=>hāi +54B4=>huī +54B5=>kuǎ +54B6=>huài +54B7=>táo +54B8=>xián +54B9=>è +54BA=>xuǎn +54BB=>xiū +54BC=>guō +54BD=>yàn +54BE=>lǎo +54BF=>yī +54C0=>āi +54C1=>pǐn +54C2=>shěn +54C3=>tóng +54C4=>hōng +54C5=>xiōng +54C6=>duō +54C7=>wa +54C8=>hā +54C9=>zāi +54CA=>yòu +54CB=>diè +54CC=>pài +54CD=>xiǎng +54CE=>āi +54CF=>gén +54D0=>kuāng +54D1=>yǎ +54D2=>dā +54D3=>xiāo +54D4=>bì +54D5=>huì +54D6=>nian +54D7=>huā +54D8=>xing +54D9=>kuài +54DA=>duǒ +54DB=>fēn +54DC=>jì +54DD=>nóng +54DE=>mōu +54DF=>yō +54E0=>hào +54E1=>yuán +54E2=>lòng +54E3=>pǒu +54E4=>máng +54E5=>gē +54E6=>ó +54E7=>chī +54E8=>shào +54E9=>li +54EA=>nǎ +54EB=>zú +54EC=>hé +54ED=>kū +54EE=>xiāo +54EF=>xiàn +54F0=>láo +54F1=>bō +54F2=>zhé +54F3=>zhā +54F4=>liàng +54F5=>bā +54F6=>miē +54F7=>liè +54F8=>suī +54F9=>fú +54FA=>bǔ +54FB=>hàn +54FC=>hēng +54FD=>gěng +54FE=>shuō +54FF=>gě +5500=>yòu +5501=>yàn +5502=>gū +5503=>gǔ +5504=>bei +5505=>hán +5506=>suō +5507=>chún +5508=>yì +5509=>āi +550A=>jiá +550B=>tū +550C=>xián +550D=>wǎn +550E=>lì +550F=>xī +5510=>táng +5511=>zuò +5512=>qiú +5513=>chē +5514=>wú +5515=>zào +5516=>yǎ +5517=>dōu +5518=>qǐ +5519=>dí +551A=>qìn +551B=>mà +551C=>mò +551D=>gòng +551E=>dǒu +551F=>qù +5520=>láo +5521=>liǎng +5522=>suǒ +5523=>zào +5524=>huàn +5525=>lang +5526=>shā +5527=>jī +5528=>zuǒ +5529=>wō +552A=>fěng +552B=>jìn +552C=>hu +552D=>qì +552E=>shòu +552F=>wéi +5530=>shuā +5531=>chàng +5532=>ér +5533=>lì +5534=>qiàng +5535=>ǎn +5536=>zé +5537=>yō +5538=>niàn +5539=>yū +553A=>tiǎn +553B=>lài +553C=>shà +553D=>xī +553E=>tuò +553F=>hū +5540=>ái +5541=>zhāo +5542=>nǒu +5543=>kěn +5544=>zhuó +5545=>zhuó +5546=>shāng +5547=>dì +5548=>hēng +5549=>lín +554A=>a +554B=>cǎi +554C=>xiāng +554D=>tūn +554E=>wǔ +554F=>wèn +5550=>cuì +5551=>shà +5552=>gǔ +5553=>qǐ +5554=>qǐ +5555=>táo +5556=>dàn +5557=>dàn +5558=>yè +5559=>zǐ +555A=>bǐ +555B=>cuì +555C=>chuài +555D=>hé +555E=>yǎ +555F=>qǐ +5560=>zhé +5561=>fēi +5562=>liǎng +5563=>xián +5564=>pí +5565=>shà +5566=>la +5567=>zé +5568=>yīng +5569=>guà +556A=>pā +556B=>zhě +556C=>sè +556D=>zhuàn +556E=>niè +556F=>guo +5570=>luō +5571=>yān +5572=>dì +5573=>quán +5574=>chǎn +5575=>bo +5576=>dìng +5577=>lāng +5578=>xiào +5579=>jú +557A=>táng +557B=>chì +557C=>tí +557D=>án +557E=>jiū +557F=>dàn +5580=>kā +5581=>yóng +5582=>wèi +5583=>nán +5584=>shàn +5585=>yù +5586=>zhé +5587=>lǎ +5588=>jiē +5589=>hóu +558A=>hǎn +558B=>dié +558C=>zhōu +558D=>chái +558E=>wāi +558F=>nuò +5590=>yù +5591=>yīn +5592=>zá +5593=>yāo +5594=>ō +5595=>miǎn +5596=>hú +5597=>yǔn +5598=>chuǎn +5599=>huì +559A=>huàn +559B=>huàn +559C=>xǐ +559D=>hē +559E=>jī +559F=>kuì +55A0=>zhǒng +55A1=>wéi +55A2=>shà +55A3=>xù +55A4=>huáng +55A5=>duó +55A6=>niè +55A7=>xuān +55A8=>liàng +55A9=>yù +55AA=>sàng +55AB=>chī +55AC=>qiáo +55AD=>yàn +55AE=>dān +55AF=>pèn +55B0=>cān +55B1=>lí +55B2=>yō +55B3=>zhā +55B4=>wēi +55B5=>miāo +55B6=>yíng +55B7=>pēn +55B8=>bǔ +55B9=>kuí +55BA=>xì +55BB=>yù +55BC=>jié +55BD=>lou +55BE=>kù +55BF=>zào +55C0=>hù +55C1=>tí +55C2=>yáo +55C3=>hè +55C4=>á +55C5=>xiù +55C6=>qiāng +55C7=>sè +55C8=>yōng +55C9=>sù +55CA=>hǒng +55CB=>xié +55CC=>ài +55CD=>suō +55CE=>ma +55CF=>chā +55D0=>hài +55D1=>kē +55D2=>dā +55D3=>sǎng +55D4=>chēn +55D5=>rù +55D6=>sōu +55D7=>wā +55D8=>jī +55D9=>pǎng +55DA=>wū +55DB=>qiǎn +55DC=>shì +55DD=>gé +55DE=>zī +55DF=>jiē +55E0=>luò +55E1=>wēng +55E2=>wà +55E3=>sì +55E4=>chī +55E5=>háo +55E6=>suo +55E8=>hāi +55E9=>suǒ +55EA=>qín +55EB=>niè +55EC=>hē +55ED=>zhí +55EE=>sài +55EF=>ń +55F0=>gè +55F1=>ná +55F2=>diǎ +55F3=>āi +55F4=>qiang +55F5=>tōng +55F6=>bì +55F7=>áo +55F8=>áo +55F9=>lián +55FA=>zuī +55FB=>zhē +55FC=>mò +55FD=>sou +55FE=>sǒu +55FF=>tǎn +5600=>dí +5601=>qī +5602=>jiào +5603=>chōng +5604=>jiāo +5605=>kǎi +5606=>tàn +5607=>shān +5608=>cáo +5609=>jiā +560A=>ái +560B=>xiāo +560C=>piào +560D=>lou +560E=>gā +560F=>gǔ +5610=>xiāo +5611=>hū +5612=>huì +5613=>guō +5614=>ǒu +5615=>xiān +5616=>zé +5617=>cháng +5618=>xū +5619=>pó +561A=>dē +561B=>ma +561C=>mà +561D=>hú +561E=>lei +561F=>dū +5620=>gā +5621=>tāng +5622=>yě +5623=>bēng +5624=>yīng +5625=>sai +5626=>jiào +5627=>mì +5628=>xiào +5629=>huā +562A=>mǎi +562B=>rán +562C=>chuài +562D=>pēng +562E=>láo +562F=>xiào +5630=>jī +5631=>zhǔ +5632=>cháo +5633=>kuì +5634=>zuǐ +5635=>xiāo +5636=>sī +5637=>háo +5638=>fǔ +5639=>liáo +563A=>qiáo +563B=>xī +563C=>chù +563D=>chǎn +563E=>dàn +563F=>hēi +5640=>xùn +5641=>ě +5642=>zǔn +5643=>fān +5644=>chī +5645=>huī +5646=>zǎn +5647=>chuáng +5648=>cù +5649=>dàn +564A=>yù +564B=>tūn +564C=>cēng +564D=>jiào +564E=>yē +564F=>xī +5650=>qì +5651=>háo +5652=>lián +5653=>xū +5654=>dēng +5655=>huī +5656=>yín +5657=>pū +5658=>juē +5659=>qín +565A=>xún +565B=>niè +565C=>lū +565D=>sī +565E=>yǎn +565F=>yìng +5660=>dā +5661=>zhān +5662=>ō +5663=>zhòu +5664=>jìn +5665=>nóng +5666=>huì +5667=>xiè +5668=>qì +5669=>è +566A=>zào +566B=>yī +566C=>shì +566D=>jiào +566E=>yuàn +566F=>āi +5670=>yōng +5671=>jué +5672=>kuài +5673=>yǔ +5674=>pēn +5675=>dào +5676=>gá +5677=>hm +5678=>dūn +5679=>dāng +567A=>xin +567B=>sāi +567C=>pī +567D=>pǐ +567E=>yīn +567F=>zuǐ +5680=>níng +5681=>dí +5682=>làn +5683=>tā +5684=>huō +5685=>rú +5686=>hāo +5687=>xià +5688=>yè +5689=>duō +568A=>pì +568B=>chóu +568C=>jì +568D=>jìn +568E=>háo +568F=>tì +5690=>cháng +5691=>xun +5692=>me +5693=>cā +5694=>tì +5695=>lǔ +5696=>huì +5697=>bó +5698=>yōu +5699=>niè +569A=>yín +569B=>hù +569C=>me +569D=>hōng +569E=>zhé +569F=>lí +56A0=>liú +56A1=>hai +56A2=>náng +56A3=>xiāo +56A4=>mó +56A5=>yàn +56A6=>lì +56A7=>lú +56A8=>lóng +56A9=>mó +56AA=>dàn +56AB=>chèn +56AC=>pín +56AD=>pǐ +56AE=>xiàng +56AF=>huò +56B0=>mó +56B1=>xì +56B2=>duǒ +56B3=>kù +56B4=>yán +56B5=>chán +56B6=>yīng +56B7=>rǎng +56B8=>diǎn +56B9=>la +56BA=>tà +56BB=>xiāo +56BC=>jué +56BD=>chuò +56BE=>huān +56BF=>huò +56C0=>zhuàn +56C1=>niè +56C2=>xiāo +56C3=>cà +56C4=>lí +56C5=>chǎn +56C6=>chài +56C7=>lì +56C8=>yì +56C9=>luō +56CA=>náng +56CB=>zá +56CC=>sū +56CD=>xǐ +56CE=>zen +56CF=>jiān +56D0=>zá +56D1=>zhǔ +56D2=>lán +56D3=>niè +56D4=>nāng +56D5=>lǎn +56D6=>lo +56D7=>wéi +56D8=>huí +56D9=>yīn +56DA=>qiú +56DB=>sì +56DC=>nín +56DD=>jiǎn +56DE=>huí +56DF=>xìn +56E0=>yīn +56E1=>nān +56E2=>tuán +56E3=>tuán +56E4=>dùn +56E5=>kàng +56E6=>yuān +56E7=>jiǒng +56E8=>piān +56E9=>yún +56EA=>cōng +56EB=>hú +56EC=>huí +56ED=>yuán +56EE=>é +56EF=>guó +56F0=>kùn +56F1=>cōng +56F2=>tōng +56F3=>tú +56F4=>wéi +56F5=>lún +56F6=>guó +56F7=>qūn +56F8=>rì +56F9=>líng +56FA=>gù +56FB=>guó +56FC=>tāi +56FD=>guó +56FE=>tú +56FF=>yòu +5700=>guó +5701=>yín +5702=>hùn +5703=>pǔ +5704=>yǔ +5705=>hán +5706=>yuán +5707=>lún +5708=>quān +5709=>yǔ +570A=>qīng +570B=>guó +570C=>chuán +570D=>wéi +570E=>yuán +570F=>quān +5710=>kū +5711=>fù +5712=>yuán +5713=>yuán +5714=>yà +5715=>tú +5716=>tú +5717=>tú +5718=>tuán +5719=>lüè +571A=>huì +571B=>yì +571C=>huán +571D=>luán +571E=>luán +571F=>tǔ +5720=>yà +5721=>tǔ +5722=>tǐng +5723=>shèng +5724=>pǔ +5725=>lù +5726=>kuai +5727=>yā +5728=>zài +5729=>wéi +572A=>gē +572B=>yù +572C=>wū +572D=>guī +572E=>pǐ +572F=>yí +5730=>de +5731=>qiān +5732=>qiān +5733=>zhèn +5734=>zhuó +5735=>dàng +5736=>qià +5737=>xia +5738=>shan +5739=>kuàng +573A=>chǎng +573B=>qí +573C=>niè +573D=>mò +573E=>jī +573F=>jiá +5740=>zhǐ +5741=>zhǐ +5742=>bǎn +5743=>xūn +5744=>yì +5745=>qǐn +5746=>méi +5747=>jūn +5748=>rǒng +5749=>tún +574A=>fang +574B=>bèn +574C=>bèn +574D=>tān +574E=>kǎn +574F=>huài +5750=>zuò +5751=>kēng +5752=>bì +5753=>jǐng +5754=>dì +5755=>jīng +5756=>jì +5757=>kuài +5758=>dǐ +5759=>jīng +575A=>jiān +575B=>tán +575C=>lì +575D=>bà +575E=>wù +575F=>fén +5760=>zhuì +5761=>pō +5762=>bàn +5763=>tāng +5764=>kūn +5765=>qū +5766=>tǎn +5767=>zhī +5768=>tuó +5769=>gān +576A=>píng +576B=>diàn +576C=>guà +576D=>ní +576E=>tái +576F=>pī +5770=>jiōng +5771=>yǎng +5772=>fó +5773=>ào +5774=>lù +5775=>qiū +5776=>mǔ +5777=>kě +5778=>gòu +5779=>xuè +577A=>bá +577B=>chí +577C=>chè +577D=>líng +577E=>zhù +577F=>fù +5780=>hū +5781=>zhì +5782=>chuí +5783=>lā +5784=>lǒng +5785=>lǒng +5786=>lú +5787=>ào +5788=>dài +5789=>páo +578A=>min +578B=>xíng +578C=>dòng +578D=>jì +578E=>hè +578F=>lǜ +5790=>cí +5791=>chǐ +5792=>lěi +5793=>gāi +5794=>yīn +5795=>hòu +5796=>duī +5797=>zhào +5798=>fú +5799=>guāng +579A=>yáo +579B=>duǒ +579C=>duǒ +579D=>guǐ +579E=>chá +579F=>yáng +57A0=>yín +57A1=>fá +57A2=>gòu +57A3=>yuán +57A4=>dié +57A5=>xié +57A6=>kěn +57A7=>shǎng +57A8=>shǒu +57A9=>è +57AA=>bing +57AB=>diàn +57AC=>hóng +57AD=>yā +57AE=>kuǎ +57AF=>da +57B0=>ka +57B1=>dàng +57B2=>kǎi +57B3=>hang +57B4=>nǎo +57B5=>ǎn +57B6=>xīng +57B7=>xiàn +57B8=>yuàn +57B9=>bāng +57BA=>fū +57BB=>bà +57BC=>yì +57BD=>yìn +57BE=>hàn +57BF=>xù +57C0=>chuí +57C1=>qín +57C2=>gěng +57C3=>āi +57C4=>běng +57C5=>fáng +57C6=>què +57C7=>yǒng +57C8=>jùn +57C9=>jiā +57CA=>dì +57CB=>mái +57CC=>làng +57CD=>juǎn +57CE=>chéng +57CF=>shān +57D0=>jīn +57D1=>zhé +57D2=>liè +57D3=>liè +57D4=>bù +57D5=>chéng +57D6=>hua +57D7=>bù +57D8=>shí +57D9=>xūn +57DA=>guō +57DB=>jiōng +57DC=>yě +57DD=>niàn +57DE=>dǐ +57DF=>yù +57E0=>bù +57E1=>yā +57E2=>quán +57E3=>suì +57E4=>pí +57E5=>qīng +57E6=>wǎn +57E7=>jù +57E8=>lǔn +57E9=>zhēng +57EA=>kōng +57EB=>chǒng +57EC=>dōng +57ED=>dài +57EE=>tàn +57EF=>ǎn +57F0=>cài +57F1=>chù +57F2=>běng +57F3=>kǎn +57F4=>zhí +57F5=>duǒ +57F6=>yì +57F7=>zhí +57F8=>yì +57F9=>péi +57FA=>jī +57FB=>zhǔn +57FC=>qí +57FD=>sào +57FE=>jù +57FF=>ní +5800=>kū +5801=>kè +5802=>táng +5803=>kūn +5804=>nì +5805=>jiān +5806=>duī +5807=>jǐn +5808=>gāng +5809=>yù +580A=>è +580B=>péng +580C=>gù +580D=>tù +580E=>lèng +580F=>fang +5810=>yá +5811=>qiàn +5812=>kun +5813=>àn +5814=>shen +5815=>duò +5816=>nǎo +5817=>tū +5818=>chéng +5819=>yīn +581A=>hún +581B=>bì +581C=>liàn +581D=>guō +581E=>dié +581F=>zhuàn +5820=>hòu +5821=>bǎo +5822=>bǎo +5823=>yú +5824=>dī +5825=>máo +5826=>jiē +5827=>ruán +5828=>yè +5829=>gèng +582A=>kān +582B=>zōng +582C=>yú +582D=>huáng +582E=>è +582F=>yáo +5830=>yàn +5831=>bào +5832=>cí +5833=>méi +5834=>chǎng +5835=>dǔ +5836=>tuó +5837=>yìn +5838=>féng +5839=>zhòng +583A=>jiè +583B=>jīn +583C=>hèng +583D=>gāng +583E=>chūn +583F=>jiǎn +5840=>ping +5841=>lei +5842=>xiàng +5843=>huāng +5844=>léng +5845=>duàn +5846=>wān +5847=>xuān +5848=>jì +5849=>jí +584A=>kuài +584B=>yíng +584C=>tā +584D=>chéng +584E=>yǒng +584F=>kǎi +5850=>sù +5851=>sù +5852=>shí +5853=>mì +5854=>tǎ +5855=>wěng +5856=>chéng +5857=>tú +5858=>táng +5859=>què +585A=>zhǒng +585B=>lì +585C=>zhǒng +585D=>bàng +585E=>sāi +585F=>zàng +5860=>duī +5861=>tián +5862=>wù +5863=>zhèng +5864=>xūn +5865=>gé +5866=>zhèn +5867=>ài +5868=>gōng +5869=>yán +586A=>kǎn +586B=>tián +586C=>yuán +586D=>wēn +586E=>xiè +586F=>liù +5870=>hai +5871=>lǎng +5872=>cháng +5873=>péng +5874=>bèng +5875=>chén +5876=>lù +5877=>lǔ +5878=>ōu +5879=>qiàn +587A=>méi +587B=>mò +587C=>zhuān +587D=>shuǎng +587E=>shú +587F=>lǒu +5880=>chí +5881=>màn +5882=>biāo +5883=>jìng +5884=>cè +5885=>shù +5886=>zhì +5887=>zhāng +5888=>kàn +5889=>yōng +588A=>diàn +588B=>chěn +588C=>zhí +588D=>xì +588E=>guō +588F=>qiǎng +5890=>jìn +5891=>dì +5892=>shāng +5893=>mù +5894=>cuī +5895=>yàn +5896=>tǎ +5897=>zēng +5898=>qián +5899=>qiáng +589A=>liáng +589B=>wei +589C=>zhuì +589D=>qiāo +589E=>zēng +589F=>xū +58A0=>shàn +58A1=>shàn +58A2=>bá +58A3=>pú +58A4=>kuài +58A5=>dǒng +58A6=>fán +58A7=>què +58A8=>mò +58A9=>dūn +58AA=>dūn +58AB=>zūn +58AC=>dì +58AD=>shèng +58AE=>duò +58AF=>duò +58B0=>tán +58B1=>dèng +58B2=>mú +58B3=>fén +58B4=>huáng +58B5=>tán +58B6=>da +58B7=>yè +58B8=>zhu +58B9=>jian +58BA=>ào +58BB=>qiáng +58BC=>jī +58BD=>qiāo +58BE=>kěn +58BF=>yì +58C0=>pí +58C1=>bì +58C2=>diàn +58C3=>jiāng +58C4=>yě +58C5=>yōng +58C6=>xué +58C7=>tán +58C8=>lǎn +58C9=>jù +58CA=>huài +58CB=>dàng +58CC=>rǎng +58CD=>qiàn +58CE=>xūn +58CF=>xiàn +58D0=>xǐ +58D1=>hè +58D2=>ài +58D3=>yā +58D4=>dǎo +58D5=>háo +58D6=>ruán +58D7=>jin +58D8=>lěi +58D9=>kuàng +58DA=>lú +58DB=>yán +58DC=>tán +58DD=>wěi +58DE=>huài +58DF=>lǒng +58E0=>lǒng +58E1=>ruì +58E2=>lì +58E3=>lín +58E4=>rǎng +58E5=>chan +58E6=>xūn +58E7=>yán +58E8=>léi +58E9=>bà +58EA=>wān +58EB=>shì +58EC=>rén +58ED=>san +58EE=>zhuàng +58EF=>zhuàng +58F0=>shēng +58F1=>yī +58F2=>mài +58F3=>ké +58F4=>zhù +58F5=>zhuàng +58F6=>hú +58F7=>hú +58F8=>kǔn +58F9=>yī +58FA=>hú +58FB=>xù +58FC=>kǔn +58FD=>shòu +58FE=>mǎng +58FF=>zūn +5900=>shòu +5901=>yī +5902=>zhǐ +5903=>gǔ +5904=>chù +5905=>jiàng +5906=>féng +5907=>bèi +5908=>zhai +5909=>biàn +590A=>suī +590B=>qūn +590C=>líng +590D=>fù +590E=>cuò +590F=>xià +5910=>xiòng +5911=>xie +5912=>náo +5913=>xià +5914=>kuí +5915=>xī +5916=>wài +5917=>yuàn +5918=>mǎo +5919=>sù +591A=>duō +591B=>duō +591C=>yè +591D=>qíng +591E=>wài +591F=>gòu +5920=>gòu +5921=>qì +5922=>mèng +5923=>mèng +5924=>yín +5925=>huǒ +5926=>chěn +5927=>dà +5928=>zè +5929=>tiān +592A=>tài +592B=>fu +592C=>guài +592D=>yāo +592E=>yāng +592F=>hāng +5930=>gǎo +5931=>shī +5932=>tāo +5933=>tài +5934=>tóu +5935=>yǎn +5936=>bǐ +5937=>yí +5938=>kuā +5939=>jiā +593A=>duó +593B=>huà +593C=>kuǎng +593D=>yǔn +593E=>jiā +593F=>bā +5940=>ēn +5941=>lián +5942=>huàn +5943=>dī +5944=>yǎn +5945=>pào +5946=>juàn +5947=>qí +5948=>nài +5949=>fèng +594A=>xié +594B=>fèn +594C=>diǎn +594D=>yang +594E=>kuí +594F=>zòu +5950=>huàn +5951=>qì +5952=>kāi +5953=>zhā +5954=>bēn +5955=>yì +5956=>jiǎng +5957=>tào +5958=>zàng +5959=>běn +595A=>xī +595B=>huǎng +595C=>fěi +595D=>diāo +595E=>xùn +595F=>bēng +5960=>diàn +5961=>ào +5962=>shē +5963=>wěng +5964=>hǎ +5965=>ào +5966=>wù +5967=>ào +5968=>jiǎng +5969=>lián +596A=>duó +596B=>yūn +596C=>jiǎng +596D=>shì +596E=>fèn +596F=>huò +5970=>bì +5971=>luán +5972=>duǒ +5973=>nǚ +5974=>nú +5975=>dǐng +5976=>nǎi +5977=>qiān +5978=>jiān +5979=>tā +597A=>jiǔ +597B=>nuán +597C=>chà +597D=>hǎo +597E=>xiān +597F=>fàn +5980=>jǐ +5981=>shuò +5982=>rú +5983=>fēi +5984=>wàng +5985=>hóng +5986=>zhuāng +5987=>fù +5988=>mā +5989=>dān +598A=>rèn +598B=>fū +598C=>jìng +598D=>yán +598E=>hài +598F=>wèn +5990=>zhōng +5991=>pā +5992=>dù +5993=>jì +5994=>kēng +5995=>zhòng +5996=>yāo +5997=>jìn +5998=>yún +5999=>miào +599A=>fǒu +599B=>chi +599C=>yuè +599D=>zhuāng +599E=>niū +599F=>yàn +59A0=>nà +59A1=>xīn +59A2=>fén +59A3=>bǐ +59A4=>yú +59A5=>tuǒ +59A6=>fēng +59A7=>wàn +59A8=>fáng +59A9=>wǔ +59AA=>yù +59AB=>guī +59AC=>dù +59AD=>bá +59AE=>nī +59AF=>zhóu +59B0=>zhuó +59B1=>zhāo +59B2=>dá +59B3=>nǎi +59B4=>yuàn +59B5=>tǒu +59B6=>xián +59B7=>zhí +59B8=>ē +59B9=>mèi +59BA=>mò +59BB=>qī +59BC=>bì +59BD=>shēn +59BE=>qiè +59BF=>ē +59C0=>hé +59C1=>xǔ +59C2=>fá +59C3=>zhēng +59C4=>mín +59C5=>bàn +59C6=>mǔ +59C7=>fū +59C8=>líng +59C9=>zǐ +59CA=>zǐ +59CB=>shǐ +59CC=>rǎn +59CD=>shān +59CE=>yāng +59CF=>mán +59D0=>jie +59D1=>gū +59D2=>sì +59D3=>xìng +59D4=>wěi +59D5=>zī +59D6=>jù +59D7=>shān +59D8=>pīn +59D9=>rèn +59DA=>yáo +59DB=>dòng +59DC=>jiāng +59DD=>shū +59DE=>jí +59DF=>gāi +59E0=>xiàng +59E1=>huá +59E2=>juān +59E3=>jiāo +59E4=>gòu +59E5=>lǎo +59E6=>jiān +59E7=>jiān +59E8=>yí +59E9=>niàn +59EA=>zhí +59EB=>jī +59EC=>jī +59ED=>xiàn +59EE=>héng +59EF=>guāng +59F0=>jūn +59F1=>kuā +59F2=>yàn +59F3=>mǐng +59F4=>liè +59F5=>pèi +59F6=>è +59F7=>yòu +59F8=>yán +59F9=>chà +59FA=>shēn +59FB=>yīn +59FC=>shí +59FD=>guǐ +59FE=>quán +59FF=>zī +5A00=>sōng +5A01=>wēi +5A02=>hóng +5A03=>wá +5A04=>lóu +5A05=>yà +5A06=>ráo +5A07=>jiāo +5A08=>luán +5A09=>pīng +5A0A=>xiàn +5A0B=>shào +5A0C=>lǐ +5A0D=>chéng +5A0E=>xiè +5A0F=>máng +5A10=>fū +5A11=>suō +5A12=>méi +5A13=>wěi +5A14=>kè +5A15=>chuò +5A16=>chuò +5A17=>tǐng +5A18=>niang +5A19=>xíng +5A1A=>nán +5A1B=>yú +5A1C=>nà +5A1D=>pōu +5A1E=>něi +5A1F=>juān +5A20=>shēn +5A21=>zhì +5A22=>hán +5A23=>dì +5A24=>zhuāng +5A25=>é +5A26=>pín +5A27=>tuì +5A28=>xiàn +5A29=>miǎn +5A2A=>wú +5A2B=>yán +5A2C=>wǔ +5A2D=>āi +5A2E=>yán +5A2F=>yú +5A30=>sì +5A31=>yú +5A32=>wā +5A33=>li +5A34=>xián +5A35=>jū +5A36=>qǔ +5A37=>zhuì +5A38=>qī +5A39=>xián +5A3A=>zhuó +5A3B=>dōng +5A3C=>chāng +5A3D=>lù +5A3E=>ǎi +5A3F=>ē +5A40=>ē +5A41=>lóu +5A42=>mián +5A43=>cóng +5A44=>pǒu +5A45=>jú +5A46=>pó +5A47=>cāi +5A48=>líng +5A49=>wǎn +5A4A=>biǎo +5A4B=>xiāo +5A4C=>shú +5A4D=>qǐ +5A4E=>huī +5A4F=>fàn +5A50=>wǒ +5A51=>ruí +5A52=>tán +5A53=>fēi +5A54=>fei +5A55=>jié +5A56=>tiān +5A57=>ní +5A58=>quán +5A59=>jìng +5A5A=>hūn +5A5B=>jīng +5A5C=>qiān +5A5D=>diàn +5A5E=>xìng +5A5F=>hù +5A60=>wān +5A61=>lái +5A62=>bì +5A63=>yīn +5A64=>chōu +5A65=>nào +5A66=>fù +5A67=>jìng +5A68=>lún +5A69=>àn +5A6A=>lán +5A6B=>kūn +5A6C=>yín +5A6D=>yà +5A6E=>jū +5A6F=>lì +5A70=>diǎn +5A71=>xián +5A72=>hua +5A73=>huà +5A74=>yīng +5A75=>chán +5A76=>shěn +5A77=>tíng +5A78=>dàng +5A79=>yǎo +5A7A=>wù +5A7B=>nàn +5A7C=>chuò +5A7D=>jiǎ +5A7E=>tōu +5A7F=>xù +5A80=>yù +5A81=>wéi +5A82=>dì +5A83=>róu +5A84=>měi +5A85=>dān +5A86=>ruǎn +5A87=>qīn +5A88=>huī +5A89=>wò +5A8A=>qián +5A8B=>chūn +5A8C=>miáo +5A8D=>fù +5A8E=>jiě +5A8F=>duān +5A90=>yí +5A91=>zhòng +5A92=>méi +5A93=>huáng +5A94=>mián +5A95=>ān +5A96=>yīng +5A97=>xuān +5A98=>jiē +5A99=>wēi +5A9A=>mèi +5A9B=>yuàn +5A9C=>zhēng +5A9D=>qiū +5A9E=>shì +5A9F=>xiè +5AA0=>tuǒ +5AA1=>liàn +5AA2=>mào +5AA3=>rǎn +5AA4=>sī +5AA5=>piān +5AA6=>wèi +5AA7=>wā +5AA8=>jiù +5AA9=>hú +5AAA=>ǎo +5AAB=>qie +5AAC=>bǎo +5AAD=>xū +5AAE=>tōu +5AAF=>guī +5AB0=>chú +5AB1=>yáo +5AB2=>pì +5AB3=>xí +5AB4=>yuán +5AB5=>yìng +5AB6=>róng +5AB7=>rù +5AB8=>chī +5AB9=>liú +5ABA=>měi +5ABB=>pán +5ABC=>ǎo +5ABD=>mā +5ABE=>gòu +5ABF=>kuì +5AC0=>qín +5AC1=>jià +5AC2=>sǎo +5AC3=>zhēn +5AC4=>yuán +5AC5=>jiē +5AC6=>róng +5AC7=>míng +5AC8=>yīng +5AC9=>jí +5ACA=>sù +5ACB=>niǎo +5ACC=>xián +5ACD=>tāo +5ACE=>páng +5ACF=>láng +5AD0=>nǎo +5AD1=>báo +5AD2=>ài +5AD3=>pì +5AD4=>pín +5AD5=>yì +5AD6=>piáo +5AD7=>yù +5AD8=>léi +5AD9=>xuán +5ADA=>mān +5ADB=>yī +5ADC=>zhāng +5ADD=>kāng +5ADE=>yōng +5ADF=>nì +5AE0=>lí +5AE1=>dí +5AE2=>guī +5AE3=>yān +5AE4=>jǐn +5AE5=>zhuān +5AE6=>cháng +5AE7=>zé +5AE8=>hān +5AE9=>nèn +5AEA=>lào +5AEB=>mó +5AEC=>zhē +5AED=>hù +5AEE=>hù +5AEF=>ào +5AF0=>nèn +5AF1=>qiáng +5AF2=>ma +5AF3=>piè +5AF4=>gū +5AF5=>wǔ +5AF6=>qiáo +5AF7=>tuǒ +5AF8=>zhǎn +5AF9=>máo +5AFA=>xián +5AFB=>xián +5AFC=>mò +5AFD=>liáo +5AFE=>lián +5AFF=>huà +5B00=>guī +5B01=>dēng +5B02=>zhí +5B03=>xū +5B04=>yī +5B05=>huà +5B06=>xī +5B07=>kuì +5B08=>ráo +5B09=>xī +5B0A=>yàn +5B0B=>chán +5B0C=>jiāo +5B0D=>měi +5B0E=>fàn +5B0F=>fān +5B10=>xiān +5B11=>yì +5B12=>huì +5B13=>jiào +5B14=>fù +5B15=>shì +5B16=>bì +5B17=>shàn +5B18=>suì +5B19=>qiáng +5B1A=>liǎn +5B1B=>huán +5B1C=>xīn +5B1D=>niǎo +5B1E=>dǒng +5B1F=>yì +5B20=>cān +5B21=>ài +5B22=>niáng +5B23=>níng +5B24=>mā +5B25=>tiǎo +5B26=>chóu +5B27=>jìn +5B28=>cí +5B29=>yú +5B2A=>pín +5B2B=>róng +5B2C=>rú +5B2D=>nǎi +5B2E=>yān +5B2F=>tái +5B30=>yīng +5B31=>cán +5B32=>niǎo +5B33=>yuè +5B34=>yíng +5B35=>mián +5B36=>bi +5B37=>mā +5B38=>shěn +5B39=>xìng +5B3A=>nì +5B3B=>dú +5B3C=>liǔ +5B3D=>yuān +5B3E=>lǎn +5B3F=>yàn +5B40=>shuāng +5B41=>líng +5B42=>jiǎo +5B43=>niáng +5B44=>lǎn +5B45=>qiān +5B46=>yīng +5B47=>shuāng +5B48=>huì +5B49=>quán +5B4A=>mǐ +5B4B=>lí +5B4C=>luán +5B4D=>yán +5B4E=>zhú +5B4F=>lǎn +5B50=>zi +5B51=>jié +5B52=>jué +5B53=>jué +5B54=>kǒng +5B55=>yùn +5B56=>mā +5B57=>zì +5B58=>cún +5B59=>sūn +5B5A=>fú +5B5B=>bèi +5B5C=>zī +5B5D=>xiào +5B5E=>xìn +5B5F=>mèng +5B60=>sì +5B61=>tāi +5B62=>bāo +5B63=>jì +5B64=>gū +5B65=>nú +5B66=>xué +5B67=>you +5B68=>zhuǎn +5B69=>hái +5B6A=>luán +5B6B=>sūn +5B6C=>nāo +5B6D=>miē +5B6E=>cóng +5B6F=>qiān +5B70=>shú +5B71=>càn +5B72=>yā +5B73=>zī +5B74=>nǐ +5B75=>fū +5B76=>zī +5B77=>lí +5B78=>xué +5B79=>bò +5B7A=>rú +5B7B=>nái +5B7C=>niè +5B7D=>niè +5B7E=>yīng +5B7F=>luán +5B80=>mián +5B81=>níng +5B82=>rǒng +5B83=>tā +5B84=>guǐ +5B85=>zhái +5B86=>qióng +5B87=>yǔ +5B88=>shǒu +5B89=>ān +5B8A=>tū +5B8B=>sòng +5B8C=>wán +5B8D=>ròu +5B8E=>yǎo +5B8F=>hóng +5B90=>yí +5B91=>jǐng +5B92=>zhūn +5B93=>mì +5B94=>zhǔ +5B95=>dàng +5B96=>hóng +5B97=>zōng +5B98=>guān +5B99=>zhòu +5B9A=>dìng +5B9B=>wǎn +5B9C=>yi +5B9D=>bǎo +5B9E=>shí +5B9F=>shí +5BA0=>chǒng +5BA1=>shěn +5BA2=>kè +5BA3=>xuān +5BA4=>shì +5BA5=>yòu +5BA6=>huàn +5BA7=>yí +5BA8=>tiǎo +5BA9=>shǐ +5BAA=>xiàn +5BAB=>gōng +5BAC=>chéng +5BAD=>qún +5BAE=>gōng +5BAF=>xiāo +5BB0=>zǎi +5BB1=>zhà +5BB2=>bǎo +5BB3=>hài +5BB4=>yàn +5BB5=>xiāo +5BB6=>jiā +5BB7=>shěn +5BB8=>chén +5BB9=>róng +5BBA=>huǎng +5BBB=>mì +5BBC=>kòu +5BBD=>kuān +5BBE=>bīn +5BBF=>sù +5BC0=>cǎi +5BC1=>zǎn +5BC2=>jì +5BC3=>yuān +5BC4=>jì +5BC5=>yín +5BC6=>mì +5BC7=>kòu +5BC8=>qīng +5BC9=>què +5BCA=>zhēn +5BCB=>jiàn +5BCC=>fù +5BCD=>níng +5BCE=>bìng +5BCF=>huán +5BD0=>mèi +5BD1=>qǐn +5BD2=>hán +5BD3=>yù +5BD4=>shí +5BD5=>níng +5BD6=>jìn +5BD7=>níng +5BD8=>zhì +5BD9=>yǔ +5BDA=>bǎo +5BDB=>kuān +5BDC=>níng +5BDD=>qǐn +5BDE=>mò +5BDF=>chá +5BE0=>jù +5BE1=>guǎ +5BE2=>qǐn +5BE3=>hū +5BE4=>wù +5BE5=>liáo +5BE6=>shí +5BE7=>níng +5BE8=>zhài +5BE9=>shěn +5BEA=>wěi +5BEB=>xiě +5BEC=>kuān +5BED=>huì +5BEE=>liáo +5BEF=>jùn +5BF0=>huán +5BF1=>yì +5BF2=>yí +5BF3=>bǎo +5BF4=>qīn +5BF5=>chǒng +5BF6=>bǎo +5BF7=>fēng +5BF8=>cùn +5BF9=>duì +5BFA=>sì +5BFB=>xún +5BFC=>dǎo +5BFD=>lǜ +5BFE=>duì +5BFF=>shòu +5C00=>pǒ +5C01=>fēng +5C02=>zhuān +5C03=>fū +5C04=>shè +5C05=>kè +5C06=>jiāng +5C07=>jiāng +5C08=>zhuān +5C09=>wèi +5C0A=>zūn +5C0B=>xún +5C0C=>shù +5C0D=>duì +5C0E=>dǎo +5C0F=>xiǎo +5C10=>jié +5C11=>shǎo +5C12=>ěr +5C13=>ěr +5C14=>ěr +5C15=>gǎ +5C16=>jiān +5C17=>shū +5C18=>chén +5C19=>shàng +5C1A=>shàng +5C1B=>mo +5C1C=>gá +5C1D=>cháng +5C1E=>liào +5C1F=>xiǎn +5C20=>xiǎn +5C21=>kun +5C22=>yóu +5C23=>wāng +5C24=>yóu +5C25=>liào +5C26=>liào +5C27=>yáo +5C28=>máng +5C29=>wāng +5C2A=>wāng +5C2B=>wāng +5C2C=>gà +5C2D=>yáo +5C2E=>duò +5C2F=>kuì +5C30=>zhǒng +5C31=>jiù +5C32=>gān +5C33=>gǔ +5C34=>gān +5C35=>tuí +5C36=>gān +5C37=>gān +5C38=>shī +5C39=>yǐn +5C3A=>chǐ +5C3B=>kāo +5C3C=>ní +5C3D=>jǐn +5C3E=>wěi +5C3F=>niào +5C40=>jú +5C41=>pì +5C42=>céng +5C43=>xì +5C44=>bī +5C45=>jū +5C46=>jiè +5C47=>tián +5C48=>qū +5C49=>ti +5C4A=>jiè +5C4B=>wū +5C4C=>diǎo +5C4D=>shī +5C4E=>shǐ +5C4F=>píng +5C50=>jī +5C51=>xiè +5C52=>zhěn +5C53=>xiè +5C54=>ní +5C55=>zhǎn +5C56=>xī +5C57=>wěi +5C58=>mǎn +5C59=>ē +5C5A=>lòu +5C5B=>píng +5C5C=>ti +5C5D=>fèi +5C5E=>shǔ +5C5F=>xiè +5C60=>tú +5C61=>lǚ +5C62=>lǚ +5C63=>xǐ +5C64=>céng +5C65=>lǚ +5C66=>jù +5C67=>xiè +5C68=>jù +5C69=>juē +5C6A=>liáo +5C6B=>jué +5C6C=>shǔ +5C6D=>xì +5C6E=>chè +5C6F=>tún +5C70=>nì +5C71=>shān +5C72=>wa +5C73=>xiān +5C74=>lì +5C75=>è +5C76=>dao +5C77=>hui +5C78=>lóng +5C79=>yì +5C7A=>qǐ +5C7B=>rèn +5C7C=>wù +5C7D=>hàn +5C7E=>shēn +5C7F=>yǔ +5C80=>chū +5C81=>suì +5C82=>qǐ +5C83=>rèn +5C84=>yuè +5C85=>bǎn +5C86=>yǎo +5C87=>áng +5C88=>yá +5C89=>wù +5C8A=>jié +5C8B=>è +5C8C=>jí +5C8D=>qiān +5C8E=>fén +5C8F=>wán +5C90=>qí +5C91=>cén +5C92=>qián +5C93=>qí +5C94=>chà +5C95=>jiè +5C96=>qū +5C97=>gǎng +5C98=>xiàn +5C99=>ào +5C9A=>lán +5C9B=>dǎo +5C9C=>bā +5C9D=>zuò +5C9E=>zuò +5C9F=>yǎng +5CA0=>jù +5CA1=>gāng +5CA2=>kě +5CA3=>gǒu +5CA4=>xué +5CA5=>pō +5CA6=>lì +5CA7=>tiáo +5CA8=>qū +5CA9=>yán +5CAA=>fú +5CAB=>xiù +5CAC=>jiǎ +5CAD=>lǐng +5CAE=>tuó +5CAF=>pí +5CB0=>ào +5CB1=>dài +5CB2=>kuàng +5CB3=>yuè +5CB4=>qū +5CB5=>hù +5CB6=>pò +5CB7=>mín +5CB8=>àn +5CB9=>tiáo +5CBA=>líng +5CBB=>chí +5CBC=>ping +5CBD=>dōng +5CBE=>hàn +5CBF=>kuī +5CC0=>xiù +5CC1=>mǎo +5CC2=>tóng +5CC3=>xué +5CC4=>yì +5CC5=>bian +5CC6=>hé +5CC7=>bā +5CC8=>luò +5CC9=>è +5CCA=>fù +5CCB=>xún +5CCC=>dié +5CCD=>lù +5CCE=>ěn +5CCF=>ér +5CD0=>gāi +5CD1=>quān +5CD2=>dòng +5CD3=>yí +5CD4=>mǔ +5CD5=>shí +5CD6=>ān +5CD7=>wéi +5CD8=>huán +5CD9=>zhì +5CDA=>mì +5CDB=>lǐ +5CDC=>jì +5CDD=>tóng +5CDE=>wéi +5CDF=>yòu +5CE0=>gu +5CE1=>xiá +5CE2=>lǐ +5CE3=>yáo +5CE4=>jiào +5CE5=>zhēng +5CE6=>luán +5CE7=>jiāo +5CE8=>é +5CE9=>é +5CEA=>yù +5CEB=>xié +5CEC=>bū +5CED=>qiào +5CEE=>qūn +5CEF=>fēng +5CF0=>fēng +5CF1=>náo +5CF2=>lǐ +5CF3=>yóu +5CF4=>xiàn +5CF5=>hóng +5CF6=>dǎo +5CF7=>shēn +5CF8=>chéng +5CF9=>tú +5CFA=>gěng +5CFB=>jùn +5CFC=>hào +5CFD=>xiá +5CFE=>yín +5CFF=>yǔ +5D00=>làng +5D01=>kàn +5D02=>láo +5D03=>lái +5D04=>xiǎn +5D05=>què +5D06=>kōng +5D07=>chóng +5D08=>chóng +5D09=>tà +5D0A=>lín +5D0B=>huà +5D0C=>jū +5D0D=>lái +5D0E=>qí +5D0F=>mín +5D10=>kūn +5D11=>kūn +5D12=>zú +5D13=>gù +5D14=>cuī +5D15=>yá +5D16=>yá +5D17=>gǎng +5D18=>lún +5D19=>lún +5D1A=>léng +5D1B=>jué +5D1C=>duō +5D1D=>zhēng +5D1E=>guō +5D1F=>yín +5D20=>dōng +5D21=>hán +5D22=>zhēng +5D23=>wěi +5D24=>xiáo +5D25=>pí +5D26=>yān +5D27=>sōng +5D28=>jié +5D29=>bēng +5D2A=>zú +5D2B=>kū +5D2C=>dōng +5D2D=>zhǎn +5D2E=>gù +5D2F=>yín +5D30=>zi +5D31=>zè +5D32=>huáng +5D33=>yú +5D34=>wǎi +5D35=>yáng +5D36=>fēng +5D37=>qiú +5D38=>yáng +5D39=>tí +5D3A=>yǐ +5D3B=>zhì +5D3C=>shì +5D3D=>zǎi +5D3E=>yǎo +5D3F=>è +5D40=>zhù +5D41=>kān +5D42=>lǜ +5D43=>yǎn +5D44=>měi +5D45=>hán +5D46=>jī +5D47=>jī +5D48=>huàn +5D49=>tíng +5D4A=>shèng +5D4B=>méi +5D4C=>qiàn +5D4D=>wù +5D4E=>yú +5D4F=>zōng +5D50=>lán +5D51=>kě +5D52=>yán +5D53=>yán +5D54=>wěi +5D55=>zōng +5D56=>chá +5D57=>suì +5D58=>róng +5D59=>ke +5D5A=>qīn +5D5B=>yú +5D5C=>ti +5D5D=>lǒu +5D5E=>tú +5D5F=>duī +5D60=>xī +5D61=>wěng +5D62=>cāng +5D63=>dàng +5D64=>róng +5D65=>jié +5D66=>kǎi +5D67=>liú +5D68=>wù +5D69=>sōng +5D6A=>qiāo +5D6B=>zī +5D6C=>wéi +5D6D=>bēng +5D6E=>diān +5D6F=>cuó +5D70=>qiǎn +5D71=>yǒng +5D72=>niè +5D73=>cuó +5D74=>jǐ +5D75=>shi +5D76=>ruo +5D77=>sǒng +5D78=>zōng +5D79=>jiàng +5D7A=>liáo +5D7B=>kāng +5D7C=>chǎn +5D7D=>dié +5D7E=>cēn +5D7F=>dǐng +5D80=>tū +5D81=>lǒu +5D82=>zhàng +5D83=>zhǎn +5D84=>zhǎn +5D85=>áo +5D86=>cáo +5D87=>qū +5D88=>qiāng +5D89=>cuī +5D8A=>zuǐ +5D8B=>dǎo +5D8C=>dǎo +5D8D=>xí +5D8E=>yù +5D8F=>pèi +5D90=>lóng +5D91=>xiàng +5D92=>céng +5D93=>bō +5D94=>qīn +5D95=>jiāo +5D96=>yǎn +5D97=>láo +5D98=>zhàn +5D99=>lín +5D9A=>liáo +5D9B=>liáo +5D9C=>jīn +5D9D=>dèng +5D9E=>duò +5D9F=>zūn +5DA0=>jiào +5DA1=>guì +5DA2=>yáo +5DA3=>jiāo +5DA4=>yáo +5DA5=>jué +5DA6=>zhān +5DA7=>yì +5DA8=>xué +5DA9=>náo +5DAA=>yè +5DAB=>yè +5DAC=>yí +5DAD=>niè +5DAE=>xiǎn +5DAF=>jí +5DB0=>xiè +5DB1=>kě +5DB2=>xī +5DB3=>dì +5DB4=>ào +5DB5=>zuǐ +5DB6=>wei +5DB7=>yí +5DB8=>róng +5DB9=>dǎo +5DBA=>lǐng +5DBB=>zá +5DBC=>yǔ +5DBD=>yuè +5DBE=>yǐn +5DBF=>ru +5DC0=>jié +5DC1=>lì +5DC2=>guī +5DC3=>lóng +5DC4=>lóng +5DC5=>diān +5DC6=>róng +5DC7=>xī +5DC8=>jú +5DC9=>chán +5DCA=>yǐng +5DCB=>kuī +5DCC=>yán +5DCD=>wēi +5DCE=>náo +5DCF=>quán +5DD0=>chǎo +5DD1=>cuán +5DD2=>luán +5DD3=>diān +5DD4=>diān +5DD5=>nie +5DD6=>yán +5DD7=>yán +5DD8=>yǎn +5DD9=>kuí +5DDA=>yǎn +5DDB=>chuān +5DDC=>kuài +5DDD=>chuān +5DDE=>zhōu +5DDF=>huāng +5DE0=>jīng +5DE1=>xún +5DE2=>cháo +5DE3=>cháo +5DE4=>liè +5DE5=>gōng +5DE6=>zuǒ +5DE7=>qiǎo +5DE8=>jù +5DE9=>gǒng +5DEA=>jù +5DEB=>wū +5DEC=>pu +5DED=>pu +5DEE=>chà +5DEF=>qiú +5DF0=>qiú +5DF1=>jǐ +5DF2=>yǐ +5DF3=>sì +5DF4=>ba +5DF5=>zhī +5DF6=>zhāo +5DF7=>xiàng +5DF8=>yí +5DF9=>jǐn +5DFA=>xùn +5DFB=>juàn +5DFC=>bā +5DFD=>xùn +5DFE=>jīn +5DFF=>fú +5E00=>zā +5E01=>bì +5E02=>shì +5E03=>bù +5E04=>dīng +5E05=>shuài +5E06=>fān +5E07=>niè +5E08=>shī +5E09=>fēn +5E0A=>pà +5E0B=>zhǐ +5E0C=>xī +5E0D=>hù +5E0E=>dàn +5E0F=>wéi +5E10=>zhàng +5E11=>tǎng +5E12=>dài +5E13=>mò +5E14=>pèi +5E15=>pà +5E16=>tiē +5E17=>bō +5E18=>lián +5E19=>zhì +5E1A=>zhou +5E1B=>bó +5E1C=>zhì +5E1D=>dì +5E1E=>mò +5E1F=>yì +5E20=>yì +5E21=>píng +5E22=>qià +5E23=>juǎn +5E24=>rú +5E25=>shuài +5E26=>dài +5E27=>zhèng +5E28=>shuì +5E29=>qiào +5E2A=>zhēn +5E2B=>shī +5E2C=>qún +5E2D=>xí +5E2E=>bāng +5E2F=>dài +5E30=>guī +5E31=>chóu +5E32=>píng +5E33=>zhàng +5E34=>sàn +5E35=>wān +5E36=>dài +5E37=>wéi +5E38=>cháng +5E39=>shà +5E3A=>qí +5E3B=>zé +5E3C=>guó +5E3D=>mào +5E3E=>dǔ +5E3F=>hóu +5E40=>zhèng +5E41=>xū +5E42=>mì +5E43=>wéi +5E44=>wò +5E45=>fú +5E46=>yì +5E47=>bāng +5E48=>píng +5E49=>die +5E4A=>gōng +5E4B=>pán +5E4C=>huǎng +5E4D=>tāo +5E4E=>mì +5E4F=>jià +5E50=>téng +5E51=>huī +5E52=>zhōng +5E53=>shān +5E54=>màn +5E55=>mù +5E56=>biāo +5E57=>guó +5E58=>zé +5E59=>mù +5E5A=>bāng +5E5B=>zhàng +5E5C=>jǐng +5E5D=>chǎn +5E5E=>fú +5E5F=>zhì +5E60=>hū +5E61=>fān +5E62=>chuáng +5E63=>bì +5E64=>bi +5E65=>zhang +5E66=>mì +5E67=>qiāo +5E68=>chān +5E69=>fén +5E6A=>méng +5E6B=>bāng +5E6C=>chóu +5E6D=>miè +5E6E=>chú +5E6F=>jié +5E70=>xiǎn +5E71=>lán +5E72=>gàn +5E73=>píng +5E74=>nián +5E75=>jiān +5E76=>bìng +5E77=>bìng +5E78=>xìng +5E79=>gàn +5E7A=>yāo +5E7B=>huàn +5E7C=>yòu +5E7D=>yōu +5E7E=>jǐ +5E7F=>guǎng +5E80=>pǐ +5E81=>tīng +5E82=>zè +5E83=>guǎng +5E84=>zhuāng +5E85=>mo +5E86=>qìng +5E87=>bì +5E88=>qín +5E89=>dùn +5E8A=>chuáng +5E8B=>guǐ +5E8C=>yǎ +5E8D=>bài +5E8E=>jiè +5E8F=>xù +5E90=>lú +5E91=>wǔ +5E92=>zhuang +5E93=>kù +5E94=>yīng +5E95=>dǐ +5E96=>páo +5E97=>diàn +5E98=>yā +5E99=>miào +5E9A=>gēng +5E9B=>cì +5E9C=>fǔ +5E9D=>tóng +5E9E=>páng +5E9F=>fèi +5EA0=>xiáng +5EA1=>yǐ +5EA2=>zhì +5EA3=>tiāo +5EA4=>zhì +5EA5=>xiū +5EA6=>dù +5EA7=>zuò +5EA8=>xiāo +5EA9=>tú +5EAA=>guǐ +5EAB=>kù +5EAC=>máng +5EAD=>tíng +5EAE=>yǒu +5EAF=>bū +5EB0=>bìng +5EB1=>chěng +5EB2=>lái +5EB3=>bì +5EB4=>jí +5EB5=>ān +5EB6=>shù +5EB7=>kāng +5EB8=>yōng +5EB9=>tuǒ +5EBA=>sōng +5EBB=>shù +5EBC=>qǐng +5EBD=>yù +5EBE=>yǔ +5EBF=>miào +5EC0=>sōu +5EC1=>cè +5EC2=>xiāng +5EC3=>fèi +5EC4=>jiù +5EC5=>è +5EC6=>guī +5EC7=>liù +5EC8=>shà +5EC9=>lián +5ECA=>láng +5ECB=>sōu +5ECC=>zhì +5ECD=>pǒu +5ECE=>qǐng +5ECF=>jiù +5ED0=>jiù +5ED1=>jǐn +5ED2=>áo +5ED3=>kuò +5ED4=>lóu +5ED5=>yìn +5ED6=>liào +5ED7=>dài +5ED8=>lù +5ED9=>yì +5EDA=>chú +5EDB=>chán +5EDC=>tú +5EDD=>sī +5EDE=>xīn +5EDF=>miào +5EE0=>chǎng +5EE1=>wǔ +5EE2=>fèi +5EE3=>guǎng +5EE4=>kù +5EE5=>kuài +5EE6=>bì +5EE7=>qiáng +5EE8=>xiè +5EE9=>lǐn +5EEA=>lǐn +5EEB=>liáo +5EEC=>lú +5EED=>ji +5EEE=>yǐng +5EEF=>xiān +5EF0=>tīng +5EF1=>yōng +5EF2=>lí +5EF3=>tīng +5EF4=>yǐn +5EF5=>xún +5EF6=>yán +5EF7=>tíng +5EF8=>dí +5EF9=>pǎi +5EFA=>jiàn +5EFB=>huí +5EFC=>nǎi +5EFD=>huí +5EFE=>gǒng +5EFF=>niàn +5F00=>kāi +5F01=>biàn +5F02=>yì +5F03=>qì +5F04=>nòng +5F05=>fèn +5F06=>jǔ +5F07=>yǎn +5F08=>yì +5F09=>zàng +5F0A=>bì +5F0B=>yì +5F0C=>yī +5F0D=>èr +5F0E=>sān +5F0F=>shì +5F10=>èr +5F11=>shì +5F12=>shì +5F13=>gōng +5F14=>diào +5F15=>yǐn +5F16=>hù +5F17=>fú +5F18=>hóng +5F19=>wū +5F1A=>tuí +5F1B=>chí +5F1C=>jiàng +5F1D=>bà +5F1E=>shěn +5F1F=>dì +5F20=>zhāng +5F21=>jué +5F22=>tāo +5F23=>fǔ +5F24=>dǐ +5F25=>mí +5F26=>xián +5F27=>hú +5F28=>chāo +5F29=>nǔ +5F2A=>jìng +5F2B=>zhěn +5F2C=>yí +5F2D=>mǐ +5F2E=>quān +5F2F=>wān +5F30=>shāo +5F31=>ruò +5F32=>xuān +5F33=>jìng +5F34=>diāo +5F35=>zhāng +5F36=>jiàng +5F37=>qiáng +5F38=>péng +5F39=>dàn +5F3A=>qiáng +5F3B=>bì +5F3C=>bì +5F3D=>shè +5F3E=>dàn +5F3F=>jiǎn +5F40=>gòu +5F41=>ge +5F42=>fā +5F43=>bì +5F44=>kōu +5F45=>jian +5F46=>biè +5F47=>xiāo +5F48=>dàn +5F49=>guō +5F4A=>jiàng +5F4B=>hóng +5F4C=>mí +5F4D=>guō +5F4E=>wān +5F4F=>jué +5F50=>jì +5F51=>jì +5F52=>guī +5F53=>dāng +5F54=>lù +5F55=>lù +5F56=>tuàn +5F57=>huì +5F58=>zhì +5F59=>huì +5F5A=>huì +5F5B=>yí +5F5C=>yí +5F5D=>yí +5F5E=>yí +5F5F=>yuē +5F60=>yuē +5F61=>shān +5F62=>xíng +5F63=>wén +5F64=>tóng +5F65=>yàn +5F66=>yàn +5F67=>yù +5F68=>chī +5F69=>cǎi +5F6A=>biāo +5F6B=>diāo +5F6C=>bīn +5F6D=>péng +5F6E=>yǒng +5F6F=>piǎo +5F70=>zhāng +5F71=>yǐng +5F72=>chī +5F73=>chì +5F74=>zhuó +5F75=>tuǒ +5F76=>jí +5F77=>fǎng +5F78=>zhōng +5F79=>yì +5F7A=>wáng +5F7B=>chè +5F7C=>bǐ +5F7D=>dī +5F7E=>líng +5F7F=>fú +5F80=>wǎng +5F81=>zhēng +5F82=>cú +5F83=>wǎng +5F84=>jìng +5F85=>dài +5F86=>xī +5F87=>xùn +5F88=>hěn +5F89=>yáng +5F8A=>huái +5F8B=>lǜ +5F8C=>hòu +5F8D=>wǎng +5F8E=>chěng +5F8F=>zhì +5F90=>xú +5F91=>jìng +5F92=>tú +5F93=>cóng +5F94=>zhi +5F95=>lái +5F96=>cóng +5F97=>de +5F98=>pái +5F99=>xǐ +5F9A=>dōng +5F9B=>jì +5F9C=>cháng +5F9D=>zhì +5F9E=>cóng +5F9F=>zhōu +5FA0=>lái +5FA1=>yù +5FA2=>xiè +5FA3=>jiè +5FA4=>jiàn +5FA5=>shì +5FA6=>jiǎ +5FA7=>biàn +5FA8=>huáng +5FA9=>fù +5FAA=>xún +5FAB=>wěi +5FAC=>páng +5FAD=>yáo +5FAE=>wēi +5FAF=>xī +5FB0=>zhēng +5FB1=>piào +5FB2=>tí +5FB3=>dé +5FB4=>zhēng +5FB5=>zhēng +5FB6=>bié +5FB7=>dé +5FB8=>chōng +5FB9=>chè +5FBA=>jiǎo +5FBB=>huì +5FBC=>jiǎo +5FBD=>huī +5FBE=>méi +5FBF=>lòng +5FC0=>xiāng +5FC1=>bào +5FC2=>qú +5FC3=>xīn +5FC4=>xin +5FC5=>bì +5FC6=>yì +5FC7=>lè +5FC8=>rén +5FC9=>dāo +5FCA=>dìng +5FCB=>gǎi +5FCC=>jì +5FCD=>rěn +5FCE=>rén +5FCF=>chàn +5FD0=>tǎn +5FD1=>tè +5FD2=>tè +5FD3=>gān +5FD4=>qì +5FD5=>shì +5FD6=>cǔn +5FD7=>zhì +5FD8=>wàng +5FD9=>máng +5FDA=>xī +5FDB=>fán +5FDC=>yīng +5FDD=>tiǎn +5FDE=>mín +5FDF=>wěn +5FE0=>zhōng +5FE1=>chōng +5FE2=>wù +5FE3=>jí +5FE4=>wǔ +5FE5=>xì +5FE6=>jiá +5FE7=>yōu +5FE8=>wàn +5FE9=>cōng +5FEA=>sōng +5FEB=>kuài +5FEC=>yù +5FED=>biàn +5FEE=>zhì +5FEF=>qí +5FF0=>cuì +5FF1=>chén +5FF2=>tài +5FF3=>tún +5FF4=>qián +5FF5=>niàn +5FF6=>hún +5FF7=>xiōng +5FF8=>niǔ +5FF9=>kuáng +5FFA=>xiān +5FFB=>xīn +5FFC=>kāng +5FFD=>hū +5FFE=>kài +5FFF=>fèn +6000=>huái +6001=>tài +6002=>sǒng +6003=>wǔ +6004=>òu +6005=>chàng +6006=>chuàng +6007=>jù +6008=>yì +6009=>bǎo +600A=>chāo +600B=>mín +600C=>pēi +600D=>zuò +600E=>zěn +600F=>yàng +6010=>jù +6011=>bàn +6012=>nù +6013=>náo +6014=>zhēng +6015=>pà +6016=>bù +6017=>tiē +6018=>hù +6019=>hù +601A=>jù +601B=>dá +601C=>lián +601D=>sī +601E=>chóu +601F=>dì +6020=>dài +6021=>yí +6022=>tū +6023=>yóu +6024=>fū +6025=>jí +6026=>pēng +6027=>xìng +6028=>yuàn +6029=>ní +602A=>guài +602B=>fú +602C=>xì +602D=>bì +602E=>yōu +602F=>qiè +6030=>xuàn +6031=>cōng +6032=>bǐng +6033=>huǎng +6034=>xù +6035=>chù +6036=>bì +6037=>shù +6038=>xī +6039=>tān +603A=>yong +603B=>zǒng +603C=>duì +603D=>mo +603E=>zhǐ +603F=>yì +6040=>shì +6041=>nèn +6042=>xún +6043=>shì +6044=>xì +6045=>lǎo +6046=>héng +6047=>kuāng +6048=>móu +6049=>zhǐ +604A=>xié +604B=>liàn +604C=>tiāo +604D=>huǎng +604E=>dié +604F=>hào +6050=>kǒng +6051=>guǐ +6052=>héng +6053=>xī +6054=>jiǎo +6055=>shù +6056=>si +6057=>hū +6058=>qiū +6059=>yàng +605A=>huì +605B=>huí +605C=>chì +605D=>jiá +605E=>yí +605F=>xiōng +6060=>guài +6061=>lìn +6062=>huī +6063=>zì +6064=>xù +6065=>chǐ +6066=>shàng +6067=>nǜ +6068=>hèn +6069=>ēn +606A=>kè +606B=>dòng +606C=>tián +606D=>gōng +606E=>quān +606F=>xi +6070=>qià +6071=>yuè +6072=>pēng +6073=>kěn +6074=>dé +6075=>huì +6076=>è +6077=>xiao +6078=>tòng +6079=>yān +607A=>kǎi +607B=>cè +607C=>nǎo +607D=>yùn +607E=>máng +607F=>yǒng +6080=>yǒng +6081=>yuān +6082=>pī +6083=>kǔn +6084=>qiāo +6085=>yuè +6086=>yù +6087=>tú +6088=>jiè +6089=>xī +608A=>zhé +608B=>lìn +608C=>tì +608D=>hàn +608E=>hào +608F=>qiè +6090=>tì +6091=>bù +6092=>yì +6093=>qiàn +6094=>huǐ +6095=>xī +6096=>bèi +6097=>mán +6098=>yī +6099=>hēng +609A=>sǒng +609B=>quān +609C=>chěng +609D=>kuī +609E=>wù +609F=>wù +60A0=>yōu +60A1=>lí +60A2=>liàng +60A3=>huàn +60A4=>cōng +60A5=>yì +60A6=>yuè +60A7=>lì +60A8=>nín +60A9=>nǎo +60AA=>è +60AB=>què +60AC=>xuán +60AD=>qiān +60AE=>wù +60AF=>mǐn +60B0=>cóng +60B1=>fěi +60B2=>bēi +60B3=>duó +60B4=>cuì +60B5=>chàng +60B6=>mèn +60B7=>sàn +60B8=>jì +60B9=>guàn +60BA=>guàn +60BB=>xìng +60BC=>dào +60BD=>qī +60BE=>kōng +60BF=>tiǎn +60C0=>lún +60C1=>xī +60C2=>kǎn +60C3=>gǔn +60C4=>nì +60C5=>qíng +60C6=>chóu +60C7=>dūn +60C8=>guǒ +60C9=>zhān +60CA=>jīng +60CB=>wǎn +60CC=>yuān +60CD=>jīn +60CE=>jì +60CF=>lán +60D0=>yù +60D1=>huò +60D2=>hé +60D3=>quán +60D4=>tán +60D5=>tì +60D6=>tì +60D7=>niè +60D8=>wǎng +60D9=>chuò +60DA=>hū +60DB=>hūn +60DC=>xī +60DD=>chǎng +60DE=>xīn +60DF=>wéi +60E0=>huì +60E1=>è +60E2=>suǒ +60E3=>zǒng +60E4=>jiān +60E5=>yǒng +60E6=>diàn +60E7=>jù +60E8=>cǎn +60E9=>chéng +60EA=>dé +60EB=>bèi +60EC=>qiè +60ED=>cán +60EE=>dàn +60EF=>guàn +60F0=>duò +60F1=>nǎo +60F2=>yùn +60F3=>xiǎng +60F4=>zhuì +60F5=>dié +60F6=>huáng +60F7=>chǔn +60F8=>qióng +60F9=>rě +60FA=>xīng +60FB=>cè +60FC=>biǎn +60FD=>mǐn +60FE=>zōng +60FF=>tí +6100=>qiǎo +6101=>chóu +6102=>bèi +6103=>xuān +6104=>wēi +6105=>gé +6106=>qiān +6107=>wěi +6108=>yù +6109=>yú +610A=>bì +610B=>xuān +610C=>huàn +610D=>mǐn +610E=>bì +610F=>yì +6110=>miǎn +6111=>yǒng +6112=>kài +6113=>dàng +6114=>yīn +6115=>è +6116=>chén +6117=>mào +6118=>qià +6119=>kè +611A=>yú +611B=>ài +611C=>qiè +611D=>yǎn +611E=>nuò +611F=>gǎn +6120=>yùn +6121=>zǒng +6122=>sāi +6123=>lèng +6124=>fèn +6125=>ying +6126=>kuì +6127=>kuì +6128=>què +6129=>gōng +612A=>yún +612B=>sù +612C=>sù +612D=>qí +612E=>yáo +612F=>sǒng +6130=>huàng +6131=>jí +6132=>gǔ +6133=>jù +6134=>chuàng +6135=>nì +6136=>xié +6137=>kǎi +6138=>zhěng +6139=>yǒng +613A=>cǎo +613B=>xùn +613C=>shèn +613D=>bó +613E=>kài +613F=>yuàn +6140=>xì +6141=>hùn +6142=>yǒng +6143=>yǎng +6144=>lì +6145=>sāo +6146=>tāo +6147=>yīn +6148=>cí +6149=>xù +614A=>qiàn +614B=>tài +614C=>huāng +614D=>yùn +614E=>shèn +614F=>mǐng +6150=>gong +6151=>shè +6152=>cóng +6153=>piāo +6154=>mù +6155=>mù +6156=>guó +6157=>chì +6158=>cǎn +6159=>cán +615A=>cán +615B=>cuī +615C=>mǐn +615D=>tè +615E=>zhāng +615F=>tòng +6160=>ào +6161=>shuǎng +6162=>màn +6163=>guàn +6164=>què +6165=>zào +6166=>jiù +6167=>huì +6168=>kǎi +6169=>lián +616A=>òu +616B=>sǒng +616C=>qín +616D=>yìn +616E=>lǜ +616F=>shāng +6170=>wèi +6171=>tuán +6172=>mán +6173=>qiān +6174=>shè +6175=>yōng +6176=>qìng +6177=>kāng +6178=>dì +6179=>zhí +617A=>lóu +617B=>juàn +617C=>qī +617D=>qī +617E=>yù +617F=>píng +6180=>liáo +6181=>còng +6182=>yōu +6183=>chōng +6184=>zhì +6185=>tòng +6186=>chēng +6187=>qì +6188=>qū +6189=>péng +618A=>bèi +618B=>biē +618C=>qióng +618D=>jiāo +618E=>zēng +618F=>chì +6190=>lián +6191=>píng +6192=>kuì +6193=>huì +6194=>qiáo +6195=>chéng +6196=>yìn +6197=>yìn +6198=>xǐ +6199=>xī +619A=>dàn +619B=>tán +619C=>duǒ +619D=>duì +619E=>duì +619F=>sù +61A0=>jué +61A1=>cè +61A2=>xiāo +61A3=>fān +61A4=>fèn +61A5=>láo +61A6=>lào +61A7=>chōng +61A8=>hān +61A9=>qì +61AA=>xián +61AB=>mǐn +61AC=>jǐng +61AD=>liǎo +61AE=>wǔ +61AF=>cǎn +61B0=>jué +61B1=>cù +61B2=>xiàn +61B3=>tǎn +61B4=>shéng +61B5=>pī +61B6=>yì +61B7=>chù +61B8=>xiān +61B9=>náo +61BA=>dàn +61BB=>tǎn +61BC=>jǐng +61BD=>sōng +61BE=>hàn +61BF=>jiǎo +61C0=>wèi +61C1=>xuān +61C2=>dǒng +61C3=>qín +61C4=>qín +61C5=>jù +61C6=>cǎo +61C7=>kěn +61C8=>xiè +61C9=>yīng +61CA=>ào +61CB=>mào +61CC=>yì +61CD=>lǐn +61CE=>sè +61CF=>jùn +61D0=>huái +61D1=>mèn +61D2=>lǎn +61D3=>ài +61D4=>lǐn +61D5=>yān +61D6=>kuò +61D7=>xià +61D8=>chì +61D9=>yǔ +61DA=>yìn +61DB=>dāi +61DC=>měng +61DD=>ài +61DE=>méng +61DF=>duì +61E0=>qí +61E1=>mǒ +61E2=>lán +61E3=>mèn +61E4=>chóu +61E5=>zhì +61E6=>nuò +61E7=>nuò +61E8=>yān +61E9=>yǎng +61EA=>bó +61EB=>zhì +61EC=>kuàng +61ED=>kuǎng +61EE=>yǒu +61EF=>fū +61F0=>liú +61F1=>miè +61F2=>chéng +61F3=>hui +61F4=>chàn +61F5=>měng +61F6=>lǎn +61F7=>huái +61F8=>xuán +61F9=>ràng +61FA=>chàn +61FB=>jì +61FC=>jù +61FD=>huān +61FE=>shè +61FF=>yì +6200=>liàn +6201=>nǎn +6202=>mí +6203=>tǎng +6204=>jué +6205=>gàng +6206=>gàng +6207=>zhuàng +6208=>gē +6209=>yuè +620A=>wù +620B=>jiān +620C=>xū +620D=>shù +620E=>róng +620F=>xì +6210=>chéng +6211=>wǒ +6212=>jiè +6213=>gē +6214=>jiān +6215=>qiāng +6216=>huò +6217=>qiāng +6218=>zhàn +6219=>dòng +621A=>qi +621B=>jiá +621C=>dié +621D=>zéi +621E=>jiá +621F=>jǐ +6220=>zhī +6221=>kān +6222=>jí +6223=>kuí +6224=>gài +6225=>děng +6226=>zhàn +6227=>qiāng +6228=>gē +6229=>jiǎn +622A=>jié +622B=>yù +622C=>jiǎn +622D=>yǎn +622E=>lù +622F=>hū +6230=>zhàn +6231=>xì +6232=>xì +6233=>chuō +6234=>dài +6235=>qú +6236=>hù +6237=>hù +6238=>hù +6239=>è +623A=>shì +623B=>tì +623C=>mǎo +623D=>hù +623E=>lì +623F=>fáng +6240=>suǒ +6241=>biǎn +6242=>diàn +6243=>jiōng +6244=>shǎng +6245=>yí +6246=>yǐ +6247=>shàn +6248=>hù +6249=>fēi +624A=>yǎn +624B=>shǒu +624C=>shou +624D=>cái +624E=>zhā +624F=>qiú +6250=>lè +6251=>pū +6252=>bā +6253=>dǎ +6254=>rēng +6255=>fǎn +6256=>ru +6257=>zài +6258=>tuō +6259=>zhàng +625A=>diǎo +625B=>káng +625C=>yū +625D=>kū +625E=>gǎn +625F=>shēn +6260=>chā +6261=>tuō +6262=>gǔ +6263=>kòu +6264=>wù +6265=>dèn +6266=>qiān +6267=>zhí +6268=>rèn +6269=>kuò +626A=>mén +626B=>sǎo +626C=>yáng +626D=>niǔ +626E=>ban +626F=>chě +6270=>rǎo +6271=>xī +6272=>qián +6273=>bān +6274=>jiá +6275=>yú +6276=>fú +6277=>ào +6278=>xī +6279=>pī +627A=>zhǐ +627B=>zhì +627C=>è +627D=>dèn +627E=>zhǎo +627F=>chéng +6280=>jì +6281=>yǎn +6282=>kuáng +6283=>biàn +6284=>chāo +6285=>jū +6286=>wěn +6287=>hú +6288=>yuè +6289=>jué +628A=>bǎ +628B=>qìn +628C=>dǎn +628D=>zhěng +628E=>yǔn +628F=>wán +6290=>nè +6291=>yì +6292=>shū +6293=>zhuā +6294=>póu +6295=>tóu +6296=>dǒu +6297=>kàng +6298=>zhé +6299=>póu +629A=>fǔ +629B=>pāo +629C=>bá +629D=>ǎo +629E=>zé +629F=>tuán +62A0=>kōu +62A1=>lūn +62A2=>qiǎng +62A3=>yun +62A4=>hù +62A5=>bào +62A6=>bǐng +62A7=>zhǐ +62A8=>pēng +62A9=>tān +62AA=>bù +62AB=>pī +62AC=>tái +62AD=>yǎo +62AE=>zhěn +62AF=>zhā +62B0=>yāng +62B1=>bào +62B2=>hē +62B3=>nǐ +62B4=>yè +62B5=>dǐ +62B6=>chì +62B7=>pī +62B8=>jiā +62B9=>mǒ +62BA=>mèi +62BB=>chēn +62BC=>yā +62BD=>chōu +62BE=>qū +62BF=>mǐn +62C0=>chù +62C1=>jiā +62C2=>fú +62C3=>zhǎ +62C4=>zhǔ +62C5=>dān +62C6=>chāi +62C7=>mu +62C8=>niān +62C9=>lā +62CA=>fǔ +62CB=>pāo +62CC=>bàn +62CD=>pāi +62CE=>līn +62CF=>ná +62D0=>guǎi +62D1=>qián +62D2=>jù +62D3=>tà +62D4=>bá +62D5=>tuō +62D6=>tuō +62D7=>ǎo +62D8=>jū +62D9=>zhuō +62DA=>pàn +62DB=>zhāo +62DC=>bài +62DD=>bài +62DE=>dǐ +62DF=>nǐ +62E0=>jù +62E1=>kuò +62E2=>lǒng +62E3=>jiǎn +62E4=>qiá +62E5=>yōng +62E6=>lán +62E7=>níng +62E8=>bō +62E9=>zé +62EA=>qiān +62EB=>hén +62EC=>kuò +62ED=>shì +62EE=>jié +62EF=>zhěng +62F0=>nǐn +62F1=>gǒng +62F2=>gǒng +62F3=>quán +62F4=>shuān +62F5=>cún +62F6=>zā +62F7=>kǎo +62F8=>yí +62F9=>xié +62FA=>cè +62FB=>huī +62FC=>pīn +62FD=>zhuāi +62FE=>shi +62FF=>ná +6300=>bāi +6301=>chí +6302=>guà +6303=>zhì +6304=>kuò +6305=>duǒ +6306=>duǒ +6307=>zhǐ +6308=>qiè +6309=>àn +630A=>nòng +630B=>zhèn +630C=>gé +630D=>jiào +630E=>kuà +630F=>dòng +6310=>ná +6311=>tiāo +6312=>liè +6313=>zhā +6314=>lǚ +6315=>dié +6316=>wā +6317=>jué +6318=>lie +6319=>jǔ +631A=>zhì +631B=>luán +631C=>yà +631D=>wō +631E=>tà +631F=>xié +6320=>náo +6321=>dǎng +6322=>jiǎo +6323=>zhēng +6324=>jǐ +6325=>huī +6326=>xián +6327=>yu +6328=>āi +6329=>tuō +632A=>nuó +632B=>cuò +632C=>bó +632D=>gěng +632E=>tǐ +632F=>zhèn +6330=>chéng +6331=>sā +6332=>sā +6333=>kēng +6334=>měi +6335=>lòng +6336=>jū +6337=>péng +6338=>jiǎn +6339=>yì +633A=>tǐng +633B=>shān +633C=>ruá +633D=>wǎn +633E=>xié +633F=>chā +6340=>féng +6341=>jiǎo +6342=>wǔ +6343=>jùn +6344=>jiù +6345=>tǒng +6346=>kǔn +6347=>huò +6348=>tú +6349=>zhuō +634A=>póu +634B=>lǚ +634C=>bā +634D=>hàn +634E=>shāo +634F=>niē +6350=>juān +6351=>zè +6352=>shù +6353=>yé +6354=>jué +6355=>bǔ +6356=>wán +6357=>bù +6358=>zùn +6359=>yì +635A=>zhāi +635B=>lǚ +635C=>sōu +635D=>tuō +635E=>lāo +635F=>sǔn +6360=>bāng +6361=>jiǎn +6362=>huàn +6363=>dǎo +6364=>wei +6365=>wàn +6366=>qín +6367=>pěng +6368=>shě +6369=>liè +636A=>mín +636B=>mén +636C=>fǔ +636D=>bǎi +636E=>jù +636F=>dáo +6370=>wǒ +6371=>ái +6372=>juǎn +6373=>yuè +6374=>zǒng +6375=>chēn +6376=>chuí +6377=>jié +6378=>tū +6379=>bèn +637A=>nà +637B=>niǎn +637C=>ruó +637D=>zuó +637E=>wò +637F=>xī +6380=>xiān +6381=>chéng +6382=>diān +6383=>sǎo +6384=>lūn +6385=>qìng +6386=>gāng +6387=>duō +6388=>shòu +6389=>diào +638A=>póu +638B=>dǐ +638C=>zhǎng +638D=>hùn +638E=>jǐ +638F=>tāo +6390=>qiā +6391=>qí +6392=>pái +6393=>shū +6394=>qiān +6395=>líng +6396=>yē +6397=>yà +6398=>jué +6399=>zhēng +639A=>liǎng +639B=>guà +639C=>yì +639D=>huò +639E=>shàn +639F=>zhěng +63A0=>è +63A1=>cǎi +63A2=>tàn +63A3=>chè +63A4=>bīng +63A5=>jiē +63A6=>tì +63A7=>kòng +63A8=>tuī +63A9=>yǎn +63AA=>cuò +63AB=>zhōu +63AC=>jū +63AD=>tiàn +63AE=>qián +63AF=>kèn +63B0=>bāi +63B1=>pá +63B2=>jiē +63B3=>lǔ +63B4=>guāi +63B5=>ming +63B6=>geng +63B7=>zhì +63B8=>dǎn +63B9=>meng +63BA=>càn +63BB=>sāo +63BC=>guàn +63BD=>pèng +63BE=>yuàn +63BF=>nuò +63C0=>jiǎn +63C1=>zhēng +63C2=>jiū +63C3=>jiǎn +63C4=>yú +63C5=>yán +63C6=>kuí +63C7=>nǎn +63C8=>hōng +63C9=>róu +63CA=>pì +63CB=>wēi +63CC=>sāi +63CD=>zòu +63CE=>xuān +63CF=>miáo +63D0=>tí +63D1=>niē +63D2=>chā +63D3=>shì +63D4=>zǒng +63D5=>zhèn +63D6=>yī +63D7=>xún +63D8=>yóng +63D9=>biān +63DA=>yáng +63DB=>huàn +63DC=>yǎn +63DD=>zǎn +63DE=>ǎn +63DF=>xū +63E0=>yà +63E1=>wò +63E2=>ké +63E3=>chuāi +63E4=>jí +63E5=>tì +63E6=>lá +63E7=>là +63E8=>chén +63E9=>kāi +63EA=>jiū +63EB=>jiū +63EC=>tú +63ED=>jiē +63EE=>huī +63EF=>gèn +63F0=>chòng +63F1=>xiāo +63F2=>dié +63F3=>xiē +63F4=>yuán +63F5=>qián +63F6=>yé +63F7=>chā +63F8=>zhā +63F9=>bēi +63FA=>yáo +63FB=>wēi +63FC=>beng +63FD=>lǎn +63FE=>wèn +63FF=>qìn +6400=>chān +6401=>gē +6402=>lǒu +6403=>zǒng +6404=>gēng +6405=>jiǎo +6406=>gòu +6407=>qìn +6408=>róng +6409=>què +640A=>chōu +640B=>chuāi +640C=>zhǎn +640D=>sǔn +640E=>sūn +640F=>bó +6410=>chù +6411=>róng +6412=>bàng +6413=>cuō +6414=>sāo +6415=>kē +6416=>yáo +6417=>dǎo +6418=>zhī +6419=>nù +641A=>lā +641B=>jiān +641C=>sōu +641D=>qiǔ +641E=>gǎo +641F=>xiǎn +6420=>shuò +6421=>sǎng +6422=>jìn +6423=>miè +6424=>è +6425=>chuí +6426=>nuò +6427=>shān +6428=>tà +6429=>zhǎ +642A=>táng +642B=>pán +642C=>bān +642D=>dā +642E=>lì +642F=>tāo +6430=>hú +6431=>zhì +6432=>wā +6433=>huá +6434=>qiān +6435=>wèn +6436=>qiǎng +6437=>tián +6438=>zhēn +6439=>è +643A=>xié +643B=>nuò +643C=>quán +643D=>chá +643E=>zhà +643F=>gé +6440=>wǔ +6441=>èn +6442=>shè +6443=>káng +6444=>shè +6445=>shū +6446=>bǎi +6447=>yáo +6448=>bìn +6449=>sōu +644A=>tān +644B=>sà +644C=>chǎn +644D=>suō +644E=>jiū +644F=>chōng +6450=>chuāng +6451=>guāi +6452=>bǐng +6453=>féng +6454=>shuāi +6455=>dì +6456=>qì +6457=>sōu +6458=>zhāi +6459=>liǎn +645A=>chēng +645B=>chī +645C=>guàn +645D=>lù +645E=>luò +645F=>lǒu +6460=>zǒng +6461=>gài +6462=>hù +6463=>zhā +6464=>chuǎng +6465=>tàng +6466=>huà +6467=>cuī +6468=>nái +6469=>mó +646A=>jiāng +646B=>guī +646C=>yǐng +646D=>zhí +646E=>áo +646F=>zhì +6470=>niè +6471=>màn +6472=>chàn +6473=>kōu +6474=>chū +6475=>shè +6476=>tuán +6477=>jiǎo +6478=>mō +6479=>mó +647A=>zhé +647B=>càn +647C=>kēng +647D=>biāo +647E=>jiàng +647F=>yīn +6480=>gòu +6481=>qiān +6482=>liào +6483=>jí +6484=>yīng +6485=>juē +6486=>piē +6487=>piē +6488=>lāo +6489=>dūn +648A=>xiàn +648B=>ruán +648C=>guì +648D=>zǎn +648E=>yì +648F=>xián +6490=>chēng +6491=>chēng +6492=>sā +6493=>náo +6494=>hòng +6495=>sī +6496=>hàn +6497=>guàng +6498=>dā +6499=>zǔn +649A=>niǎn +649B=>lǐn +649C=>zhěng +649D=>huī +649E=>zhuàng +649F=>jiǎo +64A0=>jǐ +64A1=>cāo +64A2=>dǎn +64A3=>dǎn +64A4=>chè +64A5=>bō +64A6=>chě +64A7=>juē +64A8=>fǔ +64A9=>liāo +64AA=>bèn +64AB=>fǔ +64AC=>qiào +64AD=>bō +64AE=>cuō +64AF=>zhuó +64B0=>zhuàn +64B1=>wěi +64B2=>pū +64B3=>qìn +64B4=>dūn +64B5=>niǎn +64B6=>huá +64B7=>xié +64B8=>lū +64B9=>jiǎo +64BA=>cuān +64BB=>tà +64BC=>hàn +64BD=>qiào +64BE=>wō +64BF=>jiǎn +64C0=>gǎn +64C1=>yōng +64C2=>léi +64C3=>nǎng +64C4=>lǔ +64C5=>shàn +64C6=>zhuó +64C7=>zé +64C8=>pū +64C9=>chuò +64CA=>jī +64CB=>dǎng +64CC=>sè +64CD=>cāo +64CE=>qíng +64CF=>qíng +64D0=>huàn +64D1=>jiē +64D2=>qín +64D3=>kuǎi +64D4=>dān +64D5=>xié +64D6=>kā +64D7=>pǐ +64D8=>bāi +64D9=>ào +64DA=>jù +64DB=>yè +64DC=>e +64DD=>meng +64DE=>sǒu +64DF=>mí +64E0=>jǐ +64E1=>tái +64E2=>zhuó +64E3=>dǎo +64E4=>xǐng +64E5=>lǎn +64E6=>cā +64E7=>jǔ +64E8=>yé +64E9=>rǔ +64EA=>yè +64EB=>yè +64EC=>nǐ +64ED=>wò +64EE=>jí +64EF=>bìn +64F0=>níng +64F1=>gē +64F2=>zhì +64F3=>zhì +64F4=>kuò +64F5=>mó +64F6=>jiàn +64F7=>xié +64F8=>liè +64F9=>tān +64FA=>bǎi +64FB=>sǒu +64FC=>lǔ +64FD=>lüè +64FE=>rǎo +64FF=>tī +6500=>pān +6501=>yǎng +6502=>lèi +6503=>cā +6504=>shū +6505=>zǎn +6506=>niǎn +6507=>xiǎn +6508=>jùn +6509=>huō +650A=>lì +650B=>là +650C=>huǎn +650D=>yíng +650E=>lú +650F=>lǒng +6510=>qiān +6511=>qiān +6512=>zǎn +6513=>qiān +6514=>lán +6515=>xiān +6516=>yīng +6517=>méi +6518=>rǎng +6519=>chān +651A=>ying +651B=>cuān +651C=>xié +651D=>shè +651E=>luó +651F=>jùn +6520=>mí +6521=>lí +6522=>zǎn +6523=>luán +6524=>tān +6525=>zuàn +6526=>lì +6527=>diān +6528=>wā +6529=>dǎng +652A=>jiǎo +652B=>jué +652C=>lǎn +652D=>lì +652E=>nǎng +652F=>zhī +6530=>guì +6531=>guǐ +6532=>qī +6533=>xún +6534=>pū +6535=>suī +6536=>shōu +6537=>kǎo +6538=>yōu +6539=>gǎi +653A=>yǐ +653B=>gōng +653C=>gān +653D=>bān +653E=>fàng +653F=>zhèng +6540=>pò +6541=>diān +6542=>kòu +6543=>mǐn +6544=>wù +6545=>gù +6546=>hé +6547=>cè +6548=>xiào +6549=>mǐ +654A=>chù +654B=>gé +654C=>dí +654D=>xù +654E=>jiào +654F=>mǐn +6550=>chén +6551=>jiù +6552=>shēn +6553=>duó +6554=>yǔ +6555=>chì +6556=>áo +6557=>bài +6558=>xù +6559=>jiào +655A=>duó +655B=>liǎn +655C=>niè +655D=>bì +655E=>chang +655F=>diǎn +6560=>duō +6561=>yì +6562=>gǎn +6563=>sàn +6564=>kě +6565=>yàn +6566=>dūn +6567=>jī +6568=>tǒu +6569=>xiào +656A=>duó +656B=>jiǎo +656C=>jìng +656D=>yáng +656E=>xiá +656F=>mín +6570=>shù +6571=>ái +6572=>qiāo +6573=>ái +6574=>zhěng +6575=>dí +6576=>zhèn +6577=>fū +6578=>shù +6579=>liáo +657A=>qū +657B=>xiòng +657C=>yǐ +657D=>jiǎo +657E=>shan +657F=>jiǎo +6580=>zhuó +6581=>yì +6582=>liǎn +6583=>bì +6584=>lí +6585=>xiào +6586=>xiào +6587=>wén +6588=>xué +6589=>qí +658A=>qí +658B=>zhāi +658C=>bīn +658D=>jué +658E=>zhāi +658F=>láng +6590=>fěi +6591=>bān +6592=>bān +6593=>lán +6594=>yǔ +6595=>lán +6596=>wěi +6597=>dòu +6598=>shēng +6599=>liào +659A=>jiǎ +659B=>hú +659C=>xié +659D=>jiǎ +659E=>yǔ +659F=>zhēn +65A0=>jiào +65A1=>wò +65A2=>tiǎo +65A3=>dòu +65A4=>jīn +65A5=>chì +65A6=>yín +65A7=>fǔ +65A8=>qiāng +65A9=>zhǎn +65AA=>qú +65AB=>zhuó +65AC=>zhǎn +65AD=>duàn +65AE=>cuò +65AF=>sī +65B0=>xīn +65B1=>zhuó +65B2=>zhuó +65B3=>qín +65B4=>lín +65B5=>zhuó +65B6=>chù +65B7=>duàn +65B8=>zhǔ +65B9=>fāng +65BA=>chǎn +65BB=>háng +65BC=>yú +65BD=>shī +65BE=>pèi +65BF=>yóu +65C0=>mèi +65C1=>páng +65C2=>qí +65C3=>zhān +65C4=>máo +65C5=>lǚ +65C6=>pèi +65C7=>pī +65C8=>liú +65C9=>fū +65CA=>fǎng +65CB=>xuán +65CC=>jīng +65CD=>jīng +65CE=>nǐ +65CF=>zú +65D0=>zhào +65D1=>yǐ +65D2=>liú +65D3=>shāo +65D4=>jiàn +65D5=>yú +65D6=>yǐ +65D7=>qí +65D8=>zhì +65D9=>fān +65DA=>piāo +65DB=>fān +65DC=>zhān +65DD=>kuài +65DE=>suì +65DF=>yú +65E0=>wú +65E1=>jì +65E2=>jì +65E3=>jì +65E4=>huò +65E5=>rì +65E6=>dàn +65E7=>jiù +65E8=>zhǐ +65E9=>zǎo +65EA=>xié +65EB=>tiāo +65EC=>xún +65ED=>xù +65EE=>gā +65EF=>lá +65F0=>gàn +65F1=>hàn +65F2=>tái +65F3=>dì +65F4=>xū +65F5=>chǎn +65F6=>shí +65F7=>kuàng +65F8=>yáng +65F9=>shí +65FA=>wàng +65FB=>mín +65FC=>mín +65FD=>tùn +65FE=>chūn +65FF=>wǔ +6600=>yún +6601=>bèi +6602=>áng +6603=>zè +6604=>bǎn +6605=>jié +6606=>kūn +6607=>shēng +6608=>hù +6609=>fǎng +660A=>hào +660B=>guì +660C=>chāng +660D=>xuān +660E=>míng +660F=>hūn +6610=>fēn +6611=>qǐn +6612=>hū +6613=>yì +6614=>xī +6615=>xīn +6616=>yán +6617=>zè +6618=>fǎng +6619=>tán +661A=>shèn +661B=>jù +661C=>yáng +661D=>zǎn +661E=>bǐng +661F=>xīng +6620=>yìng +6621=>xuàn +6622=>pò +6623=>zhěn +6624=>líng +6625=>chūn +6626=>hào +6627=>mèi +6628=>zuó +6629=>mò +662A=>biàn +662B=>xù +662C=>hūn +662D=>zhāo +662E=>zòng +662F=>shì +6630=>shì +6631=>yù +6632=>fèi +6633=>dié +6634=>mǎo +6635=>nì +6636=>chǎng +6637=>wēn +6638=>dōng +6639=>ǎi +663A=>bǐng +663B=>áng +663C=>zhòu +663D=>lóng +663E=>xiǎn +663F=>kuàng +6640=>tiǎo +6641=>cháo +6642=>shí +6643=>huang +6644=>huǎng +6645=>xuǎn +6646=>kuí +6647=>xū +6648=>jiǎo +6649=>jìn +664A=>zhì +664B=>jìn +664C=>shǎng +664D=>tóng +664E=>hǒng +664F=>yàn +6650=>gāi +6651=>xiǎng +6652=>shài +6653=>xiǎo +6654=>yè +6655=>yūn +6656=>huī +6657=>hán +6658=>hàn +6659=>jùn +665A=>wǎn +665B=>xiàn +665C=>kūn +665D=>zhòu +665E=>xī +665F=>chéng +6660=>shèng +6661=>bū +6662=>zhé +6663=>zhé +6664=>wù +6665=>hàn +6666=>huì +6667=>hào +6668=>chen +6669=>wǎn +666A=>tiǎn +666B=>zhuó +666C=>zuì +666D=>zhǒu +666E=>pǔ +666F=>jǐng +6670=>xī +6671=>shǎn +6672=>nǐ +6673=>xī +6674=>qíng +6675=>qǐ +6676=>jīng +6677=>guǐ +6678=>zhěng +6679=>yì +667A=>zhì +667B=>àn +667C=>wǎn +667D=>lín +667E=>liàng +667F=>chāng +6680=>wǎng +6681=>xiǎo +6682=>zàn +6683=>fei +6684=>xuān +6685=>gèng +6686=>yí +6687=>xiá +6688=>yūn +6689=>huī +668A=>xǔ +668B=>mǐn +668C=>kuí +668D=>yē +668E=>yìng +668F=>shǔ +6690=>wěi +6691=>shǔ +6692=>qíng +6693=>mào +6694=>nán +6695=>jiǎn +6696=>nuǎn +6697=>àn +6698=>yáng +6699=>chūn +669A=>yáo +669B=>suǒ +669C=>jìn +669D=>míng +669E=>jiǎo +669F=>kǎi +66A0=>gǎo +66A1=>wěng +66A2=>chàng +66A3=>qì +66A4=>hào +66A5=>yàn +66A6=>lì +66A7=>ài +66A8=>jì +66A9=>jì +66AA=>mèn +66AB=>zàn +66AC=>xiè +66AD=>hào +66AE=>mù +66AF=>mò +66B0=>cōng +66B1=>nì +66B2=>zhāng +66B3=>huì +66B4=>bào +66B5=>hàn +66B6=>xuán +66B7=>chuán +66B8=>liáo +66B9=>xiān +66BA=>dàn +66BB=>jǐng +66BC=>piē +66BD=>lín +66BE=>tūn +66BF=>xǐ +66C0=>yì +66C1=>jì +66C2=>huàng +66C3=>dài +66C4=>yè +66C5=>yè +66C6=>lì +66C7=>tán +66C8=>tóng +66C9=>xiǎo +66CA=>fèi +66CB=>shěn +66CC=>zhào +66CD=>hào +66CE=>yì +66CF=>xiǎng +66D0=>xīng +66D1=>shēn +66D2=>jiǎo +66D3=>bào +66D4=>jìng +66D5=>yàn +66D6=>ài +66D7=>yè +66D8=>rú +66D9=>shǔ +66DA=>méng +66DB=>xūn +66DC=>yào +66DD=>pù +66DE=>lì +66DF=>chén +66E0=>kuàng +66E1=>dié +66E2=>liǎo +66E3=>yàn +66E4=>huò +66E5=>lú +66E6=>xī +66E7=>róng +66E8=>lóng +66E9=>nǎng +66EA=>luǒ +66EB=>luán +66EC=>shài +66ED=>tǎng +66EE=>yǎn +66EF=>zhú +66F0=>yuē +66F1=>yuē +66F2=>qū +66F3=>yè +66F4=>gèng +66F5=>yè +66F6=>hū +66F7=>hé +66F8=>shū +66F9=>cáo +66FA=>cáo +66FB=>sheng +66FC=>màn +66FD=>cēng +66FE=>céng +66FF=>tì +6700=>zuì +6701=>cǎn +6702=>xù +6703=>huì +6704=>yǐn +6705=>qiè +6706=>fēn +6707=>pí +6708=>yuè +6709=>yǒu +670A=>ruǎn +670B=>péng +670C=>fén +670D=>fú +670E=>líng +670F=>fěi +6710=>qú +6711=>tì +6712=>nǜ +6713=>tiǎo +6714=>shuò +6715=>zhèn +6716=>lǎng +6717=>lǎng +6718=>zuī +6719=>míng +671A=>huāng +671B=>wàng +671C=>tūn +671D=>cháo +671E=>jī +671F=>qī +6720=>yīng +6721=>zōng +6722=>wàng +6723=>tóng +6724=>lǎng +6725=>lao +6726=>méng +6727=>lóng +6728=>mù +6729=>děng +672A=>wèi +672B=>mò +672C=>běn +672D=>zhá +672E=>shù +672F=>shù +6730=>mù +6731=>zhū +6732=>rén +6733=>bā +6734=>pǔ +6735=>duo +6736=>duǒ +6737=>dāo +6738=>lì +6739=>guǐ +673A=>jī +673B=>jiū +673C=>bǐ +673D=>xiǔ +673E=>chéng +673F=>cì +6740=>shā +6741=>ru +6742=>zá +6743=>quán +6744=>qiān +6745=>yú +6746=>gān +6747=>wū +6748=>chā +6749=>shān +674A=>xún +674B=>fán +674C=>wù +674D=>zǐ +674E=>li +674F=>xìng +6750=>cái +6751=>cūn +6752=>rèn +6753=>biāo +6754=>tuō +6755=>dì +6756=>zhàng +6757=>máng +6758=>chì +6759=>yì +675A=>gài +675B=>gōng +675C=>dù +675D=>lí +675E=>qǐ +675F=>shù +6760=>gāng +6761=>tiáo +6762=>jiang +6763=>shan +6764=>wan +6765=>lái +6766=>jiu +6767=>máng +6768=>yáng +6769=>mà +676A=>miǎo +676B=>sì +676C=>yuán +676D=>háng +676E=>fèi +676F=>bēi +6770=>jié +6771=>dōng +6772=>gǎo +6773=>yǎo +6774=>xiān +6775=>chǔ +6776=>chūn +6777=>pá +6778=>shū +6779=>huà +677A=>xīn +677B=>chǒu +677C=>zhù +677D=>chǒu +677E=>sōng +677F=>bǎn +6780=>sōng +6781=>jí +6782=>wò +6783=>jìn +6784=>gòu +6785=>jī +6786=>máo +6787=>pí +6788=>bì +6789=>wang +678A=>àng +678B=>fāng +678C=>fén +678D=>yì +678E=>fú +678F=>nán +6790=>xī +6791=>hù +6792=>yā +6793=>dǒu +6794=>xín +6795=>zhěn +6796=>yāo +6797=>lín +6798=>ruì +6799=>ě +679A=>méi +679B=>zhào +679C=>guǒ +679D=>zhī +679E=>cōng +679F=>yùn +67A0=>zui +67A1=>dǒu +67A2=>shū +67A3=>zǎo +67A4=>duo +67A5=>lì +67A6=>lu +67A7=>jiǎn +67A8=>chéng +67A9=>song +67AA=>qiāng +67AB=>fēng +67AC=>nán +67AD=>xiāo +67AE=>xiān +67AF=>kū +67B0=>píng +67B1=>tái +67B2=>xǐ +67B3=>zhǐ +67B4=>guǎi +67B5=>xiāo +67B6=>jià +67B7=>jiā +67B8=>gǒu +67B9=>bāo +67BA=>mò +67BB=>yì +67BC=>yè +67BD=>yè +67BE=>shì +67BF=>niè +67C0=>bǐ +67C1=>duò +67C2=>yí +67C3=>líng +67C4=>bǐng +67C5=>nǐ +67C6=>lā +67C7=>hé +67C8=>bàn +67C9=>fán +67CA=>zhōng +67CB=>dài +67CC=>cí +67CD=>yǎng +67CE=>fū +67CF=>bǎi +67D0=>mǒu +67D1=>gān +67D2=>qī +67D3=>rǎn +67D4=>róu +67D5=>mào +67D6=>sháo +67D7=>sōng +67D8=>zhè +67D9=>xiá +67DA=>yòu +67DB=>shēn +67DC=>guì +67DD=>tuò +67DE=>zhà +67DF=>nán +67E0=>níng +67E1=>yǒng +67E2=>dǐ +67E3=>zhì +67E4=>zhā +67E5=>chá +67E6=>dàn +67E7=>gū +67E8=>bù +67E9=>jiù +67EA=>āo +67EB=>fú +67EC=>jiǎn +67ED=>bā +67EE=>duò +67EF=>kē +67F0=>nài +67F1=>zhù +67F2=>bì +67F3=>liǔ +67F4=>chái +67F5=>shān +67F6=>sì +67F7=>chù +67F8=>pēi +67F9=>shì +67FA=>guǎi +67FB=>zhā +67FC=>yǎo +67FD=>chēng +67FE=>jiù +67FF=>shì +6800=>zhī +6801=>liǔ +6802=>méi +6803=>li +6804=>róng +6805=>zhà +6806=>zao +6807=>biāo +6808=>zhàn +6809=>zhì +680A=>lóng +680B=>dòng +680C=>lú +680D=>shēng +680E=>lì +680F=>lán +6810=>yǒng +6811=>shù +6812=>xún +6813=>shuān +6814=>qì +6815=>zhēn +6816=>qī +6817=>lì +6818=>yí +6819=>xiáng +681A=>zhèn +681B=>lì +681C=>sè +681D=>guā +681E=>kān +681F=>bēn +6820=>rěn +6821=>xiào +6822=>bǎi +6823=>rěn +6824=>bìng +6825=>zī +6826=>chóu +6827=>yì +6828=>cì +6829=>xǔ +682A=>zhū +682B=>jiàn +682C=>zuì +682D=>ér +682E=>ěr +682F=>yǒu +6830=>fá +6831=>gǒng +6832=>kǎo +6833=>lǎo +6834=>zhān +6835=>liè +6836=>yīn +6837=>yàng +6838=>hé +6839=>gēn +683A=>yì +683B=>shì +683C=>gé +683D=>zāi +683E=>luán +683F=>fú +6840=>jié +6841=>héng +6842=>guì +6843=>táo +6844=>guāng +6845=>wéi +6846=>kuāng +6847=>rú +6848=>àn +6849=>ān +684A=>juàn +684B=>yí +684C=>zhuō +684D=>kū +684E=>zhì +684F=>qióng +6850=>tóng +6851=>sāng +6852=>sāng +6853=>huán +6854=>jú +6855=>jiù +6856=>xuè +6857=>duò +6858=>zhuì +6859=>yú +685A=>zǎn +685C=>yīng +685D=>jie +685E=>liu +685F=>zhàn +6860=>yā +6861=>ráo +6862=>zhēn +6863=>dàng +6864=>qī +6865=>qiáo +6866=>huà +6867=>guì +6868=>jiǎng +6869=>zhuāng +686A=>xún +686B=>suō +686C=>shā +686D=>zhēn +686E=>bēi +686F=>tīng +6870=>kuò +6871=>jìng +6872=>po +6873=>bèn +6874=>fú +6875=>ruí +6876=>tǒng +6877=>jué +6878=>xī +6879=>láng +687A=>liǔ +687B=>fēng +687C=>qī +687D=>wěn +687E=>jūn +687F=>gǎn +6880=>sù +6881=>liáng +6882=>qiú +6883=>tǐng +6884=>yǒu +6885=>méi +6886=>bāng +6887=>lòng +6888=>pēng +6889=>zhuāng +688A=>dì +688B=>xuān +688C=>tú +688D=>zào +688E=>āo +688F=>gù +6890=>bì +6891=>dí +6892=>hán +6893=>zǐ +6894=>zhī +6895=>rèn +6896=>bèi +6897=>gěng +6898=>jiǎn +6899=>huàn +689A=>wǎn +689B=>nuó +689C=>jiā +689D=>tiáo +689E=>jì +689F=>xiāo +68A0=>lǚ +68A1=>hún +68A2=>shāo +68A3=>cén +68A4=>fén +68A5=>sōng +68A6=>mèng +68A7=>wú +68A8=>lí +68A9=>lí +68AA=>dòu +68AB=>qǐn +68AC=>yǐng +68AD=>suō +68AE=>jū +68AF=>tī +68B0=>xiè +68B1=>kǔn +68B2=>zhuó +68B3=>shū +68B4=>chān +68B5=>fàn +68B6=>wěi +68B7=>jìng +68B8=>lí +68B9=>bīn +68BA=>xia +68BB=>fo +68BC=>táo +68BD=>zhì +68BE=>lái +68BF=>lián +68C0=>jiǎn +68C1=>zhuō +68C2=>líng +68C3=>lí +68C4=>qì +68C5=>bìng +68C6=>lún +68C7=>cōng +68C8=>qiàn +68C9=>mián +68CA=>qí +68CB=>qí +68CC=>cài +68CD=>gùn +68CE=>chán +68CF=>dé +68D0=>fěi +68D1=>pái +68D2=>bàng +68D3=>bàng +68D4=>hūn +68D5=>zōng +68D6=>chéng +68D7=>zǎo +68D8=>jí +68D9=>lì +68DA=>péng +68DB=>yù +68DC=>yù +68DD=>gù +68DE=>jùn +68DF=>dòng +68E0=>táng +68E1=>gāng +68E2=>wǎng +68E3=>dì +68E4=>cuò +68E5=>fán +68E6=>chēng +68E7=>zhàn +68E8=>qǐ +68E9=>yuān +68EA=>yǎn +68EB=>yù +68EC=>quān +68ED=>yì +68EE=>sēn +68EF=>rěn +68F0=>chuí +68F1=>léng +68F2=>qī +68F3=>zhuō +68F4=>fú +68F5=>kē +68F6=>lái +68F7=>zōu +68F8=>zōu +68F9=>zhào +68FA=>guān +68FB=>fēn +68FC=>fén +68FD=>shēn +68FE=>qíng +68FF=>ní +6900=>wǎn +6901=>guǒ +6902=>lù +6903=>háo +6904=>jiē +6905=>yǐ +6906=>chóu +6907=>jǔ +6908=>jú +6909=>chéng +690A=>zuó +690B=>liáng +690C=>qiāng +690D=>zhí +690E=>chuí +690F=>yā +6910=>jū +6911=>bēi +6912=>jiāo +6913=>zhuó +6914=>zī +6915=>bīn +6916=>péng +6917=>dìng +6918=>chǔ +6919=>chang +691A=>men +691B=>hua +691C=>jiǎn +691D=>guī +691E=>xì +691F=>dú +6920=>qiàn +6921=>dao +6922=>gui +6923=>dian +6924=>luó +6925=>zhī +6926=>quan +6927=>mìng +6928=>fu +6929=>geng +692A=>pèng +692B=>zhǎn +692C=>yi +692D=>tuǒ +692E=>sēn +692F=>duǒ +6930=>yē +6931=>fù +6932=>wěi +6933=>wēi +6934=>duàn +6935=>jiǎ +6936=>zōng +6937=>jiān +6938=>yí +6939=>shèn +693A=>xí +693B=>yàn +693C=>yǎn +693D=>chuán +693E=>jiān +693F=>chūn +6940=>yǔ +6941=>hé +6942=>zhā +6943=>wò +6944=>pián +6945=>bī +6946=>yāo +6947=>huò +6948=>xū +6949=>ruò +694A=>yáng +694B=>là +694C=>yán +694D=>běn +694E=>huī +694F=>kuí +6950=>jiè +6951=>kuí +6952=>sī +6953=>fēng +6954=>xiē +6955=>tuǒ +6956=>zhì +6957=>jiàn +6958=>mù +6959=>mào +695A=>chu +695B=>hù +695C=>hú +695D=>liàn +695E=>léng +695F=>tíng +6960=>nán +6961=>yú +6962=>yóu +6963=>méi +6964=>sǒng +6965=>xuàn +6966=>xuàn +6967=>yǎng +6968=>zhēn +6969=>pián +696A=>yè +696B=>jí +696C=>jié +696D=>yè +696E=>chǔ +696F=>dùn +6970=>yú +6971=>zòu +6972=>wēi +6973=>méi +6974=>tì +6975=>jí +6976=>jié +6977=>kǎi +6978=>qiū +6979=>yíng +697A=>rǒu +697B=>huáng +697C=>lóu +697D=>lè +697E=>quan +697F=>xiang +6980=>pǐn +6981=>shi +6982=>gài +6983=>tán +6984=>lǎn +6985=>wēn +6986=>yú +6987=>chèn +6988=>lǘ +6989=>jǔ +698A=>shen +698B=>chu +698C=>pi +698D=>xiè +698E=>jiǎ +698F=>yì +6990=>zhǎn +6991=>fú +6992=>nuò +6993=>mì +6994=>láng +6995=>róng +6996=>gǔ +6997=>jiàn +6998=>jǔ +6999=>tā +699A=>yǎo +699B=>zhēn +699C=>bǎng +699D=>shā +699E=>yuán +699F=>zǐ +69A0=>míng +69A1=>sù +69A2=>jià +69A3=>yáo +69A4=>jié +69A5=>huàng +69A6=>gàn +69A7=>fěi +69A8=>zhà +69A9=>qián +69AA=>mà +69AB=>sǔn +69AC=>yuán +69AD=>xiè +69AE=>róng +69AF=>shí +69B0=>zhī +69B1=>cuī +69B2=>yún +69B3=>tíng +69B4=>liú +69B5=>róng +69B6=>táng +69B7=>què +69B8=>zhāi +69B9=>sī +69BA=>shèng +69BB=>tà +69BC=>kē +69BD=>xī +69BE=>gǔ +69BF=>qī +69C0=>gǎo +69C1=>gǎo +69C2=>sūn +69C3=>pán +69C4=>tāo +69C5=>gé +69C6=>xún +69C7=>diān +69C8=>nòu +69C9=>jí +69CA=>shuò +69CB=>gòu +69CC=>chuí +69CD=>qiāng +69CE=>chá +69CF=>qiǎn +69D0=>huái +69D1=>méi +69D2=>xù +69D3=>gàng +69D4=>gāo +69D5=>zhuó +69D6=>tuó +69D7=>qiao +69D8=>yàng +69D9=>diān +69DA=>jiǎ +69DB=>kǎn +69DC=>zuì +69DD=>dao +69DE=>long +69DF=>bīn +69E0=>zhū +69E1=>sang +69E2=>xí +69E3=>jī +69E4=>lián +69E5=>huì +69E6=>yōng +69E7=>qiàn +69E8=>guǒ +69E9=>gài +69EA=>gài +69EB=>tuán +69EC=>huà +69ED=>qī +69EE=>sēn +69EF=>cuī +69F0=>péng +69F1=>yǒu +69F2=>hú +69F3=>jiǎng +69F4=>hù +69F5=>huàn +69F6=>guì +69F7=>niè +69F8=>yì +69F9=>gāo +69FA=>kāng +69FB=>guī +69FC=>guī +69FD=>cáo +69FE=>màn +69FF=>jǐn +6A00=>dī +6A01=>zhuāng +6A02=>lè +6A03=>lǎng +6A04=>chén +6A05=>cōng +6A06=>lí +6A07=>xiū +6A08=>qíng +6A09=>shuǎng +6A0A=>fán +6A0B=>tǒng +6A0C=>guàn +6A0D=>zé +6A0E=>sù +6A0F=>lěi +6A10=>lǔ +6A11=>liáng +6A12=>mì +6A13=>lóu +6A14=>cháo +6A15=>sù +6A16=>kē +6A17=>chū +6A18=>táng +6A19=>biāo +6A1A=>lù +6A1B=>jiū +6A1C=>zhè +6A1D=>zhā +6A1E=>shū +6A1F=>zhāng +6A20=>mán +6A21=>mó +6A22=>niǎo +6A23=>yàng +6A24=>tiáo +6A25=>péng +6A26=>zhù +6A27=>shā +6A28=>xī +6A29=>quán +6A2A=>héng +6A2B=>jiān +6A2C=>cōng +6A2D=>ji +6A2E=>yan +6A2F=>qiáng +6A30=>xue +6A31=>yīng +6A32=>èr +6A33=>xún +6A34=>zhí +6A35=>qiáo +6A36=>zuī +6A37=>cóng +6A38=>pǔ +6A39=>shù +6A3A=>huà +6A3B=>kuì +6A3C=>zhēn +6A3D=>zūn +6A3E=>yuè +6A3F=>shàn +6A40=>xī +6A41=>chūn +6A42=>diàn +6A43=>fá +6A44=>gǎn +6A45=>mó +6A46=>wǔ +6A47=>qiāo +6A48=>ráo +6A49=>lìn +6A4A=>liú +6A4B=>qiáo +6A4C=>xiàn +6A4D=>rùn +6A4E=>fán +6A4F=>zhǎn +6A50=>tuó +6A51=>lǎo +6A52=>yún +6A53=>shùn +6A54=>dūn +6A55=>chēng +6A56=>táng +6A57=>méng +6A58=>jú +6A59=>chéng +6A5A=>sù +6A5B=>jué +6A5C=>jué +6A5D=>diàn +6A5E=>huì +6A5F=>jī +6A60=>nuǒ +6A61=>xiàng +6A62=>tuǒ +6A63=>nǐng +6A64=>ruǐ +6A65=>zhū +6A66=>tóng +6A67=>zēng +6A68=>fén +6A69=>qióng +6A6A=>rǎn +6A6B=>héng +6A6C=>qián +6A6D=>gū +6A6E=>liǔ +6A6F=>lào +6A70=>gāo +6A71=>chú +6A72=>xi +6A73=>sheng +6A74=>zi +6A75=>san +6A76=>jí +6A77=>dōu +6A78=>jing +6A79=>lǔ +6A7A=>jian +6A7B=>chu +6A7C=>yuán +6A7D=>tà +6A7E=>shū +6A7F=>jiāng +6A80=>tán +6A81=>lǐn +6A82=>nóng +6A83=>yǐn +6A84=>xí +6A85=>suì +6A86=>shān +6A87=>zuì +6A88=>xuán +6A89=>chēng +6A8A=>gàn +6A8B=>jú +6A8C=>zuì +6A8D=>yì +6A8E=>qín +6A8F=>pǔ +6A90=>yán +6A91=>léi +6A92=>fēng +6A93=>huǐ +6A94=>dàng +6A95=>jì +6A96=>suì +6A97=>bò +6A98=>píng +6A99=>chéng +6A9A=>chǔ +6A9B=>zhuā +6A9C=>guì +6A9D=>jí +6A9E=>jiě +6A9F=>jiǎ +6AA0=>qíng +6AA1=>zhái +6AA2=>jiǎn +6AA3=>qiáng +6AA4=>dào +6AA5=>yǐ +6AA6=>biǎo +6AA7=>sōng +6AA8=>shē +6AA9=>lǐn +6AAA=>li +6AAB=>chá +6AAC=>méng +6AAD=>yín +6AAE=>táo +6AAF=>tái +6AB0=>mián +6AB1=>qí +6AB2=>tuán +6AB3=>bīn +6AB4=>huò +6AB5=>jì +6AB6=>qiān +6AB7=>nǐ +6AB8=>níng +6AB9=>yī +6ABA=>gǎo +6ABB=>kǎn +6ABC=>yìn +6ABD=>nòu +6ABE=>qǐng +6ABF=>yǎn +6AC0=>qí +6AC1=>mì +6AC2=>zhào +6AC3=>guì +6AC4=>chūn +6AC5=>jī +6AC6=>kuí +6AC7=>pó +6AC8=>dèng +6AC9=>chú +6ACA=>ge +6ACB=>mián +6ACC=>yōu +6ACD=>zhì +6ACE=>huǎng +6ACF=>qiān +6AD0=>lěi +6AD1=>léi +6AD2=>sà +6AD3=>lǔ +6AD4=>lì +6AD5=>cuán +6AD6=>lǜ +6AD7=>miè +6AD8=>huì +6AD9=>ōu +6ADA=>lú +6ADB=>zhì +6ADC=>gāo +6ADD=>dú +6ADE=>yuán +6ADF=>lì +6AE0=>fèi +6AE1=>zhuó +6AE2=>sǒu +6AE3=>lián +6AE4=>jiang +6AE5=>chú +6AE6=>qing +6AE7=>zhū +6AE8=>lú +6AE9=>yán +6AEA=>lì +6AEB=>zhū +6AEC=>chèn +6AED=>jié +6AEE=>è +6AEF=>sū +6AF0=>huái +6AF1=>niè +6AF2=>yù +6AF3=>lóng +6AF4=>lài +6AF5=>jiao +6AF6=>xiǎn +6AF7=>guī +6AF8=>jǔ +6AF9=>xiāo +6AFA=>líng +6AFB=>yīng +6AFC=>jiān +6AFD=>yǐn +6AFE=>yóu +6AFF=>yíng +6B00=>xiāng +6B01=>nóng +6B02=>bó +6B03=>chán +6B04=>lán +6B05=>jǔ +6B06=>shuāng +6B07=>shè +6B08=>wéi +6B09=>cóng +6B0A=>quán +6B0B=>qú +6B0C=>cang +6B0D=>jiu +6B0E=>yù +6B0F=>luó +6B10=>lì +6B11=>cuán +6B12=>luán +6B13=>dǎng +6B14=>jué +6B15=>yan +6B16=>lǎn +6B17=>lán +6B18=>zhú +6B19=>léi +6B1A=>lǐ +6B1B=>bà +6B1C=>náng +6B1D=>yù +6B1E=>líng +6B1F=>guang +6B20=>qiàn +6B21=>cì +6B22=>huan +6B23=>xīn +6B24=>yú +6B25=>yì +6B26=>qiān +6B27=>ōu +6B28=>xū +6B29=>chāo +6B2A=>chù +6B2B=>qì +6B2C=>kài +6B2D=>yì +6B2E=>jué +6B2F=>xì +6B30=>xù +6B31=>hē +6B32=>yù +6B33=>kuì +6B34=>láng +6B35=>kuǎn +6B36=>shuò +6B37=>xī +6B38=>āi +6B39=>yī +6B3A=>qī +6B3B=>chuā +6B3C=>chǐ +6B3D=>qīn +6B3E=>kuǎn +6B3F=>kǎn +6B40=>kuǎn +6B41=>kǎn +6B42=>chuǎn +6B43=>shà +6B44=>guā +6B45=>yīn +6B46=>xīn +6B47=>xiē +6B48=>yú +6B49=>qiàn +6B4A=>xiāo +6B4B=>yè +6B4C=>gē +6B4D=>wū +6B4E=>tàn +6B4F=>jìn +6B50=>ōu +6B51=>hū +6B52=>tì +6B53=>huān +6B54=>xū +6B55=>pēn +6B56=>xǐ +6B57=>xiào +6B58=>chuā +6B59=>shè +6B5A=>shàn +6B5B=>hān +6B5C=>chù +6B5D=>yì +6B5E=>è +6B5F=>yú +6B60=>chuò +6B61=>huan +6B62=>zhǐ +6B63=>zhèng +6B64=>cǐ +6B65=>bù +6B66=>wǔ +6B67=>qí +6B68=>bù +6B69=>bù +6B6A=>wāi +6B6B=>jù +6B6C=>qián +6B6D=>chí +6B6E=>sè +6B6F=>chǐ +6B70=>sè +6B71=>zhǒng +6B72=>suì +6B73=>suì +6B74=>lì +6B75=>cuò +6B76=>yú +6B77=>lì +6B78=>guī +6B79=>dǎi +6B7A=>è +6B7B=>sǐ +6B7C=>jiān +6B7D=>zhé +6B7E=>mò +6B7F=>mò +6B80=>yāo +6B81=>mò +6B82=>cú +6B83=>yāng +6B84=>tiǎn +6B85=>shēng +6B86=>dài +6B87=>shāng +6B88=>xù +6B89=>xùn +6B8A=>shū +6B8B=>cán +6B8C=>jué +6B8D=>piǎo +6B8E=>qià +6B8F=>qiú +6B90=>sù +6B91=>qíng +6B92=>yǔn +6B93=>liàn +6B94=>yì +6B95=>fǒu +6B96=>zhí +6B97=>yè +6B98=>cán +6B99=>hūn +6B9A=>dān +6B9B=>jí +6B9C=>dié +6B9D=>zhēn +6B9E=>yǔn +6B9F=>wēn +6BA0=>chòu +6BA1=>bìn +6BA2=>tì +6BA3=>jìn +6BA4=>shāng +6BA5=>yín +6BA6=>diāo +6BA7=>jiù +6BA8=>huì +6BA9=>cuàn +6BAA=>yì +6BAB=>dān +6BAC=>dù +6BAD=>jiāng +6BAE=>liàn +6BAF=>bìn +6BB0=>dú +6BB1=>jian +6BB2=>jiān +6BB3=>shū +6BB4=>ōu +6BB5=>duàn +6BB6=>zhù +6BB7=>yīn +6BB8=>qìng +6BB9=>yì +6BBA=>shā +6BBB=>qiào +6BBC=>ké +6BBD=>xiáo +6BBE=>xùn +6BBF=>diàn +6BC0=>huǐ +6BC1=>huǐ +6BC2=>gǔ +6BC3=>qiāo +6BC4=>jī +6BC5=>yì +6BC6=>ōu +6BC7=>huǐ +6BC8=>duàn +6BC9=>yī +6BCA=>xiāo +6BCB=>wú +6BCC=>guàn +6BCD=>mǔ +6BCE=>měi +6BCF=>měi +6BD0=>ǎi +6BD1=>jiě +6BD2=>dú +6BD3=>yù +6BD4=>bǐ +6BD5=>bì +6BD6=>bì +6BD7=>pí +6BD8=>pí +6BD9=>bì +6BDA=>chán +6BDB=>máo +6BDC=>háo +6BDD=>cǎi +6BDE=>pí +6BDF=>lie +6BE0=>jiā +6BE1=>zhān +6BE2=>sāi +6BE3=>mù +6BE4=>tuò +6BE5=>xún +6BE6=>ěr +6BE7=>róng +6BE8=>xiǎn +6BE9=>jú +6BEA=>mú +6BEB=>háo +6BEC=>qiú +6BED=>dòu +6BEE=>shā +6BEF=>tǎn +6BF0=>péi +6BF1=>jú +6BF2=>duō +6BF3=>cuì +6BF4=>bī +6BF5=>sān +6BF6=>san +6BF7=>mào +6BF8=>sāi +6BF9=>shū +6BFA=>yū +6BFB=>tuò +6BFC=>hé +6BFD=>jiàn +6BFE=>tà +6BFF=>sān +6C00=>lǘ +6C01=>mú +6C02=>máo +6C03=>tóng +6C04=>rǒng +6C05=>chǎng +6C06=>pǔ +6C07=>lu +6C08=>zhān +6C09=>sào +6C0A=>zhān +6C0B=>méng +6C0C=>lǔ +6C0D=>qú +6C0E=>dié +6C0F=>shì +6C10=>dī +6C11=>mín +6C12=>jué +6C13=>máng +6C14=>qì +6C15=>piē +6C16=>nǎi +6C17=>qì +6C18=>dāo +6C19=>xiān +6C1A=>chuān +6C1B=>fēn +6C1C=>yáng +6C1D=>nèi +6C1E=>bin +6C1F=>fú +6C20=>shēn +6C21=>dōng +6C22=>qīng +6C23=>qì +6C24=>yīn +6C25=>xī +6C26=>hài +6C27=>yǎng +6C28=>ān +6C29=>yà +6C2A=>kè +6C2B=>qīng +6C2C=>yà +6C2D=>dōng +6C2E=>dàn +6C2F=>lǜ +6C30=>qíng +6C31=>yǎng +6C32=>yūn +6C33=>yūn +6C34=>shuǐ +6C35=>shui +6C36=>zhěng +6C37=>bīng +6C38=>yǒng +6C39=>dàng +6C3A=>shui +6C3B=>lè +6C3C=>nì +6C3D=>tǔn +6C3E=>fàn +6C3F=>guǐ +6C40=>tīng +6C41=>zhī +6C42=>qiú +6C43=>bīn +6C44=>zè +6C45=>miǎn +6C46=>cuān +6C47=>huì +6C48=>diāo +6C49=>hàn +6C4A=>chà +6C4B=>zhuó +6C4C=>chuàn +6C4D=>wán +6C4E=>fàn +6C4F=>dà +6C50=>xī +6C51=>tuō +6C52=>máng +6C53=>qiú +6C54=>qì +6C55=>shàn +6C56=>pìn +6C57=>hàn +6C58=>qiān +6C59=>wū +6C5A=>wū +6C5B=>xùn +6C5C=>sì +6C5D=>rǔ +6C5E=>gǒng +6C5F=>jiāng +6C60=>chí +6C61=>wū +6C62=>tu +6C63=>jiu +6C64=>tāng +6C65=>zhī +6C66=>zhǐ +6C67=>qiān +6C68=>mì +6C69=>gǔ +6C6A=>wāng +6C6B=>jǐng +6C6C=>jǐng +6C6D=>ruì +6C6E=>jūn +6C6F=>hóng +6C70=>tài +6C71=>quǎn +6C72=>jí +6C73=>biàn +6C74=>biàn +6C75=>gàn +6C76=>wèn +6C77=>zhōng +6C78=>fāng +6C79=>xiōng +6C7A=>jué +6C7B=>hǔ +6C7C=>niú +6C7D=>qì +6C7E=>fén +6C7F=>xù +6C80=>xù +6C81=>qìn +6C82=>yí +6C83=>wò +6C84=>yún +6C85=>yuán +6C86=>hàng +6C87=>yǎn +6C88=>chén +6C89=>chén +6C8A=>dàn +6C8B=>yóu +6C8C=>dùn +6C8D=>hù +6C8E=>huò +6C8F=>qī +6C90=>mù +6C91=>nǜ +6C92=>méi +6C93=>dá +6C94=>miǎn +6C95=>mì +6C96=>chōng +6C97=>pāng +6C98=>bǐ +6C99=>shā +6C9A=>zhǐ +6C9B=>pèi +6C9C=>pàn +6C9D=>zhuǐ +6C9E=>zā +6C9F=>gōu +6CA0=>liú +6CA1=>méi +6CA2=>zé +6CA3=>fēng +6CA4=>ōu +6CA5=>lì +6CA6=>lún +6CA7=>cāng +6CA8=>fēng +6CA9=>wéi +6CAA=>hù +6CAB=>mò +6CAC=>mèi +6CAD=>shù +6CAE=>jǔ +6CAF=>zǎn +6CB0=>tuō +6CB1=>tuó +6CB2=>tuó +6CB3=>hé +6CB4=>lì +6CB5=>mǐ +6CB6=>yí +6CB7=>fā +6CB8=>fèi +6CB9=>yóu +6CBA=>tián +6CBB=>zhì +6CBC=>zhǎo +6CBD=>gū +6CBE=>zhān +6CBF=>yán +6CC0=>sī +6CC1=>kuàng +6CC2=>jiǒng +6CC3=>jū +6CC4=>xiè +6CC5=>qiú +6CC6=>yì +6CC7=>jiā +6CC8=>zhōng +6CC9=>quán +6CCA=>pō +6CCB=>huì +6CCC=>mì +6CCD=>bēn +6CCE=>zé +6CCF=>zhú +6CD0=>lè +6CD1=>yōu +6CD2=>gū +6CD3=>hóng +6CD4=>gān +6CD5=>fǎ +6CD6=>mǎo +6CD7=>sì +6CD8=>hū +6CD9=>píng +6CDA=>cǐ +6CDB=>fàn +6CDC=>zhī +6CDD=>sù +6CDE=>nìng +6CDF=>chēng +6CE0=>líng +6CE1=>pào +6CE2=>bō +6CE3=>qì +6CE4=>sì +6CE5=>ní +6CE6=>jú +6CE7=>sà +6CE8=>zhù +6CE9=>shēng +6CEA=>lèi +6CEB=>xuàn +6CEC=>jué +6CED=>fú +6CEE=>pàn +6CEF=>mǐn +6CF0=>tài +6CF1=>yāng +6CF2=>jǐ +6CF3=>yǒng +6CF4=>guàn +6CF5=>bèng +6CF6=>xué +6CF7=>lóng +6CF8=>lú +6CF9=>dan +6CFA=>luò +6CFB=>xiè +6CFC=>po +6CFD=>zé +6CFE=>jīng +6CFF=>yín +6D00=>pán +6D01=>jié +6D02=>yì +6D03=>huī +6D04=>huí +6D05=>zài +6D06=>chéng +6D07=>yīn +6D08=>wéi +6D09=>hòu +6D0A=>jiàn +6D0B=>yáng +6D0C=>liè +6D0D=>sì +6D0E=>jì +6D0F=>ér +6D10=>xíng +6D11=>fú +6D12=>sǎ +6D13=>sè +6D14=>zhǐ +6D15=>yìn +6D16=>wú +6D17=>xǐ +6D18=>kǎo +6D19=>zhū +6D1A=>jiàng +6D1B=>luò +6D1C=>luò +6D1D=>àn +6D1E=>dòng +6D1F=>tì +6D20=>móu +6D21=>lèi +6D22=>yī +6D23=>mǐ +6D24=>quán +6D25=>jīn +6D26=>pò +6D27=>wěi +6D28=>xiáo +6D29=>xiè +6D2A=>hóng +6D2B=>xù +6D2C=>sù +6D2D=>kuāng +6D2E=>táo +6D2F=>qiè +6D30=>jù +6D31=>ěr +6D32=>zhōu +6D33=>rù +6D34=>píng +6D35=>xún +6D36=>xiōng +6D37=>zhì +6D38=>guāng +6D39=>huán +6D3A=>míng +6D3B=>huó +6D3C=>wā +6D3D=>qià +6D3E=>pài +6D3F=>wū +6D40=>qū +6D41=>liú +6D42=>yì +6D43=>jiā +6D44=>jìng +6D45=>qiǎn +6D46=>jiāng +6D47=>jiāo +6D48=>zhēn +6D49=>shī +6D4A=>zhuó +6D4B=>cè +6D4C=>fá +6D4D=>huì +6D4E=>jì +6D4F=>liú +6D50=>chǎn +6D51=>hún +6D52=>hǔ +6D53=>nóng +6D54=>xún +6D55=>jìn +6D56=>liè +6D57=>qiú +6D58=>wěi +6D59=>zhè +6D5A=>jùn +6D5B=>hán +6D5C=>bāng +6D5D=>máng +6D5E=>zhuó +6D5F=>yóu +6D60=>xī +6D61=>bó +6D62=>dòu +6D63=>huàn +6D64=>hóng +6D65=>yì +6D66=>pǔ +6D67=>yǐng +6D68=>lǎn +6D69=>hào +6D6A=>làng +6D6B=>hǎn +6D6C=>lǐ +6D6D=>gēng +6D6E=>fú +6D6F=>wú +6D70=>liàn +6D71=>chún +6D72=>féng +6D73=>yì +6D74=>yù +6D75=>tóng +6D76=>láo +6D77=>hǎi +6D78=>jìn +6D79=>jiā +6D7A=>chōng +6D7B=>jiǒng +6D7C=>měi +6D7D=>suī +6D7E=>chēng +6D7F=>pèi +6D80=>xiàn +6D81=>shèn +6D82=>tu +6D83=>kùn +6D84=>pīng +6D85=>niè +6D86=>hàn +6D87=>jīng +6D88=>xiāo +6D89=>shè +6D8A=>niǎn +6D8B=>tū +6D8C=>yǒng +6D8D=>xiào +6D8E=>xián +6D8F=>tǐng +6D90=>é +6D91=>sù +6D92=>tūn +6D93=>juān +6D94=>cén +6D95=>tì +6D96=>lì +6D97=>shuì +6D98=>sì +6D99=>lèi +6D9A=>shuì +6D9B=>tāo +6D9C=>dú +6D9D=>lào +6D9E=>lái +6D9F=>lián +6DA0=>wéi +6DA1=>wō +6DA2=>yún +6DA3=>huàn +6DA4=>dí +6DA5=>heng +6DA6=>rùn +6DA7=>jiàn +6DA8=>zhǎng +6DA9=>sè +6DAA=>fú +6DAB=>guàn +6DAC=>xìng +6DAD=>shòu +6DAE=>shuàn +6DAF=>yá +6DB0=>chuò +6DB1=>zhàng +6DB2=>yè +6DB3=>kōng +6DB4=>wò +6DB5=>hán +6DB6=>tuō +6DB7=>dōng +6DB8=>hé +6DB9=>wō +6DBA=>jū +6DBB=>shè +6DBC=>liáng +6DBD=>hūn +6DBE=>tà +6DBF=>zhuō +6DC0=>diàn +6DC1=>qiè +6DC2=>dé +6DC3=>juàn +6DC4=>zī +6DC5=>xī +6DC6=>xiáo +6DC7=>qí +6DC8=>gǔ +6DC9=>guǒ +6DCA=>yān +6DCB=>lín +6DCC=>tǎng +6DCD=>zhōu +6DCE=>pěng +6DCF=>hào +6DD0=>chāng +6DD1=>shū +6DD2=>qī +6DD3=>fāng +6DD4=>zhí +6DD5=>lù +6DD6=>nào +6DD7=>jú +6DD8=>táo +6DD9=>cóng +6DDA=>lèi +6DDB=>zhè +6DDC=>píng +6DDD=>féi +6DDE=>sōng +6DDF=>tiǎn +6DE0=>pì +6DE1=>dàn +6DE2=>yù +6DE3=>ní +6DE4=>yū +6DE5=>lù +6DE6=>gàn +6DE7=>mì +6DE8=>jìng +6DE9=>líng +6DEA=>lún +6DEB=>yín +6DEC=>cuì +6DED=>qú +6DEE=>huái +6DEF=>yù +6DF0=>niǎn +6DF1=>shēn +6DF2=>biāo +6DF3=>chún +6DF4=>hū +6DF5=>yuān +6DF6=>lái +6DF7=>hùn +6DF8=>qīng +6DF9=>yān +6DFA=>qiǎn +6DFB=>tiān +6DFC=>miǎo +6DFD=>zhǐ +6DFE=>yǐn +6DFF=>mì +6E00=>bèn +6E01=>yuān +6E02=>wèn +6E03=>ruò +6E04=>fēi +6E05=>qīng +6E06=>yuān +6E07=>kě +6E08=>jì +6E09=>shè +6E0A=>yuān +6E0B=>se +6E0C=>lù +6E0D=>zì +6E0E=>dú +6E0F=>qi +6E10=>jiàn +6E11=>miǎn +6E12=>pì +6E13=>xi +6E14=>yú +6E15=>yuān +6E16=>shěn +6E17=>shèn +6E18=>róu +6E19=>huàn +6E1A=>zhǔ +6E1B=>jiǎn +6E1C=>nuǎn +6E1D=>yú +6E1E=>qiú +6E1F=>tíng +6E20=>qú +6E21=>dù +6E22=>fán +6E23=>zhā +6E24=>bó +6E25=>wò +6E26=>wō +6E27=>dì +6E28=>wēi +6E29=>wēn +6E2A=>rú +6E2B=>xiè +6E2C=>cè +6E2D=>wèi +6E2E=>hé +6E2F=>gǎng +6E30=>yǎn +6E31=>hóng +6E32=>xuàn +6E33=>mǐ +6E34=>kě +6E35=>máo +6E36=>yīng +6E37=>yǎn +6E38=>yóu +6E39=>hōng +6E3A=>miǎo +6E3B=>shěng +6E3C=>měi +6E3D=>zāi +6E3E=>hún +6E3F=>nài +6E40=>guǐ +6E41=>chì +6E42=>è +6E43=>pài +6E44=>méi +6E45=>liàn +6E46=>qì +6E47=>qì +6E48=>méi +6E49=>tián +6E4A=>còu +6E4B=>wéi +6E4C=>cān +6E4D=>tuān +6E4E=>miǎn +6E4F=>huì +6E50=>mò +6E51=>xū +6E52=>jí +6E53=>pén +6E54=>jiān +6E55=>jiǎn +6E56=>hú +6E57=>fèng +6E58=>xiāng +6E59=>yì +6E5A=>yìn +6E5B=>zhàn +6E5C=>shí +6E5D=>jiē +6E5E=>chēng +6E5F=>huáng +6E60=>tàn +6E61=>yú +6E62=>bì +6E63=>mǐn +6E64=>shī +6E65=>tū +6E66=>shēng +6E67=>yǒng +6E68=>jú +6E69=>dòng +6E6A=>tuàn +6E6B=>jiǎo +6E6C=>jiǎo +6E6D=>qiú +6E6E=>yān +6E6F=>tāng +6E70=>lóng +6E71=>huò +6E72=>yuán +6E73=>nǎn +6E74=>bàn +6E75=>yǒu +6E76=>quán +6E77=>zhuāng +6E78=>liàng +6E79=>chán +6E7A=>yán +6E7B=>chún +6E7C=>niè +6E7D=>zī +6E7E=>wān +6E7F=>shī +6E80=>mǎn +6E81=>yíng +6E82=>la +6E83=>kuì +6E84=>feng +6E85=>jiàn +6E86=>xù +6E87=>lóu +6E88=>wéi +6E89=>gài +6E8A=>xia +6E8B=>yíng +6E8C=>pō +6E8D=>jìn +6E8E=>yàn +6E8F=>táng +6E90=>yuán +6E91=>suǒ +6E92=>yuán +6E93=>lián +6E94=>yǎo +6E95=>mèng +6E96=>zhǔn +6E97=>chéng +6E98=>kè +6E99=>tài +6E9A=>tǎ +6E9B=>wā +6E9C=>liū +6E9D=>gōu +6E9E=>sāo +6E9F=>míng +6EA0=>zhà +6EA1=>shí +6EA2=>yì +6EA3=>lùn +6EA4=>mǎ +6EA5=>pǔ +6EA6=>wēi +6EA7=>lì +6EA8=>cái +6EA9=>wù +6EAA=>xī +6EAB=>wēn +6EAC=>qiāng +6EAD=>zé +6EAE=>shī +6EAF=>sù +6EB0=>ái +6EB1=>qín +6EB2=>sōu +6EB3=>yún +6EB4=>xiù +6EB5=>yīn +6EB6=>róng +6EB7=>hùn +6EB8=>sù +6EB9=>suò +6EBA=>nì +6EBB=>tā +6EBC=>shī +6EBD=>rù +6EBE=>āi +6EBF=>pàn +6EC0=>chù +6EC1=>chú +6EC2=>pāng +6EC3=>wēng +6EC4=>cāng +6EC5=>miè +6EC6=>gé +6EC7=>diān +6EC8=>hào +6EC9=>huàng +6ECA=>xì +6ECB=>zī +6ECC=>dí +6ECD=>zhì +6ECE=>xíng +6ECF=>fǔ +6ED0=>jié +6ED1=>huá +6ED2=>gē +6ED3=>zǐ +6ED4=>tāo +6ED5=>téng +6ED6=>suī +6ED7=>bì +6ED8=>jiào +6ED9=>huì +6EDA=>gǔn +6EDB=>yín +6EDC=>gāo +6EDD=>lóng +6EDE=>zhì +6EDF=>yàn +6EE0=>shè +6EE1=>mǎn +6EE2=>yíng +6EE3=>chún +6EE4=>lǜ +6EE5=>làn +6EE6=>luán +6EE7=>xiao +6EE8=>bīn +6EE9=>tān +6EEA=>yù +6EEB=>xiǔ +6EEC=>hù +6EED=>bì +6EEE=>biāo +6EEF=>zhì +6EF0=>jiàng +6EF1=>kòu +6EF2=>shèn +6EF3=>shāng +6EF4=>dī +6EF5=>mì +6EF6=>áo +6EF7=>lǔ +6EF8=>hǔ +6EF9=>hū +6EFA=>yōu +6EFB=>chǎn +6EFC=>fàn +6EFD=>yōng +6EFE=>gǔn +6EFF=>mǎn +6F00=>qǐng +6F01=>yú +6F02=>piào +6F03=>jì +6F04=>yá +6F05=>cháo +6F06=>qī +6F07=>xǐ +6F08=>jì +6F09=>lù +6F0A=>lóu +6F0B=>lóng +6F0C=>jǐn +6F0D=>guó +6F0E=>cóng +6F0F=>lòu +6F10=>zhí +6F11=>gài +6F12=>qiáng +6F13=>lí +6F14=>yǎn +6F15=>cáo +6F16=>jiào +6F17=>cōng +6F18=>chún +6F19=>tuán +6F1A=>ōu +6F1B=>téng +6F1C=>yě +6F1D=>xí +6F1E=>mì +6F1F=>táng +6F20=>mò +6F21=>shāng +6F22=>hàn +6F23=>lián +6F24=>lǎn +6F25=>wā +6F26=>chí +6F27=>gān +6F28=>féng +6F29=>xuán +6F2A=>yī +6F2B=>màn +6F2C=>zì +6F2D=>mǎng +6F2E=>kāng +6F2F=>luò +6F30=>pēng +6F31=>shù +6F32=>zhǎng +6F33=>zhāng +6F34=>zhuàng +6F35=>xù +6F36=>huàn +6F37=>huǒ +6F38=>jiàn +6F39=>yān +6F3A=>shuǎng +6F3B=>liáo +6F3C=>cuǐ +6F3D=>tí +6F3E=>yàng +6F3F=>jiāng +6F40=>cóng +6F41=>yǐng +6F42=>hóng +6F43=>xún +6F44=>shù +6F45=>guàn +6F46=>yíng +6F47=>xiāo +6F48=>zong +6F49=>kun +6F4A=>xù +6F4B=>liàn +6F4C=>zhì +6F4D=>wéi +6F4E=>pì +6F4F=>yù +6F50=>jiào +6F51=>po +6F52=>dàng +6F53=>huì +6F54=>jié +6F55=>wǔ +6F56=>pá +6F57=>jí +6F58=>pān +6F59=>wéi +6F5A=>sù +6F5B=>qián +6F5C=>qián +6F5D=>xī +6F5E=>lù +6F5F=>xì +6F60=>xùn +6F61=>dùn +6F62=>huáng +6F63=>mǐn +6F64=>rùn +6F65=>sù +6F66=>lǎo +6F67=>zhēn +6F68=>cóng +6F69=>yì +6F6A=>zhè +6F6B=>wān +6F6C=>shàn +6F6D=>tán +6F6E=>cháo +6F6F=>xún +6F70=>kuì +6F71=>yē +6F72=>shào +6F73=>tú +6F74=>zhū +6F75=>sǎ +6F76=>hēi +6F77=>bì +6F78=>shān +6F79=>chán +6F7A=>chán +6F7B=>shǔ +6F7C=>tóng +6F7D=>pū +6F7E=>lín +6F7F=>wéi +6F80=>sè +6F81=>sè +6F82=>chéng +6F83=>jiǒng +6F84=>chéng +6F85=>huà +6F86=>jiāo +6F87=>lào +6F88=>chè +6F89=>gǎn +6F8A=>cūn +6F8B=>hòng +6F8C=>sī +6F8D=>shù +6F8E=>pēng +6F8F=>hán +6F90=>yún +6F91=>liù +6F92=>hòng +6F93=>fú +6F94=>hào +6F95=>hé +6F96=>xián +6F97=>jiàn +6F98=>shān +6F99=>xì +6F9A=>yu +6F9B=>lu +6F9C=>lán +6F9D=>ning +6F9E=>yú +6F9F=>lǐn +6FA0=>miǎn +6FA1=>zǎo +6FA2=>dāng +6FA3=>huàn +6FA4=>zé +6FA5=>xiè +6FA6=>yù +6FA7=>lǐ +6FA8=>shì +6FA9=>xué +6FAA=>líng +6FAB=>wàn +6FAC=>zī +6FAD=>yōng +6FAE=>huì +6FAF=>càn +6FB0=>liàn +6FB1=>diàn +6FB2=>yè +6FB3=>ào +6FB4=>huán +6FB5=>zhēn +6FB6=>chán +6FB7=>màn +6FB8=>dǎn +6FB9=>dàn +6FBA=>yì +6FBB=>suì +6FBC=>pì +6FBD=>jù +6FBE=>tà +6FBF=>qín +6FC0=>jī +6FC1=>zhuó +6FC2=>lián +6FC3=>nóng +6FC4=>guō +6FC5=>jìn +6FC6=>fén +6FC7=>sè +6FC8=>jí +6FC9=>suī +6FCA=>huì +6FCB=>chǔ +6FCC=>tà +6FCD=>sōng +6FCE=>dǐng +6FCF=>se +6FD0=>zhǔ +6FD1=>lài +6FD2=>bīn +6FD3=>lián +6FD4=>mǐ +6FD5=>shī +6FD6=>shù +6FD7=>mì +6FD8=>nìng +6FD9=>yíng +6FDA=>yíng +6FDB=>méng +6FDC=>jìn +6FDD=>qí +6FDE=>bì +6FDF=>jì +6FE0=>háo +6FE1=>rú +6FE2=>cuì +6FE3=>wò +6FE4=>tāo +6FE5=>yǐn +6FE6=>yǐn +6FE7=>duì +6FE8=>cí +6FE9=>huò +6FEA=>jìng +6FEB=>làn +6FEC=>jùn +6FED=>ǎi +6FEE=>pú +6FEF=>zhuó +6FF0=>wéi +6FF1=>bīn +6FF2=>gǔ +6FF3=>qián +6FF4=>yíng +6FF5=>bin +6FF6=>kuò +6FF7=>fèi +6FF8=>cang +6FF9=>me +6FFA=>jiàn +6FFB=>wěi +6FFC=>luò +6FFD=>zàn +6FFE=>lǜ +6FFF=>lì +7000=>yōu +7001=>yàng +7002=>lǔ +7003=>sì +7004=>zhì +7005=>yíng +7006=>dú +7007=>wǎng +7008=>huī +7009=>xiè +700A=>pán +700B=>shěn +700C=>biāo +700D=>chán +700E=>mò +700F=>liú +7010=>jiān +7011=>pù +7012=>sè +7013=>chéng +7014=>gǔ +7015=>bīn +7016=>huò +7017=>xiàn +7018=>lú +7019=>qìn +701A=>hàn +701B=>yíng +701C=>róng +701D=>lì +701E=>jìng +701F=>xiāo +7020=>yíng +7021=>suǐ +7022=>wěi +7023=>xiè +7024=>huái +7025=>xuè +7026=>zhū +7027=>lóng +7028=>lài +7029=>duì +702A=>fán +702B=>hú +702C=>lài +702D=>shu +702E=>ling +702F=>yíng +7030=>mí +7031=>jì +7032=>liàn +7033=>jiàn +7034=>yíng +7035=>fèn +7036=>lín +7037=>yì +7038=>jiān +7039=>yuè +703A=>chán +703B=>dài +703C=>ráng +703D=>jiǎn +703E=>lán +703F=>fán +7040=>shuàng +7041=>yuān +7042=>zhuó +7043=>fēng +7044=>shè +7045=>lěi +7046=>lán +7047=>cóng +7048=>qú +7049=>yōng +704A=>qián +704B=>fǎ +704C=>guàn +704D=>què +704E=>yàn +704F=>hào +7050=>ying +7051=>sǎ +7052=>zàn +7053=>luán +7054=>yàn +7055=>lí +7056=>mǐ +7057=>shàn +7058=>tān +7059=>dǎng +705A=>jiǎo +705B=>chǎn +705C=>ying +705D=>hào +705E=>bà +705F=>zhú +7060=>lǎn +7061=>lán +7062=>nǎng +7063=>wān +7064=>luán +7065=>xún +7066=>xiǎn +7067=>yàn +7068=>gàn +7069=>yàn +706A=>yù +706B=>huǒ +706C=>biāo +706D=>miè +706E=>guāng +706F=>dēng +7070=>huī +7071=>xiāo +7072=>xiāo +7073=>hui +7074=>hōng +7075=>líng +7076=>zào +7077=>zhuàn +7078=>jiǔ +7079=>zhà +707A=>xiè +707B=>chì +707C=>zhuó +707D=>zāi +707E=>zāi +707F=>càn +7080=>yáng +7081=>qì +7082=>zhōng +7083=>fén +7084=>niǔ +7085=>jiǒng +7086=>wén +7087=>pò +7088=>yì +7089=>lú +708A=>chuī +708B=>pī +708C=>kài +708D=>pàn +708E=>yán +708F=>kài +7090=>pàng +7091=>mù +7092=>chǎo +7093=>liào +7094=>guì +7095=>kàng +7096=>dùn +7097=>guāng +7098=>xīn +7099=>zhì +709A=>guang +709B=>guāng +709C=>wěi +709D=>qiàng +709E=>bian +709F=>dá +70A0=>xiá +70A1=>zhēng +70A2=>zhú +70A3=>kě +70A4=>zhào +70A5=>fú +70A6=>bá +70A7=>xiè +70A8=>duò +70A9=>lìng +70AA=>zhuō +70AB=>xuàn +70AC=>jù +70AD=>tàn +70AE=>pào +70AF=>jiǒng +70B0=>páo +70B1=>tái +70B2=>tái +70B3=>bǐng +70B4=>yǎng +70B5=>tōng +70B6=>hān +70B7=>zhù +70B8=>zhà +70B9=>diǎn +70BA=>wèi +70BB=>shí +70BC=>liàn +70BD=>chì +70BE=>huǎng +70BF=>zhou +70C0=>hū +70C1=>shuò +70C2=>làn +70C3=>tīng +70C4=>jiǎo +70C5=>xù +70C6=>héng +70C7=>quǎn +70C8=>liè +70C9=>huàn +70CA=>yáng +70CB=>xiū +70CC=>xiū +70CD=>xiǎn +70CE=>yín +70CF=>wū +70D0=>zhōu +70D1=>yáo +70D2=>shì +70D3=>wēi +70D4=>tóng +70D5=>miè +70D6=>zāi +70D7=>kài +70D8=>hōng +70D9=>lào +70DA=>xiá +70DB=>zhú +70DC=>xuǎn +70DD=>zhēng +70DE=>pò +70DF=>yān +70E0=>huí +70E1=>guāng +70E2=>chè +70E3=>huī +70E4=>kǎo +70E5=>chen +70E6=>fán +70E7=>shāo +70E8=>yè +70E9=>huì +70EB=>tàng +70EC=>jìn +70ED=>rè +70EE=>lie +70EF=>xī +70F0=>fú +70F1=>jiǒng +70F2=>xiè +70F3=>pǔ +70F4=>tīng +70F5=>zhuó +70F6=>tǐng +70F7=>wán +70F8=>hǎi +70F9=>pēng +70FA=>lǎng +70FB=>yàn +70FC=>xù +70FD=>fēng +70FE=>chì +70FF=>róng +7100=>hú +7101=>xī +7102=>shū +7103=>hè +7104=>xūn +7105=>kù +7106=>juān +7107=>xiāo +7108=>xī +7109=>yān +710A=>hàn +710B=>zhuàng +710C=>jùn +710D=>dì +710E=>xiè +710F=>jí +7110=>wù +7111=>yān +7112=>lü +7113=>hán +7114=>yàn +7115=>huàn +7116=>mèn +7117=>jú +7118=>dào +7119=>bèi +711A=>fén +711B=>lìn +711C=>kūn +711D=>hùn +711E=>tūn +711F=>xī +7120=>cuì +7121=>wú +7122=>hōng +7123=>chǎo +7124=>fǔ +7125=>wò +7126=>jiāo +7127=>cōng +7128=>fèng +7129=>píng +712A=>qióng +712B=>ruò +712C=>xī +712D=>qióng +712E=>xìn +712F=>chāo +7130=>yàn +7131=>yàn +7132=>yì +7133=>jué +7134=>yù +7135=>gàng +7136=>rán +7137=>pí +7138=>xiòng +7139=>wang +713A=>shēng +713B=>chàng +713C=>shāo +713D=>xiǒng +713E=>niǎn +713F=>gēng +7140=>wei +7141=>chén +7142=>hè +7143=>kuǐ +7144=>zhǒng +7145=>duàn +7146=>xiā +7147=>huī +7148=>fèng +7149=>liàn +714A=>xuān +714B=>xīng +714C=>huáng +714D=>jiǎo +714E=>jiān +714F=>bì +7150=>yīng +7151=>zhǔ +7152=>wěi +7153=>tuān +7154=>shǎn +7155=>xī +7156=>nuǎn +7157=>nuǎn +7158=>chán +7159=>yān +715A=>jiǒng +715B=>jiǒng +715C=>yù +715D=>mèi +715E=>shā +715F=>wèi +7160=>zhá +7161=>xìn +7162=>qióng +7163=>róu +7164=>méi +7165=>huàn +7166=>xù +7167=>zhào +7168=>wēi +7169=>fán +716A=>qiú +716B=>suì +716C=>yáng +716D=>liè +716E=>zhǔ +716F=>jiē +7170=>gào +7171=>guā +7172=>bāo +7173=>hú +7174=>yūn +7175=>xiā +7176=>shi +7177=>liang +7178=>biān +7179=>gòu +717A=>tuì +717B=>táng +717C=>chǎo +717D=>shān +717E=>ēn +717F=>bó +7180=>huǎng +7181=>xié +7182=>xì +7183=>wù +7184=>xī +7185=>yùn +7186=>hé +7187=>hè +7188=>xī +7189=>yún +718A=>xióng +718B=>nái +718C=>shǎn +718D=>qiong +718E=>yào +718F=>xūn +7190=>mì +7191=>lián +7192=>yíng +7193=>wǔ +7194=>róng +7195=>gōng +7196=>yan +7197=>qiàng +7198=>liū +7199=>xī +719A=>bì +719B=>biāo +719C=>cōng +719D=>lù +719E=>jiān +719F=>shú +71A0=>yì +71A1=>lóu +71A2=>péng +71A3=>suī +71A4=>yì +71A5=>tēng +71A6=>jué +71A7=>zōng +71A8=>yùn +71A9=>hù +71AA=>yí +71AB=>zhì +71AC=>áo +71AD=>wèi +71AE=>liǔ +71AF=>hàn +71B0=>ōu +71B1=>rè +71B2=>jiǒng +71B3=>màn +71B4=>kun +71B5=>shāng +71B6=>cuàn +71B7=>zēng +71B8=>jiān +71B9=>xī +71BA=>xī +71BB=>xī +71BC=>yì +71BD=>xiào +71BE=>chì +71BF=>huáng +71C0=>chǎn +71C1=>yè +71C2=>tán +71C3=>rán +71C4=>yàn +71C5=>xián +71C6=>qiāo +71C7=>jùn +71C8=>dēng +71C9=>dùn +71CA=>shēn +71CB=>jiāo +71CC=>fén +71CD=>sī +71CE=>liáo +71CF=>yù +71D0=>lín +71D1=>tóng +71D2=>shāo +71D3=>fēn +71D4=>fán +71D5=>yàn +71D6=>xún +71D7=>làn +71D8=>měi +71D9=>tàng +71DA=>yì +71DB=>jǐng +71DC=>mèn +71DD=>jing +71DE=>jiǎo +71DF=>yíng +71E0=>yù +71E1=>yì +71E2=>xué +71E3=>lán +71E4=>tài +71E5=>zào +71E6=>càn +71E7=>suì +71E8=>xī +71E9=>què +71EA=>cōng +71EB=>lián +71EC=>huǐ +71ED=>zhú +71EE=>xiè +71EF=>líng +71F0=>wēi +71F1=>yì +71F2=>xié +71F3=>zhào +71F4=>huì +71F5=>da +71F6=>nóng +71F7=>lán +71F8=>rú +71F9=>xiǎn +71FA=>kǎo +71FB=>xūn +71FC=>jìn +71FD=>chóu +71FE=>dào +71FF=>yào +7200=>hè +7201=>làn +7202=>biāo +7203=>róng +7204=>lì +7205=>mò +7206=>bào +7207=>ruò +7208=>lǜ +7209=>là +720A=>āo +720B=>xùn +720C=>kuàng +720D=>shuò +720E=>liao +720F=>lì +7210=>lú +7211=>jué +7212=>liǎo +7213=>yàn +7214=>xī +7215=>xiè +7216=>lóng +7217=>yè +7218=>can +7219=>rǎng +721A=>yuè +721B=>làn +721C=>cóng +721D=>jué +721E=>chóng +721F=>guàn +7220=>ju +7221=>chè +7222=>mí +7223=>tǎng +7224=>làn +7225=>zhú +7226=>lan +7227=>líng +7228=>cuàn +7229=>yù +722A=>zhǎo +722B=>zhao +722C=>pá +722D=>zhēng +722E=>páo +722F=>chēng +7230=>yuán +7231=>ài +7232=>wèi +7233=>han +7234=>jué +7235=>jué +7236=>fù +7237=>ye +7238=>bà +7239=>diē +723A=>ye +723B=>yáo +723C=>zǔ +723D=>shuǎng +723E=>ěr +723F=>pán +7240=>chuáng +7241=>kē +7242=>zāng +7243=>dié +7244=>qiāng +7245=>yóng +7246=>qiáng +7247=>piàn +7248=>bǎn +7249=>pàn +724A=>cháo +724B=>jiān +724C=>pái +724D=>dú +724E=>chuāng +724F=>yú +7250=>zhá +7251=>biān +7252=>dié +7253=>bǎng +7254=>bó +7255=>chuāng +7256=>yǒu +7257=>you +7258=>dú +7259=>yá +725A=>chēng +725B=>niú +725C=>niu +725D=>pìn +725E=>jiū +725F=>móu +7260=>tā +7261=>mǔ +7262=>láo +7263=>rèn +7264=>māng +7265=>fāng +7266=>máo +7267=>mù +7268=>gāng +7269=>wù +726A=>yàn +726B=>gē +726C=>bèi +726D=>sì +726E=>jiàn +726F=>gǔ +7270=>yòu +7271=>gē +7272=>shēng +7273=>mǔ +7274=>dǐ +7275=>qiān +7276=>quàn +7277=>quán +7278=>zì +7279=>tè +727A=>xī +727B=>máng +727C=>kēng +727D=>qiān +727E=>wǔ +727F=>gù +7280=>xī +7281=>lí +7282=>lí +7283=>pǒu +7284=>jī +7285=>gāng +7286=>zhí +7287=>bēn +7288=>quán +7289=>chún +728A=>dú +728B=>jù +728C=>jiā +728D=>jiān +728E=>fēng +728F=>piān +7290=>kē +7291=>jú +7292=>kào +7293=>chú +7294=>xì +7295=>bèi +7296=>luò +7297=>jiè +7298=>má +7299=>sān +729A=>wèi +729B=>máo +729C=>dūn +729D=>tóng +729E=>qiao +729F=>jiàng +72A0=>xi +72A1=>lì +72A2=>dú +72A3=>liè +72A4=>pái +72A5=>piāo +72A6=>bó +72A7=>xī +72A8=>chōu +72A9=>wéi +72AA=>kuí +72AB=>chōu +72AC=>quǎn +72AD=>quan +72AE=>bá +72AF=>fàn +72B0=>qiú +72B1=>jǐ +72B2=>cái +72B3=>zhuó +72B4=>àn +72B5=>gē +72B6=>zhuàng +72B7=>guǎng +72B8=>mà +72B9=>yóu +72BA=>kàng +72BB=>bó +72BC=>hǒu +72BD=>yà +72BE=>yín +72BF=>huān +72C0=>zhuàng +72C1=>yǔn +72C2=>kuáng +72C3=>niǔ +72C4=>dí +72C5=>qīng +72C6=>zhòng +72C7=>mù +72C8=>bèi +72C9=>pī +72CA=>jú +72CB=>yí +72CC=>shēng +72CD=>páo +72CE=>xiá +72CF=>tuó +72D0=>hú +72D1=>líng +72D2=>fèi +72D3=>pí +72D4=>nǐ +72D5=>yǎo +72D6=>yòu +72D7=>gǒu +72D8=>xuè +72D9=>jū +72DA=>dàn +72DB=>bó +72DC=>kǔ +72DD=>xiǎn +72DE=>níng +72DF=>huán +72E0=>hěn +72E1=>jiǎo +72E2=>hé +72E3=>zhào +72E4=>jí +72E5=>xùn +72E6=>shān +72E7=>tà +72E8=>róng +72E9=>shòu +72EA=>tóng +72EB=>lǎo +72EC=>dú +72ED=>xiá +72EE=>shī +72EF=>kuài +72F0=>zhēng +72F1=>yù +72F2=>sūn +72F3=>yú +72F4=>bì +72F5=>máng +72F6=>xī +72F7=>juàn +72F8=>li +72F9=>xiá +72FA=>yín +72FB=>suān +72FC=>láng +72FD=>bèi +72FE=>zhì +72FF=>yán +7300=>shā +7301=>lì +7302=>hàn +7303=>xiǎn +7304=>jīng +7305=>pái +7306=>fēi +7307=>xiāo +7308=>bài +7309=>qí +730A=>ní +730B=>biāo +730C=>yìn +730D=>lái +730E=>liè +730F=>jiān +7310=>qiāng +7311=>kūn +7312=>yān +7313=>guǒ +7314=>zòng +7315=>mí +7316=>chāng +7317=>yī +7318=>zhì +7319=>zhēng +731A=>yá +731B=>měng +731C=>cāi +731D=>cù +731E=>shē +731F=>lie +7320=>diǎn +7321=>luó +7322=>hú +7323=>zōng +7324=>guì +7325=>wěi +7326=>fēng +7327=>wō +7328=>yuán +7329=>xīng +732A=>zhū +732B=>māo +732C=>wei +732D=>chuān +732E=>xiàn +732F=>tuān +7330=>yà +7331=>náo +7332=>xiē +7333=>jiā +7334=>hóu +7335=>biān +7336=>yóu +7337=>yóu +7338=>méi +7339=>chá +733A=>yáo +733B=>sūn +733C=>bó +733D=>míng +733E=>huá +733F=>yuán +7340=>sōu +7341=>mà +7342=>yuán +7343=>dāi +7344=>yù +7345=>shī +7346=>háo +7347=>qiang +7348=>yì +7349=>zhēn +734A=>cāng +734B=>háo +734C=>màn +734D=>jìng +734E=>jiǎng +734F=>mò +7350=>zhāng +7351=>chán +7352=>áo +7353=>áo +7354=>háo +7355=>cuī +7356=>bèn +7357=>jué +7358=>bì +7359=>bì +735A=>huáng +735B=>pú +735C=>lín +735D=>xù +735E=>tóng +735F=>yào +7360=>liáo +7361=>shuò +7362=>xiāo +7363=>shou +7364=>dūn +7365=>jiào +7366=>gé +7367=>juàn +7368=>dú +7369=>huì +736A=>kuài +736B=>xiǎn +736C=>xiè +736D=>tǎ +736E=>xiǎn +736F=>xūn +7370=>níng +7371=>pín +7372=>huò +7373=>nòu +7374=>měng +7375=>liè +7376=>nǎo +7377=>guǎng +7378=>shòu +7379=>lú +737A=>tǎ +737B=>xiàn +737C=>mí +737D=>ráng +737E=>huān +737F=>nǎo +7380=>luó +7381=>xiǎn +7382=>qí +7383=>jué +7384=>xuán +7385=>miào +7386=>zī +7387=>lǜ +7388=>lú +7389=>yù +738A=>sù +738B=>wáng +738C=>qiú +738D=>gǎ +738E=>dīng +738F=>lè +7390=>bā +7391=>jī +7392=>hóng +7393=>dì +7394=>chuàn +7395=>gān +7396=>jiǔ +7397=>yú +7398=>qǐ +7399=>yú +739A=>chàng +739B=>mǎ +739C=>gōng +739D=>wǔ +739E=>fū +739F=>wén +73A0=>jiè +73A1=>yá +73A2=>bīn +73A3=>biàn +73A4=>bàng +73A5=>yuè +73A6=>jué +73A7=>mén +73A8=>jué +73A9=>wán +73AA=>jiān +73AB=>méi +73AC=>dǎn +73AD=>pín +73AE=>wěi +73AF=>huán +73B0=>xiàn +73B1=>qiāng +73B2=>líng +73B3=>dài +73B4=>yì +73B5=>án +73B6=>píng +73B7=>diàn +73B8=>fú +73B9=>xuán +73BA=>xǐ +73BB=>bō +73BC=>cǐ +73BD=>gǒu +73BE=>jiǎ +73BF=>sháo +73C0=>pò +73C1=>cí +73C2=>kē +73C3=>rǎn +73C4=>shēng +73C5=>shēn +73C6=>yí +73C7=>zǔ +73C8=>jiā +73C9=>mín +73CA=>shān +73CB=>liǔ +73CC=>bì +73CD=>zhēn +73CE=>zhēn +73CF=>jué +73D0=>fà +73D1=>lóng +73D2=>jīn +73D3=>jiào +73D4=>jiàn +73D5=>lì +73D6=>guàng +73D7=>xiān +73D8=>zhōu +73D9=>gǒng +73DA=>yān +73DB=>xiù +73DC=>yáng +73DD=>xǔ +73DE=>luò +73DF=>sù +73E0=>zhū +73E1=>qín +73E2=>yín +73E3=>xún +73E4=>bǎo +73E5=>ěr +73E6=>xiàng +73E7=>yáo +73E8=>xiá +73E9=>háng +73EA=>guī +73EB=>chōng +73EC=>xù +73ED=>bān +73EE=>pèi +73EF=>lao +73F0=>dāng +73F1=>ying +73F2=>huī +73F3=>wén +73F4=>é +73F5=>chéng +73F6=>dì +73F7=>wǔ +73F8=>wú +73F9=>chéng +73FA=>jùn +73FB=>méi +73FC=>bèi +73FD=>tǐng +73FE=>xiàn +73FF=>chù +7400=>hán +7401=>xuán +7402=>yán +7403=>qiú +7404=>xuàn +7405=>láng +7406=>lǐ +7407=>xiù +7408=>fú +7409=>liú +740A=>yá +740B=>xī +740C=>líng +740D=>lí +740E=>jìn +740F=>liǎn +7410=>suǒ +7411=>suo +7412=>feng +7413=>wán +7414=>diàn +7415=>pín +7416=>zhǎn +7417=>sè +7418=>mín +7419=>yù +741A=>jū +741B=>chēn +741C=>lái +741D=>wén +741E=>shèng +741F=>wéi +7420=>tiǎn +7421=>chù +7422=>zuó +7423=>běng +7424=>chēng +7425=>hǔ +7426=>qí +7427=>è +7428=>kūn +7429=>chāng +742A=>qí +742B=>běng +742C=>wǎn +742D=>lù +742E=>cóng +742F=>guǎn +7430=>yǎn +7431=>diāo +7432=>bèi +7433=>lín +7434=>qín +7435=>pí +7436=>pá +7437=>què +7438=>zhuó +7439=>qín +743A=>fà +743B=>jin +743C=>qióng +743D=>dǔ +743E=>jiè +743F=>hún +7440=>yǔ +7441=>mào +7442=>méi +7443=>chūn +7444=>xuān +7445=>tí +7446=>xīng +7447=>dài +7448=>róu +7449=>mín +744A=>jiān +744B=>wěi +744C=>ruǎn +744D=>huàn +744E=>xié +744F=>chuān +7450=>jiǎn +7451=>zhuàn +7452=>chàng +7453=>liàn +7454=>quán +7455=>xiá +7456=>duàn +7457=>yuàn +7458=>yá +7459=>nǎo +745A=>hú +745B=>yīng +745C=>yú +745D=>huáng +745E=>ruì +745F=>sè +7460=>liú +7461=>shī +7462=>róng +7463=>suǒ +7464=>yáo +7465=>wēn +7466=>wǔ +7467=>zhēn +7468=>jìn +7469=>yíng +746A=>mǎ +746B=>tāo +746C=>liú +746D=>táng +746E=>lì +746F=>láng +7470=>guī +7471=>zhèn +7472=>qiāng +7473=>cuō +7474=>jué +7475=>zhǎo +7476=>yáo +7477=>ài +7478=>bīn +7479=>shū +747A=>cháng +747B=>kūn +747C=>zhuān +747D=>cōng +747E=>jǐn +747F=>yī +7480=>cuǐ +7481=>cōng +7482=>qí +7483=>lí +7484=>yǐng +7485=>suǒ +7486=>qiú +7487=>xuán +7488=>áo +7489=>liǎn +748A=>mén +748B=>zhāng +748C=>yín +748D=>hua +748E=>yīng +748F=>wèi +7490=>lù +7491=>wú +7492=>dēng +7493=>xiù +7494=>zēng +7495=>xún +7496=>qú +7497=>dàng +7498=>lín +7499=>liáo +749A=>qióng +749B=>sù +749C=>huáng +749D=>guī +749E=>pú +749F=>jǐng +74A0=>fán +74A1=>jìn +74A2=>liú +74A3=>jī +74A4=>hui +74A5=>jǐng +74A6=>ài +74A7=>bì +74A8=>càn +74A9=>qú +74AA=>zǎo +74AB=>dāng +74AC=>jiǎo +74AD=>gùn +74AE=>tǎn +74AF=>huì +74B0=>huán +74B1=>sè +74B2=>suì +74B3=>tián +74B4=>chu +74B5=>yú +74B6=>jìn +74B7=>lú +74B8=>bīn +74B9=>shú +74BA=>wèn +74BB=>zuǐ +74BC=>lán +74BD=>xǐ +74BE=>zī +74BF=>xuán +74C0=>ruǎn +74C1=>wò +74C2=>gài +74C3=>léi +74C4=>dú +74C5=>lì +74C6=>zhì +74C7=>róu +74C8=>lí +74C9=>zàn +74CA=>qióng +74CB=>tì +74CC=>guī +74CD=>suí +74CE=>là +74CF=>lóng +74D0=>lú +74D1=>lì +74D2=>zàn +74D3=>làn +74D4=>yīng +74D5=>mí +74D6=>xiāng +74D7=>qióng +74D8=>guàn +74D9=>dào +74DA=>zàn +74DB=>huán +74DC=>guā +74DD=>bó +74DE=>dié +74DF=>bó +74E0=>hù +74E1=>zhí +74E2=>piáo +74E3=>bàn +74E4=>ráng +74E5=>lì +74E6=>wǎ +74E8=>xiáng +74E9=>qiān +74EA=>bǎn +74EB=>pén +74EC=>fǎng +74ED=>dǎn +74EE=>wèng +74EF=>ōu +74F2=>wa +74F3=>hú +74F4=>líng +74F5=>yí +74F6=>píng +74F7=>cí +74F8=>bǎi +74F9=>juān +74FA=>cháng +74FB=>chī +74FD=>dàng +74FE=>měng +74FF=>bù +7500=>zhuì +7501=>píng +7502=>biān +7503=>zhòu +7504=>zhēn +7506=>cí +7507=>yīng +7508=>qì +7509=>xián +750A=>lǒu +750B=>dì +750C=>ōu +750D=>méng +750E=>zhuān +750F=>bèng +7510=>lìn +7511=>zèng +7512=>wǔ +7513=>pì +7514=>dān +7515=>wèng +7516=>yīng +7517=>yǎn +7518=>gān +7519=>dài +751A=>shén +751B=>tián +751C=>tián +751D=>hán +751E=>cháng +751F=>shēng +7520=>qíng +7521=>shēn +7522=>chǎn +7523=>chǎn +7524=>ruí +7525=>shēng +7526=>sū +7527=>shēn +7528=>yòng +7529=>shuǎi +752A=>lù +752B=>fu +752C=>yǒng +752D=>béng +752E=>fèng +752F=>níng +7530=>tián +7531=>yóu +7532=>jiǎ +7533=>shēn +7534=>zhá +7535=>diàn +7536=>fú +7537=>nán +7538=>diān +7539=>pīng +753A=>tīng +753B=>huà +753C=>tǐng +753D=>zhèn +753E=>zāi +753F=>méng +7540=>bì +7541=>qí +7542=>liù +7543=>xún +7544=>liú +7545=>chàng +7546=>mǔ +7547=>yún +7548=>fàn +7549=>fú +754A=>gēng +754B=>tián +754C=>jiè +754D=>jiè +754E=>quǎn +754F=>wèi +7550=>fú +7551=>tián +7552=>mǔ +7553=>duō +7554=>pàn +7555=>jiāng +7556=>wā +7557=>dá +7558=>nán +7559=>liú +755A=>běn +755B=>zhěn +755C=>chù +755D=>mǔ +755E=>mǔ +755F=>cè +7560=>tián +7561=>gāi +7562=>bì +7563=>dá +7564=>zhì +7565=>è +7566=>qí +7567=>lüè +7568=>pān +7569=>yi +756A=>fān +756B=>huà +756C=>shē +756D=>yú +756E=>mǔ +756F=>jùn +7570=>yì +7571=>liú +7572=>shē +7573=>dié +7574=>chóu +7575=>huà +7576=>dāng +7577=>zhuì +7578=>jī +7579=>wǎn +757A=>jiāng +757B=>chéng +757C=>chàng +757D=>tǔn +757E=>léi +757F=>jī +7580=>chā +7581=>liú +7582=>die +7583=>tuǎn +7584=>lìn +7585=>jiāng +7586=>jiāng +7587=>chóu +7588=>pì +7589=>dié +758A=>dié +758B=>pǐ +758C=>jié +758D=>dàn +758E=>shū +758F=>shū +7590=>zhì +7591=>yí +7592=>nè +7593=>nǎi +7594=>dīng +7595=>bǐ +7596=>jiē +7597=>liáo +7598=>gāng +7599=>gē +759A=>jiù +759B=>zhǒu +759C=>xià +759D=>shàn +759E=>xū +759F=>nüè +75A0=>lì +75A1=>yáng +75A2=>chèn +75A3=>yóu +75A4=>bā +75A5=>jiè +75A6=>jué +75A7=>qí +75A8=>xiā +75A9=>cuì +75AA=>bì +75AB=>yì +75AC=>lì +75AD=>zòng +75AE=>chuāng +75AF=>fēng +75B0=>zhù +75B1=>pào +75B2=>pí +75B3=>gān +75B4=>kē +75B5=>cī +75B6=>xuē +75B7=>zhī +75B8=>dǎn +75B9=>zhěn +75BA=>fá +75BB=>zhǐ +75BC=>téng +75BD=>jū +75BE=>jí +75BF=>fèi +75C0=>jū +75C1=>shān +75C2=>jiā +75C3=>xuán +75C4=>zhà +75C5=>bìng +75C6=>niè +75C7=>zhèng +75C8=>yōng +75C9=>jìng +75CA=>quán +75CB=>téng +75CC=>tōng +75CD=>yí +75CE=>jiē +75CF=>wěi +75D0=>huí +75D1=>tān +75D2=>yǎng +75D3=>chì +75D4=>zhì +75D5=>hén +75D6=>yǎ +75D7=>mèi +75D8=>dòu +75D9=>jìng +75DA=>xiāo +75DB=>tòng +75DC=>tū +75DD=>máng +75DE=>pǐ +75DF=>xiāo +75E0=>suān +75E1=>fū +75E2=>lì +75E3=>zhì +75E4=>cuó +75E5=>duó +75E6=>wù +75E7=>shā +75E8=>láo +75E9=>shòu +75EA=>huàn +75EB=>xián +75EC=>yì +75ED=>bēng +75EE=>zhàng +75EF=>guǎn +75F0=>tán +75F1=>fèi +75F2=>má +75F3=>lín +75F4=>chī +75F5=>jì +75F6=>tiǎn +75F7=>ān +75F8=>chì +75F9=>bì +75FA=>bì +75FB=>mín +75FC=>gù +75FD=>duī +75FE=>ē +75FF=>wěi +7600=>yū +7601=>cuì +7602=>yǎ +7603=>zhú +7604=>cù +7605=>dān +7606=>shèn +7607=>zhǒng +7608=>chì +7609=>yù +760A=>hóu +760B=>fēng +760C=>là +760D=>yáng +760E=>chén +760F=>tú +7610=>yǔ +7611=>guō +7612=>wén +7613=>huàn +7614=>kù +7615=>jiǎ +7616=>yīn +7617=>yì +7618=>lòu +7619=>sào +761A=>jué +761B=>chì +761C=>xī +761D=>guān +761E=>yì +761F=>wēn +7620=>jí +7621=>chuāng +7622=>bān +7623=>huì +7624=>liú +7625=>chài +7626=>shòu +7627=>nüè +7628=>diān +7629=>da +762A=>biě +762B=>tān +762C=>zhàng +762D=>biāo +762E=>shèn +762F=>cù +7630=>luǒ +7631=>yì +7632=>zòng +7633=>chōu +7634=>zhàng +7635=>zhài +7636=>sòu +7637=>sè +7638=>qué +7639=>diào +763A=>lòu +763B=>lòu +763C=>mò +763D=>qín +763E=>yǐn +763F=>yǐng +7640=>huáng +7641=>fú +7642=>liáo +7643=>lóng +7644=>qiáo +7645=>liú +7646=>láo +7647=>xián +7648=>fèi +7649=>dān +764A=>yìn +764B=>hè +764C=>ái +764D=>bān +764E=>xián +764F=>guān +7650=>guì +7651=>nòng +7652=>yù +7653=>wéi +7654=>yì +7655=>yōng +7656=>pǐ +7657=>lěi +7658=>lì +7659=>shǔ +765A=>dàn +765B=>lǐn +765C=>diàn +765D=>lǐn +765E=>lài +765F=>biě +7660=>jì +7661=>chī +7662=>yǎng +7663=>xuǎn +7664=>jiē +7665=>zhēng +7666=>me +7667=>lì +7668=>huò +7669=>lài +766A=>ji +766B=>diān +766C=>xuǎn +766D=>yǐng +766E=>yǐn +766F=>qú +7670=>yōng +7671=>tān +7672=>diān +7673=>luǒ +7674=>luán +7675=>luán +7676=>bō +7677=>bō +7678=>guǐ +7679=>bá +767A=>fā +767B=>dēng +767C=>fā +767D=>bái +767E=>bǎi +767F=>qié +7680=>jí +7681=>zào +7682=>zào +7683=>mào +7684=>de +7685=>pā +7686=>jiē +7687=>huáng +7688=>guī +7689=>cǐ +768A=>líng +768B=>gāo +768C=>mò +768D=>jí +768E=>jiǎo +768F=>pěng +7690=>gāo +7691=>ái +7692=>é +7693=>hào +7694=>hàn +7695=>bì +7696=>wǎn +7697=>chóu +7698=>qiàn +7699=>xī +769A=>ái +769B=>xiǎo +769C=>hào +769D=>huàng +769E=>hào +769F=>zé +76A0=>cuǐ +76A1=>hào +76A2=>xiǎo +76A3=>yè +76A4=>pó +76A5=>hào +76A6=>jiǎo +76A7=>ài +76A8=>xīng +76A9=>huàng +76AA=>lì +76AB=>piǎo +76AC=>hé +76AD=>jiào +76AE=>pí +76AF=>gǎn +76B0=>pào +76B1=>zhòu +76B2=>jūn +76B3=>qiú +76B4=>cūn +76B5=>què +76B6=>zhā +76B7=>gǔ +76B8=>jūn +76B9=>jūn +76BA=>zhòu +76BB=>zhā +76BC=>gǔ +76BD=>zhāo +76BE=>dú +76BF=>mǐn +76C0=>qǐ +76C1=>yíng +76C2=>yú +76C3=>bēi +76C4=>zhāo +76C5=>zhōng +76C6=>pén +76C7=>hé +76C8=>yíng +76C9=>hé +76CA=>yì +76CB=>bō +76CC=>wǎn +76CD=>hé +76CE=>àng +76CF=>zhǎn +76D0=>yán +76D1=>jiān +76D2=>hé +76D3=>yū +76D4=>kuī +76D5=>fàn +76D6=>gài +76D7=>dào +76D8=>pán +76D9=>fǔ +76DA=>qiú +76DB=>shèng +76DC=>dào +76DD=>lù +76DE=>zhǎn +76DF=>méng +76E0=>lí +76E1=>jǐn +76E2=>xù +76E3=>jiān +76E4=>pán +76E5=>guàn +76E6=>ān +76E7=>lú +76E8=>xǔ +76E9=>zhōu +76EA=>dàng +76EB=>ān +76EC=>gǔ +76ED=>lì +76EE=>mù +76EF=>dīng +76F0=>gàn +76F1=>xū +76F2=>máng +76F3=>wàng +76F4=>zhí +76F5=>qì +76F6=>yuǎn +76F7=>tián +76F8=>xiāng +76F9=>dǔn +76FA=>xīn +76FB=>xì +76FC=>pàn +76FD=>fēng +76FE=>dùn +76FF=>mín +7700=>míng +7701=>shěng +7702=>shì +7703=>yún +7704=>miǎn +7705=>pān +7706=>fǎng +7707=>miǎo +7708=>dān +7709=>méi +770A=>mào +770B=>kàn +770C=>xiàn +770D=>kōu +770E=>shì +770F=>yāng +7710=>zhēng +7711=>yǎo +7712=>shēn +7713=>huò +7714=>dà +7715=>zhěn +7716=>kuàng +7717=>jū +7718=>shèn +7719=>yí +771A=>shěng +771B=>mèi +771C=>mò +771D=>zhù +771E=>zhēn +771F=>zhēn +7720=>mián +7721=>shì +7722=>yuān +7723=>dié +7724=>nì +7725=>zì +7726=>zì +7727=>chǎo +7728=>zhǎ +7729=>xuàn +772A=>bǐng +772B=>mǐ +772C=>lóng +772D=>suī +772E=>tóng +772F=>mī +7730=>diè +7731=>dì +7732=>nè +7733=>míng +7734=>xuàn +7735=>chī +7736=>kuàng +7737=>juàn +7738=>móu +7739=>zhèn +773A=>tiào +773B=>yáng +773C=>yǎn +773D=>mò +773E=>zhòng +773F=>mò +7740=>zhe +7741=>zhēng +7742=>méi +7743=>suō +7744=>shào +7745=>hàn +7746=>huàn +7747=>dì +7748=>chěng +7749=>cuó +774A=>juàn +774B=>é +774C=>mǎn +774D=>xiàn +774E=>xī +774F=>kùn +7750=>lài +7751=>jiǎn +7752=>shǎn +7753=>tiǎn +7754=>gùn +7755=>wǎn +7756=>lèng +7757=>shì +7758=>qióng +7759=>liè +775A=>yá +775B=>jing +775C=>zhēng +775D=>lí +775E=>lài +775F=>suì +7760=>juàn +7761=>shuì +7762=>suī +7763=>dū +7764=>bì +7765=>pì +7766=>mù +7767=>hūn +7768=>nì +7769=>lù +776A=>yì +776B=>jié +776C=>cǎi +776D=>zhǒu +776E=>yú +776F=>hūn +7770=>mà +7771=>xià +7772=>xǐng +7773=>huī +7774=>gùn +7775=>zāi +7776=>chǔn +7777=>jiān +7778=>mèi +7779=>dǔ +777A=>hóu +777B=>xuān +777C=>tiàn +777D=>kuí +777E=>gāo +777F=>ruì +7780=>mào +7781=>xù +7782=>fá +7783=>wò +7784=>miáo +7785=>chǒu +7786=>kuì +7787=>mī +7788=>wěng +7789=>kòu +778A=>dàng +778B=>chēn +778C=>kē +778D=>sǒu +778E=>xiā +778F=>qióng +7790=>mò +7791=>míng +7792=>mán +7793=>shuì +7794=>zé +7795=>zhàng +7796=>yì +7797=>diāo +7798=>kōu +7799=>mò +779A=>shùn +779B=>cōng +779C=>lōu +779D=>chī +779E=>mán +779F=>piǎo +77A0=>chēng +77A1=>guǐ +77A2=>méng +77A3=>huan +77A4=>rún +77A5=>piē +77A6=>xī +77A7=>qiáo +77A8=>pú +77A9=>zhǔ +77AA=>dèng +77AB=>shěn +77AC=>shùn +77AD=>liǎo +77AE=>chè +77AF=>xián +77B0=>kàn +77B1=>yè +77B2=>xù +77B3=>tóng +77B4=>móu +77B5=>lín +77B6=>guì +77B7=>jiàn +77B8=>yè +77B9=>ài +77BA=>huì +77BB=>zhān +77BC=>jiǎn +77BD=>gǔ +77BE=>zhào +77BF=>qú +77C0=>méi +77C1=>chǒu +77C2=>sào +77C3=>nǐng +77C4=>xūn +77C5=>yào +77C6=>huò +77C7=>méng +77C8=>mián +77C9=>pín +77CA=>mián +77CB=>lì +77CC=>kuàng +77CD=>jué +77CE=>xuān +77CF=>mián +77D0=>huò +77D1=>lú +77D2=>méng +77D3=>lóng +77D4=>guàn +77D5=>mǎn +77D6=>xǐ +77D7=>chù +77D8=>tǎng +77D9=>kàn +77DA=>zhǔ +77DB=>máo +77DC=>jīn +77DD=>lín +77DE=>yù +77DF=>shuò +77E0=>zé +77E1=>jué +77E2=>shǐ +77E3=>yǐ +77E4=>shěn +77E5=>zhī +77E6=>hóu +77E7=>shěn +77E8=>yǐng +77E9=>ju +77EA=>zhōu +77EB=>jiǎo +77EC=>cuó +77ED=>duǎn +77EE=>ǎi +77EF=>jiǎo +77F0=>zēng +77F1=>yuē +77F2=>bà +77F3=>shí +77F4=>dìng +77F5=>qì +77F6=>jī +77F7=>zǐ +77F8=>gān +77F9=>wù +77FA=>zhé +77FB=>kū +77FC=>gāng +77FD=>xì +77FE=>fán +77FF=>kuàng +7800=>dàng +7801=>mǎ +7802=>shā +7803=>dān +7804=>jué +7805=>lì +7806=>fū +7807=>mín +7808=>ě +7809=>huò +780A=>kāng +780B=>zhǐ +780C=>qì +780D=>kǎn +780E=>jiè +780F=>bīn +7810=>è +7811=>yà +7812=>pī +7813=>zhé +7814=>yán +7815=>suì +7816=>zhuān +7817=>chē +7818=>dùn +7819=>pān +781A=>yàn +781B=>jin +781C=>fēng +781D=>fá +781E=>mò +781F=>zhǎ +7820=>jū +7821=>yù +7822=>kē +7823=>tuó +7824=>tuó +7825=>dǐ +7826=>zhài +7827=>zhēn +7828=>è +7829=>fú +782A=>mǔ +782B=>zhù +782C=>lá +782D=>biān +782E=>nǔ +782F=>pīng +7830=>pēng +7831=>líng +7832=>pào +7833=>lè +7834=>pò +7835=>bō +7836=>pò +7837=>shēn +7838=>zá +7839=>ài +783A=>lì +783B=>lóng +783C=>tóng +783D=>yong +783E=>lì +783F=>kuang +7840=>chǔ +7841=>kēng +7842=>quán +7843=>zhū +7844=>kuāng +7845=>guī +7846=>è +7847=>náo +7848=>qià +7849=>lù +784A=>wěi +784B=>ài +784C=>gè +784D=>xiàn +784E=>xíng +784F=>yán +7850=>dòng +7851=>pēng +7852=>xī +7853=>lao +7854=>hóng +7855=>shuò +7856=>xiá +7857=>qiāo +7858=>qing +7859=>wéi +785A=>qiáo +785B=>yì +785C=>kēng +785D=>xiāo +785E=>què +785F=>chàn +7860=>láng +7861=>hōng +7862=>yú +7863=>xiāo +7864=>xiá +7865=>mǎng +7866=>luò +7867=>yǒng +7868=>chē +7869=>chè +786A=>wò +786B=>liú +786C=>yìng +786D=>máng +786E=>què +786F=>yàn +7870=>shā +7871=>kǔn +7872=>yù +7873=>chì +7874=>hua +7875=>lǔ +7876=>chěn +7877=>jiǎn +7878=>nüè +7879=>sōng +787A=>zhuó +787B=>kēng +787C=>péng +787D=>yān +787E=>zhuì +787F=>kōng +7880=>chéng +7881=>qí +7882=>zòng +7883=>qìng +7884=>lín +7885=>jūn +7886=>bō +7887=>dìng +7888=>mín +7889=>diāo +788A=>jiān +788B=>hè +788C=>lù +788D=>ài +788E=>suì +788F=>què +7890=>léng +7891=>bēi +7892=>yín +7893=>duì +7894=>wǔ +7895=>qí +7896=>lǔn +7897=>wǎn +7898=>diǎn +7899=>náo +789A=>bèi +789B=>qì +789C=>chěn +789D=>ruǎn +789E=>yán +789F=>dié +78A0=>dìng +78A1=>dú +78A2=>tuó +78A3=>jié +78A4=>yīng +78A5=>biǎn +78A6=>kè +78A7=>bì +78A8=>wèi +78A9=>shuò +78AA=>zhēn +78AB=>duàn +78AC=>xiá +78AD=>dàng +78AE=>tí +78AF=>nǎo +78B0=>pèng +78B1=>jiǎn +78B2=>dì +78B3=>tàn +78B4=>chá +78B5=>tian +78B6=>qì +78B7=>dun +78B8=>fēng +78B9=>xuàn +78BA=>què +78BB=>què +78BC=>mǎ +78BD=>gōng +78BE=>niǎn +78BF=>sù +78C0=>é +78C1=>cí +78C2=>liú +78C3=>sī +78C4=>táng +78C5=>bàng +78C6=>huá +78C7=>pī +78C8=>wěi +78C9=>sǎng +78CA=>lěi +78CB=>cuō +78CC=>tián +78CD=>xiá +78CE=>xī +78CF=>lián +78D0=>pán +78D1=>wéi +78D2=>yǔn +78D3=>duī +78D4=>zhé +78D5=>kē +78D6=>lá +78D7=>zhuān +78D8=>qìng +78D9=>gǔn +78DA=>zhuān +78DB=>chán +78DC=>qì +78DD=>áo +78DE=>pēng +78DF=>liù +78E0=>lǔ +78E1=>kàn +78E2=>chuǎng +78E3=>chěn +78E4=>yǐn +78E5=>lěi +78E6=>biāo +78E7=>qì +78E8=>mó +78E9=>qì +78EA=>cuī +78EB=>zōng +78EC=>qìng +78ED=>chuò +78EE=>lun +78EF=>jī +78F0=>shàn +78F1=>láo +78F2=>qú +78F3=>zēng +78F4=>dèng +78F5=>jiàn +78F6=>xì +78F7=>lín +78F8=>dìng +78F9=>tán +78FA=>huáng +78FB=>pán +78FC=>zá +78FD=>qiāo +78FE=>dī +78FF=>lì +7900=>jian +7901=>jiāo +7902=>xi +7903=>zhǎng +7904=>qiáo +7905=>dūn +7906=>jiǎn +7907=>yù +7908=>zhuì +7909=>hé +790A=>kè +790B=>zé +790C=>léi +790D=>kě +790E=>chǔ +790F=>yè +7910=>què +7911=>dàng +7912=>yǐ +7913=>jiāng +7914=>pī +7915=>pī +7916=>yù +7917=>pīn +7918=>è +7919=>ài +791A=>kē +791B=>jiān +791C=>yù +791D=>ruǎn +791E=>méng +791F=>pào +7920=>cí +7921=>bó +7922=>yang +7923=>mà +7924=>cǎ +7925=>xián +7926=>kuàng +7927=>léi +7928=>lěi +7929=>zhì +792A=>lì +792B=>lì +792C=>fán +792D=>què +792E=>pào +792F=>yīng +7930=>lì +7931=>lóng +7932=>lóng +7933=>mò +7934=>bó +7935=>shuāng +7936=>guàn +7937=>lán +7938=>zǎn +7939=>yán +793A=>shì +793B=>shì +793C=>lǐ +793D=>réng +793E=>shè +793F=>yuè +7940=>sì +7941=>qí +7942=>tā +7943=>mà +7944=>xiè +7945=>yāo +7946=>xiān +7947=>qí +7948=>qí +7949=>zhǐ +794A=>bēng +794B=>duì +794C=>zhòng +794D=>rèn +794E=>yī +794F=>shí +7950=>yòu +7951=>zhì +7952=>tiáo +7953=>fú +7954=>fù +7955=>mì +7956=>zǔ +7957=>zhī +7958=>suàn +7959=>mèi +795A=>zuò +795B=>qū +795C=>hù +795D=>zhù +795E=>shén +795F=>suì +7960=>cí +7961=>chái +7962=>mí +7963=>lǚ +7964=>yǔ +7965=>xiáng +7966=>wú +7967=>tiāo +7968=>piào +7969=>zhù +796A=>guǐ +796B=>xiá +796C=>zhī +796D=>jì +796E=>gào +796F=>zhēn +7970=>gào +7971=>shuì +7972=>jìn +7973=>shèn +7974=>gāi +7975=>kǔn +7976=>dì +7977=>dǎo +7978=>huò +7979=>táo +797A=>qí +797B=>gù +797C=>guàn +797D=>zuì +797E=>líng +797F=>lù +7980=>bǐng +7981=>jìn +7982=>dǎo +7983=>zhí +7984=>lù +7985=>chán +7986=>bēi +7987=>zhě +7988=>huī +7989=>yǒu +798A=>xì +798B=>yīn +798C=>zī +798D=>huò +798E=>zhēn +798F=>fú +7990=>yuàn +7991=>wú +7992=>xiǎn +7993=>yáng +7994=>zhī +7995=>yī +7996=>méi +7997=>sī +7998=>dì +7999=>bei +799A=>zhuó +799B=>zhēn +799C=>yǒng +799D=>jí +799E=>gào +799F=>táng +79A0=>sī +79A1=>mà +79A2=>tà +79A3=>fu +79A4=>xuān +79A5=>qí +79A6=>yù +79A7=>xǐ +79A8=>jī +79A9=>sì +79AA=>chán +79AB=>dàn +79AC=>guì +79AD=>suì +79AE=>lǐ +79AF=>nóng +79B0=>mí +79B1=>dǎo +79B2=>lì +79B3=>ráng +79B4=>yuè +79B5=>tí +79B6=>zàn +79B7=>lèi +79B8=>róu +79B9=>yǔ +79BA=>yú +79BB=>lí +79BC=>xiè +79BD=>qín +79BE=>hé +79BF=>tū +79C0=>xiù +79C1=>sī +79C2=>rén +79C3=>tū +79C4=>zǐ +79C5=>chá +79C6=>gǎn +79C7=>yì +79C8=>xiān +79C9=>bǐng +79CA=>nián +79CB=>qiū +79CC=>qiū +79CD=>zhǒng +79CE=>fèn +79CF=>hào +79D0=>yún +79D1=>kē +79D2=>miǎo +79D3=>zhī +79D4=>jīng +79D5=>bǐ +79D6=>zhī +79D7=>yù +79D8=>mì +79D9=>kù +79DA=>bàn +79DB=>pī +79DC=>ní +79DD=>lì +79DE=>yóu +79DF=>zū +79E0=>pī +79E1=>bó +79E2=>líng +79E3=>mò +79E4=>chèng +79E5=>nián +79E6=>qín +79E7=>yāng +79E8=>zuó +79E9=>zhì +79EA=>zhī +79EB=>shú +79EC=>jù +79ED=>zǐ +79EE=>huó +79EF=>jī +79F0=>chēng +79F1=>tóng +79F2=>zhì +79F3=>huó +79F4=>hé +79F5=>yīn +79F6=>zī +79F7=>zhì +79F8=>jiē +79F9=>rěn +79FA=>dù +79FB=>yí +79FC=>zhū +79FD=>huì +79FE=>nóng +79FF=>fù +7A00=>xī +7A01=>kǎo +7A02=>láng +7A03=>fū +7A04=>xùn +7A05=>shuì +7A06=>lǚ +7A07=>kǔn +7A08=>gǎn +7A09=>jīng +7A0A=>tí +7A0B=>chéng +7A0C=>tú +7A0D=>shāo +7A0E=>shuì +7A0F=>yà +7A10=>lǔn +7A11=>lù +7A12=>gù +7A13=>zuó +7A14=>rěn +7A15=>zhùn +7A16=>bàng +7A17=>bài +7A18=>jī +7A19=>zhī +7A1A=>zhì +7A1B=>kǔn +7A1C=>léng +7A1D=>péng +7A1E=>kē +7A1F=>bǐng +7A20=>chóu +7A21=>zuì +7A22=>yù +7A23=>sū +7A24=>lüè +7A25=>xiāng +7A26=>yī +7A27=>xì +7A28=>biǎn +7A29=>jì +7A2A=>fú +7A2B=>pì +7A2C=>nuò +7A2D=>jiē +7A2E=>zhǒng +7A2F=>zōng +7A30=>xǔ +7A31=>chēng +7A32=>dào +7A33=>wěn +7A34=>xián +7A35=>zī +7A36=>yù +7A37=>jì +7A38=>xù +7A39=>zhěn +7A3A=>zhì +7A3B=>dào +7A3C=>jia +7A3D=>jī +7A3E=>gǎo +7A3F=>gǎo +7A40=>gǔ +7A41=>róng +7A42=>suì +7A43=>rong +7A44=>jì +7A45=>kāng +7A46=>mù +7A47=>cǎn +7A48=>méi +7A49=>zhì +7A4A=>jì +7A4B=>lù +7A4C=>sū +7A4D=>jī +7A4E=>yǐng +7A4F=>wěn +7A50=>qiū +7A51=>sè +7A52=>hè +7A53=>yì +7A54=>huáng +7A55=>qiè +7A56=>jǐ +7A57=>suì +7A58=>xiāo +7A59=>pú +7A5A=>jiāo +7A5B=>zhuō +7A5C=>zhǒng +7A5D=>zui +7A5E=>lǚ +7A5F=>suì +7A60=>nóng +7A61=>sè +7A62=>huì +7A63=>ráng +7A64=>nuò +7A65=>yù +7A66=>pīn +7A67=>jì +7A68=>tuí +7A69=>wěn +7A6A=>chēng +7A6B=>huò +7A6C=>kuàng +7A6D=>lǚ +7A6E=>biāo +7A6F=>se +7A70=>ráng +7A71=>zhuō +7A72=>lí +7A73=>cuán +7A74=>xué +7A75=>wā +7A76=>jiū +7A77=>qióng +7A78=>xī +7A79=>qióng +7A7A=>kōng +7A7B=>yū +7A7C=>shēn +7A7D=>jǐng +7A7E=>yào +7A7F=>chuān +7A80=>zhūn +7A81=>tū +7A82=>láo +7A83=>qiè +7A84=>zhǎi +7A85=>yǎo +7A86=>biǎn +7A87=>báo +7A88=>yǎo +7A89=>bǐng +7A8A=>wā +7A8B=>zhú +7A8C=>jiào +7A8D=>qiào +7A8E=>diào +7A8F=>wū +7A90=>guī +7A91=>yáo +7A92=>zhì +7A93=>chuāng +7A94=>yào +7A95=>tiǎo +7A96=>jiào +7A97=>chuāng +7A98=>jiǒng +7A99=>xiāo +7A9A=>chéng +7A9B=>kòu +7A9C=>cuàn +7A9D=>wō +7A9E=>dàn +7A9F=>kū +7AA0=>kē +7AA1=>zhuó +7AA2=>xū +7AA3=>sū +7AA4=>guān +7AA5=>kuī +7AA6=>dòu +7AA7=>zhuo +7AA8=>xūn +7AA9=>wō +7AAA=>wā +7AAB=>yà +7AAC=>yú +7AAD=>jù +7AAE=>qióng +7AAF=>yáo +7AB0=>yáo +7AB1=>tiǎo +7AB2=>cháo +7AB3=>yǔ +7AB4=>tián +7AB5=>diào +7AB6=>jù +7AB7=>liào +7AB8=>xī +7AB9=>wù +7ABA=>kuī +7ABB=>chuāng +7ABC=>zhāo +7ABD=>kuan +7ABE=>kuǎn +7ABF=>long +7AC0=>chēng +7AC1=>cuì +7AC2=>piáo +7AC3=>zào +7AC4=>cuàn +7AC5=>qiào +7AC6=>qióng +7AC7=>dòu +7AC8=>zào +7AC9=>lǒng +7ACA=>qiè +7ACB=>lì +7ACC=>chù +7ACD=>shi +7ACE=>fù +7ACF=>qian +7AD0=>chù +7AD1=>hóng +7AD2=>qí +7AD3=>hao +7AD4=>sheng +7AD5=>fen +7AD6=>shù +7AD7=>miào +7AD8=>qǔ +7AD9=>zhàn +7ADA=>zhù +7ADB=>líng +7ADC=>lóng +7ADD=>bìng +7ADE=>jìng +7ADF=>jìng +7AE0=>zhāng +7AE1=>bai +7AE2=>sì +7AE3=>jùn +7AE4=>hóng +7AE5=>tóng +7AE6=>sǒng +7AE7=>jìng +7AE8=>diào +7AE9=>yì +7AEA=>shù +7AEB=>jìng +7AEC=>qǔ +7AED=>jié +7AEE=>pīng +7AEF=>duān +7AF0=>sháo +7AF1=>zhuǎn +7AF2=>céng +7AF3=>dēng +7AF4=>cūn +7AF5=>wāi +7AF6=>jìng +7AF7=>kǎn +7AF8=>jìng +7AF9=>zhú +7AFA=>zhú +7AFB=>lè +7AFC=>péng +7AFD=>yú +7AFE=>chí +7AFF=>gān +7B00=>máng +7B01=>zhú +7B02=>wan +7B03=>dǔ +7B04=>jī +7B05=>xiáo +7B06=>ba +7B07=>suàn +7B08=>jí +7B09=>qǐn +7B0A=>zhào +7B0B=>sǔn +7B0C=>yá +7B0D=>zhuì +7B0E=>yuán +7B0F=>hù +7B10=>háng +7B11=>xiào +7B12=>cén +7B13=>bì +7B14=>bǐ +7B15=>jiǎn +7B16=>yǐ +7B17=>dōng +7B18=>shān +7B19=>shēng +7B1A=>dā +7B1B=>dí +7B1C=>zhú +7B1D=>nà +7B1E=>chī +7B1F=>gū +7B20=>lì +7B21=>qiè +7B22=>mǐn +7B23=>bāo +7B24=>tiáo +7B25=>sì +7B26=>fú +7B27=>cè +7B28=>bèn +7B29=>pèi +7B2A=>dá +7B2B=>zǐ +7B2C=>dì +7B2D=>líng +7B2E=>zé +7B2F=>nú +7B30=>fú +7B31=>gǒu +7B32=>fán +7B33=>jiā +7B34=>gǎn +7B35=>fàn +7B36=>shǐ +7B37=>mǎo +7B38=>pǒ +7B39=>ti +7B3A=>jiān +7B3B=>qióng +7B3C=>lóng +7B3D=>min +7B3E=>biān +7B3F=>luò +7B40=>guì +7B41=>qū +7B42=>chí +7B43=>yīn +7B44=>yào +7B45=>xiǎn +7B46=>bǐ +7B47=>qióng +7B48=>kuò +7B49=>děng +7B4A=>xiáo +7B4B=>jīn +7B4C=>quán +7B4D=>sǔn +7B4E=>rú +7B4F=>fá +7B50=>kuāng +7B51=>zhù +7B52=>tǒng +7B53=>jī +7B54=>dá +7B55=>háng +7B56=>cè +7B57=>zhòng +7B58=>kòu +7B59=>lái +7B5A=>bì +7B5B=>shāi +7B5C=>dāng +7B5D=>zhēng +7B5E=>cè +7B5F=>fū +7B60=>yún +7B61=>tú +7B62=>pá +7B63=>lí +7B64=>láng +7B65=>jǔ +7B66=>guǎn +7B67=>jiǎn +7B68=>hán +7B69=>tóng +7B6A=>xiá +7B6B=>zhì +7B6C=>chéng +7B6D=>suàn +7B6E=>shì +7B6F=>zhù +7B70=>zuó +7B71=>xiǎo +7B72=>shāo +7B73=>tíng +7B74=>cè +7B75=>yán +7B76=>gào +7B77=>kuài +7B78=>gān +7B79=>chóu +7B7A=>kuang +7B7B=>gàng +7B7C=>yún +7B7D=>o +7B7E=>qiān +7B7F=>xiǎo +7B80=>jiǎn +7B81=>póu +7B82=>lái +7B83=>zōu +7B84=>bǐ +7B85=>bì +7B86=>bì +7B87=>gè +7B88=>tái +7B89=>guǎi +7B8A=>yū +7B8B=>jiān +7B8C=>dào +7B8D=>gū +7B8E=>chí +7B8F=>zhēng +7B90=>qìng +7B91=>shà +7B92=>zhǒu +7B93=>lù +7B94=>bó +7B95=>jī +7B96=>lín +7B97=>suàn +7B98=>jùn +7B99=>fú +7B9A=>zhá +7B9B=>gū +7B9C=>kōng +7B9D=>qián +7B9E=>qiān +7B9F=>jùn +7BA0=>chuí +7BA1=>guǎn +7BA2=>yuān +7BA3=>cè +7BA4=>zú +7BA5=>bǒ +7BA6=>zé +7BA7=>qiè +7BA8=>tuò +7BA9=>luó +7BAA=>dān +7BAB=>xiāo +7BAC=>ruò +7BAD=>jiàn +7BAE=>xuān +7BAF=>biān +7BB0=>sǔn +7BB1=>xiāng +7BB2=>xiǎn +7BB3=>píng +7BB4=>zhēn +7BB5=>xīng +7BB6=>hú +7BB7=>yí +7BB8=>zhù +7BB9=>yuē +7BBA=>chūn +7BBB=>lǜ +7BBC=>wū +7BBD=>dǒng +7BBE=>shuò +7BBF=>jí +7BC0=>jié +7BC1=>huáng +7BC2=>xīng +7BC3=>mèi +7BC4=>fàn +7BC5=>chuán +7BC6=>zhuàn +7BC7=>piān +7BC8=>fēng +7BC9=>zhú +7BCA=>huáng +7BCB=>qiè +7BCC=>hóu +7BCD=>qiū +7BCE=>miǎo +7BCF=>qiàn +7BD0=>gu +7BD1=>kuì +7BD2=>shi +7BD3=>lǒu +7BD4=>yún +7BD5=>hé +7BD6=>táng +7BD7=>yuè +7BD8=>chōu +7BD9=>gāo +7BDA=>fěi +7BDB=>ruò +7BDC=>zhēng +7BDD=>gōu +7BDE=>niè +7BDF=>qiàn +7BE0=>xiǎo +7BE1=>cuàn +7BE2=>lǒng +7BE3=>péng +7BE4=>dǔ +7BE5=>lì +7BE6=>bì +7BE7=>zhuó +7BE8=>chú +7BE9=>shāi +7BEA=>chí +7BEB=>zhù +7BEC=>qiāng +7BED=>lóng +7BEE=>lán +7BEF=>jiān +7BF0=>bù +7BF1=>lí +7BF2=>huì +7BF3=>bì +7BF4=>dí +7BF5=>cōng +7BF6=>yān +7BF7=>peng +7BF8=>cǎn +7BF9=>zhuàn +7BFA=>pí +7BFB=>piǎo +7BFC=>dōu +7BFD=>yù +7BFE=>miè +7BFF=>tuán +7C00=>zé +7C01=>shāi +7C02=>guì +7C03=>yí +7C04=>hù +7C05=>chǎn +7C06=>kòu +7C07=>cù +7C08=>píng +7C09=>zào +7C0A=>jī +7C0B=>guǐ +7C0C=>sù +7C0D=>lǒu +7C0E=>cè +7C0F=>lù +7C10=>niǎn +7C11=>suō +7C12=>cuàn +7C13=>diao +7C14=>suō +7C15=>lè +7C16=>duàn +7C17=>liang +7C18=>xiāo +7C19=>bó +7C1A=>mì +7C1B=>shāi +7C1C=>dàng +7C1D=>liáo +7C1E=>dān +7C1F=>diàn +7C20=>fǔ +7C21=>jiǎn +7C22=>mǐn +7C23=>kuì +7C24=>dài +7C25=>jiāo +7C26=>dēng +7C27=>huáng +7C28=>sǔn +7C29=>láo +7C2A=>zān +7C2B=>xiāo +7C2C=>lù +7C2D=>shì +7C2E=>zān +7C2F=>qi +7C30=>pái +7C31=>qi +7C32=>pái +7C33=>gǎn +7C34=>jù +7C35=>dù +7C36=>lù +7C37=>yán +7C38=>bǒ +7C39=>dāng +7C3A=>sài +7C3B=>zhuā +7C3C=>lóng +7C3D=>qiān +7C3E=>lián +7C3F=>bù +7C40=>zhòu +7C41=>lài +7C42=>shi +7C43=>lán +7C44=>kuì +7C45=>yú +7C46=>yuè +7C47=>háo +7C48=>zhēn +7C49=>tái +7C4A=>tì +7C4B=>niè +7C4C=>chóu +7C4D=>jí +7C4E=>yi +7C4F=>qi +7C50=>téng +7C51=>zhuàn +7C52=>zhòu +7C53=>fān +7C54=>sǒu +7C55=>zhòu +7C56=>qian +7C57=>zhuó +7C58=>téng +7C59=>lù +7C5A=>lú +7C5B=>jiǎn +7C5C=>tuò +7C5D=>yíng +7C5E=>yù +7C5F=>lài +7C60=>lóng +7C61=>qie +7C62=>lián +7C63=>lán +7C64=>qiān +7C65=>yuè +7C66=>zhōng +7C67=>qú +7C68=>lián +7C69=>biān +7C6A=>duàn +7C6B=>zuǎn +7C6C=>lí +7C6D=>sī +7C6E=>luó +7C6F=>yíng +7C70=>yuè +7C71=>zhuó +7C72=>yù +7C73=>mǐ +7C74=>dí +7C75=>fán +7C76=>shēn +7C77=>zhé +7C78=>shēn +7C79=>nǚ +7C7A=>hé +7C7B=>lèi +7C7C=>xiān +7C7D=>zǐ +7C7E=>ní +7C7F=>cùn +7C80=>zhang +7C81=>qiān +7C82=>zhai +7C83=>bǐ +7C84=>bǎn +7C85=>wù +7C86=>shā +7C87=>kāng +7C88=>rǒu +7C89=>fěn +7C8A=>bì +7C8B=>cuì +7C8C=>yin +7C8D=>zhé +7C8E=>chǐ +7C8F=>tai +7C90=>hu +7C91=>bā +7C92=>lì +7C93=>gān +7C94=>jù +7C95=>pò +7C96=>mò +7C97=>cū +7C98=>zhān +7C99=>zhòu +7C9A=>lí +7C9B=>sù +7C9C=>tiào +7C9D=>lì +7C9E=>xī +7C9F=>sù +7CA0=>hóng +7CA1=>tóng +7CA2=>zī +7CA3=>cè +7CA4=>yuè +7CA5=>zhōu +7CA6=>lín +7CA7=>zhuāng +7CA8=>bǎi +7CA9=>lao +7CAA=>fèn +7CAB=>ér +7CAC=>qū +7CAD=>he +7CAE=>liáng +7CAF=>xiàn +7CB0=>fú +7CB1=>liáng +7CB2=>càn +7CB3=>jīng +7CB4=>lǐ +7CB5=>yuè +7CB6=>lù +7CB7=>jú +7CB8=>qí +7CB9=>cuì +7CBA=>bài +7CBB=>zhāng +7CBC=>lín +7CBD=>zòng +7CBE=>jīng +7CBF=>guǒ +7CC0=>hua +7CC1=>sǎn +7CC2=>sǎn +7CC3=>táng +7CC4=>biǎn +7CC5=>róu +7CC6=>miàn +7CC7=>hóu +7CC8=>xǔ +7CC9=>zòng +7CCA=>hu +7CCB=>jiàn +7CCC=>zān +7CCD=>cí +7CCE=>lí +7CCF=>xiè +7CD0=>fū +7CD1=>nuò +7CD2=>bèi +7CD3=>gǔ +7CD4=>xiǔ +7CD5=>gāo +7CD6=>táng +7CD7=>qiǔ +7CD8=>jia +7CD9=>cāo +7CDA=>zhuāng +7CDB=>táng +7CDC=>mí +7CDD=>sǎn +7CDE=>fèn +7CDF=>zāo +7CE0=>kāng +7CE1=>jiàng +7CE2=>mó +7CE3=>sǎn +7CE4=>sǎn +7CE5=>nuò +7CE6=>xī +7CE7=>liáng +7CE8=>jiàng +7CE9=>kuài +7CEA=>bò +7CEB=>huán +7CEC=>shu +7CED=>zòng +7CEE=>xiàn +7CEF=>nuò +7CF0=>tuán +7CF1=>niè +7CF2=>lì +7CF3=>zuò +7CF4=>dí +7CF5=>niè +7CF6=>tiào +7CF7=>làn +7CF8=>mì +7CF9=>sī +7CFA=>jiū +7CFB=>xì +7CFC=>gōng +7CFD=>zhěng +7CFE=>jiū +7CFF=>yòu +7D00=>jì +7D01=>chà +7D02=>zhòu +7D03=>xún +7D04=>yuē +7D05=>hóng +7D06=>yū +7D07=>hé +7D08=>wán +7D09=>rèn +7D0A=>wěn +7D0B=>wén +7D0C=>qiú +7D0D=>nà +7D0E=>zī +7D0F=>tǒu +7D10=>niǔ +7D11=>fóu +7D12=>jì +7D13=>shū +7D14=>chún +7D15=>pī +7D16=>zhèn +7D17=>shā +7D18=>hóng +7D19=>zhǐ +7D1A=>jí +7D1B=>fēn +7D1C=>yún +7D1D=>rèn +7D1E=>dǎn +7D1F=>jīn +7D20=>sù +7D21=>fǎng +7D22=>suǒ +7D23=>cuì +7D24=>jiǔ +7D25=>zā +7D26=>ba +7D27=>jǐn +7D28=>fū +7D29=>zhì +7D2A=>cǐ +7D2B=>zǐ +7D2C=>chóu +7D2D=>hóng +7D2E=>zā +7D2F=>lèi +7D30=>xì +7D31=>fú +7D32=>xiè +7D33=>shēn +7D34=>bō +7D35=>zhù +7D36=>qū +7D37=>líng +7D38=>zhù +7D39=>shào +7D3A=>gàn +7D3B=>yǎng +7D3C=>fú +7D3D=>tuó +7D3E=>zhěn +7D3F=>dài +7D40=>chù +7D41=>shī +7D42=>zhōng +7D43=>xián +7D44=>zǔ +7D45=>jiōng +7D46=>bàn +7D47=>qú +7D48=>mò +7D49=>shù +7D4A=>zuì +7D4B=>kuang +7D4C=>jīng +7D4D=>rèn +7D4E=>háng +7D4F=>xiè +7D50=>jié +7D51=>zhū +7D52=>chóu +7D53=>guà +7D54=>bǎi +7D55=>jué +7D56=>kuàng +7D57=>hú +7D58=>cì +7D59=>huán +7D5A=>gēng +7D5B=>tāo +7D5C=>jié +7D5D=>kù +7D5E=>jiǎo +7D5F=>quán +7D60=>gǎi +7D61=>luò +7D62=>xuàn +7D63=>bēng +7D64=>xiàn +7D65=>fú +7D66=>gěi +7D67=>dòng +7D68=>róng +7D69=>tiào +7D6A=>yīn +7D6B=>lěi +7D6C=>xiè +7D6D=>juàn +7D6E=>xù +7D6F=>gāi +7D70=>dié +7D71=>tǒng +7D72=>sī +7D73=>jiàng +7D74=>xiáng +7D75=>huì +7D76=>jué +7D77=>zhí +7D78=>jiǎn +7D79=>juàn +7D7A=>chī +7D7B=>miǎn +7D7C=>zhěn +7D7D=>lǚ +7D7E=>chéng +7D7F=>qiú +7D80=>shū +7D81=>bǎng +7D82=>tǒng +7D83=>xiāo +7D84=>huán +7D85=>qīn +7D86=>gěng +7D87=>xiǔ +7D88=>tí +7D89=>tòu +7D8A=>xié +7D8B=>hóng +7D8C=>xì +7D8D=>fú +7D8E=>tīng +7D8F=>suī +7D90=>duì +7D91=>kǔn +7D92=>fū +7D93=>jīng +7D94=>hù +7D95=>zhī +7D96=>yán +7D97=>jiǒng +7D98=>féng +7D99=>jì +7D9A=>xu +7D9B=>ren +7D9C=>zōng +7D9D=>chēn +7D9E=>duǒ +7D9F=>lì +7DA0=>lǜ +7DA1=>liáng +7DA2=>chóu +7DA3=>quǎn +7DA4=>shào +7DA5=>qí +7DA6=>qí +7DA7=>zhǔn +7DA8=>qí +7DA9=>wǎn +7DAA=>qiàn +7DAB=>xiàn +7DAC=>shòu +7DAD=>wéi +7DAE=>qǐ +7DAF=>táo +7DB0=>wǎn +7DB1=>gāng +7DB2=>wǎng +7DB3=>bēng +7DB4=>zhui +7DB5=>cǎi +7DB6=>guǒ +7DB7=>cuì +7DB8=>lún +7DB9=>liǔ +7DBA=>qǐ +7DBB=>zhàn +7DBC=>bì +7DBD=>chuò +7DBE=>líng +7DBF=>mián +7DC0=>qī +7DC1=>qiè +7DC2=>tián +7DC3=>zōng +7DC4=>gǔn +7DC5=>zōu +7DC6=>xī +7DC7=>zī +7DC8=>xìng +7DC9=>liǎng +7DCA=>jǐn +7DCB=>fēi +7DCC=>ruí +7DCD=>mín +7DCE=>yù +7DCF=>zǒng +7DD0=>fán +7DD1=>lǜ +7DD2=>xù +7DD3=>yīng +7DD4=>shàng +7DD5=>qi +7DD6=>xù +7DD7=>xiāng +7DD8=>jiān +7DD9=>kè +7DDA=>xiàn +7DDB=>ruǎn +7DDC=>mián +7DDD=>jī +7DDE=>duàn +7DDF=>chóng +7DE0=>dì +7DE1=>mín +7DE2=>miáo +7DE3=>yuán +7DE4=>xiè +7DE5=>bǎo +7DE6=>sī +7DE7=>qiū +7DE8=>biān +7DE9=>huǎn +7DEA=>gēng +7DEB=>cōng +7DEC=>miǎn +7DED=>wèi +7DEE=>fù +7DEF=>wěi +7DF0=>tóu +7DF1=>gōu +7DF2=>miǎo +7DF3=>xié +7DF4=>liàn +7DF5=>zōng +7DF6=>biàn +7DF7=>yùn +7DF8=>yīn +7DF9=>tí +7DFA=>guā +7DFB=>zhì +7DFC=>yùn +7DFD=>chēng +7DFE=>chán +7DFF=>dài +7E00=>xiá +7E01=>yuán +7E02=>zǒng +7E03=>xū +7E04=>ying +7E05=>wei +7E06=>gēng +7E07=>xuān +7E08=>yíng +7E09=>jìn +7E0A=>yì +7E0B=>zhuì +7E0C=>nì +7E0D=>bāng +7E0E=>gǔ +7E0F=>pán +7E10=>zhòu +7E11=>jiān +7E12=>cī +7E13=>quán +7E14=>shuǎng +7E15=>yūn +7E16=>xiá +7E17=>cuī +7E18=>xī +7E19=>róng +7E1A=>tāo +7E1B=>fù +7E1C=>yún +7E1D=>chēn +7E1E=>gǎo +7E1F=>rù +7E20=>hú +7E21=>zài +7E22=>téng +7E23=>xiàn +7E24=>sù +7E25=>zhěn +7E26=>zòng +7E27=>tāo +7E28=>huang +7E29=>cài +7E2A=>bì +7E2B=>fèng +7E2C=>cù +7E2D=>lí +7E2E=>suō +7E2F=>yǎn +7E30=>xǐ +7E31=>zòng +7E32=>léi +7E33=>juàn +7E34=>qiàn +7E35=>màn +7E36=>zhí +7E37=>lǚ +7E38=>mù +7E39=>piǎo +7E3A=>lián +7E3B=>mí +7E3C=>xuàn +7E3D=>zǒng +7E3E=>jī +7E3F=>shān +7E40=>suì +7E41=>fán +7E42=>lǜ +7E43=>běng +7E44=>yī +7E45=>sāo +7E46=>móu +7E47=>yáo +7E48=>qiǎng +7E49=>hún +7E4A=>xian +7E4B=>jì +7E4C=>sha +7E4D=>xiù +7E4E=>rán +7E4F=>xuàn +7E50=>suì +7E51=>qiāo +7E52=>zēng +7E53=>zuǒ +7E54=>zhī +7E55=>shàn +7E56=>sǎn +7E57=>lín +7E58=>yù +7E59=>fān +7E5A=>liáo +7E5B=>chuò +7E5C=>zūn +7E5D=>jiàn +7E5E=>rào +7E5F=>chǎn +7E60=>ruǐ +7E61=>xiù +7E62=>huì +7E63=>huà +7E64=>zuǎn +7E65=>xī +7E66=>qiǎng +7E67=>yun +7E68=>da +7E69=>shéng +7E6A=>huì +7E6B=>xì +7E6C=>sè +7E6D=>jiǎn +7E6E=>jiāng +7E6F=>huán +7E70=>zǎo +7E71=>cōng +7E72=>xiè +7E73=>jiǎo +7E74=>bì +7E75=>dàn +7E76=>yì +7E77=>nǒng +7E78=>suì +7E79=>yì +7E7A=>shǎi +7E7B=>xū +7E7C=>jì +7E7D=>bīn +7E7E=>qiǎn +7E7F=>lán +7E80=>pú +7E81=>xūn +7E82=>zuǎn +7E83=>qí +7E84=>péng +7E85=>yào +7E86=>mò +7E87=>lèi +7E88=>xié +7E89=>zuǎn +7E8A=>kuàng +7E8B=>yōu +7E8C=>xù +7E8D=>léi +7E8E=>xiān +7E8F=>chán +7E90=>jiao +7E91=>lú +7E92=>chán +7E93=>yīng +7E94=>cái +7E95=>rǎng +7E96=>xiān +7E97=>zuī +7E98=>zuǎn +7E99=>luò +7E9A=>lí +7E9B=>dào +7E9C=>lǎn +7E9D=>léi +7E9E=>liàn +7E9F=>sī +7EA0=>jiū +7EA1=>yū +7EA2=>hóng +7EA3=>zhòu +7EA4=>xiān +7EA5=>gē +7EA6=>yuē +7EA7=>jí +7EA8=>wán +7EA9=>kuàng +7EAA=>jì +7EAB=>rèn +7EAC=>wěi +7EAD=>yún +7EAE=>hóng +7EAF=>chún +7EB0=>pī +7EB1=>shā +7EB2=>gāng +7EB3=>nà +7EB4=>rèn +7EB5=>zòng +7EB6=>lún +7EB7=>fēn +7EB8=>zhǐ +7EB9=>wén +7EBA=>fǎng +7EBB=>zhù +7EBC=>zhèn +7EBD=>niǔ +7EBE=>shū +7EBF=>xiàn +7EC0=>gàn +7EC1=>xiè +7EC2=>fú +7EC3=>liàn +7EC4=>zǔ +7EC5=>shēn +7EC6=>xì +7EC7=>zhī +7EC8=>zhōng +7EC9=>zhòu +7ECA=>bàn +7ECB=>fú +7ECC=>chù +7ECD=>shào +7ECE=>yì +7ECF=>jīng +7ED0=>dài +7ED1=>bǎng +7ED2=>róng +7ED3=>jié +7ED4=>kù +7ED5=>rào +7ED6=>dié +7ED7=>háng +7ED8=>huì +7ED9=>gěi +7EDA=>xuàn +7EDB=>jiàng +7EDC=>luò +7EDD=>jué +7EDE=>jiǎo +7EDF=>tǒng +7EE0=>gěng +7EE1=>xiāo +7EE2=>juàn +7EE3=>xiù +7EE4=>xì +7EE5=>suí +7EE6=>tāo +7EE7=>jì +7EE8=>tí +7EE9=>jī +7EEA=>xù +7EEB=>líng +7EEC=>yīng +7EED=>xù +7EEE=>qǐ +7EEF=>fēi +7EF0=>chuò +7EF1=>shàng +7EF2=>gǔn +7EF3=>shéng +7EF4=>wéi +7EF5=>mián +7EF6=>shòu +7EF7=>běng +7EF8=>chóu +7EF9=>táo +7EFA=>liǔ +7EFB=>quǎn +7EFC=>zōng +7EFD=>zhàn +7EFE=>wǎn +7EFF=>lǜ +7F00=>zhui +7F01=>zī +7F02=>kè +7F03=>xiāng +7F04=>jiān +7F05=>miǎn +7F06=>lǎn +7F07=>tí +7F08=>miǎo +7F09=>jī +7F0A=>yūn +7F0B=>huì +7F0C=>sī +7F0D=>duǒ +7F0E=>duàn +7F0F=>biàn +7F10=>xiàn +7F11=>gōu +7F12=>zhuì +7F13=>huǎn +7F14=>dì +7F15=>lǚ +7F16=>biān +7F17=>mín +7F18=>yuán +7F19=>jìn +7F1A=>fù +7F1B=>rù +7F1C=>zhěn +7F1D=>fèng +7F1E=>cuī +7F1F=>gǎo +7F20=>chán +7F21=>lí +7F22=>yì +7F23=>jiān +7F24=>bīn +7F25=>piāo +7F26=>màn +7F27=>léi +7F28=>yīng +7F29=>suō +7F2A=>móu +7F2B=>sāo +7F2C=>xié +7F2D=>liáo +7F2E=>shàn +7F2F=>zēng +7F30=>jiāng +7F31=>qiǎn +7F32=>qiāo +7F33=>huán +7F34=>jiǎo +7F35=>zuǎn +7F36=>fǒu +7F37=>xiè +7F38=>gāng +7F39=>fǒu +7F3A=>quē +7F3B=>fǒu +7F3C=>qi +7F3D=>bō +7F3E=>píng +7F3F=>xiàng +7F40=>zhao +7F41=>gāng +7F42=>yīng +7F43=>yīng +7F44=>qìng +7F45=>xià +7F46=>guàn +7F47=>zūn +7F48=>tán +7F49=>cang +7F4A=>qì +7F4B=>wèng +7F4C=>yīng +7F4D=>léi +7F4E=>tán +7F4F=>lú +7F50=>guàn +7F51=>wǎng +7F52=>wǎng +7F53=>gāng +7F54=>wǎng +7F55=>hǎn +7F56=>luó +7F57=>luō +7F58=>fú +7F59=>mí +7F5A=>fá +7F5B=>gū +7F5C=>zhǔ +7F5D=>jū +7F5E=>máo +7F5F=>gǔ +7F60=>mín +7F61=>gāng +7F62=>ba +7F63=>guà +7F64=>tí +7F65=>juàn +7F66=>fú +7F67=>shèn +7F68=>yǎn +7F69=>zhào +7F6A=>zuì +7F6B=>guà +7F6C=>zhuó +7F6D=>yù +7F6E=>zhì +7F6F=>ǎn +7F70=>fá +7F71=>lǎn +7F72=>shǔ +7F73=>sī +7F74=>pí +7F75=>mà +7F76=>liǔ +7F77=>ba +7F78=>fá +7F79=>lí +7F7A=>cháo +7F7B=>wèi +7F7C=>bì +7F7D=>jì +7F7E=>zēng +7F7F=>chōng +7F80=>liǔ +7F81=>jī +7F82=>juàn +7F83=>mì +7F84=>zhào +7F85=>luó +7F86=>pí +7F87=>jī +7F88=>jī +7F89=>luán +7F8A=>yáng +7F8B=>mǐ +7F8C=>qiāng +7F8D=>dá +7F8E=>měi +7F8F=>yáng +7F90=>yǒu +7F91=>yǒu +7F92=>fén +7F93=>bā +7F94=>gāo +7F95=>yàng +7F96=>gǔ +7F97=>qiāng +7F98=>zāng +7F99=>gāo +7F9A=>líng +7F9B=>yì +7F9C=>zhù +7F9D=>dī +7F9E=>xiū +7F9F=>qiǎng +7FA0=>yí +7FA1=>xiàn +7FA2=>róng +7FA3=>qún +7FA4=>qún +7FA5=>qiǎng +7FA6=>huán +7FA7=>suō +7FA8=>xiàn +7FA9=>yì +7FAA=>yang +7FAB=>qiāng +7FAC=>qián +7FAD=>yú +7FAE=>gēng +7FAF=>jié +7FB0=>tāng +7FB1=>yuán +7FB2=>xī +7FB3=>fán +7FB4=>shān +7FB5=>fén +7FB6=>shān +7FB7=>liǎn +7FB8=>léi +7FB9=>gēng +7FBA=>nóu +7FBB=>qiàng +7FBC=>chàn +7FBD=>yǔ +7FBE=>gòng +7FBF=>yì +7FC0=>chōng +7FC1=>wēng +7FC2=>fēn +7FC3=>hóng +7FC4=>chì +7FC5=>chì +7FC6=>cuì +7FC7=>fú +7FC8=>xiá +7FC9=>běn +7FCA=>yì +7FCB=>lā +7FCC=>yì +7FCD=>pī +7FCE=>líng +7FCF=>liù +7FD0=>zhì +7FD1=>qú +7FD2=>xí +7FD3=>xié +7FD4=>xiáng +7FD5=>xī +7FD6=>xì +7FD7=>ké +7FD8=>qiào +7FD9=>huì +7FDA=>huī +7FDB=>xiāo +7FDC=>shà +7FDD=>hóng +7FDE=>jiāng +7FDF=>dí +7FE0=>cuì +7FE1=>fěi +7FE2=>dào +7FE3=>shà +7FE4=>chì +7FE5=>zhù +7FE6=>jiǎn +7FE7=>xuān +7FE8=>chì +7FE9=>piān +7FEA=>zōng +7FEB=>wán +7FEC=>huī +7FED=>hóu +7FEE=>hé +7FEF=>hè +7FF0=>hàn +7FF1=>áo +7FF2=>piāo +7FF3=>yì +7FF4=>lián +7FF5=>hóu +7FF6=>ao +7FF7=>lín +7FF8=>pěn +7FF9=>qiào +7FFA=>áo +7FFB=>fān +7FFC=>yì +7FFD=>huì +7FFE=>xuān +7FFF=>dào +8000=>yào +8001=>lǎo +8002=>lǎo +8003=>kǎo +8004=>mào +8005=>zhě +8006=>qí +8007=>gǒu +8008=>gǒu +8009=>gǒu +800A=>diè +800B=>dié +800C=>ér +800D=>shuǎ +800E=>ruǎn +800F=>nài +8010=>nài +8011=>duān +8012=>lěi +8013=>tīng +8014=>zǐ +8015=>gēng +8016=>chào +8017=>hào +8018=>yún +8019=>bà +801A=>pī +801B=>yí +801C=>sì +801D=>qù +801E=>jiā +801F=>jù +8020=>huō +8021=>chú +8022=>lào +8023=>lǔn +8024=>jí +8025=>tāng +8026=>ǒu +8027=>lóu +8028=>nòu +8029=>jiǎng +802A=>pǎng +802B=>zhá +802C=>lóu +802D=>jī +802E=>lào +802F=>huò +8030=>yōu +8031=>mò +8032=>huái +8033=>ěr +8034=>yì +8035=>dīng +8036=>yé +8037=>dā +8038=>sǒng +8039=>qín +803A=>yún +803B=>chǐ +803C=>dān +803D=>dān +803E=>hóng +803F=>gěng +8040=>zhí +8041=>pàn +8042=>niè +8043=>dān +8044=>zhěn +8045=>chè +8046=>líng +8047=>zhēng +8048=>yǒu +8049=>wà +804A=>liáo +804B=>lóng +804C=>zhí +804D=>níng +804E=>tiāo +804F=>ér +8050=>yà +8051=>tiē +8052=>guā +8053=>xu +8054=>lián +8055=>hào +8056=>shèng +8057=>liè +8058=>pìn +8059=>jīng +805A=>jù +805B=>bǐ +805C=>dǐ +805D=>guó +805E=>wén +805F=>xù +8060=>pīng +8061=>cōng +8062=>ding +8063=>ní +8064=>tíng +8065=>jǔ +8066=>cōng +8067=>kuī +8068=>lian +8069=>kuì +806A=>cōng +806B=>lián +806C=>wěng +806D=>kuì +806E=>lián +806F=>lián +8070=>cōng +8071=>áo +8072=>shēng +8073=>sǒng +8074=>tīng +8075=>kuì +8076=>niè +8077=>zhí +8078=>dān +8079=>níng +807A=>qié +807B=>nǐ +807C=>tīng +807D=>tīng +807E=>lóng +807F=>yù +8080=>yù +8081=>zhào +8082=>sì +8083=>sù +8084=>yì +8085=>sù +8086=>sì +8087=>zhào +8088=>zhào +8089=>ròu +808A=>yì +808B=>lē +808C=>jī +808D=>qiú +808E=>kěn +808F=>cào +8090=>gē +8091=>bó +8092=>huàn +8093=>huāng +8094=>yǐ +8095=>rèn +8096=>xiào +8097=>rǔ +8098=>zhǒu +8099=>yuàn +809A=>dù +809B=>gāng +809C=>róng +809D=>gān +809E=>chā +809F=>wò +80A0=>cháng +80A1=>gǔ +80A2=>zhī +80A3=>hán +80A4=>fū +80A5=>féi +80A6=>fén +80A7=>pēi +80A8=>pàng +80A9=>jiān +80AA=>fáng +80AB=>zhūn +80AC=>yóu +80AD=>nà +80AE=>āng +80AF=>kěn +80B0=>rán +80B1=>gōng +80B2=>yù +80B3=>wěn +80B4=>yáo +80B5=>qí +80B6=>pí +80B7=>qiǎn +80B8=>xī +80B9=>xī +80BA=>fèi +80BB=>kěn +80BC=>jǐng +80BD=>tài +80BE=>shèn +80BF=>zhǒng +80C0=>zhàng +80C1=>xié +80C2=>shèn +80C3=>wèi +80C4=>zhòu +80C5=>dié +80C6=>dǎn +80C7=>fèi +80C8=>bá +80C9=>bó +80CA=>qú +80CB=>tián +80CC=>bèi +80CD=>guā +80CE=>tāi +80CF=>zǐ +80D0=>kū +80D1=>zhī +80D2=>nì +80D3=>píng +80D4=>zì +80D5=>fǔ +80D6=>pàng +80D7=>zhēn +80D8=>xián +80D9=>zuò +80DA=>pēi +80DB=>jiǎ +80DC=>shèng +80DD=>zhī +80DE=>bāo +80DF=>mǔ +80E0=>qū +80E1=>hú +80E2=>kē +80E3=>chǐ +80E4=>yìn +80E5=>xū +80E6=>yāng +80E7=>lóng +80E8=>dòng +80E9=>kǎ +80EA=>lú +80EB=>jìng +80EC=>nǔ +80ED=>yān +80EE=>pāng +80EF=>kuà +80F0=>yí +80F1=>guāng +80F2=>hǎi +80F3=>gē +80F4=>dòng +80F5=>chī +80F6=>jiāo +80F7=>xiōng +80F8=>xiōng +80F9=>ér +80FA=>àn +80FB=>héng +80FC=>pián +80FD=>néng +80FE=>zì +80FF=>guī +8100=>chéng +8101=>tiǎo +8102=>zhī +8103=>cuì +8104=>méi +8105=>xié +8106=>cuì +8107=>xié +8108=>mài +8109=>mài +810A=>jí +810B=>xie +810C=>nin +810D=>kuài +810E=>sà +810F=>zàng +8110=>qí +8111=>nǎo +8112=>mǐ +8113=>nóng +8114=>luán +8115=>wàn +8116=>bó +8117=>wěn +8118=>wǎn +8119=>xiū +811A=>jiǎo +811B=>jìng +811C=>yǒu +811D=>hēng +811E=>cuǒ +811F=>liè +8120=>shān +8121=>tǐng +8122=>méi +8123=>chún +8124=>shèn +8125=>qiǎn +8126=>de +8127=>juān +8128=>cù +8129=>xiū +812A=>xìn +812B=>tuō +812C=>pāo +812D=>chéng +812E=>něi +812F=>pú +8130=>dòu +8131=>tuō +8132=>niào +8133=>nao +8134=>pǐ +8135=>gǔ +8136=>luó +8137=>lì +8138=>liǎn +8139=>zhàng +813A=>cuì +813B=>jiē +813C=>liǎng +813D=>shuí +813E=>pí +813F=>biāo +8140=>lún +8141=>pián +8142=>lěi +8143=>kuì +8144=>chuí +8145=>dàn +8146=>tiǎn +8147=>něi +8148=>jīng +8149=>nái +814A=>là +814B=>yè +814C=>yān +814D=>rèn +814E=>shèn +814F=>chuò +8150=>fǔ +8151=>fǔ +8152=>jū +8153=>féi +8154=>qiāng +8155=>wàn +8156=>dòng +8157=>pí +8158=>guó +8159=>zōng +815A=>dìng +815B=>wò +815C=>měi +815D=>ní +815E=>zhuàn +815F=>chì +8160=>còu +8161=>luó +8162=>ǒu +8163=>dì +8164=>ān +8165=>xīng +8166=>nǎo +8167=>shù +8168=>shuàn +8169=>nǎn +816A=>yùn +816B=>zhǒng +816C=>ròu +816D=>è +816E=>sāi +816F=>tú +8170=>yāo +8171=>jiàn +8172=>wěi +8173=>jiǎo +8174=>yú +8175=>jiā +8176=>duàn +8177=>bì +8178=>cháng +8179=>fù +817A=>xiàn +817B=>nì +817C=>miǎn +817D=>wà +817E=>téng +817F=>tuǐ +8180=>bǎng +8181=>qiǎn +8182=>lǚ +8183=>wà +8184=>sòu +8185=>táng +8186=>sù +8187=>zhuì +8188=>gé +8189=>yì +818A=>bo +818B=>liáo +818C=>jí +818D=>pí +818E=>xié +818F=>gāo +8190=>lǚ +8191=>bìn +8192=>óu +8193=>cháng +8194=>lù +8195=>guó +8196=>pāng +8197=>chuái +8198=>biāo +8199=>jiǎng +819A=>fū +819B=>táng +819C=>mó +819D=>xī +819E=>zhuān +819F=>lǜ +81A0=>jiāo +81A1=>yìng +81A2=>lǘ +81A3=>zhì +81A4=>xue +81A5=>chūn +81A6=>lìn +81A7=>tóng +81A8=>péng +81A9=>nì +81AA=>chuài +81AB=>liáo +81AC=>cuì +81AD=>guī +81AE=>xiāo +81AF=>tēng +81B0=>fán +81B1=>zhí +81B2=>jiāo +81B3=>shàn +81B4=>hū +81B5=>cuì +81B6=>rùn +81B7=>xiāng +81B8=>suǐ +81B9=>fèn +81BA=>yīng +81BB=>shān +81BC=>zhuā +81BD=>dǎn +81BE=>kuài +81BF=>nóng +81C0=>tún +81C1=>lián +81C2=>bì +81C3=>yōng +81C4=>jué +81C5=>chù +81C6=>yì +81C7=>juǎn +81C8=>là +81C9=>liǎn +81CA=>sāo +81CB=>tún +81CC=>gǔ +81CD=>qí +81CE=>cuì +81CF=>bìn +81D0=>xūn +81D1=>nào +81D2=>wò +81D3=>zàng +81D4=>xiàn +81D5=>biāo +81D6=>xìng +81D7=>kuān +81D8=>là +81D9=>yān +81DA=>lú +81DB=>huò +81DC=>zā +81DD=>luǒ +81DE=>qú +81DF=>zàng +81E0=>luán +81E1=>ní +81E2=>zā +81E3=>chén +81E4=>qiān +81E5=>wò +81E6=>guàng +81E7=>zāng +81E8=>lín +81E9=>guǎng +81EA=>zì +81EB=>jiǎo +81EC=>niè +81ED=>chòu +81EE=>jì +81EF=>gāo +81F0=>chòu +81F1=>mián +81F2=>niè +81F3=>zhì +81F4=>zhì +81F5=>gé +81F6=>jiàn +81F7=>dié +81F8=>zhī +81F9=>xiū +81FA=>tái +81FB=>zhēn +81FC=>jiù +81FD=>xiàn +81FE=>yú +81FF=>chā +8200=>yǎo +8201=>yú +8202=>chōng +8203=>xì +8204=>xì +8205=>jiù +8206=>yú +8207=>yǔ +8208=>xìng +8209=>jǔ +820A=>jiù +820B=>xìn +820C=>shé +820D=>shě +820E=>she +820F=>jiǔ +8210=>shì +8211=>tān +8212=>shū +8213=>shì +8214=>tiǎn +8215=>tàn +8216=>pù +8217=>pù +8218=>guǎn +8219=>huà +821A=>tiàn +821B=>chuǎn +821C=>shùn +821D=>xiá +821E=>wǔ +821F=>zhōu +8220=>dāo +8221=>chuán +8222=>shān +8223=>yǐ +8224=>fan +8225=>pā +8226=>tài +8227=>fán +8228=>bǎn +8229=>chuán +822A=>háng +822B=>fǎng +822C=>bān +822D=>bǐ +822E=>lu +822F=>zhōng +8230=>jiàn +8231=>cāng +8232=>líng +8233=>zhú +8234=>zé +8235=>duò +8236=>bó +8237=>xián +8238=>gě +8239=>chuán +823A=>xiá +823B=>lú +823C=>qióng +823D=>páng +823E=>xī +823F=>kua +8240=>fú +8241=>zào +8242=>féng +8243=>lí +8244=>shāo +8245=>yú +8246=>láng +8247=>tǐng +8248=>yù +8249=>wěi +824A=>bó +824B=>měng +824C=>niàn +824D=>jū +824E=>huáng +824F=>shǒu +8250=>kè +8251=>biàn +8252=>mù +8253=>dié +8254=>dou +8255=>bàng +8256=>chā +8257=>yì +8258=>sōu +8259=>cāng +825A=>cáo +825B=>lóu +825C=>dài +825D=>xue +825E=>yào +825F=>chōng +8260=>deng +8261=>dāng +8262=>qiáng +8263=>lǔ +8264=>yǐ +8265=>jí +8266=>jiàn +8267=>huò +8268=>méng +8269=>qí +826A=>lǔ +826B=>lú +826C=>chán +826D=>shuāng +826E=>gěn +826F=>liáng +8270=>jiān +8271=>jiān +8272=>sè +8273=>yàn +8274=>fú +8275=>pīng +8276=>yàn +8277=>yàn +8278=>cǎo +8279=>cao +827A=>yì +827B=>lè +827C=>tīng +827D=>jiāo +827E=>ài +827F=>nǎi +8280=>tiáo +8281=>jiāo +8282=>jié +8283=>péng +8284=>wán +8285=>yì +8286=>chāi +8287=>mián +8288=>mǐ +8289=>gān +828A=>qiān +828B=>yù +828C=>yù +828D=>sháo +828E=>qiōng +828F=>dù +8290=>hù +8291=>qǐ +8292=>máng +8293=>zì +8294=>huì +8295=>suī +8296=>zhì +8297=>xiāng +8298=>pí +8299=>fú +829A=>tún +829B=>wěi +829C=>wú +829D=>zhī +829E=>qì +829F=>shān +82A0=>wén +82A1=>qiàn +82A2=>rén +82A3=>fú +82A4=>kōu +82A5=>jiè +82A6=>lú +82A7=>xù +82A8=>jī +82A9=>qín +82AA=>qí +82AB=>yán +82AC=>fēn +82AD=>bā +82AE=>ruì +82AF=>xīn +82B0=>jì +82B1=>huā +82B2=>huā +82B3=>fāng +82B4=>wù +82B5=>jué +82B6=>gǒu +82B7=>zhǐ +82B8=>yún +82B9=>qín +82BA=>ǎo +82BB=>chú +82BC=>mào +82BD=>yá +82BE=>fèi +82BF=>rèng +82C0=>háng +82C1=>cōng +82C2=>yín +82C3=>yǒu +82C4=>biàn +82C5=>yì +82C6=>qie +82C7=>wěi +82C8=>lì +82C9=>pǐ +82CA=>è +82CB=>xiàn +82CC=>cháng +82CD=>cāng +82CE=>zhù +82CF=>sū +82D0=>tí +82D1=>yuàn +82D2=>rǎn +82D3=>líng +82D4=>tái +82D5=>sháo +82D6=>dí +82D7=>miáo +82D8=>qǐng +82D9=>lì +82DA=>yòng +82DB=>kē +82DC=>mù +82DD=>bèi +82DE=>bāo +82DF=>gǒu +82E0=>mín +82E1=>yǐ +82E2=>yǐ +82E3=>jù +82E4=>piě +82E5=>ruò +82E6=>kǔ +82E7=>níng +82E8=>nǐ +82E9=>bó +82EA=>bǐng +82EB=>shān +82EC=>xiú +82ED=>yǎo +82EE=>xiān +82EF=>běn +82F0=>hóng +82F1=>yīng +82F2=>zhǎ +82F3=>dōng +82F4=>jū +82F5=>dié +82F6=>nié +82F7=>gān +82F8=>hū +82F9=>píng +82FA=>méi +82FB=>fú +82FC=>shēng +82FD=>gū +82FE=>bì +82FF=>wèi +8300=>fú +8301=>zhuó +8302=>mào +8303=>fàn +8304=>jiā +8305=>máo +8306=>máo +8307=>bá +8308=>cí +8309=>mò +830A=>zī +830B=>dǐ +830C=>chí +830D=>jì +830E=>jīng +830F=>lóng +8310=>cong +8311=>niǎo +8312=>yuán +8313=>xué +8314=>yíng +8315=>qióng +8316=>gé +8317=>míng +8318=>lì +8319=>róng +831A=>yìn +831B=>gèn +831C=>qiàn +831D=>chǎi +831E=>chén +831F=>yù +8320=>hāo +8321=>zì +8322=>liè +8323=>wú +8324=>jì +8325=>guī +8326=>cì +8327=>jiǎn +8328=>cí +8329=>gòu +832A=>guāng +832B=>máng +832C=>chá +832D=>jiāo +832E=>jiāo +832F=>fú +8330=>yú +8331=>zhū +8332=>zī +8333=>jiāng +8334=>huí +8335=>yīn +8336=>chá +8337=>fá +8338=>rōng +8339=>rú +833A=>chōng +833B=>mǎng +833C=>tóng +833D=>zhòng +833E=>qiān +833F=>zhú +8340=>xún +8341=>huán +8342=>fū +8343=>quán +8344=>gāi +8345=>dā +8346=>jīng +8347=>xìng +8348=>chuǎn +8349=>cǎo +834A=>jīng +834B=>ér +834C=>àn +834D=>qiáo +834E=>chí +834F=>rěn +8350=>jiàn +8351=>tí +8352=>huāng +8353=>píng +8354=>lì +8355=>jīn +8356=>lǎo +8357=>shù +8358=>zhuāng +8359=>dá +835A=>jiá +835B=>ráo +835C=>bì +835D=>zé +835E=>qiáo +835F=>huì +8360=>jì +8361=>dàng +8362=>yu +8363=>róng +8364=>hūn +8365=>xíng +8366=>luò +8367=>yíng +8368=>xún +8369=>jìn +836A=>sūn +836B=>yīn +836C=>mǎi +836D=>hóng +836E=>zhòu +836F=>yào +8370=>dù +8371=>wěi +8372=>lí +8373=>dòu +8374=>fū +8375=>rěn +8376=>yín +8377=>hé +8378=>bí +8379=>bù +837A=>yǔn +837B=>dí +837C=>tú +837D=>suī +837E=>suī +837F=>chéng +8380=>chén +8381=>wú +8382=>bié +8383=>xī +8384=>gěng +8385=>lì +8386=>pú +8387=>zhù +8388=>mò +8389=>lì +838A=>zhuāng +838B=>zuó +838C=>tuō +838D=>qiú +838E=>shā +838F=>suō +8390=>chén +8391=>péng +8392=>jǔ +8393=>méi +8394=>méng +8395=>xìng +8396=>jīng +8397=>chē +8398=>shēn +8399=>jūn +839A=>yán +839B=>tíng +839C=>yóu +839D=>cuò +839E=>guǎn +839F=>hàn +83A0=>yǒu +83A1=>cuò +83A2=>jiá +83A3=>wáng +83A4=>sù +83A5=>niǔ +83A6=>shāo +83A7=>xiàn +83A8=>làng +83A9=>fú +83AA=>é +83AB=>mò +83AC=>wèn +83AD=>jié +83AE=>nán +83AF=>mù +83B0=>kǎn +83B1=>lái +83B2=>lián +83B3=>shí +83B4=>wō +83B5=>tu +83B6=>xiān +83B7=>huò +83B8=>yóu +83B9=>yíng +83BA=>yīng +83BB=>gòng +83BC=>chún +83BD=>mǎng +83BE=>mǎng +83BF=>cì +83C0=>wǎn +83C1=>jīng +83C2=>dì +83C3=>qú +83C4=>dōng +83C5=>jiān +83C6=>zōu +83C7=>gu +83C8=>lā +83C9=>lù +83CA=>jú +83CB=>wèi +83CC=>jūn +83CD=>niè +83CE=>kūn +83CF=>hé +83D0=>pú +83D1=>zāi +83D2=>gǎo +83D3=>guǒ +83D4=>fú +83D5=>lún +83D6=>chāng +83D7=>chóu +83D8=>sōng +83D9=>chuí +83DA=>zhàn +83DB=>mén +83DC=>cài +83DD=>bá +83DE=>lí +83DF=>tú +83E0=>bō +83E1=>hàn +83E2=>bào +83E3=>qìn +83E4=>juǎn +83E5=>xī +83E6=>qín +83E7=>dǐ +83E8=>jiē +83E9=>pú +83EA=>dàng +83EB=>jǐn +83EC=>qiáo +83ED=>tái +83EE=>gēng +83EF=>huá +83F0=>gū +83F1=>líng +83F2=>fēi +83F3=>qín +83F4=>ān +83F5=>wǎng +83F6=>běng +83F7=>zhǒu +83F8=>yān +83F9=>jū +83FA=>jiān +83FB=>lǐn +83FC=>tǎn +83FD=>shū +83FE=>tián +83FF=>dào +8400=>hǔ +8401=>qí +8402=>hé +8403=>cuì +8404=>táo +8405=>chūn +8406=>bì +8407=>cháng +8408=>huán +8409=>fèi +840A=>lái +840B=>qī +840C=>méng +840D=>píng +840E=>wēi +840F=>dàn +8410=>shà +8411=>huán +8412=>yǎn +8413=>yí +8414=>tiáo +8415=>qí +8416=>wǎn +8417=>cè +8418=>nài +8419=>zhen +841A=>tuò +841B=>jiū +841C=>tiē +841D=>luó +841E=>bi +841F=>yi +8420=>méng +8421=>bo +8422=>pao +8423=>ding +8424=>yíng +8425=>yíng +8426=>yíng +8427=>xiāo +8428=>sà +8429=>qiū +842A=>kē +842B=>xiàng +842C=>wàn +842D=>yǔ +842E=>yú +842F=>fù +8430=>liàn +8431=>xuān +8432=>xuān +8433=>nǎn +8434=>cè +8435=>wō +8436=>chǔn +8437=>xiāo +8438=>yú +8439=>biǎn +843A=>mào +843B=>ān +843C=>è +843D=>luò +843E=>yíng +843F=>kuò +8440=>kuò +8441=>jiāng +8442=>miǎn +8443=>zuò +8444=>zuò +8445=>zū +8446=>bǎo +8447=>róu +8448=>xǐ +8449=>yè +844A=>ān +844B=>qú +844C=>jiān +844D=>fú +844E=>lǜ +844F=>jīng +8450=>pén +8451=>fēng +8452=>hóng +8453=>hóng +8454=>hóu +8455=>yàn +8456=>tū +8457=>zhe +8458=>zī +8459=>xiāng +845A=>rèn +845B=>gé +845C=>qiā +845D=>qíng +845E=>mǐ +845F=>huáng +8460=>shēn +8461=>pú +8462=>gài +8463=>dǒng +8464=>zhòu +8465=>jiàn +8466=>wěi +8467=>bó +8468=>wēi +8469=>pā +846A=>jì +846B=>hú +846C=>zàng +846D=>jiā +846E=>duàn +846F=>yào +8470=>suī +8471=>cōng +8472=>quán +8473=>wēi +8474=>zhēn +8475=>kuí +8476=>tíng +8477=>hūn +8478=>xǐ +8479=>shī +847A=>qì +847B=>lán +847C=>zōng +847D=>yāo +847E=>yuān +847F=>méi +8480=>yūn +8481=>shù +8482=>dì +8483=>zhuàn +8484=>guān +8485=>ran +8486=>xuē +8487=>chǎn +8488=>kǎi +8489=>kuì +848A=>huā +848B=>jiǎng +848C=>lóu +848D=>wěi +848E=>pài +848F=>you +8490=>sōu +8491=>yìn +8492=>shī +8493=>chún +8494=>shí +8495=>yūn +8496=>zhēn +8497=>làng +8498=>rú +8499=>méng +849A=>lì +849B=>quē +849C=>suàn +849D=>yuán +849E=>lì +849F=>jǔ +84A0=>xī +84A1=>bàng +84A2=>chú +84A3=>xú +84A4=>tú +84A5=>liú +84A6=>huò +84A7=>diǎn +84A8=>qiàn +84A9=>zū +84AA=>pò +84AB=>cuó +84AC=>yuān +84AD=>chú +84AE=>yù +84AF=>kuǎi +84B0=>pán +84B1=>pú +84B2=>pú +84B3=>nà +84B4=>shuò +84B5=>xí +84B6=>fén +84B7=>yún +84B8=>zhēng +84B9=>jiān +84BA=>jí +84BB=>ruò +84BC=>cāng +84BD=>ēn +84BE=>mí +84BF=>hāo +84C0=>sūn +84C1=>zhēn +84C2=>míng +84C3=>sōu +84C4=>xù +84C5=>liú +84C6=>xí +84C7=>gǔ +84C8=>láng +84C9=>róng +84CA=>wěng +84CB=>gài +84CC=>cuò +84CD=>shī +84CE=>táng +84CF=>luǒ +84D0=>rù +84D1=>suō +84D2=>xuān +84D3=>bèi +84D4=>yǎo +84D5=>guì +84D6=>bì +84D7=>zǒng +84D8=>gǔn +84D9=>zuo +84DA=>tiáo +84DB=>cè +84DC=>pei +84DD=>lán +84DE=>dàn +84DF=>jì +84E0=>lí +84E1=>shēn +84E2=>lǎng +84E3=>yù +84E4=>ling +84E5=>yíng +84E6=>mò +84E7=>diào +84E8=>tiáo +84E9=>mǎo +84EA=>tōng +84EB=>chù +84EC=>péng +84ED=>ān +84EE=>lián +84EF=>cōng +84F0=>xǐ +84F1=>píng +84F2=>qiū +84F3=>jǐn +84F4=>chún +84F5=>jié +84F6=>wéi +84F7=>tuī +84F8=>cáo +84F9=>yù +84FA=>yì +84FB=>zí +84FC=>liǎo +84FD=>bì +84FE=>lǔ +84FF=>xu +8500=>bù +8501=>zhāng +8502=>léi +8503=>qiáng +8504=>màn +8505=>yán +8506=>líng +8507=>jì +8508=>biāo +8509=>gǔn +850A=>hǎn +850B=>dí +850C=>sù +850D=>lù +850E=>shè +850F=>shāng +8510=>dí +8511=>miè +8512=>xūn +8513=>màn +8514=>bó +8515=>dì +8516=>cuó +8517=>zhe +8518=>shēn +8519=>xuàn +851A=>wèi +851B=>hú +851C=>áo +851D=>mǐ +851E=>lóu +851F=>cù +8520=>zhōng +8521=>cài +8522=>pó +8523=>jiǎng +8524=>mì +8525=>cōng +8526=>niǎo +8527=>huì +8528=>juàn +8529=>yín +852A=>jiàn +852B=>niān +852C=>shū +852D=>yīn +852E=>guó +852F=>chén +8530=>hù +8531=>shā +8532=>kòu +8533=>qiàn +8534=>má +8535=>zāng +8536=>ze +8537=>qiáng +8538=>dōu +8539=>liǎn +853A=>lìn +853B=>kòu +853C=>ǎi +853D=>bì +853E=>lí +853F=>wěi +8540=>jí +8541=>qián +8542=>shèng +8543=>fān +8544=>méng +8545=>ǒu +8546=>chǎn +8547=>diǎn +8548=>xùn +8549=>jiāo +854A=>ruǐ +854B=>ruǐ +854C=>lěi +854D=>yú +854E=>qiáo +854F=>chú +8550=>huá +8551=>jiān +8552=>mǎi +8553=>yún +8554=>bāo +8555=>yóu +8556=>qú +8557=>lù +8558=>ráo +8559=>huì +855A=>è +855B=>tí +855C=>fěi +855D=>jué +855E=>zuì +855F=>fà +8560=>rú +8561=>fén +8562=>kuì +8563=>shùn +8564=>ruí +8565=>yǎ +8566=>xū +8567=>fù +8568=>jué +8569=>dàng +856A=>wú +856B=>dǒng +856C=>sī +856D=>xiāo +856E=>xì +856F=>lóng +8570=>wēn +8571=>shao +8572=>qí +8573=>jiān +8574=>yùn +8575=>sūn +8576=>líng +8577=>yù +8578=>xiá +8579=>wèng +857A=>jí +857B=>hóng +857C=>sì +857D=>nóng +857E=>lěi +857F=>xuān +8580=>yùn +8581=>yù +8582=>xí +8583=>hào +8584=>báo +8585=>hāo +8586=>ài +8587=>wēi +8588=>huì +8589=>huì +858A=>jì +858B=>cí +858C=>xiāng +858D=>wàn +858E=>miè +858F=>yì +8590=>léng +8591=>jiāng +8592=>càn +8593=>shēn +8594=>qiáng +8595=>lián +8596=>kē +8597=>yuán +8598=>dá +8599=>tì +859A=>tāng +859B=>xuē +859C=>bì +859D=>zhān +859E=>sūn +859F=>xiān +85A0=>fán +85A1=>dǐng +85A2=>xiè +85A3=>gǔ +85A4=>xiè +85A5=>shǔ +85A6=>jiàn +85A7=>hāo +85A8=>hōng +85A9=>sà +85AA=>xīn +85AB=>xūn +85AC=>yào +85AD=>bai +85AE=>sǒu +85AF=>shǔ +85B0=>xūn +85B1=>duì +85B2=>pín +85B3=>wěi +85B4=>níng +85B5=>chóu +85B6=>mái +85B7=>rú +85B8=>piáo +85B9=>tái +85BA=>jì +85BB=>zǎo +85BC=>chén +85BD=>zhēn +85BE=>ěr +85BF=>nǐ +85C0=>yíng +85C1=>gǎo +85C2=>cóng +85C3=>xiāo +85C4=>qí +85C5=>fá +85C6=>jiǎn +85C7=>xù +85C8=>kuí +85C9=>jí +85CA=>biǎn +85CB=>diào +85CC=>mì +85CD=>lán +85CE=>jìn +85CF=>cáng +85D0=>miǎo +85D1=>qióng +85D2=>qiè +85D3=>xiǎn +85D4=>liáo +85D5=>ǒu +85D6=>xián +85D7=>sù +85D8=>lǘ +85D9=>yì +85DA=>xù +85DB=>xiě +85DC=>lí +85DD=>yì +85DE=>lǎ +85DF=>lěi +85E0=>jiào +85E1=>dí +85E2=>zhǐ +85E3=>bēi +85E4=>téng +85E5=>yào +85E6=>mò +85E7=>huàn +85E8=>biāo +85E9=>fān +85EA=>sǒu +85EB=>tán +85EC=>tuī +85ED=>qióng +85EE=>qiáo +85EF=>wèi +85F0=>liú +85F1=>huì +85F2=>ou +85F3=>gǎo +85F4=>yùn +85F5=>bao +85F6=>lì +85F7=>shǔ +85F8=>chú +85F9=>ǎi +85FA=>lìn +85FB=>zǎo +85FC=>xuān +85FD=>qìn +85FE=>lài +85FF=>huò +8600=>tuò +8601=>wù +8602=>ruǐ +8603=>ruǐ +8604=>qí +8605=>héng +8606=>lú +8607=>sū +8608=>tuí +8609=>méng +860A=>yùn +860B=>píng +860C=>yǔ +860D=>xūn +860E=>jì +860F=>jiōng +8610=>xuān +8611=>mó +8612=>qiu +8613=>sū +8614=>jiōng +8615=>feng +8616=>niè +8617=>bò +8618=>ráng +8619=>yì +861A=>xiǎn +861B=>yú +861C=>jú +861D=>liàn +861E=>liǎn +861F=>yǐn +8620=>qiáng +8621=>yīng +8622=>lóng +8623=>tǒu +8624=>wěi +8625=>yuè +8626=>líng +8627=>qú +8628=>yáo +8629=>fán +862A=>méi +862B=>hàn +862C=>kuī +862D=>lán +862E=>jì +862F=>dàng +8630=>man +8631=>lèi +8632=>léi +8633=>huī +8634=>fēng +8635=>zhī +8636=>wèi +8637=>kuí +8638=>zhàn +8639=>huái +863A=>lí +863B=>jì +863C=>mí +863D=>lěi +863E=>huài +863F=>luó +8640=>jī +8641=>kuí +8642=>lù +8643=>jiān +8644=>sà +8645=>teng +8646=>léi +8647=>quǎn +8648=>xiāo +8649=>yì +864A=>luán +864B=>mén +864C=>biē +864D=>hū +864E=>hǔ +864F=>lǔ +8650=>nüè +8651=>lǜ +8652=>sī +8653=>xiāo +8654=>qián +8655=>chù +8656=>hū +8657=>xū +8658=>cuó +8659=>fú +865A=>xū +865B=>xū +865C=>lǔ +865D=>hǔ +865E=>yú +865F=>hào +8660=>jiāo +8661=>jù +8662=>guó +8663=>bào +8664=>yán +8665=>zhàn +8666=>zhàn +8667=>kuī +8668=>bīn +8669=>xì +866A=>shù +866B=>chóng +866C=>qiú +866D=>diāo +866E=>jǐ +866F=>qiú +8670=>dīng +8671=>shī +8672=>xiā +8673=>jué +8674=>zhé +8675=>shé +8676=>yū +8677=>hán +8678=>zǐ +8679=>hóng +867A=>huī +867B=>méng +867C=>gè +867D=>suī +867E=>xiā +867F=>chài +8680=>shí +8681=>yǐ +8682=>mǎ +8683=>xiàng +8684=>fāng +8685=>è +8686=>bā +8687=>chǐ +8688=>qiān +8689=>wén +868A=>wén +868B=>ruì +868C=>bàng +868D=>pí +868E=>yuè +868F=>yuè +8690=>jūn +8691=>qí +8692=>tóng +8693=>yǐn +8694=>qí +8695=>cán +8696=>yuán +8697=>jué +8698=>huí +8699=>qín +869A=>qí +869B=>zhòng +869C=>yá +869D=>háo +869E=>mù +869F=>wáng +86A0=>fén +86A1=>fén +86A2=>háng +86A3=>gong +86A4=>zǎo +86A5=>fù +86A6=>rán +86A7=>jiè +86A8=>fú +86A9=>chī +86AA=>dǒu +86AB=>bào +86AC=>xiǎn +86AD=>ní +86AE=>tè +86AF=>qiū +86B0=>yóu +86B1=>zhà +86B2=>píng +86B3=>chí +86B4=>yòu +86B5=>hé +86B6=>hān +86B7=>jù +86B8=>lì +86B9=>fù +86BA=>rán +86BB=>zhá +86BC=>gǒu +86BD=>pí +86BE=>pí +86BF=>xián +86C0=>zhù +86C1=>diāo +86C2=>bié +86C3=>bǐng +86C4=>gū +86C5=>zhān +86C6=>qū +86C7=>shé +86C8=>tiě +86C9=>líng +86CA=>gǔ +86CB=>dàn +86CC=>gǔ +86CD=>yíng +86CE=>lì +86CF=>chēng +86D0=>qū +86D1=>móu +86D2=>gé +86D3=>cì +86D4=>huí +86D5=>huí +86D6=>máng +86D7=>fù +86D8=>yáng +86D9=>wā +86DA=>liè +86DB=>zhū +86DC=>yī +86DD=>xián +86DE=>kuò +86DF=>jiāo +86E0=>lì +86E1=>yì +86E2=>píng +86E3=>qī +86E4=>há +86E5=>shé +86E6=>yí +86E7=>wǎng +86E8=>mò +86E9=>qióng +86EA=>qiè +86EB=>guǐ +86EC=>qióng +86ED=>zhì +86EE=>mán +86EF=>lao +86F0=>zhé +86F1=>jiá +86F2=>náo +86F3=>sī +86F4=>qí +86F5=>xīng +86F6=>jiè +86F7=>qiú +86F8=>shāo +86F9=>yǒng +86FA=>jiá +86FB=>tuì +86FC=>chē +86FD=>bài +86FE=>é +86FF=>hàn +8700=>shǔ +8701=>xuán +8702=>fēng +8703=>shèn +8704=>shèn +8705=>fǔ +8706=>xiàn +8707=>zhē +8708=>wú +8709=>fú +870A=>lí +870B=>láng +870C=>bì +870D=>chú +870E=>yuān +870F=>yǒu +8710=>jié +8711=>dàn +8712=>yán +8713=>tíng +8714=>diàn +8715=>tuì +8716=>huí +8717=>wō +8718=>zhī +8719=>sōng +871A=>fēi +871B=>jū +871C=>mì +871D=>qí +871E=>qí +871F=>yù +8720=>jùn +8721=>là +8722=>měng +8723=>qiāng +8724=>sī +8725=>xī +8726=>lún +8727=>lì +8728=>dié +8729=>tiáo +872A=>táo +872B=>kūn +872C=>hán +872D=>hàn +872E=>yù +872F=>bàng +8730=>féi +8731=>pí +8732=>wēi +8733=>dūn +8734=>yì +8735=>yuān +8736=>suò +8737=>quán +8738=>qiǎn +8739=>ruì +873A=>ní +873B=>qīng +873C=>wèi +873D=>liǎng +873E=>guǒ +873F=>wān +8740=>dōng +8741=>è +8742=>bǎn +8743=>dì +8744=>wǎng +8745=>cán +8746=>yǎng +8747=>ying +8748=>guō +8749=>chán +874A=>dìng +874B=>là +874C=>kē +874D=>jié +874E=>xiē +874F=>tíng +8750=>mào +8751=>xū +8752=>mián +8753=>yú +8754=>jiē +8755=>shí +8756=>xuān +8757=>huáng +8758=>yǎn +8759=>biān +875A=>róu +875B=>wēi +875C=>fù +875D=>yuán +875E=>mèi +875F=>wei +8760=>fú +8761=>rú +8762=>xié +8763=>yóu +8764=>qiú +8765=>máo +8766=>xiā +8767=>yīng +8768=>shī +8769=>chóng +876A=>tāng +876B=>zhū +876C=>zōng +876D=>tí +876E=>fù +876F=>yuán +8770=>kuí +8771=>méng +8772=>là +8773=>dú +8774=>hú +8775=>qiū +8776=>dié +8777=>lì +8778=>wō +8779=>yūn +877A=>qǔ +877B=>nǎn +877C=>lóu +877D=>chūn +877E=>róng +877F=>yíng +8780=>jiāng +8781=>ban +8782=>láng +8783=>páng +8784=>sī +8785=>xī +8786=>cì +8787=>xī +8788=>yuán +8789=>wēng +878A=>lián +878B=>sōu +878C=>bān +878D=>róng +878E=>róng +878F=>jí +8790=>wū +8791=>xiù +8792=>hàn +8793=>qín +8794=>yí +8795=>bī +8796=>huá +8797=>táng +8798=>yǐ +8799=>dù +879A=>nài +879B=>hé +879C=>hú +879D=>guī +879E=>mǎ +879F=>míng +87A0=>yì +87A1=>wén +87A2=>yíng +87A3=>tè +87A4=>zhōng +87A5=>cāng +87A6=>sao +87A7=>qi +87A8=>mǎn +87A9=>tiao +87AA=>shāng +87AB=>shì +87AC=>cáo +87AD=>chī +87AE=>dì +87AF=>áo +87B0=>lù +87B1=>wèi +87B2=>zhì +87B3=>táng +87B4=>chén +87B5=>piāo +87B6=>qú +87B7=>pí +87B8=>yú +87B9=>jiàn +87BA=>luó +87BB=>lóu +87BC=>qǐn +87BD=>zhōng +87BE=>yǐn +87BF=>jiāng +87C0=>shuài +87C1=>wén +87C2=>xiāo +87C3=>wàn +87C4=>zhé +87C5=>zhè +87C6=>ma +87C7=>má +87C8=>guō +87C9=>liú +87CA=>máo +87CB=>xī +87CC=>cōng +87CD=>lí +87CE=>mǎn +87CF=>xiāo +87D0=>chang +87D1=>zhāng +87D2=>mǎng +87D3=>xiàng +87D4=>mò +87D5=>zuī +87D6=>sī +87D7=>qiū +87D8=>tè +87D9=>zhí +87DA=>péng +87DB=>péng +87DC=>jiǎo +87DD=>qú +87DE=>biē +87DF=>liáo +87E0=>pán +87E1=>guǐ +87E2=>xǐ +87E3=>jǐ +87E4=>zhuān +87E5=>huáng +87E6=>féi +87E7=>láo +87E8=>jué +87E9=>jué +87EA=>huì +87EB=>yín +87EC=>chán +87ED=>jiāo +87EE=>shàn +87EF=>náo +87F0=>xiāo +87F1=>wú +87F2=>chóng +87F3=>xún +87F4=>sī +87F5=>chu +87F6=>chēng +87F7=>dāng +87F8=>lǐ +87F9=>xiè +87FA=>shàn +87FB=>yǐ +87FC=>jǐng +87FD=>dá +87FE=>chán +87FF=>qì +8800=>cī +8801=>xiǎng +8802=>shè +8803=>luǒ +8804=>qín +8805=>ying +8806=>chài +8807=>lì +8808=>zéi +8809=>xuān +880A=>lián +880B=>zhú +880C=>zé +880D=>xiē +880E=>mǎng +880F=>xiè +8810=>qí +8811=>róng +8812=>jiǎn +8813=>měng +8814=>háo +8815=>rú +8816=>huò +8817=>zhuó +8818=>jié +8819=>pín +881A=>hē +881B=>miè +881C=>fán +881D=>léi +881E=>jié +881F=>là +8820=>mǐn +8821=>lí +8822=>chǔn +8823=>lì +8824=>qiū +8825=>niè +8826=>lú +8827=>dù +8828=>xiāo +8829=>zhū +882A=>lóng +882B=>lì +882C=>lóng +882D=>fēng +882E=>yē +882F=>bèng +8830=>náng +8831=>gǔ +8832=>juān +8833=>yīng +8834=>shu +8835=>xī +8836=>cán +8837=>qú +8838=>quán +8839=>dù +883A=>cán +883B=>mán +883C=>qú +883D=>jié +883E=>zhú +883F=>zhuō +8840=>xuè +8841=>huāng +8842=>niù +8843=>pēi +8844=>nǜ +8845=>xìn +8846=>zhòng +8847=>mài +8848=>èr +8849=>kā +884A=>miè +884B=>xì +884C=>xíng +884D=>yǎn +884E=>kàn +884F=>yuàn +8850=>qu +8851=>líng +8852=>xuàn +8853=>shù +8854=>xián +8855=>tòng +8856=>xiàng +8857=>jiē +8858=>xián +8859=>yá +885A=>hú +885B=>wèi +885C=>dào +885D=>chōng +885E=>wèi +885F=>dào +8860=>zhūn +8861=>héng +8862=>qú +8863=>yī +8864=>yi +8865=>bǔ +8866=>gǎn +8867=>yú +8868=>biǎo +8869=>chǎ +886A=>yí +886B=>shān +886C=>chèn +886D=>fū +886E=>gǔn +886F=>fēn +8870=>shuāi +8871=>jié +8872=>nà +8873=>zhōng +8874=>dǎn +8875=>yì +8876=>zhòng +8877=>zhōng +8878=>jiè +8879=>zhǐ +887A=>xié +887B=>rán +887C=>zhī +887D=>rèn +887E=>qīn +887F=>jīn +8880=>jūn +8881=>yuán +8882=>mèi +8883=>chài +8884=>ǎo +8885=>niǎo +8886=>huī +8887=>rán +8888=>jiā +8889=>tuó +888A=>lǐng +888B=>dài +888C=>bào +888D=>páo +888E=>yào +888F=>zuò +8890=>bì +8891=>shào +8892=>tǎn +8893=>jù +8894=>hè +8895=>xué +8896=>xiù +8897=>zhěn +8898=>yí +8899=>pà +889A=>bō +889B=>dī +889C=>wà +889D=>fù +889E=>gǔn +889F=>zhì +88A0=>zhì +88A1=>rán +88A2=>pàn +88A3=>yì +88A4=>mào +88A5=>tuō +88A6=>nà +88A7=>gōu +88A8=>xuàn +88A9=>zhé +88AA=>qū +88AB=>bèi +88AC=>gǔn +88AD=>xí +88AE=>ni +88AF=>bó +88B0=>bō +88B1=>fu +88B2=>chǐ +88B3=>chǐ +88B4=>kù +88B5=>rèn +88B6=>jiàng +88B7=>jiá +88B8=>jiàn +88B9=>bó +88BA=>jié +88BB=>ér +88BC=>gē +88BD=>rú +88BE=>zhū +88BF=>guī +88C0=>yīn +88C1=>cái +88C2=>liè +88C3=>ka +88C4=>xing +88C5=>zhuāng +88C6=>dāng +88C7=>xū +88C8=>kūn +88C9=>kèn +88CA=>niǎo +88CB=>shù +88CC=>jiá +88CD=>kǔn +88CE=>chéng +88CF=>lǐ +88D0=>juān +88D1=>shēn +88D2=>póu +88D3=>gé +88D4=>yì +88D5=>yù +88D6=>zhěn +88D7=>liú +88D8=>qiú +88D9=>qún +88DA=>jì +88DB=>yì +88DC=>bǔ +88DD=>zhuāng +88DE=>shuì +88DF=>shā +88E0=>qún +88E1=>li +88E2=>lián +88E3=>liǎn +88E4=>kù +88E5=>jiǎn +88E6=>fóu +88E7=>chān +88E8=>bì +88E9=>kūn +88EA=>táo +88EB=>yuàn +88EC=>líng +88ED=>chǐ +88EE=>chāng +88EF=>chóu +88F0=>duō +88F1=>biǎo +88F2=>liǎng +88F3=>shang +88F4=>péi +88F5=>péi +88F6=>fēi +88F7=>yuān +88F8=>luǒ +88F9=>guǒ +88FA=>yǎn +88FB=>dú +88FC=>tì +88FD=>zhì +88FE=>jū +88FF=>yǐ +8900=>jì +8901=>zhí +8902=>guà +8903=>kèn +8904=>qi +8905=>tì +8906=>tí +8907=>fù +8908=>chóng +8909=>xiè +890A=>biǎn +890B=>dié +890C=>kūn +890D=>duān +890E=>xiù +890F=>xiù +8910=>hè +8911=>yuàn +8912=>bāo +8913=>bǎo +8914=>fù +8915=>yú +8916=>tuàn +8917=>yǎn +8918=>huī +8919=>bèi +891A=>chǔ +891B=>lǚ +891C=>pao +891D=>dān +891E=>yǔn +891F=>tā +8920=>gōu +8921=>dā +8922=>huái +8923=>róng +8924=>yuàn +8925=>rù +8926=>nài +8927=>jiǒng +8928=>suǒ +8929=>bān +892A=>tuì +892B=>chǐ +892C=>sǎng +892D=>niǎo +892E=>yīng +892F=>jiè +8930=>qiān +8931=>huái +8932=>kù +8933=>lián +8934=>lán +8935=>lí +8936=>zhě +8937=>shī +8938=>lǚ +8939=>yì +893A=>diē +893B=>xiè +893C=>xiān +893D=>wèi +893E=>biǎo +893F=>cáo +8940=>jī +8941=>qiǎng +8942=>sēn +8943=>bāo +8944=>xiāng +8945=>bi +8946=>fú +8947=>jiǎn +8948=>zhuàn +8949=>jiǎn +894A=>cuì +894B=>jí +894C=>dān +894D=>zá +894E=>fán +894F=>bó +8950=>xiàng +8951=>xín +8952=>bié +8953=>ráo +8954=>mǎn +8955=>lán +8956=>ǎo +8957=>zé +8958=>guì +8959=>cào +895A=>suì +895B=>nóng +895C=>chān +895D=>liǎn +895E=>bì +895F=>jīn +8960=>dāng +8961=>shǔ +8962=>tǎn +8963=>bì +8964=>lán +8965=>pú +8966=>rú +8967=>zhǐ +8968=>dùi +8969=>shǔ +896A=>wà +896B=>shì +896C=>bǎi +896D=>xié +896E=>bó +896F=>chèn +8970=>lài +8971=>lóng +8972=>xí +8973=>xiān +8974=>lán +8975=>zhě +8976=>dài +8977=>ju +8978=>zàn +8979=>shī +897A=>jiǎn +897B=>pàn +897C=>yì +897D=>lan +897E=>yà +897F=>xi +8980=>xī +8981=>yào +8982=>fěng +8983=>tán +8984=>fu +8985=>fiào +8986=>fù +8987=>bà +8988=>hé +8989=>jī +898A=>jī +898B=>jiàn +898C=>guān +898D=>biàn +898E=>yàn +898F=>guī +8990=>jué +8991=>piǎn +8992=>mào +8993=>mì +8994=>mì +8995=>miè +8996=>shì +8997=>sì +8998=>chān +8999=>luó +899A=>jué +899B=>mì +899C=>tiào +899D=>lián +899E=>yào +899F=>zhì +89A0=>jūn +89A1=>xí +89A2=>shǎn +89A3=>wēi +89A4=>xì +89A5=>tiǎn +89A6=>yú +89A7=>lǎn +89A8=>è +89A9=>dǔ +89AA=>qīn +89AB=>pǎng +89AC=>jì +89AD=>míng +89AE=>yíng +89AF=>gòu +89B0=>qū +89B1=>zhàn +89B2=>jìn +89B3=>guān +89B4=>dēng +89B5=>jiàn +89B6=>luó +89B7=>qù +89B8=>jiān +89B9=>wéi +89BA=>jué +89BB=>qū +89BC=>luó +89BD=>lǎn +89BE=>shěn +89BF=>dí +89C0=>guān +89C1=>jiàn +89C2=>guān +89C3=>yàn +89C4=>guī +89C5=>mì +89C6=>shì +89C7=>chān +89C8=>lǎn +89C9=>jué +89CA=>jì +89CB=>xí +89CC=>dí +89CD=>tiǎn +89CE=>yú +89CF=>gòu +89D0=>jìn +89D1=>qù +89D2=>jiǎo +89D3=>qiú +89D4=>jīn +89D5=>cū +89D6=>jué +89D7=>zhì +89D8=>chào +89D9=>jí +89DA=>gū +89DB=>dàn +89DC=>zī +89DD=>dǐ +89DE=>shāng +89DF=>huà +89E0=>quán +89E1=>gé +89E2=>shì +89E3=>jiě +89E4=>guǐ +89E5=>gōng +89E6=>chù +89E7=>jiě +89E8=>hùn +89E9=>qiú +89EA=>xīng +89EB=>sù +89EC=>ní +89ED=>jī +89EE=>lù +89EF=>zhì +89F0=>zhā +89F1=>bì +89F2=>xīng +89F3=>hú +89F4=>shāng +89F5=>gōng +89F6=>zhì +89F7=>xué +89F8=>chù +89F9=>xī +89FA=>yí +89FB=>lì +89FC=>jué +89FD=>xī +89FE=>yàn +89FF=>xī +8A00=>yán +8A01=>yan +8A02=>dìng +8A03=>fù +8A04=>qiú +8A05=>qiú +8A06=>jiào +8A07=>hōng +8A08=>jì +8A09=>fān +8A0A=>xùn +8A0B=>diào +8A0C=>hòng +8A0D=>chài +8A0E=>tǎo +8A0F=>xū +8A10=>jié +8A11=>yí +8A12=>rèn +8A13=>xun +8A14=>yín +8A15=>shàn +8A16=>qì +8A17=>tuō +8A18=>jì +8A19=>xùn +8A1A=>yín +8A1B=>é +8A1C=>fēn +8A1D=>yà +8A1E=>yāo +8A1F=>sòng +8A20=>shěn +8A21=>yín +8A22=>xīn +8A23=>jué +8A24=>xiáo +8A25=>nè +8A26=>chén +8A27=>yóu +8A28=>zhǐ +8A29=>xiōng +8A2A=>fǎng +8A2B=>xìn +8A2C=>chāo +8A2D=>shè +8A2E=>xiān +8A2F=>sǎ +8A30=>zhùn +8A31=>xǔ +8A32=>yì +8A33=>yì +8A34=>su +8A35=>chī +8A36=>hē +8A37=>shēn +8A38=>hé +8A39=>xù +8A3A=>zhěn +8A3B=>zhù +8A3C=>zhèng +8A3D=>gòu +8A3E=>zī +8A3F=>zǐ +8A40=>zhān +8A41=>gǔ +8A42=>fù +8A43=>jiǎn +8A44=>dié +8A45=>líng +8A46=>dǐ +8A47=>yàng +8A48=>lì +8A49=>náo +8A4A=>pàn +8A4B=>zhòu +8A4C=>gàn +8A4D=>yì +8A4E=>jù +8A4F=>yào +8A50=>zhà +8A51=>yí +8A52=>yí +8A53=>qǔ +8A54=>zhào +8A55=>píng +8A56=>bì +8A57=>xiòng +8A58=>qū +8A59=>bá +8A5A=>dá +8A5B=>zǔ +8A5C=>tāo +8A5D=>zhǔ +8A5E=>cí +8A5F=>zhé +8A60=>yǒng +8A61=>xǔ +8A62=>xún +8A63=>yì +8A64=>huǎng +8A65=>hé +8A66=>shì +8A67=>chá +8A68=>xiào +8A69=>shī +8A6A=>hěn +8A6B=>chà +8A6C=>gòu +8A6D=>guǐ +8A6E=>quán +8A6F=>huì +8A70=>jié +8A71=>huà +8A72=>gāi +8A73=>xiáng +8A74=>wēi +8A75=>shēn +8A76=>zhòu +8A77=>tóng +8A78=>mí +8A79=>zhān +8A7A=>mìng +8A7B=>è +8A7C=>huī +8A7D=>yán +8A7E=>xiōng +8A7F=>guà +8A80=>èr +8A81=>bìng +8A82=>tiǎo +8A83=>yí +8A84=>lěi +8A85=>zhū +8A86=>kuāng +8A87=>kuā +8A88=>wú +8A89=>yù +8A8A=>téng +8A8B=>jì +8A8C=>zhì +8A8D=>rèn +8A8E=>cù +8A8F=>lǎng +8A90=>é +8A91=>kuáng +8A92=>éi +8A93=>shì +8A94=>tǐng +8A95=>dàn +8A96=>bèi +8A97=>chán +8A98=>yòu +8A99=>kēng +8A9A=>qiào +8A9B=>qīn +8A9C=>shuà +8A9D=>ān +8A9E=>yǔ +8A9F=>xiào +8AA0=>chéng +8AA1=>jiè +8AA2=>xiàn +8AA3=>wū +8AA4=>wù +8AA5=>gào +8AA6=>sòng +8AA7=>bū +8AA8=>huì +8AA9=>jìng +8AAA=>shuō +8AAB=>zhèn +8AAC=>shuō +8AAD=>dú +8AAE=>hua +8AAF=>chàng +8AB0=>shuí +8AB1=>jié +8AB2=>kè +8AB3=>qū +8AB4=>cóng +8AB5=>xiáo +8AB6=>suì +8AB7=>wǎng +8AB8=>xián +8AB9=>fěi +8ABA=>chī +8ABB=>tà +8ABC=>yì +8ABD=>nì +8ABE=>yín +8ABF=>diào +8AC0=>pǐ +8AC1=>zhuó +8AC2=>chǎn +8AC3=>chēn +8AC4=>zhūn +8AC5=>jì +8AC6=>qī +8AC7=>tán +8AC8=>zhuì +8AC9=>wěi +8ACA=>jú +8ACB=>qǐng +8ACC=>dǒng +8ACD=>zhèng +8ACE=>zé +8ACF=>zōu +8AD0=>qiān +8AD1=>zhuó +8AD2=>liàng +8AD3=>jiàn +8AD4=>chù +8AD5=>háo +8AD6=>lùn +8AD7=>shěn +8AD8=>biǎo +8AD9=>huài +8ADA=>pián +8ADB=>yú +8ADC=>dié +8ADD=>xū +8ADE=>piǎn +8ADF=>shì +8AE0=>xuān +8AE1=>shì +8AE2=>hùn +8AE3=>huà +8AE4=>è +8AE5=>zhòng +8AE6=>dì +8AE7=>xié +8AE8=>fú +8AE9=>pǔ +8AEA=>tíng +8AEB=>jiàn +8AEC=>qǐ +8AED=>yù +8AEE=>zī +8AEF=>zhuān +8AF0=>xǐ +8AF1=>huì +8AF2=>yīn +8AF3=>ān +8AF4=>xián +8AF5=>nán +8AF6=>chén +8AF7=>fěng +8AF8=>zhū +8AF9=>yáng +8AFA=>yàn +8AFB=>huáng +8AFC=>xuān +8AFD=>gé +8AFE=>nuò +8AFF=>qī +8B00=>móu +8B01=>yè +8B02=>wèi +8B03=>xing +8B04=>téng +8B05=>zhōu +8B06=>shàn +8B07=>jiǎn +8B08=>pó +8B09=>kuì +8B0A=>huǎng +8B0B=>huò +8B0C=>gē +8B0D=>yíng +8B0E=>mí +8B0F=>xiǎo +8B10=>mì +8B11=>xǐ +8B12=>qiāng +8B13=>chēn +8B14=>xuè +8B15=>tí +8B16=>sù +8B17=>bàng +8B18=>chí +8B19=>qiān +8B1A=>shì +8B1B=>jiǎng +8B1C=>yuán +8B1D=>xiè +8B1E=>hè +8B1F=>tāo +8B20=>yáo +8B21=>yáo +8B22=>zhi +8B23=>yú +8B24=>biāo +8B25=>còng +8B26=>qìng +8B27=>lí +8B28=>mó +8B29=>mò +8B2A=>shāng +8B2B=>zhé +8B2C=>miù +8B2D=>jiǎn +8B2E=>zé +8B2F=>jiē +8B30=>lián +8B31=>lóu +8B32=>càn +8B33=>ōu +8B34=>gùn +8B35=>xí +8B36=>zhuó +8B37=>áo +8B38=>áo +8B39=>jǐn +8B3A=>zhé +8B3B=>yí +8B3C=>hū +8B3D=>jiàng +8B3E=>mán +8B3F=>cháo +8B40=>hàn +8B41=>huá +8B42=>chǎn +8B43=>xū +8B44=>zēng +8B45=>sè +8B46=>xī +8B47=>zhā +8B48=>duì +8B49=>zhèng +8B4A=>náo +8B4B=>lán +8B4C=>é +8B4D=>yīng +8B4E=>jué +8B4F=>jī +8B50=>zǔn +8B51=>jiǎo +8B52=>bò +8B53=>huì +8B54=>zhuàn +8B55=>wú +8B56=>zèn +8B57=>zhá +8B58=>shi +8B59=>qiào +8B5A=>tán +8B5B=>zèn +8B5C=>pǔ +8B5D=>shéng +8B5E=>xuān +8B5F=>zào +8B60=>tán +8B61=>dǎng +8B62=>suì +8B63=>xiǎn +8B64=>jī +8B65=>jiào +8B66=>jǐng +8B67=>zhàn +8B68=>náng +8B69=>yī +8B6A=>ài +8B6B=>zhān +8B6C=>pì +8B6D=>huǐ +8B6E=>huà +8B6F=>yì +8B70=>yì +8B71=>shàn +8B72=>ràng +8B73=>nòu +8B74=>qiǎn +8B75=>zhuì +8B76=>tà +8B77=>hù +8B78=>zhōu +8B79=>háo +8B7A=>ài +8B7B=>yīng +8B7C=>jiàn +8B7D=>yù +8B7E=>jiǎn +8B7F=>huì +8B80=>dú +8B81=>zhé +8B82=>xuàn +8B83=>zàn +8B84=>lěi +8B85=>shěn +8B86=>wèi +8B87=>chǎn +8B88=>lì +8B89=>yí +8B8A=>biàn +8B8B=>zhé +8B8C=>yàn +8B8D=>è +8B8E=>chóu +8B8F=>wèi +8B90=>chóu +8B91=>yào +8B92=>chán +8B93=>ràng +8B94=>yǐn +8B95=>lán +8B96=>chèn +8B97=>xié +8B98=>niè +8B99=>huān +8B9A=>zàn +8B9B=>yì +8B9C=>dǎng +8B9D=>zhán +8B9E=>yàn +8B9F=>dú +8BA0=>yán +8BA1=>jì +8BA2=>dìng +8BA3=>fù +8BA4=>rèn +8BA5=>jī +8BA6=>jié +8BA7=>hòng +8BA8=>tǎo +8BA9=>ràng +8BAA=>shàn +8BAB=>qì +8BAC=>tuō +8BAD=>xun +8BAE=>yì +8BAF=>xùn +8BB0=>jì +8BB1=>rèn +8BB2=>jiǎng +8BB3=>huì +8BB4=>ōu +8BB5=>jù +8BB6=>yà +8BB7=>nè +8BB8=>xǔ +8BB9=>é +8BBA=>lùn +8BBB=>xiōng +8BBC=>sòng +8BBD=>fěng +8BBE=>shè +8BBF=>fǎng +8BC0=>jué +8BC1=>zhèng +8BC2=>gǔ +8BC3=>hē +8BC4=>píng +8BC5=>zǔ +8BC6=>shi +8BC7=>xiòng +8BC8=>zhà +8BC9=>su +8BCA=>zhěn +8BCB=>dǐ +8BCC=>zhōu +8BCD=>cí +8BCE=>qū +8BCF=>zhào +8BD0=>bì +8BD1=>yì +8BD2=>yí +8BD3=>kuāng +8BD4=>lěi +8BD5=>shì +8BD6=>guà +8BD7=>shī +8BD8=>jí +8BD9=>huī +8BDA=>chéng +8BDB=>zhū +8BDC=>shēn +8BDD=>huà +8BDE=>dàn +8BDF=>gòu +8BE0=>quán +8BE1=>guǐ +8BE2=>xún +8BE3=>yì +8BE4=>zhēng +8BE5=>gāi +8BE6=>xiáng +8BE7=>chà +8BE8=>hùn +8BE9=>xǔ +8BEA=>zhōu +8BEB=>jiè +8BEC=>wū +8BED=>yǔ +8BEE=>qiào +8BEF=>wù +8BF0=>gào +8BF1=>yòu +8BF2=>huì +8BF3=>kuáng +8BF4=>shuō +8BF5=>sòng +8BF6=>éi +8BF7=>qǐng +8BF8=>zhū +8BF9=>zōu +8BFA=>nuò +8BFB=>dú +8BFC=>zhuó +8BFD=>fěi +8BFE=>kè +8BFF=>wěi +8C00=>yú +8C01=>shuí +8C02=>shěn +8C03=>diào +8C04=>chǎn +8C05=>liàng +8C06=>zhūn +8C07=>suì +8C08=>tán +8C09=>shěn +8C0A=>yì +8C0B=>móu +8C0C=>chén +8C0D=>dié +8C0E=>huǎng +8C0F=>jiàn +8C10=>xié +8C11=>xuè +8C12=>yè +8C13=>wèi +8C14=>è +8C15=>yù +8C16=>xuān +8C17=>chán +8C18=>zī +8C19=>ān +8C1A=>yàn +8C1B=>dì +8C1C=>mí +8C1D=>piǎn +8C1E=>xū +8C1F=>mó +8C20=>dǎng +8C21=>sù +8C22=>xiè +8C23=>yáo +8C24=>bàng +8C25=>shì +8C26=>qiān +8C27=>mì +8C28=>jǐn +8C29=>mán +8C2A=>zhé +8C2B=>jiǎn +8C2C=>miù +8C2D=>tán +8C2E=>zèn +8C2F=>qiáo +8C30=>lán +8C31=>pǔ +8C32=>jué +8C33=>yàn +8C34=>qiǎn +8C35=>zhān +8C36=>chèn +8C37=>gǔ +8C38=>qiān +8C39=>hóng +8C3A=>xiā +8C3B=>jí +8C3C=>hóng +8C3D=>hān +8C3E=>hōng +8C3F=>xī +8C40=>xī +8C41=>huō +8C42=>liáo +8C43=>hǎn +8C44=>dú +8C45=>lóng +8C46=>dòu +8C47=>jiāng +8C48=>qǐ +8C49=>shì +8C4A=>lǐ +8C4B=>dēng +8C4C=>wān +8C4D=>bī +8C4E=>shù +8C4F=>xiàn +8C50=>fēng +8C51=>zhì +8C52=>zhì +8C53=>yàn +8C54=>yàn +8C55=>shǐ +8C56=>chù +8C57=>huī +8C58=>tún +8C59=>yì +8C5A=>tún +8C5B=>yì +8C5C=>jiān +8C5D=>bā +8C5E=>hòu +8C5F=>è +8C60=>chú +8C61=>xiàng +8C62=>huàn +8C63=>jiān +8C64=>kěn +8C65=>gāi +8C66=>jù +8C67=>fū +8C68=>xī +8C69=>bīn +8C6A=>háo +8C6B=>yù +8C6C=>zhū +8C6D=>jiā +8C6E=>fén +8C6F=>xī +8C70=>bó +8C71=>wēn +8C72=>huán +8C73=>bīn +8C74=>dí +8C75=>zōng +8C76=>fén +8C77=>yì +8C78=>zhì +8C79=>bào +8C7A=>chái +8C7B=>àn +8C7C=>pí +8C7D=>nà +8C7E=>pī +8C7F=>gǒu +8C80=>nà +8C81=>yòu +8C82=>diāo +8C83=>mò +8C84=>sì +8C85=>xiū +8C86=>huán +8C87=>kūn +8C88=>hé +8C89=>háo +8C8A=>mò +8C8B=>hàn +8C8C=>mào +8C8D=>lí +8C8E=>ní +8C8F=>bǐ +8C90=>yǔ +8C91=>jiā +8C92=>tuān +8C93=>māo +8C94=>pí +8C95=>xī +8C96=>è +8C97=>jù +8C98=>mò +8C99=>chū +8C9A=>tán +8C9B=>huān +8C9C=>jué +8C9D=>bèi +8C9E=>zhēn +8C9F=>yuán +8CA0=>fù +8CA1=>cái +8CA2=>gòng +8CA3=>tè +8CA4=>yí +8CA5=>háng +8CA6=>wàn +8CA7=>pín +8CA8=>huò +8CA9=>fàn +8CAA=>tān +8CAB=>guàn +8CAC=>zé +8CAD=>zhí +8CAE=>èr +8CAF=>zhù +8CB0=>shì +8CB1=>bì +8CB2=>zī +8CB3=>èr +8CB4=>guì +8CB5=>piǎn +8CB6=>biǎn +8CB7=>mǎi +8CB8=>dài +8CB9=>shèng +8CBA=>kuàng +8CBB=>fèi +8CBC=>tiē +8CBD=>yí +8CBE=>chí +8CBF=>mào +8CC0=>hè +8CC1=>bì +8CC2=>lù +8CC3=>lìn +8CC4=>huì +8CC5=>gāi +8CC6=>pián +8CC7=>zī +8CC8=>jiǎ +8CC9=>xù +8CCA=>zéi +8CCB=>jiǎo +8CCC=>gài +8CCD=>zāng +8CCE=>jiàn +8CCF=>yīng +8CD0=>xùn +8CD1=>zhèn +8CD2=>shē +8CD3=>bīn +8CD4=>bīn +8CD5=>qiú +8CD6=>shē +8CD7=>chuàn +8CD8=>zāng +8CD9=>zhōu +8CDA=>lài +8CDB=>zàn +8CDC=>cì +8CDD=>chēn +8CDE=>shǎng +8CDF=>tiǎn +8CE0=>péi +8CE1=>gēng +8CE2=>xián +8CE3=>mài +8CE4=>jiàn +8CE5=>suì +8CE6=>fù +8CE7=>tàn +8CE8=>cóng +8CE9=>cóng +8CEA=>zhì +8CEB=>jī +8CEC=>zhàng +8CED=>dǔ +8CEE=>jìn +8CEF=>xiōng +8CF0=>chǔn +8CF1=>yǔn +8CF2=>bǎo +8CF3=>zāi +8CF4=>lài +8CF5=>fèng +8CF6=>càng +8CF7=>jī +8CF8=>shèng +8CF9=>yì +8CFA=>zhuàn +8CFB=>fù +8CFC=>gòu +8CFD=>sài +8CFE=>zé +8CFF=>liáo +8D00=>yì +8D01=>bài +8D02=>chěn +8D03=>wàn +8D04=>zhì +8D05=>zhuì +8D06=>biāo +8D07=>yūn +8D08=>zèng +8D09=>dàn +8D0A=>zàn +8D0B=>yàn +8D0C=>pu +8D0D=>shàn +8D0E=>wàn +8D0F=>yíng +8D10=>jìn +8D11=>gàn +8D12=>xián +8D13=>zāng +8D14=>bì +8D15=>dú +8D16=>shú +8D17=>yàn +8D18=>shǎng +8D19=>xuàn +8D1A=>lòng +8D1B=>gàn +8D1C=>zāng +8D1D=>bèi +8D1E=>zhēn +8D1F=>fù +8D20=>yuán +8D21=>gòng +8D22=>cái +8D23=>zé +8D24=>xián +8D25=>bài +8D26=>zhàng +8D27=>huò +8D28=>zhì +8D29=>fàn +8D2A=>tān +8D2B=>pín +8D2C=>biǎn +8D2D=>gòu +8D2E=>zhù +8D2F=>guàn +8D30=>èr +8D31=>jiàn +8D32=>bēn +8D33=>shì +8D34=>tiē +8D35=>guì +8D36=>kuàng +8D37=>dài +8D38=>mào +8D39=>fèi +8D3A=>hè +8D3B=>yí +8D3C=>zéi +8D3D=>zhì +8D3E=>jiǎ +8D3F=>huì +8D40=>zī +8D41=>lìn +8D42=>lù +8D43=>zāng +8D44=>zī +8D45=>gāi +8D46=>jìn +8D47=>qiú +8D48=>zhèn +8D49=>lài +8D4A=>shē +8D4B=>fù +8D4C=>dǔ +8D4D=>jī +8D4E=>shú +8D4F=>shǎng +8D50=>cì +8D51=>bì +8D52=>zhōu +8D53=>gēng +8D54=>péi +8D55=>dǎn +8D56=>lài +8D57=>fèng +8D58=>zhuì +8D59=>fù +8D5A=>zhuàn +8D5B=>sài +8D5C=>zé +8D5D=>yàn +8D5E=>zàn +8D5F=>yūn +8D60=>zèng +8D61=>shàn +8D62=>yíng +8D63=>gàn +8D64=>chì +8D65=>xī +8D66=>shè +8D67=>nǎn +8D68=>tóng +8D69=>xì +8D6A=>chēng +8D6B=>hè +8D6C=>chēng +8D6D=>zhě +8D6E=>xiá +8D6F=>táng +8D70=>zǒu +8D71=>zǒu +8D72=>lì +8D73=>jiū +8D74=>fù +8D75=>zhào +8D76=>gǎn +8D77=>qǐ +8D78=>shàn +8D79=>qióng +8D7A=>yǐn +8D7B=>xiǎn +8D7C=>cī +8D7D=>jué +8D7E=>qǐn +8D7F=>chí +8D80=>cī +8D81=>chèn +8D82=>chèn +8D83=>dié +8D84=>jū +8D85=>chāo +8D86=>dī +8D87=>xì +8D88=>zhān +8D89=>jué +8D8A=>yuè +8D8B=>qū +8D8C=>jí +8D8D=>chí +8D8E=>chú +8D8F=>guā +8D90=>xuè +8D91=>zī +8D92=>tiáo +8D93=>duǒ +8D94=>liè +8D95=>gǎn +8D96=>suō +8D97=>cù +8D98=>xí +8D99=>zhào +8D9A=>sù +8D9B=>yǐn +8D9C=>jú +8D9D=>jiàn +8D9E=>què +8D9F=>tàng +8DA0=>chuò +8DA1=>cuǐ +8DA2=>lù +8DA3=>qù +8DA4=>dàng +8DA5=>qiū +8DA6=>zī +8DA7=>tí +8DA8=>qū +8DA9=>chì +8DAA=>huáng +8DAB=>qiáo +8DAC=>qiāo +8DAD=>jiào +8DAE=>zào +8DAF=>tì +8DB0=>ěr +8DB1=>zǎn +8DB2=>zǎn +8DB3=>zú +8DB4=>pā +8DB5=>bào +8DB6=>kù +8DB7=>kē +8DB8=>dǔn +8DB9=>jué +8DBA=>fū +8DBB=>chěn +8DBC=>jiǎn +8DBD=>fàng +8DBE=>zhǐ +8DBF=>tā +8DC0=>yuè +8DC1=>bà +8DC2=>qí +8DC3=>yuè +8DC4=>qiāng +8DC5=>tuò +8DC6=>tái +8DC7=>yì +8DC8=>niǎn +8DC9=>líng +8DCA=>mèi +8DCB=>bá +8DCC=>diē +8DCD=>kū +8DCE=>tuó +8DCF=>jiā +8DD0=>cī +8DD1=>pǎo +8DD2=>qiǎ +8DD3=>zhù +8DD4=>jū +8DD5=>diǎn +8DD6=>zhí +8DD7=>fū +8DD8=>pán +8DD9=>jù +8DDA=>shān +8DDB=>bǒ +8DDC=>ní +8DDD=>jù +8DDE=>lì +8DDF=>gēn +8DE0=>yí +8DE1=>jī +8DE2=>duò +8DE3=>xiǎn +8DE4=>jiāo +8DE5=>duò +8DE6=>zhū +8DE7=>quán +8DE8=>kuà +8DE9=>zhuǎi +8DEA=>guì +8DEB=>qióng +8DEC=>kuǐ +8DED=>xiáng +8DEE=>chì +8DEF=>lù +8DF0=>pián +8DF1=>zhì +8DF2=>jiá +8DF3=>tiào +8DF4=>cǎi +8DF5=>jiàn +8DF6=>tà +8DF7=>qiāo +8DF8=>bì +8DF9=>xiān +8DFA=>duò +8DFB=>jī +8DFC=>jú +8DFD=>jì +8DFE=>shū +8DFF=>tú +8E00=>chù +8E01=>jìng +8E02=>niè +8E03=>xiāo +8E04=>bù +8E05=>xué +8E06=>cūn +8E07=>mǔ +8E08=>shū +8E09=>liáng +8E0A=>yǒng +8E0B=>jiǎo +8E0C=>chóu +8E0D=>qiāo +8E0E=>móu +8E0F=>tà +8E10=>jiàn +8E11=>qí +8E12=>wō +8E13=>wěi +8E14=>chuō +8E15=>jié +8E16=>jí +8E17=>niè +8E18=>jū +8E19=>jū +8E1A=>lún +8E1B=>lù +8E1C=>lèng +8E1D=>huái +8E1E=>jù +8E1F=>chí +8E20=>wǎn +8E21=>quán +8E22=>tī +8E23=>bó +8E24=>zú +8E25=>qiè +8E26=>yǐ +8E27=>cù +8E28=>zōng +8E29=>cǎi +8E2A=>zōng +8E2B=>pèng +8E2C=>zhì +8E2D=>zhēng +8E2E=>diǎn +8E2F=>zhí +8E30=>yú +8E31=>duó +8E32=>dùn +8E33=>chuǎn +8E34=>yǒng +8E35=>zhǒng +8E36=>dì +8E37=>zhǎ +8E38=>chěn +8E39=>chuài +8E3A=>jiàn +8E3B=>guā +8E3C=>táng +8E3D=>jǔ +8E3E=>fú +8E3F=>zú +8E40=>dié +8E41=>pián +8E42=>róu +8E43=>nuò +8E44=>tí +8E45=>chǎ +8E46=>tuǐ +8E47=>jiǎn +8E48=>dǎo +8E49=>cuō +8E4A=>qī +8E4B=>tà +8E4C=>qiāng +8E4D=>niǎn +8E4E=>diān +8E4F=>tí +8E50=>jí +8E51=>niè +8E52=>pán +8E53=>liū +8E54=>zàn +8E55=>bì +8E56=>chōng +8E57=>lù +8E58=>liáo +8E59=>cù +8E5A=>tāng +8E5B=>dài +8E5C=>sù +8E5D=>xǐ +8E5E=>kuǐ +8E5F=>jī +8E60=>zhí +8E61=>qiāng +8E62=>dí +8E63=>pán +8E64=>zōng +8E65=>lián +8E66=>bèng +8E67=>zāo +8E68=>niǎn +8E69=>bié +8E6A=>tuí +8E6B=>jú +8E6C=>dēng +8E6D=>cèng +8E6E=>xiān +8E6F=>fán +8E70=>chú +8E71=>zhōng +8E72=>dūn +8E73=>bō +8E74=>cù +8E75=>cù +8E76=>jué +8E77=>jué +8E78=>lìn +8E79=>tá +8E7A=>qiāo +8E7B=>juē +8E7C=>pǔ +8E7D=>liāo +8E7E=>dūn +8E7F=>cuān +8E80=>kuàng +8E81=>zào +8E82=>dá +8E83=>bì +8E84=>bì +8E85=>zhú +8E86=>jù +8E87=>chú +8E88=>qiào +8E89=>dǔn +8E8A=>chóu +8E8B=>jī +8E8C=>wǔ +8E8D=>yuè +8E8E=>niǎn +8E8F=>lìn +8E90=>liè +8E91=>zhí +8E92=>lì +8E93=>zhì +8E94=>chán +8E95=>chú +8E96=>duàn +8E97=>wèi +8E98=>lóng +8E99=>lìn +8E9A=>xiān +8E9B=>wèi +8E9C=>zuān +8E9D=>lán +8E9E=>xiè +8E9F=>ráng +8EA0=>sǎ +8EA1=>niè +8EA2=>tà +8EA3=>qú +8EA4=>jiè +8EA5=>cuān +8EA6=>cuó +8EA7=>xǐ +8EA8=>kuí +8EA9=>jué +8EAA=>lìn +8EAB=>shēn +8EAC=>gōng +8EAD=>dān +8EAE=>fen +8EAF=>qū +8EB0=>tǐ +8EB1=>duǒ +8EB2=>duǒ +8EB3=>gōng +8EB4=>láng +8EB5=>ren +8EB6=>luǒ +8EB7=>ǎi +8EB8=>jī +8EB9=>jú +8EBA=>tǎng +8EBB=>kong +8EBC=>lào +8EBD=>yǎn +8EBE=>mei +8EBF=>kāng +8EC0=>qū +8EC1=>lóu +8EC2=>lào +8EC3=>duǒ +8EC4=>zhí +8EC5=>yan +8EC6=>tǐ +8EC7=>dào +8EC8=>ying +8EC9=>yù +8ECA=>chē +8ECB=>yà +8ECC=>guǐ +8ECD=>jūn +8ECE=>wèi +8ECF=>yuè +8ED0=>xìn +8ED1=>dài +8ED2=>xuān +8ED3=>fàn +8ED4=>rèn +8ED5=>shān +8ED6=>kuáng +8ED7=>shū +8ED8=>tún +8ED9=>chén +8EDA=>dài +8EDB=>è +8EDC=>nà +8EDD=>qí +8EDE=>máo +8EDF=>ruǎn +8EE0=>rèn +8EE1=>qián +8EE2=>zhuǎn +8EE3=>hōng +8EE4=>hū +8EE5=>qú +8EE6=>kuàng +8EE7=>dǐ +8EE8=>líng +8EE9=>dài +8EEA=>āo +8EEB=>zhěn +8EEC=>fàn +8EED=>kuāng +8EEE=>yǎng +8EEF=>pēng +8EF0=>bèi +8EF1=>gū +8EF2=>gū +8EF3=>páo +8EF4=>zhù +8EF5=>rǒng +8EF6=>è +8EF7=>bá +8EF8=>zhóu +8EF9=>zhǐ +8EFA=>yáo +8EFB=>kē +8EFC=>yì +8EFD=>zhì +8EFE=>shì +8EFF=>píng +8F00=>ér +8F01=>gǒng +8F02=>jú +8F03=>jiào +8F04=>guāng +8F05=>hé +8F06=>kǎi +8F07=>quán +8F08=>zhōu +8F09=>zài +8F0A=>zhì +8F0B=>shē +8F0C=>liàng +8F0D=>yù +8F0E=>shāo +8F0F=>yóu +8F10=>wàn +8F11=>yǐn +8F12=>zhé +8F13=>wǎn +8F14=>fǔ +8F15=>qīng +8F16=>zhōu +8F17=>ní +8F18=>léng +8F19=>zhé +8F1A=>zhàn +8F1B=>liàng +8F1C=>zī +8F1D=>huī +8F1E=>wǎng +8F1F=>chuò +8F20=>guǒ +8F21=>kǎn +8F22=>yǐ +8F23=>péng +8F24=>qiàn +8F25=>gǔn +8F26=>niǎn +8F27=>píng +8F28=>guǎn +8F29=>bèi +8F2A=>lún +8F2B=>pái +8F2C=>liáng +8F2D=>ruǎn +8F2E=>róu +8F2F=>ji +8F30=>yáng +8F31=>xián +8F32=>chuán +8F33=>còu +8F34=>chūn +8F35=>gé +8F36=>yóu +8F37=>hōng +8F38=>shū +8F39=>fù +8F3A=>zī +8F3B=>fú +8F3C=>wēn +8F3D=>bèn +8F3E=>zhǎn +8F3F=>yú +8F40=>wēn +8F41=>tāo +8F42=>gǔ +8F43=>zhēn +8F44=>xiá +8F45=>yuán +8F46=>lù +8F47=>jiāo +8F48=>cháo +8F49=>zhuǎn +8F4A=>wèi +8F4B=>hún +8F4C=>xue +8F4D=>zhé +8F4E=>jiào +8F4F=>zhàn +8F50=>bú +8F51=>lǎo +8F52=>fén +8F53=>fān +8F54=>lín +8F55=>gé +8F56=>sè +8F57=>kǎn +8F58=>huán +8F59=>yǐ +8F5A=>jí +8F5B=>zhuì +8F5C=>ér +8F5D=>yù +8F5E=>jiàn +8F5F=>hōng +8F60=>léi +8F61=>pèi +8F62=>lì +8F63=>lì +8F64=>lú +8F65=>lìn +8F66=>chē +8F67=>yà +8F68=>guǐ +8F69=>xuān +8F6A=>dài +8F6B=>rèn +8F6C=>zhuǎn +8F6D=>è +8F6E=>lún +8F6F=>ruǎn +8F70=>hōng +8F71=>gū +8F72=>kē +8F73=>lú +8F74=>zhóu +8F75=>zhǐ +8F76=>yì +8F77=>hū +8F78=>zhěn +8F79=>lì +8F7A=>yáo +8F7B=>qīng +8F7C=>shì +8F7D=>zài +8F7E=>zhì +8F7F=>jiào +8F80=>zhōu +8F81=>quán +8F82=>lù +8F83=>jiào +8F84=>zhé +8F85=>fǔ +8F86=>liàng +8F87=>niǎn +8F88=>bèi +8F89=>huī +8F8A=>gǔn +8F8B=>wǎng +8F8C=>liáng +8F8D=>chuò +8F8E=>zī +8F8F=>còu +8F90=>fú +8F91=>ji +8F92=>wēn +8F93=>shū +8F94=>pèi +8F95=>yuán +8F96=>xiá +8F97=>niǎn +8F98=>lù +8F99=>zhé +8F9A=>lín +8F9B=>xīn +8F9C=>gū +8F9D=>cí +8F9E=>cí +8F9F=>pì +8FA0=>zuì +8FA1=>biàn +8FA2=>là +8FA3=>là +8FA4=>cí +8FA5=>xuē +8FA6=>bàn +8FA7=>biàn +8FA8=>biàn +8FA9=>biàn +8FAA=>xuē +8FAB=>biàn +8FAC=>bān +8FAD=>cí +8FAE=>biàn +8FAF=>biàn +8FB0=>chén +8FB1=>rǔ +8FB2=>nóng +8FB3=>nóng +8FB4=>chǎn +8FB5=>chuò +8FB6=>chuò +8FB7=>yi +8FB8=>réng +8FB9=>biān +8FBA=>biān +8FBB=>shí +8FBC=>ru +8FBD=>liáo +8FBE=>dá +8FBF=>chān +8FC0=>gān +8FC1=>qiān +8FC2=>yū +8FC3=>yū +8FC4=>qì +8FC5=>xùn +8FC6=>yí +8FC7=>guò +8FC8=>mài +8FC9=>qī +8FCA=>zā +8FCB=>wàng +8FCC=>tù +8FCD=>zhūn +8FCE=>yíng +8FCF=>tì +8FD0=>yùn +8FD1=>jìn +8FD2=>háng +8FD3=>yà +8FD4=>fǎn +8FD5=>wù +8FD6=>dá +8FD7=>é +8FD8=>hái +8FD9=>zhè +8FDA=>zhong +8FDB=>jìn +8FDC=>yuǎn +8FDD=>wéi +8FDE=>lián +8FDF=>chí +8FE0=>chè +8FE1=>nì +8FE2=>tiáo +8FE3=>zhì +8FE4=>yí +8FE5=>jiǒng +8FE6=>jiā +8FE7=>chén +8FE8=>dài +8FE9=>ěr +8FEA=>dí +8FEB=>pò +8FEC=>zhù +8FED=>dié +8FEE=>zé +8FEF=>táo +8FF0=>shù +8FF1=>tuó +8FF2=>qu +8FF3=>jìng +8FF4=>huí +8FF5=>dòng +8FF6=>yòu +8FF7=>mí +8FF8=>bèng +8FF9=>jī +8FFA=>nǎi +8FFB=>yí +8FFC=>jié +8FFD=>zhuī +8FFE=>liè +8FFF=>xùn +9000=>tuì +9001=>sòng +9002=>shì +9003=>táo +9004=>páng +9005=>hòu +9006=>nì +9007=>dùn +9008=>jiǒng +9009=>xuǎn +900A=>xùn +900B=>bū +900C=>yōu +900D=>xiāo +900E=>qiú +900F=>tòu +9010=>zhú +9011=>qiú +9012=>dì +9013=>dì +9014=>tú +9015=>jìng +9016=>tì +9017=>dòu +9018=>yǐ +9019=>zhè +901A=>tōng +901B=>guàng +901C=>wù +901D=>shì +901E=>chěng +901F=>sù +9020=>zào +9021=>qūn +9022=>féng +9023=>lián +9024=>suò +9025=>huí +9026=>lǐ +9027=>gu +9028=>lái +9029=>bèn +902A=>cuò +902B=>jué +902C=>bèng +902D=>huàn +902E=>dǎi +902F=>lù +9030=>yóu +9031=>zhōu +9032=>jìn +9033=>yù +9034=>chuō +9035=>kuí +9036=>wēi +9037=>tì +9038=>yì +9039=>dá +903A=>yuǎn +903B=>luó +903C=>bī +903D=>nuò +903E=>yú +903F=>dàng +9040=>suí +9041=>dùn +9042=>suì +9043=>yǎn +9044=>chuán +9045=>chí +9046=>tí +9047=>yù +9048=>shí +9049=>zhēn +904A=>yóu +904B=>yùn +904C=>è +904D=>biàn +904E=>guò +904F=>è +9050=>xiá +9051=>huáng +9052=>qiú +9053=>dào +9054=>dá +9055=>wéi +9056=>nan +9057=>yí +9058=>gòu +9059=>yáo +905A=>chòu +905B=>liú +905C=>xùn +905D=>tà +905E=>dì +905F=>chí +9060=>yuǎn +9061=>sù +9062=>tà +9063=>qiǎn +9064=>ma +9065=>yáo +9066=>guàn +9067=>zhāng +9068=>áo +9069=>shì +906A=>cà +906B=>chì +906C=>sù +906D=>zāo +906E=>zhē +906F=>dùn +9070=>dì +9071=>lóu +9072=>chí +9073=>cuō +9074=>lín +9075=>zūn +9076=>rào +9077=>qiān +9078=>xuǎn +9079=>yù +907A=>yí +907B=>wù +907C=>liáo +907D=>jù +907E=>shì +907F=>bì +9080=>yāo +9081=>mài +9082=>xiè +9083=>suì +9084=>hái +9085=>zhān +9086=>téng +9087=>ěr +9088=>miǎo +9089=>biān +908A=>biān +908B=>lā +908C=>lí +908D=>yuán +908E=>yáo +908F=>luó +9090=>lǐ +9091=>yì +9092=>tíng +9093=>dèng +9094=>qǐ +9095=>yōng +9096=>shān +9097=>hán +9098=>yú +9099=>máng +909A=>rú +909B=>qióng +909C=>wan +909D=>kuàng +909E=>fū +909F=>kàng +90A0=>bīn +90A1=>fāng +90A2=>xíng +90A3=>nà +90A4=>xīn +90A5=>shěn +90A6=>bāng +90A7=>yuán +90A8=>cūn +90A9=>huǒ +90AA=>xié +90AB=>bāng +90AC=>wū +90AD=>jù +90AE=>yóu +90AF=>hán +90B0=>tái +90B1=>qiū +90B2=>bì +90B3=>pī +90B4=>bǐng +90B5=>shào +90B6=>bèi +90B7=>wǎ +90B8=>dǐ +90B9=>zōu +90BA=>yè +90BB=>lín +90BC=>kuāng +90BD=>guī +90BE=>zhū +90BF=>shī +90C0=>kū +90C1=>yù +90C2=>gāi +90C3=>hé +90C4=>qiè +90C5=>zhì +90C6=>jí +90C7=>huán +90C8=>hòu +90C9=>xíng +90CA=>jiāo +90CB=>xí +90CC=>guī +90CD=>nuó +90CE=>láng +90CF=>jiá +90D0=>kuài +90D1=>zhèng +90D2=>lang +90D3=>yùn +90D4=>yán +90D5=>chéng +90D6=>dòu +90D7=>xī +90D8=>lǘ +90D9=>fǔ +90DA=>wú +90DB=>fú +90DC=>gào +90DD=>hǎo +90DE=>láng +90DF=>jiá +90E0=>gěng +90E1=>jùn +90E2=>yǐng +90E3=>bó +90E4=>xì +90E5=>bèi +90E6=>lì +90E7=>yún +90E8=>bù +90E9=>xiáo +90EA=>qī +90EB=>pí +90EC=>qīng +90ED=>guō +90EE=>zhōu +90EF=>tán +90F0=>zōu +90F1=>píng +90F2=>lái +90F3=>ní +90F4=>chēn +90F5=>yóu +90F6=>bù +90F7=>xiāng +90F8=>dān +90F9=>jú +90FA=>yōng +90FB=>qiāo +90FC=>yī +90FD=>dōu +90FE=>yǎn +90FF=>méi +9100=>ruò +9101=>bèi +9102=>è +9103=>shū +9104=>juàn +9105=>yǔ +9106=>yùn +9107=>hóu +9108=>kuí +9109=>xiāng +910A=>xiāng +910B=>sōu +910C=>táng +910D=>míng +910E=>xī +910F=>rǔ +9110=>chù +9111=>zī +9112=>zōu +9113=>yè +9114=>wū +9115=>xiāng +9116=>yún +9117=>hào +9118=>yōng +9119=>bǐ +911A=>mào +911B=>cháo +911C=>fū +911D=>liǎo +911E=>yín +911F=>zhuān +9120=>hù +9121=>qiāo +9122=>yān +9123=>zhāng +9124=>màn +9125=>qiāo +9126=>xǔ +9127=>dèng +9128=>bì +9129=>xún +912A=>bì +912B=>zēng +912C=>wéi +912D=>zhèng +912E=>mào +912F=>shàn +9130=>lín +9131=>pó +9132=>dān +9133=>méng +9134=>yè +9135=>cào +9136=>kuài +9137=>fēng +9138=>méng +9139=>zōu +913A=>kuàng +913B=>liǎn +913C=>zàn +913D=>chán +913E=>yōu +913F=>jī +9140=>yàn +9141=>chán +9142=>cuó +9143=>líng +9144=>huān +9145=>xī +9146=>fēng +9147=>zàn +9148=>lì +9149=>yǒu +914A=>dīng +914B=>qiú +914C=>zhuó +914D=>pèi +914E=>zhòu +914F=>yǐ +9150=>gān +9151=>yú +9152=>jiǔ +9153=>yǎn +9154=>zuì +9155=>máo +9156=>zhèn +9157=>xù +9158=>dòu +9159=>zhēn +915A=>fēn +915B=>yuan +915C=>fu +915D=>yùn +915E=>tài +915F=>tiān +9160=>qiǎ +9161=>tuó +9162=>cù +9163=>hān +9164=>gū +9165=>sū +9166=>fā +9167=>chóu +9168=>zài +9169=>mǐng +916A=>lào +916B=>chuò +916C=>chou +916D=>yòu +916E=>tóng +916F=>zhǐ +9170=>xiān +9171=>jiàng +9172=>chéng +9173=>yìn +9174=>tú +9175=>jiào +9176=>méi +9177=>kù +9178=>suān +9179=>lèi +917A=>pú +917B=>zuì +917C=>hǎi +917D=>yàn +917E=>shāi +917F=>niàng +9180=>wéi +9181=>lù +9182=>lǎn +9183=>yān +9184=>táo +9185=>pēi +9186=>zhǎn +9187=>chún +9188=>tán +9189=>zuì +918A=>zhuì +918B=>cù +918C=>kūn +918D=>tí +918E=>xián +918F=>dū +9190=>hú +9191=>xǔ +9192=>xǐng +9193=>tǎn +9194=>qiú +9195=>chún +9196=>yùn +9197=>pò +9198=>kē +9199=>sōu +919A=>mí +919B=>quán +919C=>chǒu +919D=>cuō +919E=>yùn +919F=>yòng +91A0=>àng +91A1=>zhà +91A2=>hǎi +91A3=>táng +91A4=>jiàng +91A5=>piǎo +91A6=>chěn +91A7=>yù +91A8=>lí +91A9=>zāo +91AA=>láo +91AB=>yī +91AC=>jiàng +91AD=>bú +91AE=>jiào +91AF=>xī +91B0=>tán +91B1=>fā +91B2=>nóng +91B3=>yì +91B4=>lǐ +91B5=>jù +91B6=>yàn +91B7=>yì +91B8=>niàng +91B9=>rú +91BA=>xūn +91BB=>chóu +91BC=>yàn +91BD=>líng +91BE=>mí +91BF=>mí +91C0=>niàng +91C1=>xìn +91C2=>jiào +91C3=>shāi +91C4=>mí +91C5=>yàn +91C6=>biàn +91C7=>cǎi +91C8=>shì +91C9=>yòu +91CA=>shì +91CB=>shì +91CC=>lǐ +91CD=>zhòng +91CE=>yě +91CF=>liàng +91D0=>lí +91D1=>jīn +91D2=>jin +91D3=>qiú +91D4=>yǐ +91D5=>liǎo +91D6=>dāo +91D7=>zhāo +91D8=>dīng +91D9=>pò +91DA=>qiú +91DB=>bā +91DC=>fǔ +91DD=>zhēn +91DE=>zhí +91DF=>bā +91E0=>luàn +91E1=>fǔ +91E2=>nǎi +91E3=>diào +91E4=>shàn +91E5=>qiǎo +91E6=>kòu +91E7=>chuàn +91E8=>zǐ +91E9=>fǎn +91EA=>huá +91EB=>huá +91EC=>hàn +91ED=>gāng +91EE=>qí +91EF=>máng +91F0=>rì +91F1=>dì +91F2=>sì +91F3=>xì +91F4=>yì +91F5=>chāi +91F6=>shī +91F7=>tǔ +91F8=>xī +91F9=>nǚ +91FA=>qiān +91FB=>qiu +91FC=>jiàn +91FD=>pì +91FE=>yé +91FF=>jīn +9200=>bǎ +9201=>fāng +9202=>chén +9203=>xíng +9204=>dǒu +9205=>yuè +9206=>qiān +9207=>fū +9208=>pī +9209=>nà +920A=>xīn +920B=>é +920C=>jué +920D=>dùn +920E=>gōu +920F=>yǐn +9210=>qián +9211=>bǎn +9212=>sà +9213=>rén +9214=>chāo +9215=>niǔ +9216=>fēn +9217=>yǔn +9218=>jǐ +9219=>qín +921A=>pī +921B=>guō +921C=>hóng +921D=>yín +921E=>jūn +921F=>shī +9220=>yì +9221=>zhōng +9222=>xǐ +9223=>gài +9224=>rì +9225=>huǒ +9226=>tài +9227=>kàng +9228=>yuan +9229=>lu +922A=>è +922B=>wen +922C=>duó +922D=>zī +922E=>nǐ +922F=>tú +9230=>shì +9231=>mín +9232=>gū +9233=>kē +9234=>líng +9235=>bǐng +9236=>sì +9237=>gǔ +9238=>bó +9239=>pī +923A=>yù +923B=>sì +923C=>zuó +923D=>bū +923E=>yóu +923F=>tián +9240=>jiǎ +9241=>zhēn +9242=>shǐ +9243=>shì +9244=>zhí +9245=>jù +9246=>chān +9247=>shī +9248=>shī +9249=>xuàn +924A=>zhāo +924B=>bào +924C=>hé +924D=>bì +924E=>shēng +924F=>chú +9250=>shí +9251=>bó +9252=>zhù +9253=>chì +9254=>zā +9255=>pō +9256=>tóng +9257=>qián +9258=>fú +9259=>zhǎi +925A=>liǔ +925B=>qiān +925C=>fú +925D=>lì +925E=>yuè +925F=>pī +9260=>yāng +9261=>bàn +9262=>bō +9263=>jié +9264=>gōu +9265=>shù +9266=>zhēng +9267=>mǔ +9268=>xǐ +9269=>xǐ +926A=>dì +926B=>jiā +926C=>mù +926D=>tǎn +926E=>huán +926F=>yǐ +9270=>sī +9271=>kuàng +9272=>kǎ +9273=>běi +9274=>jiàn +9275=>tóng +9276=>xíng +9277=>hóng +9278=>jiǎo +9279=>chǐ +927A=>èr +927B=>luò +927C=>bǐng +927D=>shì +927E=>móu +927F=>jiā +9280=>yín +9281=>jūn +9282=>zhōu +9283=>chòng +9284=>xiǎng +9285=>tóng +9286=>mò +9287=>lèi +9288=>jī +9289=>yù +928A=>xù +928B=>rén +928C=>zùn +928D=>zhì +928E=>qióng +928F=>shàn +9290=>chì +9291=>xiǎn +9292=>xíng +9293=>quán +9294=>pī +9295=>tiě +9296=>zhū +9297=>xiàng +9298=>míng +9299=>kuǎ +929A=>yáo +929B=>xiān +929C=>xián +929D=>xiū +929E=>jūn +929F=>chā +92A0=>lǎo +92A1=>jí +92A2=>pǐ +92A3=>rú +92A4=>mǐ +92A5=>yī +92A6=>yīn +92A7=>guāng +92A8=>ǎn +92A9=>diū +92AA=>yǒu +92AB=>sè +92AC=>kào +92AD=>qián +92AE=>luán +92AF=>si +92B0=>āi +92B1=>diào +92B2=>hàn +92B3=>ruì +92B4=>shì +92B5=>kēng +92B6=>qiú +92B7=>xiāo +92B8=>zhé +92B9=>xiù +92BA=>zàng +92BB=>tí +92BC=>cuò +92BD=>guā +92BE=>hòng +92BF=>zhōng +92C0=>tōu +92C1=>lǚ +92C2=>méi +92C3=>láng +92C4=>wǎn +92C5=>xīn +92C6=>yún +92C7=>bèi +92C8=>wù +92C9=>sù +92CA=>yù +92CB=>chán +92CC=>dìng +92CD=>bó +92CE=>hàn +92CF=>jiá +92D0=>hóng +92D1=>cuān +92D2=>fēng +92D3=>chān +92D4=>wǎn +92D5=>zhì +92D6=>sī +92D7=>xuān +92D8=>huá +92D9=>yǔ +92DA=>tiáo +92DB=>gǒng +92DC=>zhuó +92DD=>lüè +92DE=>xíng +92DF=>qǐn +92E0=>shèn +92E1=>hán +92E2=>lüè +92E3=>yé +92E4=>chú +92E5=>zèng +92E6=>jū +92E7=>xiàn +92E8=>tiě +92E9=>máng +92EA=>pù +92EB=>lí +92EC=>pàn +92ED=>ruì +92EE=>chéng +92EF=>gào +92F0=>lǐ +92F1=>tè +92F2=>bing +92F3=>zhù +92F4=>zhen +92F5=>tū +92F6=>liǔ +92F7=>zuì +92F8=>jù +92F9=>chǎng +92FA=>yuǎn +92FB=>jiàn +92FC=>gāng +92FD=>diào +92FE=>táo +92FF=>cháng +9300=>lún +9301=>guǒ +9302=>líng +9303=>bēi +9304=>lù +9305=>lí +9306=>qiāng +9307=>póu +9308=>juǎn +9309=>mín +930A=>zuì +930B=>péng +930C=>àn +930D=>pī +930E=>xiàn +930F=>yā +9310=>zhuī +9311=>lèi +9312=>kē +9313=>kōng +9314=>tà +9315=>kūn +9316=>dú +9317=>nèi +9318=>chuí +9319=>zī +931A=>zhēng +931B=>bēn +931C=>niè +931D=>zòng +931E=>chún +931F=>tán +9320=>dìng +9321=>qí +9322=>qián +9323=>zhuì +9324=>jī +9325=>yù +9326=>jǐn +9327=>guǎn +9328=>máo +9329=>chāng +932A=>tiǎn +932B=>xī +932C=>liàn +932D=>táo +932E=>gù +932F=>cuò +9330=>shù +9331=>zhēn +9332=>lù +9333=>měng +9334=>lù +9335=>huā +9336=>biǎo +9337=>gá +9338=>lái +9339=>kěn +933A=>fang +933B=>wu +933C=>nài +933D=>wàn +933E=>zàn +933F=>hu +9340=>dé +9341=>xiān +9342=>piān +9343=>huō +9344=>liàng +9345=>fa +9346=>mén +9347=>kǎi +9348=>yīng +9349=>dī +934A=>liàn +934B=>guō +934C=>xiǎn +934D=>dù +934E=>tú +934F=>wéi +9350=>zōng +9351=>fù +9352=>róu +9353=>jí +9354=>è +9355=>jūn +9356=>chěn +9357=>tí +9358=>zhá +9359=>hù +935A=>yáng +935B=>duàn +935C=>xiá +935D=>yú +935E=>kēng +935F=>xīng +9360=>huáng +9361=>wěi +9362=>fù +9363=>zhāo +9364=>chā +9365=>qiè +9366=>shī +9367=>hōng +9368=>kuí +9369=>tiǎn +936A=>móu +936B=>qiāo +936C=>qiāo +936D=>hóu +936E=>tōu +936F=>cōng +9370=>huán +9371=>yè +9372=>mín +9373=>jiàn +9374=>duān +9375=>jiàn +9376=>sōng +9377=>kuí +9378=>hú +9379=>xuān +937A=>duǒ +937B=>jié +937C=>zhēn +937D=>biān +937E=>zhōng +937F=>zī +9380=>xiū +9381=>yé +9382=>měi +9383=>pài +9384=>āi +9385=>jiè +9386=>qian +9387=>méi +9388=>suǒ +9389=>dá +938A=>bàng +938B=>xiá +938C=>lián +938D=>suǒ +938E=>kài +938F=>liú +9390=>yáo +9391=>yè +9392=>nòu +9393=>wēng +9394=>róng +9395=>táng +9396=>suǒ +9397=>qiāng +9398=>lì +9399=>shuò +939A=>chuí +939B=>bó +939C=>pán +939D=>dā +939E=>bī +939F=>sǎng +93A0=>gāng +93A1=>zī +93A2=>wū +93A3=>yíng +93A4=>huàng +93A5=>tiáo +93A6=>liú +93A7=>kǎi +93A8=>sǔn +93A9=>shā +93AA=>sōu +93AB=>wàn +93AC=>hào +93AD=>zhèn +93AE=>zhèn +93AF=>láng +93B0=>yì +93B1=>yuán +93B2=>tǎng +93B3=>niè +93B4=>xí +93B5=>jiā +93B6=>gē +93B7=>mǎ +93B8=>juān +93B9=>song +93BA=>zu +93BB=>suǒ +93BC=>xià +93BD=>feng +93BE=>wen +93BF=>ná +93C0=>lǔ +93C1=>suǒ +93C2=>ōu +93C3=>zú +93C4=>tuán +93C5=>xiū +93C6=>guàn +93C7=>xuàn +93C8=>liàn +93C9=>shòu +93CA=>ào +93CB=>mǎn +93CC=>mò +93CD=>luó +93CE=>bì +93CF=>wèi +93D0=>liú +93D1=>dí +93D2=>sǎn +93D3=>zǒng +93D4=>yí +93D5=>lù +93D6=>áo +93D7=>kēng +93D8=>qiāng +93D9=>cuī +93DA=>qī +93DB=>cháng +93DC=>tāng +93DD=>màn +93DE=>yōng +93DF=>chǎn +93E0=>fēng +93E1=>jìng +93E2=>biāo +93E3=>shù +93E4=>lòu +93E5=>xiù +93E6=>cōng +93E7=>lóng +93E8=>zàn +93E9=>jiàn +93EA=>cáo +93EB=>lí +93EC=>xià +93ED=>xī +93EE=>kāng +93EF=>shuang +93F0=>bèng +93F1=>zhang +93F2=>qian +93F3=>chēng +93F4=>lù +93F5=>huá +93F6=>jí +93F7=>pú +93F8=>huì +93F9=>qiǎng +93FA=>pō +93FB=>lín +93FC=>sè +93FD=>xiù +93FE=>sǎn +93FF=>chēng +9400=>kuì +9401=>sī +9402=>liù +9403=>náo +9404=>huáng +9405=>piě +9406=>suì +9407=>fán +9408=>qiáo +9409=>quān +940A=>yáng +940B=>tāng +940C=>xiàng +940D=>jué +940E=>jiāo +940F=>zūn +9410=>liáo +9411=>qiè +9412=>láo +9413=>duì +9414=>xín +9415=>zān +9416=>jī +9417=>jiǎn +9418=>zhōng +9419=>dèng +941A=>yā +941B=>yìng +941C=>duī +941D=>jué +941E=>nòu +941F=>zān +9420=>pǔ +9421=>tiě +9422=>fán +9423=>zhang +9424=>dǐng +9425=>shàn +9426=>kāi +9427=>jiān +9428=>fèi +9429=>suì +942A=>lǔ +942B=>juān +942C=>huì +942D=>yù +942E=>lián +942F=>zhuó +9430=>qiāo +9431=>jiàn +9432=>zhuó +9433=>léi +9434=>bì +9435=>tiě +9436=>huán +9437=>yè +9438=>duó +9439=>guǒ +943A=>dang +943B=>jù +943C=>fén +943D=>dá +943E=>bèi +943F=>yì +9440=>ài +9441=>zōng +9442=>xùn +9443=>diào +9444=>zhù +9445=>héng +9446=>zhuì +9447=>jī +9448=>niè +9449=>hé +944A=>huò +944B=>qīng +944C=>bīn +944D=>yīng +944E=>kuì +944F=>níng +9450=>xū +9451=>jiàn +9452=>jiàn +9453=>qian +9454=>chǎ +9455=>zhì +9456=>miè +9457=>lí +9458=>léi +9459=>jī +945A=>zuàn +945B=>kuàng +945C=>shǎng +945D=>péng +945E=>là +945F=>dú +9460=>shuò +9461=>chuò +9462=>lǜ +9463=>biāo +9464=>bào +9465=>lǔ +9466=>xian +9467=>kuan +9468=>lóng +9469=>è +946A=>lú +946B=>xīn +946C=>jiàn +946D=>làn +946E=>bó +946F=>jiān +9470=>yào +9471=>chán +9472=>xiāng +9473=>jiàn +9474=>xī +9475=>guàn +9476=>cáng +9477=>niè +9478=>lěi +9479=>cuān +947A=>qú +947B=>pàn +947C=>luó +947D=>zuān +947E=>luán +947F=>záo +9480=>niè +9481=>jué +9482=>tǎng +9483=>shǔ +9484=>lán +9485=>jīn +9486=>gá +9487=>yǐ +9488=>zhēn +9489=>dīng +948A=>zhāo +948B=>pō +948C=>liǎo +948D=>tǔ +948E=>qiān +948F=>chuàn +9490=>shān +9491=>jí +9492=>fán +9493=>diào +9494=>mén +9495=>nǚ +9496=>yáng +9497=>chāi +9498=>xíng +9499=>gài +949A=>bù +949B=>tài +949C=>jù +949D=>dùn +949E=>chāo +949F=>zhōng +94A0=>nà +94A1=>bèi +94A2=>gāng +94A3=>bǎn +94A4=>qián +94A5=>yào +94A6=>qīn +94A7=>jūn +94A8=>wū +94A9=>gōu +94AA=>kàng +94AB=>fāng +94AC=>huǒ +94AD=>tǒu +94AE=>niǔ +94AF=>bǎ +94B0=>yù +94B1=>qián +94B2=>zhēng +94B3=>qián +94B4=>gǔ +94B5=>bō +94B6=>ē +94B7=>pǒ +94B8=>bù +94B9=>bó +94BA=>yuè +94BB=>zuān +94BC=>mù +94BD=>tǎn +94BE=>jiǎ +94BF=>diàn +94C0=>yóu +94C1=>tiě +94C2=>bó +94C3=>líng +94C4=>shuò +94C5=>qiān +94C6=>mǎo +94C7=>bào +94C8=>shì +94C9=>xuàn +94CA=>tā +94CB=>bì +94CC=>ní +94CD=>pī +94CE=>duó +94CF=>xíng +94D0=>kào +94D1=>lǎo +94D2=>ěr +94D3=>máng +94D4=>yà +94D5=>yǒu +94D6=>chéng +94D7=>jiá +94D8=>yé +94D9=>náo +94DA=>zhì +94DB=>dang +94DC=>tóng +94DD=>lǚ +94DE=>diào +94DF=>yīn +94E0=>kǎi +94E1=>zhá +94E2=>zhū +94E3=>xǐ +94E4=>dìng +94E5=>diū +94E6=>xiān +94E7=>huá +94E8=>quán +94E9=>shā +94EA=>hā +94EB=>diào +94EC=>gè +94ED=>míng +94EE=>zhēng +94EF=>sè +94F0=>jiǎo +94F1=>yī +94F2=>chǎn +94F3=>chòng +94F4=>tāng +94F5=>ǎn +94F6=>yín +94F7=>rú +94F8=>zhù +94F9=>láo +94FA=>pù +94FB=>wú +94FC=>lái +94FD=>tè +94FE=>liàn +94FF=>kēng +9500=>xiāo +9501=>suǒ +9502=>lǐ +9503=>zèng +9504=>chú +9505=>guō +9506=>gào +9507=>é +9508=>xiù +9509=>cuò +950A=>lüè +950B=>fēng +950C=>xīn +950D=>liǔ +950E=>kāi +950F=>jiǎn +9510=>ruì +9511=>tī +9512=>láng +9513=>qǐn +9514=>jū +9515=>ā +9516=>qiāng +9517=>zhě +9518=>nuò +9519=>cuò +951A=>máo +951B=>bēn +951C=>qí +951D=>dé +951E=>kè +951F=>kūn +9520=>chāng +9521=>xī +9522=>gù +9523=>luó +9524=>chuí +9525=>zhuī +9526=>jǐn +9527=>zhì +9528=>xiān +9529=>juǎn +952A=>huō +952B=>péi +952C=>tán +952D=>dìng +952E=>jiàn +952F=>jù +9530=>měng +9531=>zī +9532=>qiè +9533=>yīng +9534=>kǎi +9535=>qiāng +9536=>sī +9537=>è +9538=>chā +9539=>qiāo +953A=>zhōng +953B=>duàn +953C=>sōu +953D=>huáng +953E=>huán +953F=>āi +9540=>dù +9541=>měi +9542=>lòu +9543=>zī +9544=>fèi +9545=>méi +9546=>mò +9547=>zhèn +9548=>bó +9549=>gé +954A=>niè +954B=>tǎng +954C=>juān +954D=>niè +954E=>ná +954F=>liú +9550=>gǎo +9551=>bàng +9552=>yì +9553=>jiā +9554=>bīn +9555=>róng +9556=>biāo +9557=>tāng +9558=>màn +9559=>luó +955A=>bèng +955B=>yōng +955C=>jìng +955D=>dī +955E=>zú +955F=>xuàn +9560=>liú +9561=>chán +9562=>jué +9563=>liào +9564=>pú +9565=>lǔ +9566=>duì +9567=>lán +9568=>pǔ +9569=>cuān +956A=>qiāng +956B=>dèng +956C=>huò +956D=>léi +956E=>huán +956F=>zhuó +9570=>lián +9571=>yì +9572=>chǎ +9573=>biāo +9574=>là +9575=>chán +9576=>xiāng +9577=>zhǎng +9578=>cháng +9579=>jiǔ +957A=>ǎo +957B=>dié +957C=>qū +957D=>liǎo +957E=>mí +957F=>zhǎng +9580=>mén +9581=>mà +9582=>shuān +9583=>shǎn +9584=>huò +9585=>mén +9586=>yán +9587=>bì +9588=>hàn +9589=>bì +958A=>shan +958B=>kāi +958C=>kàng +958D=>bēng +958E=>hóng +958F=>rùn +9590=>sàn +9591=>xián +9592=>xián +9593=>jiān +9594=>mǐn +9595=>xiā +9596=>shui +9597=>dòu +9598=>zhá +9599=>nào +959A=>zhān +959B=>pēng +959C=>xiǎ +959D=>líng +959E=>biàn +959F=>bì +95A0=>rùn +95A1=>ài +95A2=>guān +95A3=>gé +95A4=>gé +95A5=>fá +95A6=>chù +95A7=>hòng +95A8=>guī +95A9=>mǐn +95AA=>sē +95AB=>kǔn +95AC=>làng +95AD=>lǘ +95AE=>tíng +95AF=>shà +95B0=>jú +95B1=>yuè +95B2=>yuè +95B3=>chǎn +95B4=>qù +95B5=>lìn +95B6=>chāng +95B7=>shài +95B8=>kǔn +95B9=>yān +95BA=>wén +95BB=>yán +95BC=>è +95BD=>hūn +95BE=>yù +95BF=>wén +95C0=>xiàng +95C1=>bāo +95C2=>hòng +95C3=>qù +95C4=>yǎo +95C5=>wén +95C6=>bǎn +95C7=>àn +95C8=>wéi +95C9=>yīn +95CA=>kuò +95CB=>què +95CC=>lán +95CD=>dū +95CE=>quan +95CF=>fēng +95D0=>tián +95D1=>niè +95D2=>tà +95D3=>kǎi +95D4=>hé +95D5=>què +95D6=>chuǎng +95D7=>guān +95D8=>dòu +95D9=>qǐ +95DA=>kuī +95DB=>táng +95DC=>guān +95DD=>piáo +95DE=>kàn +95DF=>xì +95E0=>huì +95E1=>chǎn +95E2=>pì +95E3=>dàng +95E4=>huán +95E5=>tà +95E6=>wén +95E7=>tā +95E8=>mén +95E9=>shuān +95EA=>shǎn +95EB=>yàn +95EC=>hàn +95ED=>bì +95EE=>wèn +95EF=>chuǎng +95F0=>rùn +95F1=>wéi +95F2=>xián +95F3=>hóng +95F4=>jiān +95F5=>mǐn +95F6=>kāng +95F7=>mèn +95F8=>zhá +95F9=>nào +95FA=>guī +95FB=>wén +95FC=>tà +95FD=>mǐn +95FE=>lǘ +95FF=>kǎi +9600=>fá +9601=>gé +9602=>hé +9603=>kǔn +9604=>jiū +9605=>yuè +9606=>láng +9607=>dū +9608=>yù +9609=>yān +960A=>chāng +960B=>xì +960C=>wén +960D=>hūn +960E=>yán +960F=>è +9610=>chǎn +9611=>lán +9612=>qù +9613=>huì +9614=>kuò +9615=>què +9616=>hé +9617=>tián +9618=>dá +9619=>quē +961A=>hǎn +961B=>huán +961C=>fù +961D=>fù +961E=>lè +961F=>duì +9620=>xìn +9621=>qiān +9622=>wù +9623=>gài +9624=>zhì +9625=>yīn +9626=>yáng +9627=>dǒu +9628=>è +9629=>shēng +962A=>bǎn +962B=>péi +962C=>kēng +962D=>yǔn +962E=>ruǎn +962F=>zhǐ +9630=>pí +9631=>jǐng +9632=>fáng +9633=>yáng +9634=>yīn +9635=>zhèn +9636=>jiē +9637=>chēng +9638=>è +9639=>qū +963A=>dǐ +963B=>zǔ +963C=>zuò +963D=>diàn +963E=>lǐng +963F=>ā +9640=>tuó +9641=>tuó +9642=>bēi +9643=>bǐng +9644=>fù +9645=>jì +9646=>lù +9647=>lǒng +9648=>chén +9649=>xíng +964A=>duò +964B=>lòu +964C=>mò +964D=>jiàng +964E=>shū +964F=>duò +9650=>xiàn +9651=>ér +9652=>guǐ +9653=>yū +9654=>gāi +9655=>shǎn +9656=>jùn +9657=>qiào +9658=>xíng +9659=>chún +965A=>fù +965B=>bì +965C=>xiá +965D=>shǎn +965E=>shēng +965F=>zhì +9660=>pū +9661=>dǒu +9662=>yuàn +9663=>zhèn +9664=>chú +9665=>xiàn +9666=>dao +9667=>niè +9668=>yǔn +9669=>xiǎn +966A=>péi +966B=>fèi +966C=>zōu +966D=>yì +966E=>duì +966F=>lún +9670=>yīn +9671=>jū +9672=>chuí +9673=>chén +9674=>pí +9675=>líng +9676=>táo +9677=>xiàn +9678=>lù +9679=>shēng +967A=>xiǎn +967B=>yīn +967C=>zhǔ +967D=>yáng +967E=>réng +967F=>xiá +9680=>chóng +9681=>yàn +9682=>yīn +9683=>shù +9684=>dī +9685=>yú +9686=>lóng +9687=>wēi +9688=>wēi +9689=>niè +968A=>duì +968B=>suí +968C=>ǎn +968D=>huáng +968E=>jiē +968F=>suí +9690=>yǐn +9691=>gài +9692=>yǎn +9693=>huī +9694=>gé +9695=>yǔn +9696=>wù +9697=>kuí +9698=>ài +9699=>xì +969A=>táng +969B=>jì +969C=>zhàng +969D=>dǎo +969E=>áo +969F=>xì +96A0=>yǐn +96A1=>sa +96A2=>rǎo +96A3=>lín +96A4=>tuí +96A5=>dèng +96A6=>jiǎo +96A7=>suì +96A8=>suí +96A9=>ào +96AA=>xiǎn +96AB=>fén +96AC=>nǐ +96AD=>ér +96AE=>jī +96AF=>dǎo +96B0=>xí +96B1=>yǐn +96B2=>é +96B3=>huī +96B4=>lǒng +96B5=>xī +96B6=>lì +96B7=>lì +96B8=>lì +96B9=>zhuī +96BA=>hú +96BB=>zhī +96BC=>sǔn +96BD=>juàn +96BE=>nán +96BF=>yì +96C0=>què +96C1=>yàn +96C2=>qín +96C3=>qiān +96C4=>xióng +96C5=>yǎ +96C6=>jí +96C7=>gù +96C8=>huán +96C9=>zhì +96CA=>gòu +96CB=>juàn +96CC=>cí +96CD=>yōng +96CE=>jū +96CF=>chú +96D0=>hū +96D1=>zá +96D2=>luò +96D3=>yú +96D4=>chóu +96D5=>diāo +96D6=>suī +96D7=>hàn +96D8=>wò +96D9=>shuāng +96DA=>guàn +96DB=>chú +96DC=>zá +96DD=>yōng +96DE=>jī +96DF=>xī +96E0=>chóu +96E1=>liù +96E2=>lí +96E3=>nán +96E4=>xué +96E5=>zá +96E6=>jí +96E7=>jí +96E8=>yǔ +96E9=>yú +96EA=>xuě +96EB=>nǎ +96EC=>fǒu +96ED=>sè +96EE=>mù +96EF=>wén +96F0=>fēn +96F1=>pāng +96F2=>yún +96F3=>lì +96F4=>chì +96F5=>yāng +96F6=>líng +96F7=>léi +96F8=>án +96F9=>báo +96FA=>wù +96FB=>diàn +96FC=>dàng +96FD=>hù +96FE=>wù +96FF=>diào +9700=>xū +9701=>jì +9702=>mù +9703=>chén +9704=>xiāo +9705=>zhà +9706=>tíng +9707=>zhèn +9708=>pèi +9709=>méi +970A=>líng +970B=>qī +970C=>zhōu +970D=>huò +970E=>shà +970F=>fēi +9710=>hóng +9711=>zhān +9712=>yīn +9713=>ní +9714=>zhù +9715=>tún +9716=>lín +9717=>ling +9718=>dòng +9719=>yīng +971A=>wù +971B=>líng +971C=>shuāng +971D=>líng +971E=>xiá +971F=>hóng +9720=>yīn +9721=>mài +9722=>mài +9723=>yǔn +9724=>liù +9725=>mèng +9726=>bīn +9727=>wù +9728=>wèi +9729=>kuò +972A=>yín +972B=>xí +972C=>yì +972D=>ǎi +972E=>dàn +972F=>tèng +9730=>sǎn +9731=>yù +9732=>lù +9733=>lóng +9734=>dài +9735=>jí +9736=>pāng +9737=>yáng +9738=>bà +9739=>pī +973A=>wéi +973B=>fēng +973C=>xì +973D=>jì +973E=>mái +973F=>méng +9740=>méng +9741=>léi +9742=>lì +9743=>huò +9744=>ǎi +9745=>fèi +9746=>dài +9747=>lóng +9748=>líng +9749=>ài +974A=>fēng +974B=>lì +974C=>bǎo +974D=>he +974E=>hè +974F=>hè +9750=>bìng +9751=>qīng +9752=>qīng +9753=>jìng +9754=>tiān +9755=>zhēn +9756=>jìng +9757=>chēng +9758=>qìng +9759=>jìng +975A=>jìng +975B=>diàn +975C=>jìng +975D=>tiān +975E=>fēi +975F=>fēi +9760=>kào +9761=>mí +9762=>miàn +9763=>miàn +9764=>bào +9765=>yè +9766=>tiǎn +9767=>huì +9768=>yè +9769=>gé +976A=>dīng +976B=>chá +976C=>qián +976D=>rèn +976E=>dí +976F=>dù +9770=>wù +9771=>rèn +9772=>qín +9773=>jìn +9774=>xuē +9775=>niǔ +9776=>bǎ +9777=>yǐn +9778=>sǎ +9779=>nà +977A=>mò +977B=>zǔ +977C=>dá +977D=>bàn +977E=>yì +977F=>yào +9780=>táo +9781=>bèi +9782=>jiá +9783=>hóng +9784=>páo +9785=>yāng +9786=>bing +9787=>yīn +9788=>gé +9789=>táo +978A=>jié +978B=>xié +978C=>ān +978D=>ān +978E=>hén +978F=>gǒng +9790=>qia +9791=>dá +9792=>qiáo +9793=>tīng +9794=>mán +9795=>yìng +9796=>suī +9797=>tiáo +9798=>qiào +9799=>xuàn +979A=>kòng +979B=>běng +979C=>tà +979D=>shàng +979E=>bǐng +979F=>kuò +97A0=>jū +97A1=>la +97A2=>xiè +97A3=>róu +97A4=>bāng +97A5=>ēng +97A6=>qiū +97A7=>qiū +97A8=>hé +97A9=>xiào +97AA=>mù +97AB=>jū +97AC=>jiān +97AD=>biān +97AE=>dī +97AF=>jiān +97B0=>wēn +97B1=>tāo +97B2=>gōu +97B3=>tà +97B4=>bèi +97B5=>xié +97B6=>pán +97B7=>gé +97B8=>bì +97B9=>kuò +97BA=>tāng +97BB=>lóu +97BC=>guì +97BD=>qiáo +97BE=>xuē +97BF=>jī +97C0=>jiān +97C1=>jiāng +97C2=>chàn +97C3=>dá +97C4=>hù +97C5=>xiǎn +97C6=>qiān +97C7=>dú +97C8=>wà +97C9=>jiān +97CA=>lán +97CB=>wéi +97CC=>rèn +97CD=>fú +97CE=>mèi +97CF=>quàn +97D0=>gé +97D1=>wěi +97D2=>qiào +97D3=>hán +97D4=>chàng +97D5=>kuo +97D6=>rǒu +97D7=>yùn +97D8=>shè +97D9=>wěi +97DA=>gé +97DB=>bài +97DC=>tāo +97DD=>gōu +97DE=>yùn +97DF=>gao +97E0=>bì +97E1=>wěi +97E2=>suì +97E3=>dú +97E4=>wà +97E5=>dú +97E6=>wéi +97E7=>rèn +97E8=>fú +97E9=>hán +97EA=>wěi +97EB=>yùn +97EC=>tāo +97ED=>jiǔ +97EE=>jiǔ +97EF=>xiān +97F0=>xiè +97F1=>xiān +97F2=>jī +97F3=>yīn +97F4=>zá +97F5=>yùn +97F6=>sháo +97F7=>lè +97F8=>péng +97F9=>huáng +97FA=>yīng +97FB=>yùn +97FC=>péng +97FD=>ān +97FE=>yīn +97FF=>xiǎng +9800=>hù +9801=>yè +9802=>dǐng +9803=>qǐng +9804=>kuí +9805=>xiàng +9806=>shùn +9807=>hān +9808=>xū +9809=>yí +980A=>xū +980B=>ě +980C=>sòng +980D=>kuǐ +980E=>qí +980F=>háng +9810=>yù +9811=>wán +9812=>bān +9813=>dùn +9814=>dí +9815=>dān +9816=>pàn +9817=>pō +9818=>lǐng +9819=>chè +981A=>jǐng +981B=>lèi +981C=>hé +981D=>qiāo +981E=>è +981F=>é +9820=>wěi +9821=>xié +9822=>kuò +9823=>shěn +9824=>yí +9825=>shěn +9826=>hái +9827=>duǐ +9828=>yǔ +9829=>pīng +982A=>lèi +982B=>fǔ +982C=>jiá +982D=>tóu +982E=>huì +982F=>kuí +9830=>jiá +9831=>luō +9832=>tǐng +9833=>chēng +9834=>yǐng +9835=>yūn +9836=>hú +9837=>hàn +9838=>jǐng +9839=>tuí +983A=>tuí +983B=>pín +983C=>lài +983D=>tuí +983E=>zī +983F=>zī +9840=>chuí +9841=>dìng +9842=>lài +9843=>tán +9844=>hàn +9845=>qiān +9846=>kē +9847=>cuì +9848=>xuǎn +9849=>qīn +984A=>yí +984B=>sāi +984C=>tí +984D=>é +984E=>è +984F=>yán +9850=>wèn +9851=>kǎn +9852=>yóng +9853=>zhuān +9854=>yán +9855=>xiǎn +9856=>xìn +9857=>yǐ +9858=>yuàn +9859=>sǎng +985A=>diān +985B=>diān +985C=>jiǎng +985D=>kuī +985E=>lèi +985F=>láo +9860=>piǎo +9861=>wài +9862=>mán +9863=>cù +9864=>yáo +9865=>hào +9866=>qiáo +9867=>gù +9868=>xùn +9869=>yǎn +986A=>huì +986B=>chàn +986C=>rú +986D=>méng +986E=>bīn +986F=>xiǎn +9870=>pín +9871=>lú +9872=>lǎn +9873=>niè +9874=>quán +9875=>yè +9876=>dǐng +9877=>qǐng +9878=>hān +9879=>xiàng +987A=>shùn +987B=>xū +987C=>xū +987D=>wán +987E=>gù +987F=>dùn +9880=>qí +9881=>bān +9882=>sòng +9883=>háng +9884=>yù +9885=>lú +9886=>lǐng +9887=>pō +9888=>jǐng +9889=>jié +988A=>jiá +988B=>tǐng +988C=>hé +988D=>yǐng +988E=>jiǒng +988F=>kē +9890=>yí +9891=>pín +9892=>huì +9893=>tuí +9894=>hàn +9895=>yǐng +9896=>yǐng +9897=>kē +9898=>tí +9899=>yóng +989A=>è +989B=>zhuān +989C=>yán +989D=>é +989E=>niè +989F=>mān +98A0=>diān +98A1=>sǎng +98A2=>hào +98A3=>lèi +98A4=>chàn +98A5=>rú +98A6=>pín +98A7=>quán +98A8=>fēng +98A9=>biāo +98AA=>gua +98AB=>fú +98AC=>xiā +98AD=>zhǎn +98AE=>biāo +98AF=>sà +98B0=>bá +98B1=>tái +98B2=>liè +98B3=>guā +98B4=>xuàn +98B5=>shāo +98B6=>jù +98B7=>biāo +98B8=>sī +98B9=>wěi +98BA=>yáng +98BB=>yáo +98BC=>sōu +98BD=>kǎi +98BE=>sōu +98BF=>fān +98C0=>liú +98C1=>xí +98C2=>liù +98C3=>piāo +98C4=>piāo +98C5=>liú +98C6=>biāo +98C7=>biāo +98C8=>biāo +98C9=>liáo +98CA=>biao +98CB=>sè +98CC=>fēng +98CD=>xiū +98CE=>fēng +98CF=>yáng +98D0=>zhǎn +98D1=>biāo +98D2=>sà +98D3=>jù +98D4=>sī +98D5=>sōu +98D6=>yáo +98D7=>liú +98D8=>piāo +98D9=>biāo +98DA=>biāo +98DB=>fēi +98DC=>fān +98DD=>fēi +98DE=>fēi +98DF=>shí +98E0=>shí +98E1=>cān +98E2=>jī +98E3=>dìng +98E4=>sì +98E5=>tuō +98E6=>zhān +98E7=>sūn +98E8=>xiǎng +98E9=>tún +98EA=>rèn +98EB=>yù +98EC=>juàn +98ED=>chì +98EE=>yǐn +98EF=>fàn +98F0=>fàn +98F1=>sūn +98F2=>yǐn +98F3=>tǒu +98F4=>yí +98F5=>zuò +98F6=>bì +98F7=>jiě +98F8=>tāo +98F9=>liǔ +98FA=>cí +98FB=>tiè +98FC=>sì +98FD=>bǎo +98FE=>shì +98FF=>duò +9900=>hài +9901=>rèn +9902=>tiǎn +9903=>jiǎo +9904=>jiá +9905=>bǐng +9906=>yáo +9907=>tóng +9908=>cí +9909=>xiǎng +990A=>yǎng +990B=>juàn +990C=>ěr +990D=>yàn +990E=>le +990F=>xī +9910=>cān +9911=>bō +9912=>něi +9913=>è +9914=>bù +9915=>jùn +9916=>dòu +9917=>sù +9918=>yú +9919=>shì +991A=>yáo +991B=>hún +991C=>guǒ +991D=>shì +991E=>jiàn +991F=>zhuì +9920=>bǐng +9921=>xiàn +9922=>bù +9923=>yè +9924=>tán +9925=>fēi +9926=>zhāng +9927=>wèi +9928=>guǎn +9929=>è +992A=>nuǎn +992B=>yùn +992C=>hú +992D=>huáng +992E=>tiè +992F=>huì +9930=>jiān +9931=>hóu +9932=>ài +9933=>táng +9934=>fēn +9935=>wèi +9936=>gǔ +9937=>chā +9938=>sòng +9939=>táng +993A=>bó +993B=>gāo +993C=>xì +993D=>kuì +993E=>liù +993F=>sōu +9940=>táo +9941=>yè +9942=>yún +9943=>mó +9944=>táng +9945=>mán +9946=>bì +9947=>yù +9948=>xiū +9949=>jǐn +994A=>sǎn +994B=>kuì +994C=>zhuàn +994D=>shàn +994E=>chì +994F=>dàn +9950=>yì +9951=>jī +9952=>ráo +9953=>chēng +9954=>yōng +9955=>tāo +9956=>wèi +9957=>xiǎng +9958=>zhān +9959=>fēn +995A=>hài +995B=>méng +995C=>yàn +995D=>mó +995E=>chán +995F=>xiǎng +9960=>luó +9961=>zàn +9962=>náng +9963=>shí +9964=>dìng +9965=>jī +9966=>tuō +9967=>táng +9968=>tún +9969=>xì +996A=>rèn +996B=>yù +996C=>chì +996D=>fàn +996E=>yǐn +996F=>jiàn +9970=>shì +9971=>bǎo +9972=>sì +9973=>duò +9974=>yí +9975=>ěr +9976=>ráo +9977=>xiǎng +9978=>hé +9979=>le +997A=>jiǎo +997B=>xī +997C=>bǐng +997D=>bō +997E=>dòu +997F=>è +9980=>yú +9981=>něi +9982=>jùn +9983=>guǒ +9984=>hún +9985=>xiàn +9986=>guǎn +9987=>chā +9988=>kuì +9989=>gǔ +998A=>sōu +998B=>chán +998C=>yè +998D=>mó +998E=>bó +998F=>liú +9990=>xiū +9991=>jǐn +9992=>mán +9993=>sǎn +9994=>zhuàn +9995=>náng +9996=>shǒu +9997=>kuí +9998=>guó +9999=>xiāng +999A=>fén +999B=>bó +999C=>nǐ +999D=>bì +999E=>bó +999F=>tú +99A0=>hān +99A1=>fēi +99A2=>jiān +99A3=>ān +99A4=>ài +99A5=>fù +99A6=>xiān +99A7=>yūn +99A8=>xīn +99A9=>fén +99AA=>pīn +99AB=>xīn +99AC=>mǎ +99AD=>yù +99AE=>féng +99AF=>hàn +99B0=>dí +99B1=>tuó +99B2=>zhé +99B3=>chí +99B4=>xún +99B5=>zhù +99B6=>zhī +99B7=>pèi +99B8=>xìn +99B9=>rì +99BA=>sà +99BB=>yǔn +99BC=>wén +99BD=>zhí +99BE=>dàn +99BF=>lǘ +99C0=>yóu +99C1=>bó +99C2=>bǎo +99C3=>jué +99C4=>tuó +99C5=>yì +99C6=>qū +99C7=>pu +99C8=>qū +99C9=>jiōng +99CA=>pǒ +99CB=>zhāo +99CC=>yuān +99CD=>péi +99CE=>zhòu +99CF=>jù +99D0=>zhù +99D1=>nú +99D2=>jū +99D3=>pī +99D4=>zǎng +99D5=>jià +99D6=>líng +99D7=>zhěn +99D8=>tái +99D9=>fù +99DA=>yǎng +99DB=>shǐ +99DC=>bì +99DD=>tuo +99DE=>tuó +99DF=>sì +99E0=>liú +99E1=>mà +99E2=>pián +99E3=>táo +99E4=>zhì +99E5=>róng +99E6=>téng +99E7=>dòng +99E8=>xūn +99E9=>quān +99EA=>shēn +99EB=>jiōng +99EC=>ěr +99ED=>hài +99EE=>bó +99EF=>zhū +99F0=>yīn +99F1=>luò +99F2=>zhou +99F3=>dàn +99F4=>xiè +99F5=>liú +99F6=>jú +99F7=>sǒng +99F8=>qīn +99F9=>máng +99FA=>láng +99FB=>hàn +99FC=>tú +99FD=>xuān +99FE=>tuì +99FF=>jùn +9A00=>ě +9A01=>chěng +9A02=>xīng +9A03=>ái +9A04=>lù +9A05=>zhuī +9A06=>zhōu +9A07=>shè +9A08=>pián +9A09=>kūn +9A0A=>táo +9A0B=>lái +9A0C=>zōng +9A0D=>kè +9A0E=>qí +9A0F=>qí +9A10=>yàn +9A11=>fēi +9A12=>sāo +9A13=>yǎn +9A14=>gé +9A15=>yǎo +9A16=>wù +9A17=>piàn +9A18=>cōng +9A19=>piàn +9A1A=>qián +9A1B=>fēi +9A1C=>huáng +9A1D=>qián +9A1E=>huō +9A1F=>yú +9A20=>tí +9A21=>quán +9A22=>xiá +9A23=>zōng +9A24=>kuí +9A25=>róu +9A26=>sī +9A27=>guā +9A28=>tuó +9A29=>guī +9A2A=>sōu +9A2B=>qiān +9A2C=>chéng +9A2D=>zhì +9A2E=>liú +9A2F=>péng +9A30=>téng +9A31=>xí +9A32=>cǎo +9A33=>dú +9A34=>yàn +9A35=>yuán +9A36=>zōu +9A37=>sāo +9A38=>shàn +9A39=>lí +9A3A=>zhì +9A3B=>shuāng +9A3C=>lù +9A3D=>xí +9A3E=>luó +9A3F=>zhāng +9A40=>mò +9A41=>ào +9A42=>cān +9A43=>biāo +9A44=>cōng +9A45=>qū +9A46=>bì +9A47=>zhì +9A48=>yù +9A49=>xū +9A4A=>huá +9A4B=>bō +9A4C=>sù +9A4D=>xiāo +9A4E=>lín +9A4F=>zhàn +9A50=>dūn +9A51=>liú +9A52=>tuó +9A53=>céng +9A54=>diàn +9A55=>jiāo +9A56=>tiě +9A57=>yàn +9A58=>luó +9A59=>zhān +9A5A=>jīng +9A5B=>yì +9A5C=>yè +9A5D=>tuō +9A5E=>pīn +9A5F=>zhòu +9A60=>yàn +9A61=>lóng +9A62=>lǘ +9A63=>téng +9A64=>xiāng +9A65=>jì +9A66=>shuāng +9A67=>jú +9A68=>xí +9A69=>huān +9A6A=>lí +9A6B=>biāo +9A6C=>mǎ +9A6D=>yù +9A6E=>tuó +9A6F=>xún +9A70=>chí +9A71=>qū +9A72=>rì +9A73=>bó +9A74=>lǘ +9A75=>zǎng +9A76=>shǐ +9A77=>sì +9A78=>fù +9A79=>jū +9A7A=>zōu +9A7B=>zhù +9A7C=>tuo +9A7D=>nú +9A7E=>jià +9A7F=>yì +9A80=>dài +9A81=>xiāo +9A82=>mà +9A83=>yīn +9A84=>jiāo +9A85=>huá +9A86=>luò +9A87=>hài +9A88=>pián +9A89=>biāo +9A8A=>lí +9A8B=>chěng +9A8C=>yàn +9A8D=>xīng +9A8E=>qīn +9A8F=>jùn +9A90=>qí +9A91=>qí +9A92=>kè +9A93=>zhuī +9A94=>zōng +9A95=>sù +9A96=>cān +9A97=>piàn +9A98=>zhì +9A99=>kuí +9A9A=>sāo +9A9B=>wù +9A9C=>ào +9A9D=>liú +9A9E=>qiān +9A9F=>shàn +9AA0=>biāo +9AA1=>luó +9AA2=>cōng +9AA3=>chǎn +9AA4=>zhòu +9AA5=>jì +9AA6=>shuāng +9AA7=>xiāng +9AA8=>gǔ +9AA9=>wěi +9AAA=>wěi +9AAB=>wěi +9AAC=>yú +9AAD=>gàn +9AAE=>yì +9AAF=>āng +9AB0=>tóu +9AB1=>jiè +9AB2=>bào +9AB3=>bèi +9AB4=>cī +9AB5=>tǐ +9AB6=>dǐ +9AB7=>kū +9AB8=>hái +9AB9=>qiāo +9ABA=>hóu +9ABB=>kuà +9ABC=>gé +9ABD=>tuǐ +9ABE=>gěng +9ABF=>pián +9AC0=>bì +9AC1=>kē +9AC2=>qià +9AC3=>yú +9AC4=>suǐ +9AC5=>lóu +9AC6=>bó +9AC7=>xiāo +9AC8=>bǎng +9AC9=>bó +9ACA=>cī +9ACB=>kuān +9ACC=>bìn +9ACD=>mó +9ACE=>liáo +9ACF=>lóu +9AD0=>xiāo +9AD1=>dú +9AD2=>zāng +9AD3=>suǐ +9AD4=>tǐ +9AD5=>bìn +9AD6=>kuān +9AD7=>lú +9AD8=>gāo +9AD9=>gāo +9ADA=>qiào +9ADB=>kāo +9ADC=>qiǎo +9ADD=>láo +9ADE=>sào +9ADF=>biāo +9AE0=>kūn +9AE1=>kūn +9AE2=>dí +9AE3=>fǎng +9AE4=>xiū +9AE5=>rán +9AE6=>máo +9AE7=>dàn +9AE8=>kūn +9AE9=>bìn +9AEA=>fà +9AEB=>tiáo +9AEC=>pī +9AED=>zī +9AEE=>fà +9AEF=>rán +9AF0=>tì +9AF1=>bào +9AF2=>bì +9AF3=>máo +9AF4=>fú +9AF5=>ér +9AF6=>róng +9AF7=>qū +9AF8=>gōng +9AF9=>xiū +9AFA=>kuò +9AFB=>jì +9AFC=>péng +9AFD=>zhuā +9AFE=>shāo +9AFF=>suō +9B00=>tì +9B01=>lì +9B02=>bìn +9B03=>zōng +9B04=>dí +9B05=>péng +9B06=>sōng +9B07=>zhēng +9B08=>quán +9B09=>zōng +9B0A=>shùn +9B0B=>jiǎn +9B0C=>tuǒ +9B0D=>hú +9B0E=>là +9B0F=>jiū +9B10=>qí +9B11=>lián +9B12=>zhěn +9B13=>bìn +9B14=>péng +9B15=>mà +9B16=>sān +9B17=>mán +9B18=>mán +9B19=>sēng +9B1A=>xū +9B1B=>liè +9B1C=>qiān +9B1D=>qiān +9B1E=>náng +9B1F=>huán +9B20=>kuò +9B21=>níng +9B22=>bìn +9B23=>liè +9B24=>ráng +9B25=>dòu +9B26=>dòu +9B27=>nào +9B28=>hòng +9B29=>xì +9B2A=>dòu +9B2B=>hǎn +9B2C=>dòu +9B2D=>dòu +9B2E=>jiū +9B2F=>chàng +9B30=>yù +9B31=>yù +9B32=>gé +9B33=>yàn +9B34=>fǔ +9B35=>qín +9B36=>guī +9B37=>zōng +9B38=>liù +9B39=>guī +9B3A=>shāng +9B3B=>yù +9B3C=>guǐ +9B3D=>mèi +9B3E=>jì +9B3F=>qí +9B40=>gà +9B41=>kuí +9B42=>hún +9B43=>bá +9B44=>pò +9B45=>mèi +9B46=>xū +9B47=>yǎn +9B48=>xiāo +9B49=>liǎng +9B4A=>yù +9B4B=>tuí +9B4C=>qī +9B4D=>wǎng +9B4E=>liǎng +9B4F=>wèi +9B50=>gān +9B51=>chī +9B52=>piāo +9B53=>bì +9B54=>mó +9B55=>jǐ +9B56=>xū +9B57=>chǒu +9B58=>yǎn +9B59=>zhān +9B5A=>yú +9B5B=>dāo +9B5C=>rén +9B5D=>jié +9B5E=>ba +9B5F=>hóng +9B60=>tuō +9B61=>diào +9B62=>jǐ +9B63=>xù +9B64=>é +9B65=>è +9B66=>shā +9B67=>háng +9B68=>tún +9B69=>mò +9B6A=>jiè +9B6B=>shěn +9B6C=>bǎn +9B6D=>yuán +9B6E=>pí +9B6F=>lǔ +9B70=>wén +9B71=>hú +9B72=>lú +9B73=>zā +9B74=>fáng +9B75=>fén +9B76=>nà +9B77=>yóu +9B78=>pian +9B79=>mo +9B7A=>hé +9B7B=>xiá +9B7C=>qū +9B7D=>hán +9B7E=>pī +9B7F=>líng +9B80=>tuó +9B81=>bō +9B82=>qiú +9B83=>píng +9B84=>fú +9B85=>bì +9B86=>cǐ +9B87=>wèi +9B88=>jū +9B89=>diāo +9B8A=>bà +9B8B=>yóu +9B8C=>gǔn +9B8D=>pī +9B8E=>nián +9B8F=>xīng +9B90=>tái +9B91=>bào +9B92=>fù +9B93=>zhǎ +9B94=>jù +9B95=>gū +9B96=>shi +9B97=>dong +9B98=>dai +9B99=>tà +9B9A=>jié +9B9B=>shū +9B9C=>hòu +9B9D=>xiǎng +9B9E=>ér +9B9F=>àn +9BA0=>wéi +9BA1=>zhào +9BA2=>zhū +9BA3=>yìn +9BA4=>liè +9BA5=>luò +9BA6=>tóng +9BA7=>tǐ +9BA8=>yì +9BA9=>bìng +9BAA=>wěi +9BAB=>jiāo +9BAC=>kū +9BAD=>guī +9BAE=>xiān +9BAF=>gé +9BB0=>huí +9BB1=>lao +9BB2=>fu +9BB3=>kào +9BB4=>xiu +9BB5=>duó +9BB6=>jūn +9BB7=>tí +9BB8=>miǎn +9BB9=>shāo +9BBA=>zhǎ +9BBB=>suō +9BBC=>qīn +9BBD=>yú +9BBE=>něi +9BBF=>zhé +9BC0=>gǔn +9BC1=>gěng +9BC2=>su +9BC3=>wú +9BC4=>qiú +9BC5=>shān +9BC6=>pū +9BC7=>huàn +9BC8=>tiáo +9BC9=>lǐ +9BCA=>shā +9BCB=>shā +9BCC=>kào +9BCD=>méng +9BCE=>cheng +9BCF=>li +9BD0=>zou +9BD1=>xi +9BD2=>yǒng +9BD3=>ní +9BD4=>zī +9BD5=>qí +9BD6=>zhēng +9BD7=>xiǎng +9BD8=>něi +9BD9=>chún +9BDA=>jì +9BDB=>diāo +9BDC=>qiè +9BDD=>gù +9BDE=>zhǒu +9BDF=>dōng +9BE0=>lái +9BE1=>fèi +9BE2=>ní +9BE3=>yì +9BE4=>kūn +9BE5=>lù +9BE6=>jiù +9BE7=>chāng +9BE8=>jīng +9BE9=>lún +9BEA=>líng +9BEB=>zōu +9BEC=>lí +9BED=>měng +9BEE=>zōng +9BEF=>zhì +9BF0=>nián +9BF1=>hu +9BF2=>yu +9BF3=>di +9BF4=>shī +9BF5=>shēn +9BF6=>hǔn +9BF7=>tí +9BF8=>hóu +9BF9=>xīng +9BFA=>zhū +9BFB=>là +9BFC=>zōng +9BFD=>zéi +9BFE=>biān +9BFF=>biān +9C00=>huàn +9C01=>quán +9C02=>zéi +9C03=>wēi +9C04=>wēi +9C05=>yú +9C06=>chūn +9C07=>róu +9C08=>dié +9C09=>huáng +9C0A=>liàn +9C0B=>yǎn +9C0C=>qiū +9C0D=>qiū +9C0E=>jiǎn +9C0F=>bī +9C10=>è +9C11=>yáng +9C12=>fù +9C13=>sāi +9C14=>gǎn +9C15=>xiā +9C16=>tuǒ +9C17=>hú +9C18=>shi +9C19=>ruò +9C1A=>xuan +9C1B=>wēn +9C1C=>qiàn +9C1D=>hào +9C1E=>wū +9C1F=>fáng +9C20=>sāo +9C21=>liú +9C22=>mǎ +9C23=>shí +9C24=>shī +9C25=>guān +9C26=>zī +9C27=>téng +9C28=>tǎ +9C29=>yáo +9C2A=>é +9C2B=>yóng +9C2C=>qián +9C2D=>qí +9C2E=>wēn +9C2F=>ruò +9C30=>shen +9C31=>lián +9C32=>áo +9C33=>lè +9C34=>huī +9C35=>mǐn +9C36=>jì +9C37=>tiáo +9C38=>qū +9C39=>jiān +9C3A=>shēn +9C3B=>mán +9C3C=>xí +9C3D=>qiú +9C3E=>biào +9C3F=>jì +9C40=>jì +9C41=>zhú +9C42=>jiāng +9C43=>xiū +9C44=>zhuān +9C45=>yōng +9C46=>zhāng +9C47=>kāng +9C48=>xuě +9C49=>biē +9C4A=>yù +9C4B=>qū +9C4C=>xiàng +9C4D=>bō +9C4E=>jiǎo +9C4F=>xún +9C50=>sù +9C51=>huáng +9C52=>zūn +9C53=>shàn +9C54=>shàn +9C55=>fān +9C56=>guì +9C57=>lín +9C58=>xún +9C59=>miáo +9C5A=>xǐ +9C5B=>zeng +9C5C=>xiang +9C5D=>fèn +9C5E=>guān +9C5F=>hòu +9C60=>kuài +9C61=>zéi +9C62=>sāo +9C63=>zhān +9C64=>gǎn +9C65=>guì +9C66=>yìng +9C67=>lǐ +9C68=>cháng +9C69=>lei +9C6A=>shu +9C6B=>ai +9C6C=>rú +9C6D=>jì +9C6E=>xù +9C6F=>hù +9C70=>shu +9C71=>lì +9C72=>liè +9C73=>lì +9C74=>miè +9C75=>zhēn +9C76=>xiǎng +9C77=>è +9C78=>lú +9C79=>guàn +9C7A=>lí +9C7B=>xiān +9C7C=>yú +9C7D=>dāo +9C7E=>jǐ +9C7F=>yóu +9C80=>tún +9C81=>lǔ +9C82=>fáng +9C83=>bā +9C84=>hé +9C85=>bà +9C86=>píng +9C87=>nián +9C88=>lú +9C89=>yóu +9C8A=>zhǎ +9C8B=>fù +9C8C=>bà +9C8D=>bào +9C8E=>hòu +9C8F=>pí +9C90=>tái +9C91=>guī +9C92=>jié +9C93=>kǎo +9C94=>wěi +9C95=>ér +9C96=>tóng +9C97=>zéi +9C98=>hòu +9C99=>kuài +9C9A=>jì +9C9B=>jiāo +9C9C=>xiān +9C9D=>zhǎ +9C9E=>xiǎng +9C9F=>xún +9CA0=>gěng +9CA1=>lí +9CA2=>lián +9CA3=>jiān +9CA4=>lǐ +9CA5=>shí +9CA6=>tiáo +9CA7=>gǔn +9CA8=>shā +9CA9=>huàn +9CAA=>jūn +9CAB=>jì +9CAC=>yǒng +9CAD=>qīng +9CAE=>líng +9CAF=>qí +9CB0=>zōu +9CB1=>fēi +9CB2=>kūn +9CB3=>chāng +9CB4=>gù +9CB5=>ní +9CB6=>nián +9CB7=>diāo +9CB8=>jīng +9CB9=>shēn +9CBA=>shī +9CBB=>zī +9CBC=>fèn +9CBD=>dié +9CBE=>bī +9CBF=>cháng +9CC0=>tí +9CC1=>wēn +9CC2=>wēi +9CC3=>sāi +9CC4=>è +9CC5=>qiū +9CC6=>fù +9CC7=>huáng +9CC8=>quán +9CC9=>jiāng +9CCA=>biān +9CCB=>sāo +9CCC=>áo +9CCD=>qí +9CCE=>tǎ +9CCF=>guān +9CD0=>yáo +9CD1=>páng +9CD2=>jiān +9CD3=>lè +9CD4=>biào +9CD5=>xuě +9CD6=>biē +9CD7=>mán +9CD8=>mǐn +9CD9=>yōng +9CDA=>wèi +9CDB=>xí +9CDC=>guì +9CDD=>shàn +9CDE=>lín +9CDF=>zūn +9CE0=>hù +9CE1=>gǎn +9CE2=>lǐ +9CE3=>zhān +9CE4=>guǎn +9CE5=>niǎo +9CE6=>yǐ +9CE7=>fú +9CE8=>lì +9CE9=>jiū +9CEA=>bú +9CEB=>yàn +9CEC=>fǔ +9CED=>diāo +9CEE=>jī +9CEF=>fèng +9CF0=>ru +9CF1=>gān +9CF2=>shī +9CF3=>fèng +9CF4=>míng +9CF5=>bǎo +9CF6=>yuān +9CF7=>zhī +9CF8=>hù +9CF9=>qín +9CFA=>fū +9CFB=>bān +9CFC=>wén +9CFD=>jiān +9CFE=>shī +9CFF=>yù +9D00=>fǒu +9D01=>yāo +9D02=>jué +9D03=>jué +9D04=>pǐ +9D05=>huān +9D06=>zhèn +9D07=>bǎo +9D08=>yàn +9D09=>yā +9D0A=>zhèng +9D0B=>fāng +9D0C=>fèng +9D0D=>wén +9D0E=>ōu +9D0F=>dài +9D10=>gē +9D11=>rú +9D12=>líng +9D13=>miè +9D14=>fú +9D15=>tuó +9D16=>mín +9D17=>lì +9D18=>biǎn +9D19=>zhì +9D1A=>gē +9D1B=>yuān +9D1C=>cí +9D1D=>qú +9D1E=>xiāo +9D1F=>chī +9D20=>dàn +9D21=>jū +9D22=>yǎo +9D23=>gū +9D24=>dōng +9D25=>yù +9D26=>yāng +9D27=>ròng +9D28=>yā +9D29=>tiě +9D2A=>yù +9D2B=>tian +9D2C=>yīng +9D2D=>duī +9D2E=>wū +9D2F=>ér +9D30=>guā +9D31=>ài +9D32=>zhī +9D33=>yàn +9D34=>héng +9D35=>xiāo +9D36=>jiá +9D37=>liè +9D38=>zhū +9D39=>yáng +9D3A=>tí +9D3B=>hóng +9D3C=>luò +9D3D=>rú +9D3E=>móu +9D3F=>gē +9D40=>rén +9D41=>jiāo +9D42=>xiū +9D43=>zhōu +9D44=>zhī +9D45=>luò +9D46=>heng +9D47=>nian +9D48=>e +9D49=>luán +9D4A=>jiá +9D4B=>jì +9D4C=>tú +9D4D=>huān +9D4E=>tuǒ +9D4F=>bǔ +9D50=>wú +9D51=>juān +9D52=>yù +9D53=>bó +9D54=>jùn +9D55=>xùn +9D56=>bī +9D57=>xī +9D58=>jùn +9D59=>jú +9D5A=>tū +9D5B=>jīng +9D5C=>tí +9D5D=>é +9D5E=>é +9D5F=>kuáng +9D60=>hú +9D61=>wǔ +9D62=>shēn +9D63=>lài +9D64=>jiao +9D65=>pan +9D66=>lù +9D67=>pí +9D68=>shū +9D69=>fú +9D6A=>ān +9D6B=>zhuó +9D6C=>péng +9D6D=>qín +9D6E=>qiān +9D6F=>bēi +9D70=>diāo +9D71=>lù +9D72=>que +9D73=>jiān +9D74=>jú +9D75=>tù +9D76=>yā +9D77=>yuān +9D78=>qí +9D79=>lí +9D7A=>yè +9D7B=>zhuī +9D7C=>kōng +9D7D=>duò +9D7E=>kūn +9D7F=>shēng +9D80=>qí +9D81=>jīng +9D82=>yì +9D83=>yì +9D84=>jīng +9D85=>zī +9D86=>lái +9D87=>dōng +9D88=>qī +9D89=>chun +9D8A=>gēng +9D8B=>jū +9D8C=>jué +9D8D=>yi +9D8E=>zun +9D8F=>jī +9D90=>shù +9D91=>yīng +9D92=>chì +9D93=>miáo +9D94=>róu +9D95=>ān +9D96=>qiū +9D97=>tí +9D98=>hú +9D99=>tí +9D9A=>è +9D9B=>jiē +9D9C=>máo +9D9D=>fú +9D9E=>chūn +9D9F=>tú +9DA0=>yǎn +9DA1=>hé +9DA2=>yuán +9DA3=>piān +9DA4=>kūn +9DA5=>méi +9DA6=>hú +9DA7=>yīng +9DA8=>chuàn +9DA9=>wù +9DAA=>jú +9DAB=>dong +9DAC=>cāng +9DAD=>fǎng +9DAE=>hè +9DAF=>yīng +9DB0=>yuán +9DB1=>xiān +9DB2=>wēng +9DB3=>shī +9DB4=>hè +9DB5=>chú +9DB6=>táng +9DB7=>xiá +9DB8=>ruò +9DB9=>liú +9DBA=>jí +9DBB=>gú +9DBC=>jiān +9DBD=>sǔn +9DBE=>hàn +9DBF=>cí +9DC0=>cí +9DC1=>yì +9DC2=>yào +9DC3=>yàn +9DC4=>jī +9DC5=>lì +9DC6=>tián +9DC7=>kòu +9DC8=>tī +9DC9=>tī +9DCA=>yì +9DCB=>tú +9DCC=>mǎ +9DCD=>jiāo +9DCE=>gāo +9DCF=>tián +9DD0=>chén +9DD1=>jí +9DD2=>tuán +9DD3=>zhè +9DD4=>áo +9DD5=>yǎo +9DD6=>yī +9DD7=>ōu +9DD8=>chì +9DD9=>zhì +9DDA=>liù +9DDB=>yōng +9DDC=>lǘ +9DDD=>bì +9DDE=>shuāng +9DDF=>zhuó +9DE0=>yú +9DE1=>wú +9DE2=>jué +9DE3=>yín +9DE4=>tí +9DE5=>sī +9DE6=>jiāo +9DE7=>yì +9DE8=>huá +9DE9=>bì +9DEA=>yīng +9DEB=>sù +9DEC=>huáng +9DED=>fán +9DEE=>jiāo +9DEF=>liáo +9DF0=>yàn +9DF1=>gāo +9DF2=>jiù +9DF3=>xián +9DF4=>xián +9DF5=>tú +9DF6=>mǎi +9DF7=>zūn +9DF8=>yù +9DF9=>yīng +9DFA=>lù +9DFB=>tuán +9DFC=>xián +9DFD=>xué +9DFE=>yì +9DFF=>pì +9E00=>chǔ +9E01=>luó +9E02=>xī +9E03=>yí +9E04=>jī +9E05=>zé +9E06=>yú +9E07=>zhān +9E08=>yè +9E09=>yáng +9E0A=>pì +9E0B=>níng +9E0C=>hù +9E0D=>mí +9E0E=>yīng +9E0F=>méng +9E10=>dí +9E11=>yuè +9E12=>yù +9E13=>lěi +9E14=>bào +9E15=>lú +9E16=>hè +9E17=>lóng +9E18=>shuāng +9E19=>yuè +9E1A=>yīng +9E1B=>guàn +9E1C=>qú +9E1D=>lí +9E1E=>luán +9E1F=>niǎo +9E20=>jiū +9E21=>jī +9E22=>yuān +9E23=>míng +9E24=>shī +9E25=>ōu +9E26=>yā +9E27=>cāng +9E28=>bǎo +9E29=>zhèn +9E2A=>gū +9E2B=>dōng +9E2C=>lú +9E2D=>yā +9E2E=>xiāo +9E2F=>yāng +9E30=>líng +9E31=>chī +9E32=>qú +9E33=>yuān +9E34=>xué +9E35=>tuó +9E36=>sī +9E37=>zhì +9E38=>ér +9E39=>guā +9E3A=>xiū +9E3B=>héng +9E3C=>zhōu +9E3D=>gē +9E3E=>luán +9E3F=>hóng +9E40=>wú +9E41=>bó +9E42=>lí +9E43=>juān +9E44=>gǔ +9E45=>é +9E46=>yù +9E47=>xián +9E48=>tí +9E49=>wǔ +9E4A=>que +9E4B=>miáo +9E4C=>ān +9E4D=>kūn +9E4E=>bēi +9E4F=>péng +9E50=>qiān +9E51=>chun +9E52=>gēng +9E53=>yuān +9E54=>sù +9E55=>hú +9E56=>hé +9E57=>è +9E58=>gǔ +9E59=>qiū +9E5A=>cí +9E5B=>méi +9E5C=>wù +9E5D=>yì +9E5E=>yào +9E5F=>wēng +9E60=>liú +9E61=>jí +9E62=>yì +9E63=>jiān +9E64=>hè +9E65=>yī +9E66=>yīng +9E67=>zhè +9E68=>liù +9E69=>liáo +9E6A=>jiāo +9E6B=>jiù +9E6C=>yù +9E6D=>lù +9E6E=>huán +9E6F=>zhān +9E70=>yīng +9E71=>hù +9E72=>méng +9E73=>guàn +9E74=>shuāng +9E75=>lǔ +9E76=>jīn +9E77=>líng +9E78=>jiǎn +9E79=>xián +9E7A=>cuó +9E7B=>jiǎn +9E7C=>jiǎn +9E7D=>yán +9E7E=>cuó +9E7F=>lù +9E80=>yōu +9E81=>cū +9E82=>jǐ +9E83=>páo +9E84=>cū +9E85=>páo +9E86=>zhù +9E87=>jūn +9E88=>zhǔ +9E89=>jiān +9E8A=>mí +9E8B=>mí +9E8C=>yǔ +9E8D=>liú +9E8E=>chén +9E8F=>jūn +9E90=>lín +9E91=>ní +9E92=>qí +9E93=>lù +9E94=>jiù +9E95=>jūn +9E96=>jīng +9E97=>lì +9E98=>xiāng +9E99=>xián +9E9A=>jiā +9E9B=>mí +9E9C=>lì +9E9D=>shè +9E9E=>zhāng +9E9F=>lín +9EA0=>jīng +9EA1=>qí +9EA2=>líng +9EA3=>yán +9EA4=>cū +9EA5=>mài +9EA6=>mài +9EA7=>hé +9EA8=>chǎo +9EA9=>fū +9EAA=>miàn +9EAB=>miǎn +9EAC=>fū +9EAD=>pào +9EAE=>qù +9EAF=>qū +9EB0=>móu +9EB1=>fū +9EB2=>xiàn +9EB3=>lái +9EB4=>qū +9EB5=>miàn +9EB6=>chi +9EB7=>fēng +9EB8=>fū +9EB9=>qū +9EBA=>miàn +9EBB=>má +9EBC=>me +9EBD=>mó +9EBE=>huī +9EBF=>mo +9EC0=>zōu +9EC1=>nún +9EC2=>fén +9EC3=>huáng +9EC4=>huáng +9EC5=>jīn +9EC6=>guāng +9EC7=>tiān +9EC8=>tǒu +9EC9=>hóng +9ECA=>huà +9ECB=>kuàng +9ECC=>hóng +9ECD=>shǔ +9ECE=>lí +9ECF=>nián +9ED0=>chī +9ED1=>hēi +9ED2=>hēi +9ED3=>yì +9ED4=>qián +9ED5=>dǎn +9ED6=>xì +9ED7=>tūn +9ED8=>mò +9ED9=>mò +9EDA=>qián +9EDB=>dài +9EDC=>chù +9EDD=>yǒu +9EDE=>diǎn +9EDF=>yī +9EE0=>xiá +9EE1=>yǎn +9EE2=>qū +9EE3=>měi +9EE4=>yǎn +9EE5=>qíng +9EE6=>yuè +9EE7=>lí +9EE8=>dǎng +9EE9=>dú +9EEA=>cǎn +9EEB=>yān +9EEC=>yán +9EED=>yǎn +9EEE=>dǎn +9EEF=>àn +9EF0=>zhěn +9EF1=>dài +9EF2=>cǎn +9EF3=>yī +9EF4=>méi +9EF5=>zhǎn +9EF6=>yǎn +9EF7=>dú +9EF8=>lú +9EF9=>zhǐ +9EFA=>fěn +9EFB=>fú +9EFC=>fǔ +9EFD=>miǎn +9EFE=>miǎn +9EFF=>yuán +9F00=>cù +9F01=>qù +9F02=>cháo +9F03=>wā +9F04=>zhū +9F05=>zhī +9F06=>méng +9F07=>áo +9F08=>biē +9F09=>tuó +9F0A=>bì +9F0B=>yuán +9F0C=>cháo +9F0D=>tuó +9F0E=>dǐng +9F0F=>mì +9F10=>nài +9F11=>dǐng +9F12=>zī +9F13=>gǔ +9F14=>gǔ +9F15=>dōng +9F16=>fén +9F17=>táo +9F18=>yuān +9F19=>pí +9F1A=>chāng +9F1B=>gāo +9F1C=>qì +9F1D=>yuān +9F1E=>tāng +9F1F=>tēng +9F20=>shǔ +9F21=>shǔ +9F22=>fén +9F23=>fèi +9F24=>wén +9F25=>bá +9F26=>diāo +9F27=>tuó +9F28=>zhōng +9F29=>qú +9F2A=>shēng +9F2B=>shí +9F2C=>yòu +9F2D=>shí +9F2E=>tíng +9F2F=>wú +9F30=>niàn +9F31=>jīng +9F32=>hún +9F33=>jú +9F34=>yǎn +9F35=>tū +9F36=>sī +9F37=>xī +9F38=>xiàn +9F39=>yǎn +9F3A=>léi +9F3B=>bí +9F3C=>yǎo +9F3D=>qiú +9F3E=>hān +9F3F=>wù +9F40=>wù +9F41=>hōu +9F42=>xiè +9F43=>è +9F44=>zhā +9F45=>xiù +9F46=>wèng +9F47=>zhā +9F48=>nòng +9F49=>nàng +9F4A=>qí +9F4B=>zhāi +9F4C=>jì +9F4D=>zī +9F4E=>jī +9F4F=>jī +9F50=>qí +9F51=>jī +9F52=>chǐ +9F53=>chèn +9F54=>chèn +9F55=>hé +9F56=>yá +9F57=>yín +9F58=>xiè +9F59=>bāo +9F5A=>zé +9F5B=>xiè +9F5C=>chái +9F5D=>chī +9F5E=>yǎn +9F5F=>jǔ +9F60=>tiáo +9F61=>líng +9F62=>líng +9F63=>chū +9F64=>quán +9F65=>xiè +9F66=>kěn +9F67=>niè +9F68=>jiù +9F69=>yǎo +9F6A=>chuò +9F6B=>kǔn +9F6C=>yǔ +9F6D=>chǔ +9F6E=>yǐ +9F6F=>ní +9F70=>zé +9F71=>zōu +9F72=>qǔ +9F73=>yǔn +9F74=>yǎn +9F75=>óu +9F76=>è +9F77=>wò +9F78=>yì +9F79=>cī +9F7A=>zōu +9F7B=>diān +9F7C=>chǔ +9F7D=>jìn +9F7E=>yà +9F7F=>chǐ +9F80=>chèn +9F81=>hé +9F82=>yín +9F83=>jǔ +9F84=>líng +9F85=>bāo +9F86=>tiáo +9F87=>zī +9F88=>kěn +9F89=>yǔ +9F8A=>chuò +9F8B=>qǔ +9F8C=>wò +9F8D=>lóng +9F8E=>páng +9F8F=>gōng +9F90=>páng +9F91=>yǎn +9F92=>lóng +9F93=>lǒng +9F94=>gōng +9F95=>kān +9F96=>dá +9F97=>líng +9F98=>dá +9F99=>lóng +9F9A=>gōng +9F9B=>kān +9F9C=>guī +9F9D=>qiū +9F9E=>biē +9F9F=>guī +9FA0=>yuè +9FA1=>chuì +9FA2=>hé +9FA3=>jué +9FA4=>xié +9FA5=>yù +9FC3=>shǎn +20000=>hē +20001=>qī +20003=>qiě +20005=>hài +20009=>qiū +2000A=>cāo +2000D=>shì +20013=>sī +20014=>jué +2001B=>yù +2001D=>kōng +20022=>zī +20026=>xíng +20031=>mǒu +20037=>jī +20038=>yè +20039=>jūn +2003C=>qián +2003D=>lù +20049=>chū +20057=>shì +20060=>qiè +20065=>gǎ +2006D=>qí +20077=>chǎn +20084=>huān +20086=>yì +20087=>zuǒ +20088=>jié +20091=>zōu +20094=>zǐ +2009F=>jīn +200A2=>pài +200A4=>duī +200A5=>cóng +200A7=>shèn +200B8=>huáng +200CA=>yǐn +200CC=>gǔn +200D6=>jiū +200EB=>shēn +200FA=>jiù +20105=>yè +20109=>dòng +2010C=>jué +2010D=>jié +2010F=>diǎo +20111=>jué +20112=>chuí +20116=>líng +2011A=>tīng +20123=>gèn +2012E=>yà +20131=>yí +2013F=>wéi +20142=>jié +2014C=>yí +20157=>diè +2015A=>qí +2016C=>bāo +20171=>xiè +20179=>zhàng +2018C=>yōng +20190=>xù +20199=>diè +2019B=>dān +2019F=>wěi +201A3=>guǎ +201A9=>fàn +201AE=>mò +201B1=>xī +201B2=>yǎn +201B5=>ní +201B6=>dàn +201CB=>dǎn +201CF=>tāo +201D2=>gōng +201D7=>kuā +201D8=>chù +201EF=>qù +201F1=>mò +201F3=>shī +201F5=>gǎn +201F7=>shēng +20201=>tuō +20205=>shōu +2020A=>niě +20224=>yùn +20225=>guǎ +2022C=>xiāo +2022D=>láo +20230=>dàn +20231=>suō +20235=>mǎng +20236=>yí +20238=>tè +2023A=>bì +20242=>tà +20257=>luò +20262=>xǐ +20263=>hūn +20264=>dá +20267=>jù +20269=>dú +2026C=>ǎn +20289=>mèi +2028C=>rán +2028E=>ái +2028F=>yù +20292=>jiàn +20294=>qì +2029F=>mǐn +202A3=>zhòu +202A4=>zhì +202A5=>zhǒng +202A6=>nǎo +202A7=>bìng +202A9=>zhuàn +202AA=>shù +202AB=>xùn +202AC=>jué +202AD=>qiǎn +202B0=>guǎ +202B2=>tū +202B6=>yìng +202B7=>zhì +202BE=>kuí +202C6=>chèn +202D6=>liàn +202D7=>yā +202DC=>guò +202DD=>miǎo +202DE=>shé +202DF=>yǔ +202E1=>sì +202E2=>sǒu +202E4=>zhì +202E7=>qiē +202E9=>fù +202EC=>jú +202ED=>bèi +202EF=>bì +202F2=>suǒ +202F5=>qiǎn +202F6=>mǐng +202F7=>chǎn +202FA=>sāo +202FB=>jī +20315=>gòng +20316=>qióng +2031A=>ròng +2031E=>sǒu +2031F=>sǒu +20320=>yáo +2032A=>chōu +2032D=>shuài +2032E=>zhē +2032F=>lì +20330=>gài +20331=>suī +20332=>zhān +20334=>zhuàng +2033D=>fù +20343=>jī +20344=>dōu +20357=>huì +2035A=>jiǎn +2035B=>yǎn +2035C=>zhì +20368=>měi +20369=>yào +2036A=>dī +2036B=>yí +2036F=>bié +20372=>qú +20373=>yì +20375=>yàng +20379=>zhá +2037D=>shà +20399=>lái +203AE=>jué +203B0=>qī +203B3=>yú +203B6=>zǎi +203B7=>sà +203B8=>sè +203BB=>dùn +203BF=>jiě +203C0=>kē +203C3=>yuē +203C7=>jiǎn +203C8=>yáo +203D3=>xiān +203D5=>xiào +203D6=>qiāo +203DA=>yù +203DB=>qú +203E1=>xiān +203E2=>luò +203E4=>guǎng +203E7=>chēng +203E8=>chuǎng +203E9=>yí +203EB=>zhěng +203ED=>zòng +203EE=>duì +203F0=>zhǎi +203FF=>fěi +20400=>yí +20401=>méng +20408=>biān +20409=>jié +2040A=>shù +2040B=>liáo +2040C=>bǐ +2040D=>sú +20411=>dì +20421=>bèi +20422=>wèn +20427=>méng +20429=>chǎn +20435=>dǎo +2043A=>pín +2043B=>jiǎn +2043C=>lìn +2043D=>guì +2043E=>qī +2043F=>hōng +20443=>jí +20444=>xiè +20445=>zhēng +20446=>chǎn +20450=>yáo +20451=>chǎn +20458=>diān +20459=>chòng +2045A=>néi +2045B=>néi +2045E=>zhài +2045F=>biān +20461=>chǎn +2046A=>xiāo +2046F=>cù +20470=>xīn +20471=>jǐng +20472=>qiān +20474=>qīng +20479=>gǔ +20484=>wù +2049C=>yuǎn +2049D=>bǐng +204A2=>wán +204B0=>niǎo +204B5=>liàn +204B8=>rǎo +204BE=>fàn +204BF=>dí +204CA=>huī +204CB=>yì +204CC=>xián +204D6=>lán +204D7=>fù +204D9=>xiòng +204DC=>liǎng +204DD=>tāo +204DE=>jí +204E2=>jiè +204E3=>zhá +204E4=>shī +204EA=>qí +204EB=>biǎn +204ED=>lǎn +204EE=>lǐn +204F6=>zhì +204F7=>bì +204F8=>shèng +204FD=>shèng +204FF=>qín +20502=>biāo +20503=>xī +20509=>juàn +2050B=>jī +2050D=>xī +2050E=>qǐn +20511=>hài +20515=>lún +20520=>yuè +20528=>lián +2052F=>bān +20532=>héng +20536=>qī +2053A=>qiān +2053B=>zhèng +2053C=>mǎo +20541=>cóng +20544=>nà +2054A=>tǐng +2054C=>zōng +20555=>jiōng +20556=>zhǎo +2055F=>niǎn +20560=>chéng +20563=>qià +20566=>yù +20567=>jiǎo +2056D=>zhào +20573=>dí +20574=>jiū +20578=>suǐ +2057B=>yāo +2057F=>wāng +20582=>liáo +20584=>tóng +20586=>mèng +2058B=>yǒu +20593=>sī +2059B=>lòu +2059F=>yīn +205A5=>chǒng +205AB=>gǎn +205AC=>jiū +205B6=>qìn +205B7=>jiǒng +205B9=>xié +205C2=>hè +205C6=>tāo +205C8=>qiú +205C9=>xié +205CA=>jìng +205CB=>niǎn +205CC=>jìng +205CF=>jí +205D8=>tiǎn +205DA=>cuì +205DB=>dié +205DD=>qǐng +205E5=>pìng +205E6=>píng +205E8=>dié +205E9=>lòu +205F3=>liǎn +205F4=>hán +205F5=>pāng +205F6=>táng +205FA=>yí +205FB=>xuán +205FC=>suò +205FD=>liú +205FE=>shuǎng +205FF=>shèn +20601=>bù +20602=>sōu +20605=>qín +20606=>shěn +2060A=>nòng +2060B=>tǐng +2060C=>jiāng +20615=>xī +20616=>zhì +2061D=>lài +2061E=>lì +2061F=>lì +20622=>hé +20623=>jiào +20625=>yán +20627=>shū +2062A=>shǐ +20631=>zhěn +20633=>yōu +2063A=>suò +2063B=>wú +20641=>cháng +20642=>cóng +20646=>jù +2064E=>shū +20654=>jiù +20655=>wéi +2065E=>huò +20664=>jiē +2066C=>zǎo +20676=>ǒu +2067C=>guǎ +20683=>háo +20684=>lǐ +20685=>zhì +20686=>xiàn +20689=>bū +2068A=>chàng +20693=>yūn +20694=>hé +2069C=>tāo +206A0=>biāo +206A5=>diāo +206A7=>èr +206A8=>jiū +206AD=>dì +206AE=>yì +206AF=>kūn +206B1=>zhé +206B3=>kuò +206B4=>zhōu +206B5=>jù +206B9=>shàn +206BA=>shà +206BB=>diāo +206BC=>bān +206BD=>jī +206C0=>zhōng +206C3=>yí +206C5=>kōu +206C6=>wū +206CA=>gē +206CB=>bā +206CE=>gōu +206D1=>xián +206D2=>guā +206D3=>liǔ +206D4=>chǐ +206D5=>guāi +206D6=>chuān +206D8=>lí +206D9=>cù +206DA=>shuā +206E1=>bǐ +206E5=>bǐng +206E6=>lì +206E9=>jiǔ +206EA=>tiāo +206EB=>duǒ +206ED=>yān +206EE=>quān +206F1=>liè +206F3=>kè +206F5=>gēn +206F6=>zhēn +206F8=>fén +20701=>yí +20703=>jiù +20704=>xù +20705=>jiǎo +20708=>lǜ +20709=>jiǔ +2070B=>chǒu +2070E=>xiàn +20710=>kuài +20711=>duì +20716=>luō +20717=>xī +20718=>qìn +20719=>bù +20724=>qià +20731=>pī +20732=>yā +20733=>bēng +20734=>guǒ +20735=>guā +20739=>jú +2073C=>qiā +2073E=>jué +20744=>lì +20750=>huā +20751=>jiāo +20758=>qià +2075A=>zhá +2075B=>qiā +2075D=>zhé +2075E=>chā +2075F=>yǐng +20762=>yān +20764=>chōng +20768=>chǐ +2076A=>wān +2076C=>sōu +20772=>kǎn +20773=>yuán +2077D=>chóu +2077F=>suǒ +20780=>tū +20783=>zhé +20784=>tī +20786=>wū +20788=>dā +20789=>lì +2078A=>chā +20795=>róng +20796=>gòng +20797=>què +20799=>lí +2079E=>tāo +207A4=>lì +207A7=>mí +207A9=>chì +207AC=>gùn +207AD=>lóu +207AE=>chuǎng +207AF=>suǒ +207B0=>jiǎo +207B1=>jìn +207B5=>fá +207B6=>zhāi +207BE=>jìn +207BF=>cuì +207C2=>cèng +207C3=>zǔn +207C5=>zhào +207C8=>piē +207C9=>zhǎn +207CA=>xī +207CB=>yào +207CC=>fǔ +207CD=>chōng +207D3=>cuì +207D7=>guā +207E3=>jī +207E6=>sè +207E7=>zhān +207E8=>lìng +207E9=>sè +207EA=>yè +207F0=>jū +207F6=>tū +207FA=>rú +207FB=>zé +207FC=>huán +20801=>xiǎn +20803=>qiān +20804=>zhào +2080B=>cán +2080E=>kuò +2080F=>lì +20810=>róu +20814=>dú +20817=>liè +2081C=>yīng +2081D=>lì +20820=>dú +20822=>líng +2082A=>wān +2082F=>dié +20833=>jiū +20835=>lì +20836=>kū +20837=>kēng +20839=>zhěn +20840=>hè +20842=>bì +20844=>pī +2084A=>hāng +20851=>zhuó +20852=>duǐ +20854=>yì +2085C=>kè +2085D=>yì +2085E=>mò +20861=>cán +20863=>gěng +20864=>kè +20865=>shì +2086D=>líng +2086E=>bēng +20871=>duàn +20876=>juān +20877=>nǎo +20878=>zǐ +2087B=>zòng +20883=>táng +20886=>xiá +20887=>hàn +2088C=>lüè +2088D=>qián +20893=>mò +20894=>ōu +20895=>háo +20899=>zhá +2089A=>juàn +2089B=>cóng +208A0=>lì +208A1=>zhá +208A2=>yǒu +208A3=>diàn +208A4=>jué +208A5=>bèi +208A9=>yǎo +208AA=>piē +208B1=>jìn +208B2=>kǎi +208B3=>sè +208B4=>yǎng +208B5=>jìn +208B9=>kè +208C4=>chān +208C7=>niǎn +208C9=>wàn +208CA=>lǜ +208D0=>yún +208D1=>yāo +208D2=>bāo +208D5=>jūn +208D6=>xuán +208D8=>zhōu +208E0=>kuì +208E1=>fèng +208EA=>qú +208EB=>shào +208EC=>sǔn +208F0=>dū +208F2=>kuǎi +208F3=>pào +208FA=>bào +208FE=>fù +208FF=>jiù +20900=>rán +20904=>jū +2090A=>qióng +2090D=>zhōu +2090E=>huà +2090F=>bǎo +20915=>yí +20917=>yí +20918=>yí +2091D=>mào +20926=>ruǎn +2092B=>cí +2092E=>hán +20930=>cóng +20934=>xì +20939=>quán +2093A=>tiáo +2093C=>diào +2093E=>hán +20947=>yě +2094D=>ē +2094E=>wéi +20950=>cāng +20951=>diào +20955=>è +20956=>dì +20958=>suǎn +20959=>quán +2095C=>è +2095D=>ōu +2095E=>xuán +20962=>wǔ +20966=>yì +20968=>móu +20970=>hū +20974=>hán +2097F=>shí +20983=>sà +20988=>bì +2098A=>hán +2098B=>jìng +2098E=>qìn +2098F=>cuó +20990=>cì +20992=>bān +20997=>duī +2099C=>xì +209A7=>zhī +209A8=>luàn +209AA=>hū +209AB=>jí +209AC=>guāi +209B2=>pāng +209C0=>zhū +209C5=>bǐ +209C7=>yú +209D2=>qǐ +209D5=>hé +209D6=>chǔ +209D9=>shào +209DA=>chì +209DB=>bó +209DF=>réng +209E0=>yóu +209E4=>nǎi +209E9=>huì +209EA=>tiáo +209EB=>bǎn +209F0=>xū +209F4=>yóu +209F5=>chì +209FF=>héng +20A03=>wài +20A06=>xiè +20A0A=>jué +20A0C=>suī +20A0D=>qīng +20A0E=>zhuàn +20A15=>jì +20A18=>bì +20A1A=>xī +20A20=>jí +20A22=>jùn +20A25=>liáo +20A26=>yōu +20A2D=>jú +20A32=>yuè +20A35=>bàng +20A38=>pí +20A3B=>zè +20A3E=>yì +20A3F=>dǐ +20A42=>qiè +20A44=>suǒ +20A46=>cì +20A48=>zhù +20A49=>yuè +20A4F=>jiāo +20A54=>shí +20A57=>yí +20A58=>xiá +20A60=>yuán +20A65=>guó +20A67=>kè +20A6A=>cuì +20A6B=>yì +20A75=>lì +20A77=>diǎn +20A7A=>xī +20A7F=>bì +20A82=>biǎn +20A83=>méi +20A84=>lì +20A87=>sǒu +20A90=>liú +20A91=>guì +20A92=>kè +20A97=>yí +20A99=>xǐ +20A9A=>yín +20A9F=>kè +20AA3=>shè +20AA7=>wǒ +20AAE=>pì +20AB6=>yuè +20AB7=>hóng +20ABA=>lì +20ABB=>fù +20AC3=>jué +20AC4=>xiān +20AC9=>diān +20ACC=>lì +20AD3=>tū +20AD8=>jiān +20ADB=>bǎi +20ADC=>dì +20ADD=>zhǎng +20AE3=>yù +20AE8=>duì +20AED=>cān +20AEE=>tú +20AF6=>tān +20AF7=>jí +20AF8=>qí +20AF9=>shàn +20AFA=>nián +20B06=>guàn +20B08=>bǐ +20B0B=>xīng +20B13=>zhěn +20B19=>sā +20B1B=>mò +20B1D=>fú +20B22=>tāo +20B23=>bàng +20B2A=>biào +20B2C=>xī +20B2E=>jié +20B36=>jìn +20B3E=>qiān +20B48=>sì +20B49=>jǐng +20B4B=>chǐ +20B57=>jǐng +20B65=>suì +20B6F=>zhā +20B70=>lí +20B74=>zhuō +20B79=>biàn +20B7F=>tún +20B83=>bì +20B86=>fèi +20B8A=>dé +20B8C=>zhú +20B91=>jū +20B99=>yǐ +20B9C=>yà +20B9F=>chì +20BA0=>guǎ +20BA1=>zhǐ +20BA8=>réng +20BAB=>yōu +20BAD=>bó +20BAF=>jǐ +20BB0=>pǐn +20BB3=>yīng +20BB4=>yāng +20BB5=>màng +20BBD=>lòng +20BBE=>ǹ +20BBF=>sa +20BC0=>chuān +20BC2=>cí +20BC3=>wǔ +20BC4=>rèn +20BC8=>dài +20BC9=>jí +20BCB=>yǐ +20BCD=>rán +20BD0=>huò +20BD1=>guā +20BD3=>zhé +20BD4=>pì +20BD7=>zā +20BD8=>bàn +20BD9=>jié +20BDC=>hōu +20BDF=>xiàn +20BE0=>huī +20BE9=>zhā +20BEA=>dāi +20BEB=>gē +20BED=>pì +20BEF=>piàn +20BF0=>shí +20BF1=>liǎng +20BF2=>yuè +20BF3=>hù +20BF4=>biàn +20BF7=>réng +20BF9=>réng +20C04=>yī +20C05=>zhī +20C07=>jīn +20C08=>wēng +20C09=>chāo +20C0B=>qiū +20C0D=>zhǔ +20C0F=>zhá +20C10=>pǒ +20C11=>àn +20C13=>hé +20C15=>chū +20C16=>yán +20C1A=>shì +20C1B=>hù +20C1C=>è +20C34=>shí +20C39=>tuō +20C3A=>dài +20C3B=>wài +20C3C=>pō +20C3D=>rǒng +20C3E=>jū +20C40=>bō +20C50=>yǔ +20C51=>dōu +20C53=>guǐ +20C54=>shòu +20C57=>suō +20C58=>nì +20C59=>zhōu +20C5A=>lòng +20C5B=>bǐng +20C5C=>zùn +20C5D=>yè +20C5E=>rǎn +20C60=>líng +20C61=>sà +20C64=>lěi +20C65=>è +20C67=>zhòng +20C68=>jǐ +20C6B=>è +20C6F=>zuò +20C72=>nà +20C73=>yǔn +20C8A=>xiè +20C8B=>zuǐ +20C8C=>shù +20C8D=>diū +20C8E=>fa +20C8F=>rěn +20C91=>bāng +20C92=>hán +20C93=>hóng +20C94=>yī +20C96=>yī +20C99=>kē +20C9A=>yì +20C9B=>huí +20C9C=>zhēng +20CAE=>jìng +20CB1=>gé +20CB4=>nóu +20CB5=>qiè +20CB7=>dié +20CB9=>jì +20CBA=>yì +20CBB=>yí +20CBD=>fú +20CBE=>shuò +20CBF=>shuò +20CC0=>yǒng +20CC1=>kěn +20CC2=>huá +20CC3=>hòng +20CC7=>hé +20CCA=>hē +20CCB=>qiǎn +20CCC=>qià +20CCE=>sì +20CD0=>bāng +20CEC=>jīng +20CED=>kè +20CF3=>āi +20CF4=>lóu +20CF6=>tū +20CF9=>chuáng +20CFC=>sòng +20CFD=>chéng +20CFF=>wēi +20D02=>nǔ +20D04=>jiǔ +20D07=>bīn +20D21=>xiào +20D22=>shēng +20D23=>hǒu +20D26=>zhù +20D28=>guān +20D29=>jī +20D2B=>jì +20D2D=>xī +20D2F=>shè +20D30=>ǒu +20D31=>hú +20D32=>tà +20D33=>xiáo +20D35=>zào +20D38=>bò +20D39=>qì +20D3A=>wā +20D3B=>tuō +20D3C=>dào +20D3E=>nà +20D60=>zhāi +20D63=>yà +20D66=>wǔ +20D67=>zhén +20D68=>de +20D69=>hē +20D6B=>āng +20D6C=>pí +20D6D=>sè +20D6E=>fěn +20D6F=>guā +20D73=>pǒ +20D77=>xuàn +20D78=>hān +20D79=>gāng +20D7A=>bā +20D7B=>zōng +20D7C=>mèng +20D7E=>huò +20DA7=>diān +20DA8=>xī +20DAB=>dà +20DAC=>nàng +20DB0=>diāo +20DB1=>luò +20DB2=>kè +20DB7=>yì +20DB8=>jué +20DB9=>hé +20DBB=>jí +20DBE=>hè +20DBF=>niè +20DC0=>rǔn +20DC1=>qián +20DC2=>dài +20DC3=>shāo +20DC4=>kè +20DC5=>zhú +20DC7=>shī +20DC8=>lǜ +20DC9=>jiā +20DCA=>pián +20DCB=>hòu +20DCC=>jī +20DCD=>tà +20DCE=>chóu +20DCF=>wō +20DD0=>jìng +20DD1=>pō +20DD2=>zhāi +20DD3=>xīn +20DD6=>biàn +20DD9=>xù +20DDE=>gū +20DDF=>jiè +20DE2=>xián +20DF8=>é +20DFA=>bó +20DFB=>piāo +20DFF=>zǎ +20E01=>pài +20E02=>tū +20E04=>yīng +20E2E=>xiǎng +20E31=>nuò +20E32=>gē +20E33=>bó +20E34=>xiè +20E38=>zhēn +20E39=>yú +20E3A=>nì +20E40=>xùn +20E41=>wà +20E43=>àng +20E44=>hàn +20E45=>hōng +20E46=>dān +20E48=>nuó +20E4A=>cǎo +20E4B=>jí +20E4C=>něng +20E4D=>yǒng +20E4E=>xiāo +20E50=>chuǎ +20E51=>yào +20E53=>gé +20E54=>táng +20E55=>bào +20E56=>chǎn +20E58=>xù +20E5B=>hái +20E5D=>chóu +20E5F=>jiǎn +20E60=>zuō +20E64=>wèi +20E65=>dā +20E66=>pī +20E90=>huàn +20E92=>xī +20E94=>pèn +20E95=>liū +20E96=>mǔ +20E97=>miē +20E98=>làng +20E99=>tuì +20E9A=>bān +20E9D=>gē +20E9F=>kù +20EA2=>jiā +20EA3=>bō +20ECD=>huàn +20ECF=>zú +20ED0=>luò +20ED7=>lí +20ED9=>hé +20EDA=>mó +20EDC=>shuì +20EDD=>shēn +20EDE=>kǎng +20EDF=>chì +20EE0=>líng +20EE1=>luǒ +20EE4=>yǎn +20EE5=>zhào +20EE6=>chuǎ +20EE7=>gǔ +20EE8=>qǐn +20EEA=>tán +20EEB=>fèn +20EEC=>tú +20EF1=>líng +20EF4=>lǎng +20F16=>lán +20F17=>zàn +20F18=>wù +20F1D=>lí +20F1E=>ā +20F1F=>lüè +20F20=>zhǐ +20F21=>chóu +20F22=>jiàng +20F24=>jiān +20F29=>lún +20F2A=>yí +20F2C=>shāng +20F3B=>jī +20F5C=>yì +20F5D=>nín +20F61=>huì +20F63=>zhā +20F66=>hǎn +20F68=>yǐn +20F69=>bì +20F6A=>ān +20F6B=>xiā +20F6C=>ní +20F70=>dī +20F71=>jiǎn +20F72=>pán +20F75=>yù +20F76=>chuài +20F77=>zā +20F79=>chá +20F7B=>zhé +20F7C=>sè +20F7E=>pēn +20F7F=>gū +20F80=>zhé +20F86=>lí +20F87=>dōu +20F89=>chóu +20F8B=>zuǐ +20F8C=>pò +20F8F=>shē +20F90=>lóng +20FA2=>shù +20FA4=>jìn +20FA5=>líng +20FA8=>kāng +20FA9=>là +20FAB=>xū +20FAC=>jìn +20FAE=>chuān +20FB2=>yuè +20FC6=>mǎi +20FC7=>xiè +20FC8=>jiū +20FC9=>jì +20FCB=>yuè +20FCF=>jiān +20FD1=>hán +20FD3=>sà +20FD4=>huì +20FD5=>qiào +20FD7=>sè +20FD8=>zuǐ +20FDB=>lǔ +20FDC=>huà +20FDD=>chū +20FDE=>shǎn +20FDF=>wò +20FE0=>jí +20FE1=>zhuó +20FE2=>xián +20FE3=>yī +20FE4=>guó +20FE5=>kuì +21011=>zhōu +21014=>lù +21016=>bō +21017=>shí +21018=>yìng +21019=>kū +21039=>zhì +2103A=>xié +2103D=>yè +2103E=>è +2103F=>lǜ +21040=>hàn +21041=>yè +21046=>luò +21047=>chuò +21048=>fàn +21049=>zhí +2104A=>yìng +2104B=>wěn +2104C=>wā +2104D=>ài +2104E=>yú +21051=>huā +21053=>liè +21054=>jīng +21055=>zá +21067=>zāng +21068=>duì +2106A=>jì +2106E=>wō +21070=>jí +21071=>xī +21073=>zhàn +21074=>tuán +2108A=>yú +2108F=>liè +21092=>zhì +21093=>shī +21095=>lǎo +21096=>lài +21097=>wěi +21098=>páo +21099=>chí +2109A=>yǐng +2109B=>dòu +2109D=>dòu +2109F=>bào +210A0=>qiè +210A1=>shù +210A3=>zhí +210A9=>liè +210AB=>péng +210AD=>zhē +210BF=>ōu +210C2=>xiè +210C3=>jí +210C4=>lài +210C5=>yíng +210C6=>cēng +210D6=>lē +210DD=>lùn +210E1=>lóng +210E2=>xì +210E6=>lìn +210E9=>guī +210F3=>xīng +210F7=>lí +210F8=>cī +21107=>qǐng +21111=>jiān +21112=>dào +21113=>jiǎn +21114=>qìng +21115=>xiè +21116=>yìng +2111F=>há +21121=>zhe +21122=>shē +21123=>mí +21124=>huán +21131=>cù +21132=>rú +21133=>sǎ +21134=>huò +21135=>yī +21137=>dī +21139=>luàn +2113B=>yì +21142=>bò +21143=>páng +21144=>tán +21145=>é +21146=>zāng +21147=>cóng +21153=>zhāi +21155=>xǐ +21156=>mǎng +21158=>là +21159=>yùn +21161=>è +21165=>dié +2116D=>guān +21171=>huàn +21175=>shì +21176=>jiǎn +21179=>zhān +2117A=>jí +2117B=>huàn +21185=>wàn +21186=>luǒ +2118F=>dòu +21195=>liàn +211A3=>niè +211A4=>nǎn +211A5=>jiù +211A6=>yuè +211A9=>yāo +211AA=>chuāng +211AE=>cǎn +211AF=>lǐ +211B0=>dùn +211B1=>nǎn +211B2=>nǎn +211B8=>rì +211BD=>yuè +211C0=>yóu +211C2=>yīn +211C4=>guó +211C8=>dàng +211D1=>zhēn +211D2=>mí +211D3=>dié +211D6=>zhēn +211DA=>kuā +211DC=>hán +211DD=>sòng +211DE=>hé +211DF=>jī +211E0=>zhé +211E4=>bǐng +211E6=>wéi +211E7=>tōu +211E9=>tú +211EC=>gāng +211ED=>lóu +211EE=>quán +211EF=>hùn +211F0=>zhuǎn +211F1=>què +211F3=>hóng +211F5=>dàng +211F6=>hé +211F7=>tài +211F8=>guāi +211FA=>yù +211FC=>yà +211FF=>wān +21200=>qūn +21205=>jué +21206=>ōu +21209=>quān +2120A=>zhí +2120D=>líng +2120E=>wū +2120F=>xìn +21210=>dá +21212=>yuān +21213=>yuàn +21217=>mò +21219=>yóu +2121E=>wǔ +21220=>zhāng +21223=>xuān +21226=>rǎo +21227=>gǔn +21228=>yù +2122E=>xiá +2122F=>biǎn +21230=>yóu +21232=>yīn +21234=>xuán +21235=>yóu +21236=>léi +2123C=>tǐng +2123F=>zhēn +21244=>zài +21245=>gā +21246=>lá +21249=>què +2124E=>jú +21250=>chūn +21251=>dā +21252=>tún +21253=>āi +21257=>zǐ +2125A=>huáng +2125B=>yì +21269=>bào +2126A=>chí +2126D=>rì +21274=>lú +21277=>jié +21278=>shì +2127A=>zuān +21281=>yì +21284=>fèn +21285=>fèn +21289=>mò +2128D=>shù +2129B=>áo +2129D=>pǐ +2129E=>píng +2129F=>pō +212A0=>jiá +212A1=>zhóu +212A3=>qiū +212A7=>yǒu +212A8=>tán +212AB=>rǒng +212AD=>mì +212B6=>yì +212B8=>rǒng +212BB=>liè +212BC=>qióng +212D9=>huí +212DA=>jì +212DF=>gào +212E7=>yóu +212E8=>chā +212E9=>dé +212EA=>yīn +212EC=>yù +212ED=>bèi +212EF=>bó +21314=>qiāo +2131A=>chǎ +2131C=>xīn +2131E=>chí +21323=>zào +21324=>kuí +21326=>fèi +21329=>tā +2132A=>guài +2132D=>duō +21332=>guī +21334=>zhí +2134C=>chǎn +2134D=>nǎo +21350=>hú +21352=>táo +21361=>yì +21364=>niè +21365=>zhài +21366=>huán +21368=>dù +2136A=>qì +2136B=>cè +2136E=>chuí +21372=>dā +21376=>zhì +21377=>gèng +2137B=>wèng +21389=>dù +2138D=>chí +21391=>àn +21392=>kuò +21394=>wò +21398=>yīng +2139A=>piǎn +213AB=>zhá +213AC=>zhuǎ +213AE=>sù +213B3=>nì +213BA=>zhú +213BB=>chán +213BE=>bèng +213BF=>ní +213C0=>zhí +213C1=>huì +213D8=>xià +213DA=>zhì +213DB=>xī +213DE=>jiǎng +213E9=>duī +213EA=>fū +213ED=>jiāo +213EE=>cháo +213EF=>bài +213F5=>liè +213FC=>áo +2140B=>zāo +2140C=>chù +2140F=>tuǒ +21412=>háo +21413=>kāng +21414=>yín +21416=>xiàn +2141D=>fù +2141E=>biē +21420=>kuī +21424=>qiè +21425=>sà +2143F=>dā +21440=>yě +21444=>zhǎng +21446=>liáng +21448=>duǐ +2144D=>láo +2144E=>xūn +21458=>zhì +2145A=>kū +2145E=>suì +2145F=>wō +21463=>kū +2146F=>jiǎn +21476=>jiǎng +2147B=>zhuì +2147D=>shuǎng +2147E=>yú +21481=>sà +21483=>yù +21484=>lǎn +2148A=>yù +2148C=>qiǎn +2148D=>jù +2148F=>liè +21492=>shú +21493=>xiàn +21496=>gài +214A2=>tái +214A7=>tiǎn +214AF=>mèng +214B1=>dí +214B3=>mián +214BE=>huī +214C9=>duò +214CD=>liè +214D2=>lài +214D3=>yín +214D4=>lǎn +214D6=>jiāo +214D8=>huò +214E3=>guō +214E6=>zhàn +214ED=>mǐ +214F0=>kuī +214F7=>duò +214FF=>yín +21507=>lèi +21515=>gòng +2151B=>tǐng +2151C=>yáo +2151E=>wǎng +21523=>jié +21528=>xiū +2152A=>shù +21531=>wèi +21534=>yù +21541=>zhān +21549=>āng +2154F=>sǎng +21550=>chóu +21552=>kuà +21556=>jǔ +21557=>hài +21562=>miǎn +21567=>hàng +2156A=>chóu +2156E=>líng +21570=>zōng +21589=>kūn +2158C=>zhōng +2158E=>zhāo +21590=>diě +21591=>gǒu +21592=>yún +21593=>dān +21594=>nuǒ +2159B=>bǐng +2159D=>rán +2159E=>chān +215A2=>rǒng +215A3=>yīn +215A4=>chān +215A7=>zhì +215AA=>guài +215AB=>nuó +215AC=>shēn +215AF=>sù +215B2=>wǒ +215B3=>chǐ +215BA=>miè +215BB=>zhí +215BE=>qī +215C1=>gōu +215C6=>lǒu +215C8=>zī +215CD=>dǎng +215CF=>xiǎn +215D1=>rǒu +215D7=>pěng +215DE=>xī +215E2=>kuā +215E4=>guì +215E5=>chún +215E6=>jiè +215F2=>jiè +215F3=>xī +215F5=>kū +215F7=>gū +215F8=>zhà +215F9=>fàn +215FC=>xiè +2160D=>huán +2160F=>niǎo +21610=>xì +2161B=>cū +2161D=>gǔn +21621=>xī +21627=>qiá +2162A=>māng +2162D=>zhé +21630=>juàn +21634=>biē +21640=>biē +21645=>quán +2164B=>xì +2164E=>jiǎo +21650=>quán +21651=>zhǐ +21652=>tiān +21653=>kāi +21658=>sǎn +2165B=>zī +21663=>jié +2166A=>bié +2166C=>dòu +2166D=>zuī +21676=>yǎn +21681=>bì +21685=>kuǎi +21687=>yàn +21688=>wéi +2168A=>huān +2168C=>hào +21691=>gōng +21694=>méng +21697=>lěi +21699=>dì +2169B=>bǐng +2169C=>huān +2169F=>wā +216A0=>jué +216A8=>chì +216AD=>bā +216AE=>jiǔ +216B7=>dì +216B9=>zhàng +216BB=>dà +216BC=>shí +216BD=>hào +216CC=>yè +216D7=>bì +216D8=>pǐ +216D9=>yǎo +216DC=>dī +216DD=>càn +216DE=>pín +216DF=>yuè +216E0=>qiē +216E1=>pī +216F5=>tuǒ +216F6=>xiè +216FD=>yè +21700=>fàn +21701=>guā +21702=>hù +21703=>rǔ +21709=>rǎn +2170A=>fǒu +2170B=>huāng +2171A=>rú +21722=>mǎo +21725=>duī +21726=>huì +21727=>xì +21728=>xiū +2172B=>rǎn +2172C=>yī +2172F=>zhé +21731=>jì +21732=>gào +21733=>yòu +21735=>pū +21748=>chù +21749=>cū +2174A=>zhé +2174B=>niǎo +2174D=>qiè +21750=>chá +21752=>niǎo +21753=>suī +21759=>chá +2175A=>chéng +2175B=>yáo +2175C=>dù +2175D=>wāng +2175F=>niàn +21760=>mí +21766=>nǒu +21767=>xì +21769=>yāo +2176B=>chān +21798=>xiè +21799=>miè +2179A=>kěng +2179C=>cù +2179E=>shěng +2179F=>pàn +217A0=>hù +217A2=>kè +217A3=>xiàn +217A5=>hóu +217A6=>qióng +217A7=>zōng +217AA=>fú +217AB=>nài +217AD=>nì +217AF=>kǔ +217BE=>nèn +217CD=>gē +217D1=>hóu +217D3=>āi +217D5=>shī +217DE=>xiū +217DF=>cōng +217E0=>jiāo +217E2=>zhá +217E3=>xiāo +217E4=>liàn +217E5=>qǔ +217E8=>shǎn +217E9=>xiè +217EB=>gòng +217EC=>miè +217ED=>chái +217EF=>ēn +217F3=>dòu +21806=>kòu +2180A=>tiáo +2180B=>shī +2180F=>sāng +21812=>guān +21816=>hào +21817=>zhì +21818=>yàng +21819=>tōng +2181A=>bì +2181C=>mó +2181E=>fú +21825=>qiáng +21839=>zhì +2183C=>sōu +2183F=>niǎo +21840=>juàn +21842=>yàng +21844=>huāng +21848=>bēng +21849=>mó +2184A=>cháo +2184E=>lǚ +2184F=>shāo +21850=>bǔ +21851=>zēng +21852=>sī +21854=>zuì +21855=>yuē +21856=>zān +21857=>luǎn +21865=>qú +2187A=>miǎo +21880=>zhuàn +21888=>dàng +2188A=>yuān +21892=>jǔ +21895=>huǐ +21896=>qì +21898=>yùn +2189A=>màn +2189C=>mǒ +218B1=>piāo +218B3=>jìn +218B9=>yāo +218C0=>chì +218C1=>nì +218C2=>sōu +218C8=>shù +218CB=>piāo +218D4=>hàn +218E0=>yāo +218E2=>néi +218EA=>shì +218EC=>yuān +218EE=>cài +218EF=>jié +218F9=>xiè +218FD=>yán +218FE=>xiāo +2190B=>xiè +2190C=>lì +2190E=>fàn +21917=>zhù +21919=>nà +2191B=>zhuǎn +2191E=>kuī +21922=>luó +2192B=>qiā +21936=>wān +2193D=>shǔ +2193F=>chèng +21941=>yì +21946=>hǎo +21948=>jiào +2194B=>huì +2194D=>xiào +2194E=>cí +2195E=>jì +21966=>nǐ +21968=>nǐ +21969=>tǐ +21976=>jù +21978=>mìng +2197D=>lí +2197F=>zhòng +21981=>xù +21983=>qióng +21984=>fú +21986=>bìn +2198A=>jì +2198D=>qí +2198E=>xì +21994=>dèng +21995=>ér +2199B=>shú +2199C=>tóng +2199D=>xiáo +2199F=>pí +219A8=>dǎn +219AA=>jí +219B3=>xiào +219B7=>cóng +219BB=>bīn +219BC=>rǒng +219CD=>miàn +219D2=>miàn +219D4=>shū +219D5=>xiáo +219D6=>bǎo +219D7=>wà +219D9=>pào +219E3=>gǎi +219E5=>hū +219E6=>héng +219E8=>zhú +219E9=>guāi +219ED=>guì +219F9=>dài +219FC=>bīn +219FD=>huǎng +21A00=>chá +21A04=>xià +21A05=>jú +21A07=>yǎo +21A16=>fěn +21A17=>zào +21A1B=>fēng +21A22=>jū +21A23=>yù +21A29=>hūn +21A32=>jié +21A33=>xiòng +21A35=>nài +21A3B=>nǒu +21A3D=>shěng +21A3F=>yù +21A42=>huán +21A43=>gěng +21A44=>wǎn +21A46=>tuó +21A47=>qiāo +21A58=>yìn +21A5A=>jiā +21A61=>suǒ +21A63=>jié +21A64=>xī +21A65=>wěng +21A69=>máng +21A76=>yáng +21A78=>yáo +21A7D=>máng +21A7E=>ōu +21A81=>án +21A85=>lòu +21A91=>è +21A92=>zǐ +21A97=>è +21A99=>àn +21A9E=>huò +21AA0=>céng +21AB0=>xiòng +21AB1=>jì +21AB3=>zuó +21AB5=>qí +21ABA=>zhēng +21AC0=>jī +21AC1=>qī +21AC2=>juǎn +21AC3=>níng +21ADF=>sè +21AE5=>hè +21AE6=>rǒng +21AE7=>qǐn +21AEC=>jū +21AEF=>lì +21AF5=>shí +21AF8=>nì +21AF9=>xián +21AFA=>fū +21AFD=>rǔ +21B01=>xiòng +21B02=>guì +21B04=>jì +21B06=>měng +21B07=>fū +21B09=>sài +21B0A=>yù +21B0B=>jiào +21B0C=>mèng +21B0D=>lóng +21B0E=>qiāng +21B10=>mí +21B13=>yí +21B16=>hān +21B17=>nì +21B18=>lào +21B19=>sèng +21B1C=>lǐn +21B1E=>yù +21B25=>nuó +21B2B=>wù +21B2F=>biǎn +21B32=>biǎn +21B33=>xuān +21B35=>jiān +21B38=>biǎn +21B42=>dé +21B47=>zhuān +21B4B=>rǒng +21B50=>shuàn +21B58=>jiā +21B5B=>huǐ +21B5E=>zhān +21B62=>bài +21B63=>liè +21B65=>xiē +21B6D=>jiǎn +21B6E=>shǒu +21B73=>kào +21B77=>guān +21B78=>luàn +21B7E=>nǒu +21B7F=>chǎng +21B8E=>liáng +21B99=>nài +21B9A=>rǔ +21B9E=>zhì +21BA6=>cáo +21BB0=>lì +21BBB=>lán +21BBF=>chān +21BC1=>wāng +21BC4=>lì +21BC7=>wù +21BC8=>páo +21BC9=>yòu +21BCB=>gān +21BCF=>ān +21BD0=>xiū +21BD1=>shuǐ +21BD2=>ruǐ +21BD8=>bǎn +21BD9=>yóu +21BE2=>huó +21BE5=>huī +21BE8=>zuò +21BE9=>xiāo +21BEB=>mián +21BF0=>gà +21BF1=>yuǎn +21BF3=>bò +21BF4=>chào +21BF5=>tuǐ +21BF7=>bò +21BFD=>gà +21BFF=>tiāo +21C00=>ná +21C05=>hú +21C06=>niè +21C0B=>huí +21C0C=>lǒu +21C0E=>tí +21C10=>qiào +21C11=>qiáo +21C12=>zhǒng +21C16=>dī +21C1A=>lín +21C1D=>quán +21C1E=>zhuān +21C20=>léi +21C22=>xié +21C25=>rén +21C28=>dāng +21C2A=>dū +21C2B=>niǎn +21C2F=>shǐ +21C32=>xián +21C39=>zhí +21C3D=>ài +21C3E=>cī +21C3F=>pú +21C41=>shǐ +21C45=>qū +21C46=>shǔ +21C47=>diān +21C49=>xiǎo +21C4A=>shuǐ +21C4C=>huán +21C50=>yí +21C51=>juān +21C54=>zhǐ +21C5C=>zhào +21C63=>xù +21C6F=>lòng +21C71=>zhù +21C73=>suǒ +21C77=>dié +21C7A=>qú +21C7C=>kè +21C7D=>hū +21C7E=>jū +21C80=>qǐng +21C8D=>bīng +21C95=>tì +21C97=>jué +21C9A=>qiú +21CA3=>jiàng +21CAA=>yùn +21CAD=>mèi +21CAE=>pī +21CB0=>qú +21CBC=>mì +21CBF=>tì +21CC2=>kài +21CC4=>bǐ +21CC6=>qū +21CCF=>tiāo +21CD1=>chù +21CD8=>jú +21CDA=>xī +21CDE=>lìn +21CED=>chǐ +21CEE=>jī +21CF4=>lú +21CF8=>lì +21CFE=>jué +21D05=>zhū +21D06=>lù +21D0E=>niè +21D14=>quán +21D2D=>yà +21D2F=>è +21D31=>hù +21D40=>máng +21D49=>wù +21D4C=>chā +21D51=>qīn +21D52=>jié +21D53=>hóng +21D55=>dān +21D56=>ěn +21D57=>zè +21D58=>hù +21D59=>àng +21D5A=>jiè +21D5B=>fù +21D5C=>yòng +21D5E=>fēng +21D6C=>mù +21D76=>sè +21D77=>cóng +21D7B=>kāng +21D82=>yào +21D83=>ài +21D84=>bāo +21D86=>pǒ +21D88=>shǐ +21D89=>fàn +21D8B=>jú +21D8C=>pí +21D8E=>wèi +21D8F=>kū +21D90=>qié +21D91=>gān +21DA2=>kuàng +21DA3=>suì +21DA4=>bēng +21DA5=>jiā +21DA6=>yà +21DAA=>kàn +21DAB=>niè +21DAD=>xíng +21DAF=>xì +21DB1=>lìn +21DB2=>duǒ +21DB4=>chǎn +21DC8=>shì +21DCB=>duì +21DCD=>jiāng +21DCE=>yǔ +21DCF=>lù +21DD0=>ěn +21DD3=>gǔ +21DD5=>wěi +21DD6=>chē +21DD7=>huàn +21DD8=>bié +21DDB=>hàn +21DDC=>tuí +21DDD=>nà +21DDE=>qǐ +21DE0=>tóu +21DE1=>yuān +21DE2=>wáng +21DE4=>wú +21DE5=>gào +21DE8=>kēng +21DEA=>yí +21DF8=>xiāo +21DFA=>guǐ +21DFB=>yà +21DFC=>suì +21DFD=>sǒng +21DFF=>zhuó +21E02=>tū +21E03=>xiǎn +21E08=>zè +21E09=>lì +21E0C=>zhù +21E0E=>jié +21E11=>tì +21E14=>xié +21E15=>qióng +21E17=>yà +21E18=>jū +21E1B=>yín +21E1C=>zhí +21E1E=>kǎn +21E1F=>zī +21E21=>kē +21E23=>niè +21E24=>qiáng +21E25=>wǎn +21E26=>zé +21E28=>jū +21E2A=>zì +21E44=>yà +21E47=>lín +21E49=>qí +21E4E=>huí +21E53=>qì +21E55=>yáng +21E56=>suì +21E58=>qǐ +21E59=>guī +21E62=>qìn +21E63=>ē +21E65=>zuò +21E68=>zè +21E69=>qì +21E6A=>jí +21E6C=>tuó +21E6D=>dié +21E6F=>huì +21E70=>máo +21E72=>xǔ +21E75=>hóu +21E76=>yǎn +21E77=>xiáng +21E78=>cōng +21E79=>hú +21E7C=>àn +21E7E=>bǐng +21E87=>duǒ +21E90=>zhǔ +21E91=>dié +21E92=>yōu +21E93=>qǐ +21E94=>shí +21E95=>xūn +21E96=>yōu +21E97=>kān +21E98=>qiǎo +21E9B=>qiāng +21E9C=>pén +21E9F=>quán +21EA1=>yíng +21EA7=>shā +21EAB=>tāo +21EAD=>hòng +21EAE=>pǐ +21EAF=>yáo +21EB4=>tú +21EB5=>chái +21EB7=>xià +21EB8=>qí +21EBA=>qióng +21EBD=>jìn +21EC8=>zhēn +21ECC=>zhū +21ECE=>xī +21ED0=>wēng +21ED1=>zhǒng +21ED5=>suì +21ED8=>kē +21ED9=>kuò +21EDA=>kǎng +21EDD=>cháo +21EDE=>bì +21EDF=>mò +21EE0=>zhù +21EE1=>hàn +21EE2=>yǔ +21EE3=>yí +21EE4=>má +21EE7=>qì +21EE8=>gùn +21EE9=>màn +21EEA=>liáo +21EEB=>lín +21EEC=>zú +21EED=>lěi +21EEE=>hù +21EEF=>chuǎng +21EF0=>qì +21EF1=>léi +21F01=>chī +21F03=>pó +21F04=>dié +21F0A=>lěi +21F0E=>yǐ +21F13=>diàn +21F16=>dūn +21F17=>gāo +21F18=>hū +21F1A=>xiāo +21F1B=>gá +21F1C=>pēng +21F2C=>shěn +21F31=>wéi +21F3B=>duì +21F3C=>cháo +21F3D=>yǐn +21F3E=>kuài +21F3F=>kū +21F41=>zuì +21F42=>gǔ +21F45=>yùn +21F46=>zhì +21F49=>jì +21F4A=>chēng +21F56=>xiè +21F5B=>zuǐ +21F5C=>án +21F5D=>hāo +21F60=>pǒ +21F62=>dí +21F63=>yè +21F67=>náo +21F71=>jié +21F72=>bàng +21F73=>lǎn +21F74=>cáng +21F76=>bì +21F7B=>zhǎn +21F7C=>qì +21F82=>náo +21F85=>lǜ +21F87=>kuàng +21F89=>mó +21F8B=>lěi +21F8C=>páo +21F92=>lì +21F93=>céng +21F95=>dàng +21F96=>lěi +21F99=>è +21F9B=>bèng +21F9C=>jué +21FA5=>xuán +21FA6=>niè +21FA8=>hài +21FAE=>xiǎn +21FB0=>jiǎn +21FB1=>mí +21FB2=>niè +21FBB=>cáng +21FBC=>sǒng +21FBD=>zēng +21FBE=>yì +21FC2=>chóng +21FC4=>cáng +21FC9=>lěi +21FCA=>nuó +21FCB=>lì +21FCE=>lí +21FCF=>luó +21FD3=>tǎng +21FD6=>niè +21FD7=>niè +21FD9=>jī +21FDB=>lěi +21FDD=>nàng +21FE0=>lín +21FE1=>líng +21FE4=>xián +21FE5=>yù +21FE7=>zāi +21FE8=>quǎn +21FE9=>liè +21FEF=>yù +21FF0=>huāng +21FFA=>nǎo +21FFC=>xùn +21FFE=>jú +21FFF=>huò +22001=>yì +2200A=>xī +2200B=>sè +2200C=>jiǎo +2200D=>yōng +22015=>shī +22016=>jīng +22017=>wàn +22018=>yě +22019=>jiū +2201C=>gǒng +22021=>huī +2202A=>ěr +22035=>hàn +2203C=>fú +22040=>fú +22041=>zhuó +22042=>jī +2204F=>bāng +22052=>qí +22053=>shǐ +22055=>diǎo +22056=>pèi +22057=>xiǎn +22058=>sān +2205D=>cháng +2205E=>yuē +22060=>gōng +22062=>wū +22064=>fēn +22067=>chǎn +22069=>nèi +2206A=>jué +2206C=>zhǎo +2206E=>qián +22071=>ǎo +22076=>wǎng +22077=>zhōng +22079=>huāng +2207B=>bù +2207C=>zhǔ +2207D=>bì +2207E=>chāo +2207F=>zhēng +22080=>fú +22081=>kōu +22083=>zuó +22084=>xuàn +22086=>fù +2208A=>yǎo +2208D=>bō +2208F=>bèi +22090=>xié +22091=>shì +22092=>yí +22094=>hóng +22095=>cuì +22097=>yì +22098=>zhuān +2209D=>chì +220A4=>pō +220A8=>yín +220B1=>yuàn +220B6=>jiōng +220B9=>mào +220BA=>qiàn +220BC=>yì +220C0=>wú +220CD=>bēi +220CE=>huò +220CF=>cóng +220D0=>kōng +220D5=>tà +220D7=>hàn +220D8=>qiàn +220DC=>zhí +220E2=>sè +220E5=>qiān +220E6=>guǒ +220E9=>gǔn +220EC=>jiān +220ED=>zhōng +220EE=>miǎn +220EF=>guǐ +220F0=>shì +220F1=>móu +220F2=>è +220F3=>bǎ +220F4=>là +220F8=>zhòu +220FA=>jí +22100=>zǎo +22104=>zhā +22105=>yì +22107=>gǒu +2210A=>guī +2210B=>yīng +2210C=>shǎi +2210D=>hé +2210E=>bàng +2210F=>mò +22110=>méng +22113=>wù +22114=>dài +22117=>jiǒng +2211C=>hàn +2211F=>tōng +22120=>kōu +22121=>lí +22122=>zhì +22123=>huì +22124=>zǎn +22126=>diǎo +22127=>cù +22131=>zhì +22133=>kuǎ +22135=>xiàng +22136=>huà +22137=>liáo +22138=>cuì +22139=>qiāo +2213A=>jiǎo +2213C=>xū +2213D=>èr +2213F=>tuō +22140=>tán +22141=>zhì +22148=>nǎo +22149=>mào +2214A=>dì +2214B=>céng +2214E=>jiǎo +2214F=>lián +22151=>shā +22152=>dàn +22155=>suì +22156=>lián +22157=>guò +2215A=>biǎo +2215C=>cì +2215D=>diàn +2215E=>lǜ +2215F=>nǐ +22160=>yǎn +22161=>lán +22164=>gài +22165=>chú +22169=>bì +2216A=>zú +2216B=>huì +2216D=>lǎi +2216E=>xián +2216F=>fèn +22170=>hè +22179=>yào +2217A=>zhǎn +2217C=>néi +2217E=>luǒ +22180=>yuán +22182=>néng +22189=>rěn +2219C=>gé +2219E=>jiǎn +2219F=>píng +221A3=>biè +221A6=>jiàn +221A9=>bìng +221AF=>mì +221B0=>hù +221B4=>diǎo +221B6=>yōu +221B7=>yāo +221B8=>bēng +221BA=>chén +221BB=>jī +221BD=>yāo +221C7=>guān +221C8=>yàn +221D5=>chǐ +221D7=>shà +221D8=>yǎn +221D9=>yì +221DA=>yì +221DB=>chè +221DE=>hàn +221DF=>huāng +221E4=>shuì +221E5=>suì +221E6=>rén +221E7=>tán +221E8=>zhǐ +221EA=>fàn +221EB=>fěng +221F0=>tán +221F2=>mí +221F3=>pí +221F4=>bù +221F5=>nà +221F6=>tián +221F7=>bá +221F8=>yì +22202=>yǎn +22204=>tiāo +22206=>yáo +22207=>shěn +22208=>kē +22209=>tóng +2220B=>xuǎn +22213=>yòu +22215=>bài +22219=>xiá +2221A=>lǚ +2221B=>kùn +2221C=>zāng +2221D=>qiú +22220=>cù +22221=>zuī +22222=>lǒu +22224=>xiá +2222F=>shēn +22232=>pú +22234=>jīng +22235=>qiāng +22236=>yì +22238=>niè +22239=>duī +2223B=>jié +2223C=>suì +2223D=>zhàn +2223E=>cōu +22241=>bēng +22242=>guān +22243=>shě +22245=>jìn +22246=>dì +22251=>dān +22253=>nǎi +22255=>nóu +22257=>jí +22258=>yán +2225A=>nòu +2225C=>dù +2225D=>wèi +2225E=>piān +22262=>hú +22264=>jià +22265=>yè +22266=>jǔn +22267=>lán +22268=>là +22269=>yīn +2226D=>tuí +22275=>nǎo +2227A=>zǔ +2227F=>mà +22280=>sī +22281=>zhì +22284=>huī +22285=>zhuì +22287=>huì +2228D=>chú +2228F=>chè +22292=>xiū +22293=>lán +22295=>cōng +22296=>shèn +22297=>mò +22298=>yī +22299=>yáo +2229A=>xǐ +2229B=>zuǐ +2229C=>bìng +222A7=>yú +222A9=>lù +222AE=>tuí +222AF=>wěi +222B1=>fén +222B2=>shěn +222BB=>liáo +222C2=>shǔ +222C3=>dǎn +222C4=>juǎn +222C5=>yú +222C6=>xìn +222C7=>yáo +222C8=>sū +222D2=>huó +222D4=>qiān +222DA=>má +222DD=>kǎi +222E1=>lǔ +222E3=>yōu +222EE=>xiàn +222F9=>wú +222FB=>yǐn +222FC=>xī +222FF=>zhāi +22300=>xiè +22304=>qú +22308=>lí +2230D=>qiān +22314=>líng +22315=>luán +2231A=>chān +22326=>zhèng +22328=>yán +22332=>yìn +22333=>kuí +22337=>qū +22339=>fú +2233B=>yù +22341=>qí +22346=>qì +22347=>jì +22348=>yuān +2234E=>gào +2234F=>juàn +22351=>qí +22353=>gǎi +22355=>quàn +2235A=>wèi +22367=>zhì +2236B=>jiǎn +2236D=>sì +22370=>yì +22371=>qiān +2237C=>lì +2237F=>zāng +22380=>yì +22382=>cái +22383=>yì +22384=>gē +22386=>dié +22388=>zhī +22389=>yì +2238B=>zāi +2238C=>dài +2238E=>sù +22394=>jié +22395=>chèn +22396=>qú +22398=>hàn +22399=>xián +223A0=>quán +223A1=>jié +223A5=>juàn +223AA=>dàn +223AD=>jīn +223B4=>bīng +223B5=>hú +223B9=>jué +223BB=>yú +223C3=>lǐ +223C4=>qiáng +223C5=>shuǐ +223C6=>kū +223C8=>zhěn +223CD=>fú +223CE=>shēn +223D2=>chuí +223D5=>tóng +223D7=>yì +223D9=>yáng +223DC=>tuó +223DD=>zhōu +223DE=>jí +223E4=>xùn +223E6=>shěn +223E7=>xuān +223ED=>liú +223EE=>yuān +223EF=>hú +223F0=>zhèng +223F3=>pēng +223F7=>jué +22402=>zhì +22403=>piān +22404=>yuàn +22406=>jiān +2240A=>páng +2240E=>zhuàn +22410=>xián +22412=>bēng +22414=>cōng +22416=>mò +2241A=>guó +2241E=>chéng +2241F=>qiāo +22426=>bì +22429=>qiǎng +2242B=>zhōu +22432=>fán +22433=>biē +2243E=>bó +2243F=>rǒng +22445=>dǐng +22446=>quán +22447=>jiù +22448=>yáo +22453=>xiá +22456=>zǎo +2245D=>dān +2245F=>wǔ +22460=>tuó +22462=>hū +22467=>xī +2246C=>lái +2246E=>fēi +22479=>hú +22486=>xiān +22489=>shǎn +2248D=>fèi +22490=>cuò +22492=>fú +22494=>chù +2249D=>diū +2249E=>làn +224A9=>xǐ +224AF=>biāo +224B0=>yù +224B1=>suì +224B2=>xǐ +224B7=>póu +224BE=>jiào +224C0=>yì +224C3=>wán +224C4=>jǐ +224C6=>wán +224C7=>tuì +224CB=>àng +224CD=>tiān +224CE=>chí +224D2=>rán +224D4=>sà +224D5=>yín +224D6=>pī +224D7=>cǐ +224D8=>tóng +224D9=>yǐn +224DC=>gé +224DD=>tiāo +224DE=>zhēng +224DF=>zhòu +224E1=>yí +224E2=>kuà +224E3=>sōng +224E7=>dì +224EC=>xié +224EE=>xiāo +224EF=>guàng +224F0=>tuǒ +224F1=>fēng +224F2=>wú +224F5=>xiù +224FF=>yóu +22501=>líng +22502=>yàn +22505=>dōng +22506=>qì +22507=>táo +22508=>hán +2250A=>chí +2250B=>sōng +22511=>quǎn +22514=>hàn +2251F=>rǒu +22520=>qì +22521=>kāi +22522=>yú +22523=>chā +22524=>chèng +22525=>yù +22527=>bìng +22529=>cōng +2252A=>zhū +2252C=>yù +22531=>jué +22532=>liù +22533=>sāo +22534=>yù +22545=>shuài +2254B=>yuàn +2254E=>zhāng +22551=>shuài +22553=>chǔ +22554=>zhāng +22555=>sǎn +22556=>xiān +22558=>cuī +22559=>měng +2255A=>dí +2255E=>zhì +2255F=>ào +22566=>xiū +22568=>pián +2256A=>jiào +2256B=>kuǎn +2256C=>sà +2256D=>xiàn +2256E=>zhà +2256F=>diàn +22577=>yí +2257A=>huì +2257B=>shàn +22584=>chóng +22585=>yí +22586=>xiè +22587=>zhì +22588=>tiào +2258A=>pīng +2258B=>xián +2258E=>xiān +2258F=>sù +22591=>cuán +22597=>sǒng +2259B=>hēi +2259D=>xiàn +2259F=>yóu +225A1=>yù +225A4=>tái +225A6=>jué +225A7=>nàng +225A9=>diān +225AB=>yì +225AC=>bì +225B3=>xū +225B4=>yì +225B5=>rù +225B7=>gōng +225BA=>yì +225BF=>zhì +225C0=>xīn +225C2=>jì +225C4=>xià +225C8=>zhāo +225C9=>nè +225CA=>xiè +225CE=>yì +225EB=>fǔ +225ED=>shè +225EF=>yuán +225F0=>fǎn +225F2=>fū +225F3=>wù +225F4=>xī +225F5=>hǒng +225F9=>jì +225FA=>chàng +225FF=>mò +22600=>pèi +22603=>mú +22604=>qiú +22605=>mào +22607=>dá +22609=>xiá +2260A=>shēn +2260B=>tè +2260C=>hóng +2260D=>bì +2261D=>nǐ +2261F=>qiáo +22627=>ruǎn +22638=>jiàng +22639=>chā +2263A=>mǐ +2263D=>yì +2263F=>suō +22641=>wù +22642=>xuān +22645=>xí +22647=>yǐ +22650=>náo +22653=>wèi +2266E=>kàn +22671=>lòng +22672=>lǚ +22673=>zhuǎng +2267A=>zhì +2267C=>xìng +2267E=>gěng +2267F=>jìn +22680=>xiàn +22681=>jì +22682=>cuò +22684=>láo +22685=>fěn +22686=>jù +2268B=>miào +2268C=>xiá +22691=>sù +226A8=>zhì +226AA=>hù +226AB=>kòu +226AD=>suǒ +226AE=>nì +226BA=>tēng +226BB=>zhù +226C1=>dá +226C3=>qiú +226C4=>yà +226C6=>xián +226C9=>nèi +226CD=>zhǐ +226CE=>bié +226D2=>chǒng +226D3=>lán +226D4=>dōng +226D5=>qūn +226D6=>xiàng +226D8=>xiáo +226D9=>wǎn +226DA=>rù +226DB=>wàng +226DC=>nì +226DE=>bāi +226DF=>yà +226E5=>sī +226E6=>yǐn +226E8=>yù +226EE=>lí +226EF=>huò +22717=>bàng +22723=>xī +22725=>jiū +22728=>xiè +22729=>qiān +2272A=>nuò +2272B=>xǐng +2272C=>duó +2272D=>jǐ +2272E=>wǔ +2272F=>mú +22730=>yàn +22731=>qì +22732=>ná +22733=>chì +22734=>hóu +22736=>sào +22738=>náo +2273B=>chěng +2273C=>chěng +2273D=>kuǐ +2273F=>jià +22740=>tú +22742=>dú +22745=>xiá +22746=>zhòng +22747=>huò +22748=>chóng +22749=>dá +2274C=>mào +2274D=>yào +22753=>juān +2276C=>shì +2276F=>yín +22773=>gǔ +22774=>wù +22778=>guò +22779=>tì +2277B=>hōng +22787=>rě +22789=>yí +2278B=>tǔn +2278F=>qióng +22790=>hài +22792=>qì +22795=>huò +22796=>tì +22797=>pī +2279A=>gěng +2279C=>xiè +2279E=>mì +2279F=>gào +227A0=>tā +227A1=>xiǎng +227A3=>shū +227A6=>fú +227AC=>zhuān +227AD=>liù +227C5=>yóu +227CA=>chěng +227CB=>duī +227E2=>lí +227E3=>yàng +227E4=>lí +227E7=>lǔ +227E8=>mǔ +227E9=>suì +227EA=>ài +227ED=>kòu +227EF=>zhé +227F0=>ài +227F1=>téng +227F3=>lǜ +227F4=>tuí +227F5=>bī +227FE=>huì +227FF=>huán +2281B=>kuò +2281D=>xīn +22821=>sào +2282B=>shù +2282C=>què +2282D=>bā +2282E=>tuì +22832=>fù +22833=>biē +22835=>tǎng +22837=>xiàng +22839=>sī +2283A=>bó +2283C=>mái +2283D=>dàng +2283F=>guì +22840=>hēi +22841=>xī +22842=>dàng +22843=>yì +22845=>bī +22847=>gū +22848=>cuì +22849=>sè +2284D=>gé +2284E=>yù +2284F=>nǎ +22851=>lì +22852=>zhì +22870=>zhào +22874=>jī +22875=>ruǎn +22879=>chòng +22882=>jié +2288C=>chàng +2288D=>zhé +22892=>sù +22893=>yōng +22896=>qì +22897=>zhuó +2289A=>kài +2289C=>yè +2289E=>qì +228B9=>xiòng +228C9=>yī +228CA=>chǒu +228CE=>tuǎn +228CF=>ài +228D0=>pīn +228D3=>liè +228D4=>mián +228D5=>ài +228D7=>mǒ +228D8=>wèi +228D9=>yìng +228DA=>nǐ +228DE=>bó +228E0=>liù +228F3=>ruì +228FB=>lǘ +228FC=>chá +228FF=>chù +22901=>sào +22902=>lí +22904=>sōng +22906=>lì +2290B=>xì +2290D=>yān +2290E=>cuō +22910=>liú +22918=>méng +2291A=>zhàn +22924=>zhuàng +22927=>miǎo +22929=>lì +2292B=>jǔ +2292F=>xiè +22930=>xiè +22931=>lǒng +22932=>lóng +22942=>téng +22943=>zhù +2294B=>chán +2294C=>xiǎn +2294F=>yíng +22950=>pèi +22958=>xié +2295A=>jiào +2295E=>chōng +22973=>hē +2297D=>tǔn +22985=>hǒng +22988=>mán +2298A=>jīn +2298C=>qú +2298D=>dǒu +2298E=>qiú +2298F=>zāi +22991=>shēng +22992=>zāi +22995=>yǐ +2299A=>huà +2299F=>kān +229B0=>yuè +229B1=>nì +229B2=>sī +229B4=>wǒ +229B8=>cán +229BA=>jiān +229BC=>miè +229BD=>sháo +229BF=>rǒng +229C0=>gān +229C5=>qiáng +229C7=>shú +229C8=>zhuó +229CF=>shī +229D1=>tì +229D6=>zhá +229D7=>zhān +229DD=>fèn +229DE=>miè +229E0=>zè +229E4=>zhì +229E5=>qiān +229E6=>hàn +229E7=>gé +229EE=>cán +229F0=>guó +229F1=>jiāo +229F3=>yōng +229F4=>áo +229FB=>zhá +229FD=>xì +22A01=>xū +22A02=>wǔ +22A0F=>jué +22A10=>jī +22A12=>chì +22A14=>wǎn +22A16=>miè +22A17=>zéi +22A1C=>jié +22A1D=>shí +22A1F=>xī +22A21=>è +22A25=>hù +22A26=>hù +22A28=>lì +22A2B=>chù +22A2E=>yī +22A2F=>mǎo +22A30=>xū +22A31=>zhōng +22A33=>yì +22A3A=>liáo +22A3F=>jiān +22A40=>jiǎn +22A41=>jú +22A44=>zhù +22A48=>wǔ +22A4F=>kè +22A50=>kě +22A51=>lì +22A52=>bǐ +22A53=>gé +22A55=>xū +22A56=>shā +22A57=>líng +22A58=>kē +22A5E=>bó +22A5F=>biān +22A60=>shuān +22A61=>qí +22A62=>shàn +22A66=>jī +22A68=>qiǎo +22A6E=>yì +22A6F=>jué +22A70=>zhǎng +22A72=>xìn +22A77=>tuō +22A78=>hài +22A79=>xià +22A7B=>tuó +22A7C=>yí +22A83=>cù +22A87=>jiāng +22A88=>nán +22A8B=>pěng +22A8D=>jié +22A8E=>xuē +22A8F=>hú +22AA5=>yǒu +22AA6=>nǔ +22AA7=>yè +22AAA=>yìn +22AAC=>kǒng +22AB6=>xiāo +22AB7=>xiāng +22ABC=>náo +22ABE=>zhàng +22AD0=>jié +22AD3=>nǔ +22AD4=>shàn +22AE2=>jiá +22AE7=>zhǒu +22AE8=>rǒng +22AEB=>lù +22AEC=>sà +22AED=>nù +22AEF=>bó +22AF0=>zhé +22AF2=>qǐn +22AF4=>cī +22AF5=>zú +22AF7=>wǒ +22AF8=>wǔ +22AFB=>nié +22AFF=>xiān +22B00=>hóng +22B2B=>tìng +22B2C=>jǐn +22B31=>jié +22B32=>hè +22B33=>tū +22B34=>zhé +22B35=>pīn +22B36=>jìn +22B37=>nàn +22B3C=>dùn +22B3E=>xī +22B3F=>xiè +22B41=>xì +22B42=>láo +22B43=>duǎn +22B44=>jì +22B45=>chā +22B46=>chōu +22B48=>gāng +22B4E=>xiáng +22B4F=>dǎo +22B65=>biàn +22B66=>xiāo +22B67=>xīn +22B81=>yǔ +22B82=>xián +22B83=>lí +22B84=>qiǎn +22B87=>měi +22B89=>qiāo +22B8A=>yà +22B8C=>qiā +22B8D=>qiòng +22B8F=>bàng +22B90=>zhēng +22B9A=>zè +22B9B=>shuàn +22B9E=>sào +22BC5=>lù +22BC9=>xié +22BCB=>fǔ +22BCC=>zhài +22BE9=>zè +22BEB=>duàn +22BED=>dèng +22BEE=>yù +22BF0=>lǜ +22BF2=>wàn +22BF3=>xué +22BF4=>jiǎo +22BF5=>yuě +22BF6=>zhì +22BF7=>wěi +22BF9=>gé +22BFA=>jǔ +22BFC=>yǎn +22BFD=>cuò +22BFE=>mào +22C06=>fú +22C07=>āi +22C0A=>xuān +22C0C=>gāng +22C0D=>ān +22C12=>jí +22C18=>pí +22C19=>zhǐ +22C1C=>nuó +22C3F=>pàn +22C41=>yí +22C44=>jié +22C46=>zī +22C48=>jià +22C49=>wǎi +22C4C=>jià +22C5F=>chǎn +22C61=>suǒ +22C62=>suǒ +22C63=>jí +22C64=>sǒng +22C66=>tī +22C67=>pī +22C68=>pó +22C6E=>mì +22C74=>yè +22C76=>qìn +22C77=>jìn +22C7A=>juē +22C7D=>yuān +22C7E=>ruán +22C94=>bàn +22CB0=>bīn +22CB4=>wèi +22CB5=>zào +22CB6=>qiè +22CB7=>sōu +22CB8=>lǔ +22CBC=>dié +22CBD=>chuāi +22CBE=>bì +22CBF=>zhú +22CC0=>mā +22CC1=>fèi +22CC2=>piē +22CC3=>yìn +22CC4=>xuàn +22CC6=>ào +22CC7=>zhuó +22CC8=>zú +22CCB=>bǐ +22CD1=>làng +22CD3=>tì +22CD9=>tiǎo +22CDA=>jiān +22CDF=>tǒng +22CFD=>duō +22CFE=>dòng +22D02=>biǎn +22D20=>zhì +22D22=>fén +22D26=>káng +22D27=>zhì +22D28=>zhāi +22D29=>bì +22D2A=>kuǎn +22D2C=>bàn +22D2D=>juē +22D2E=>qū +22D30=>qī +22D31=>léi +22D32=>xié +22D33=>tāng +22D3C=>sōu +22D3E=>bèi +22D47=>yàng +22D48=>jiǎn +22D65=>zào +22D80=>zhuài +22D83=>fán +22D85=>shé +22D87=>qióng +22D89=>pò +22D8B=>tiě +22D8C=>shā +22D8D=>zá +22D91=>niǎo +22D92=>guài +22D93=>cuǐ +22DA1=>qiào +22DA3=>dié +22DB3=>pīn +22DB4=>cí +22DB6=>bàng +22DCD=>yìn +22DD1=>xiǎn +22DD4=>yǐ +22DD5=>miǎo +22DD6=>duǎn +22DD7=>zhòu +22DD9=>kōng +22DE2=>zhāng +22DF6=>liú +22DF8=>zhǐ +22DF9=>chǎn +22DFA=>dú +22DFB=>yuán +22DFE=>suò +22DFF=>jié +22E00=>lì +22E01=>gǒng +22E0C=>bāng +22E17=>guó +22E18=>liáo +22E19=>shěn +22E23=>niǎo +22E25=>cuàn +22E26=>wěi +22E28=>tuō +22E2B=>sū +22E2D=>lóng +22E33=>xiāo +22E34=>yǎn +22E43=>qǐng +22E4D=>xī +22E4F=>yú +22E51=>zhèng +22E52=>xiè +22E53=>chāi +22E54=>fèn +22E56=>guó +22E58=>jǐng +22E59=>làn +22E5A=>xiān +22E5D=>líng +22E6E=>lěi +22E72=>jùn +22E73=>xiào +22E7C=>zá +22E84=>guān +22E85=>qiè +22E86=>luò +22E87=>yào +22E88=>luán +22E89=>tà +22E91=>luò +22E9E=>bǎ +22E9F=>chàn +22EA1=>zhuó +22EAB=>tiǎo +22EAF=>wān +22EB0=>líng +22EB4=>yù +22EB5=>qì +22EB7=>qí +22EBC=>jì +22EBD=>bó +22EBF=>shī +22EC0=>fǔ +22EC2=>guī +22EC5=>diǎn +22EC7=>hāo +22EC9=>gǎi +22ECB=>qí +22ED3=>chéng +22ED4=>huì +22ED7=>xiá +22ED8=>shí +22ED9=>zhì +22EDA=>qí +22EDC=>hài +22EDF=>jiǎo +22EE0=>lì +22EE2=>liǎo +22EE4=>qiāo +22EE8=>sà +22EEA=>qī +22EEB=>shī +22EEE=>jié +22EF5=>bèi +22EF6=>biān +22EF7=>bā +22EF8=>jūn +22EF9=>pī +22EFC=>dǎn +22EFF=>táng +22F00=>kuǐ +22F01=>kū +22F03=>kǒu +22F09=>shī +22F0A=>shī +22F0B=>jī +22F0C=>bào +22F10=>kě +22F11=>kuāng +22F16=>mǐn +22F19=>liáo +22F1A=>è +22F1B=>gé +22F1F=>wǎng +22F20=>duó +22F23=>qià +22F24=>huá +22F26=>hǒng +22F29=>pēng +22F2B=>jiào +22F30=>qū +22F31=>zì +22F32=>zhòu +22F33=>kuāng +22F35=>shā +22F37=>jì +22F38=>wēi +22F39=>pū +22F3A=>xué +22F3C=>shāo +22F42=>láng +22F43=>zhǐ +22F44=>tǐng +22F47=>dà +22F55=>yáng +22F56=>jìn +22F57=>zhǐ +22F5A=>zhuó +22F5C=>zá +22F5D=>chán +22F62=>mào +22F66=>kōng +22F67=>zhōu +22F68=>hū +22F69=>pēng +22F6D=>jiù +22F78=>chuò +22F79=>mǐn +22F7E=>xiào +22F80=>dǔ +22F81=>wéi +22F83=>cán +22F84=>yú +22F85=>dù +22F86=>kāi +22F87=>pì +22F8A=>chéng +22F8E=>chǔn +22F90=>shǎo +22F91=>yǎn +22F92=>kuài +22F94=>yuē +22FA6=>qí +22FA7=>zhēng +22FA9=>kè +22FAA=>qí +22FAB=>zhǐ +22FAC=>lù +22FB1=>pī +22FB2=>nuò +22FB3=>pǎo +22FBA=>fěi +22FBF=>wén +22FC2=>méng +22FC8=>shǎn +22FCC=>xiòng +22FCE=>duò +22FCF=>biào +22FDA=>yōu +22FDC=>màn +22FDE=>liǎo +22FE1=>xié +22FE2=>luàn +22FE3=>qiāo +22FE4=>dèng +22FE6=>chéng +22FE7=>chéng +22FED=>chuò +22FF8=>cè +23000=>léi +23001=>zhǎn +23002=>lǐ +23003=>lián +23004=>qún +2300D=>chén +2300F=>chéng +23010=>gū +23012=>zòng +23013=>chóu +23014=>chuàn +2301C=>lèi +2301D=>shuò +2301E=>lǜ +23023=>fú +23025=>lì +23027=>sàn +2302B=>sān +2302F=>sà +23033=>niè +23036=>zuān +23037=>lǐ +2303B=>shǔ +2303E=>fú +23049=>bì +2304D=>dào +23052=>shī +23056=>gàn +23057=>tàn +2305C=>màn +2305F=>lí +23062=>bì +23066=>pán +23068=>yōu +2306D=>jiū +2306F=>guō +23070=>liáo +23073=>wò +23074=>qià +23075=>dǒu +23077=>liè +23079=>jiǎo +2307B=>liè +23081=>tiāo +23084=>guō +23086=>pāng +23087=>qiāo +23089=>dí +2308A=>yùn +23092=>lè +23096=>sī +23097=>xīn +2309C=>xīn +2309D=>xiàng +2309E=>luǒ +230A4=>bēng +230A5=>tiāo +230AC=>xiào +230AE=>dōu +230B3=>dàng +230B4=>tíng +230B5=>zhuàn +230BB=>ōu +230BD=>wò +230C4=>xīn +230C5=>ruǎn +230C8=>zhuó +230C9=>dàng +230CD=>cuì +230D1=>zhuó +230D7=>cóng +230D8=>chǎn +230DD=>yǎng +230E7=>yǎn +230F3=>yǎn +230F5=>zhèn +230FD=>nuǒ +230FE=>yàn +23105=>fǎng +23109=>yǎn +2310A=>yú +2310D=>tí +2310E=>fù +2310F=>běn +23111=>yǎn +23113=>huī +23119=>huǎng +2311C=>guì +2311D=>yàn +2311F=>hú +23120=>biāo +23127=>suì +2312E=>zì +2312F=>jì +23130=>ě +23131=>jì +23132=>kuǐ +23134=>liàng +23138=>huò +2313A=>wéi +2313B=>zhuō +2313F=>tǐng +23143=>zǎi +23144=>yòu +23149=>rèn +2314D=>miàn +2315A=>nà +2315D=>tū +2315F=>dān +23161=>jué +23164=>xū +23165=>dī +23170=>xiàng +23177=>xiòng +2317A=>yǒu +2317B=>guǎ +2317E=>xī +23188=>hè +2318D=>dǐng +23190=>lú +23192=>xú +23194=>zhòu +23195=>xiàn +23196=>huāng +23197=>chā +23198=>shǐ +23199=>gàn +2319A=>nuǒ +2319B=>àn +2319F=>xiē +231A7=>hào +231B2=>qīn +231B3=>gěng +231B4=>shān +231B5=>fú +231BD=>zè +231C7=>dàn +231D6=>diǎn +231D7=>shēn +231D9=>zǔ +231E2=>biē +231E6=>chuí +231E7=>zhè +231E8=>dài +231EB=>wǒ +231EC=>qióng +231F0=>lín +231F2=>hūn +231F3=>jī +23205=>cáo +2320A=>mù +2320D=>dié +2320E=>wèi +23220=>biàn +23221=>tǐ +23225=>tú +23236=>gèng +23244=>chí +23245=>còu +23246=>tǐ +23252=>huò +23253=>qī +23254=>sāo +23255=>sàng +23256=>xuǎn +23257=>àng +23258=>nài +2325A=>yáng +2325B=>shū +2325C=>shā +23261=>tǐng +23269=>yà +2326A=>huǎng +2326E=>bīn +2327E=>òu +2327F=>cáo +23281=>áo +23283=>mào +23294=>méng +23296=>tiān +2329D=>sàng +2329E=>xù +2329F=>kàn +232A7=>lǎng +232B6=>biē +232B7=>cóng +232BA=>xián +232C4=>tūn +232C9=>yù +232CA=>dàn +232CB=>yìng +232CD=>zhāo +232CF=>pù +232D8=>huì +232DE=>ài +232DF=>mǒ +232E2=>jīng +232E3=>lán +232F2=>liè +232F3=>piǎo +232F5=>bó +232F6=>qióng +232F9=>bì +232FF=>yōng +23305=>lì +2330D=>niè +2330F=>dé +23313=>huān +23317=>yuè +2331A=>chūn +2331C=>lì +2331E=>zhāng +2331F=>líng +23320=>chún +23327=>cè +23328=>xún +2332C=>jǔ +2332D=>hui +2333E=>tōng +23346=>níng +23347=>jù +2334F=>chà +23356=>zāo +2335B=>yù +2335F=>kěn +23366=>kuàng +23367=>fěi +2336F=>yùn +23370=>qiǎn +23374=>quán +23378=>pò +2337A=>pěi +23384=>gèng +23385=>yì +23386=>luò +23391=>kuān +23393=>xuǎn +23394=>niàn +2339A=>hú +2339B=>jú +233A9=>yè +233AE=>xī +233B1=>yuè +233B2=>tǎng +233B3=>pìn +233B4=>dǔn +233B5=>bèi +233B8=>liǎo +233C0=>yǒng +233CE=>yā +233D1=>jiǎo +233D4=>kùn +233D6=>zhèn +233D7=>shù +233DA=>shí +233DE=>yóu +233DF=>pài +233E0=>xiáo +233E1=>jí +233F6=>qī +233F7=>hé +233FA=>kǒng +23402=>yè +23403=>chì +2340A=>kǎo +2340B=>yuè +2340E=>wǎ +2340F=>niǎn +23411=>cí +23413=>yí +23424=>jiu +2342B=>yāng +2342C=>lí +2342E=>dāi +2342F=>chóng +23435=>yí +2343A=>hàn +2343F=>yī +23441=>chòng +23442=>hù +23443=>zhuǎ +23466=>qióng +23467=>duò +23478=>tóng +23479=>xiān +2347F=>fú +23482=>diàn +23483=>xí +23484=>xiē +23485=>zhèn +23486=>qiào +23487=>tū +234B7=>hàn +234B8=>kuàng +234B9=>suō +234BB=>shòu +234BC=>tiáo +234C0=>zhēn +234C3=>nèi +234C5=>qiǎn +234C6=>yín +234C8=>liǎng +234C9=>shà +234CA=>zì +234CB=>pí +234CC=>gāo +234CF=>jìn +234D0=>yóu +234D2=>shàn +234D4=>mì +234D5=>òu +234D7=>hū +234DB=>yòu +234DD=>měng +23510=>zhǐ +23513=>bǐ +23517=>shēn +23518=>qì +23519=>xiān +2351A=>pán +2351B=>kǎng +2352B=>shuān +2352C=>pí +2352E=>zāi +2352F=>zhǔ +23531=>sōu +23532=>jiǒng +23535=>chán +23536=>fán +23537=>xiáo +23538=>yǐn +23539=>hóu +2353A=>mào +2353B=>tú +2353D=>jì +23541=>yí +23543=>yù +23544=>jiōng +23545=>pào +23547=>xiāo +23549=>gǒu +2354C=>gōu +2354D=>sǔn +2354E=>xiǎn +2354F=>zhuǎn +2357E=>chóu +23584=>qiāo +23585=>tí +23586=>yún +23589=>shān +2358A=>liè +2358C=>zhǐ +23590=>pāi +235A3=>jú +235A4=>lái +235A8=>zǐ +235AA=>qú +235AB=>gǔ +235AC=>jué +235AD=>zhí +235AE=>àng +235AF=>qìn +235B0=>pí +235B1=>zuī +235B3=>qián +235B5=>cuó +235B7=>jí +235B8=>tí +235B9=>rú +235BB=>hǎi +235BC=>xún +235BE=>bèi +235BF=>zhí +235C1=>dùn +235CB=>dǎng +235D0=>réng +235F2=>gān +235F5=>gàng +235F6=>tà +235F8=>tuò +235F9=>yàng +235FA=>kū +235FB=>zhì +23616=>jiān +23617=>nì +23618=>shēn +23619=>bàng +2361A=>shuài +2361B=>dōu +2361D=>qiān +2361E=>hán +2361F=>qiā +23620=>gǎn +23623=>chún +23624=>chá +23625=>bì +23626=>yī +23627=>fū +23628=>ě +2362A=>láo +2362B=>háo +2362C=>lí +23631=>tè +23632=>shēn +23634=>yín +23637=>jiān +2363B=>chá +23657=>niè +23658=>còu +2365B=>yí +2365F=>táng +23662=>juàn +23670=>chì +23671=>gǒu +23674=>jié +23675=>zhé +23676=>hú +23677=>máng +2367B=>zōu +2367C=>sì +2367F=>fèi +23680=>zī +23681=>zī +23683=>jié +23684=>sī +23686=>chūn +23687=>pào +2368B=>yé +2368C=>dī +2368E=>léi +2368F=>xū +23690=>rú +23692=>pá +23693=>juàn +23694=>xì +23695=>yè +23696=>ān +23698=>yì +23699=>jiān +2369C=>sōng +2369D=>wǒ +2369F=>sè +236A0=>zhǐ +236A1=>bī +236A2=>zhuàn +236A6=>jiàng +236A7=>hào +236A9=>chì +236AA=>dùn +236D3=>bó +236D4=>jí +236D5=>chuǎ +236D7=>luò +236DA=>ruǐ +236EB=>hú +236F1=>dàn +236F4=>hǎn +236F5=>què +236F6=>shā +236F7=>zhǎn +236F8=>zé +236F9=>chuán +236FA=>qī +236FB=>dié +236FD=>zhà +236FE=>tòu +23701=>cī +23702=>sà +23704=>luó +23707=>jí +23722=>luǒ +23723=>qín +23727=>qióng +23728=>juàn +2372C=>ài +2372D=>jiǎn +23739=>tì +2373A=>wén +2373D=>qiāo +23741=>pái +23742=>hún +23745=>ài +23747=>shuò +23748=>lián +23749=>duì +2374B=>tà +2374C=>jǐn +2374D=>bì +2374E=>yǎn +2374F=>gào +23750=>piáo +23751=>yù +23752=>shè +23755=>jiān +23757=>hú +2375A=>liè +2375C=>biàn +2375D=>sù +2375E=>jiāo +23778=>zhuì +2377D=>hān +23787=>dùn +23790=>xiě +23791=>méng +23792=>fū +23793=>lù +23794=>tàn +23797=>liú +23798=>xiān +23799=>sǎng +2379C=>còu +2379D=>zhuāng +2379F=>chēn +237B0=>liàn +237B4=>lí +237C0=>pèng +237C1=>tuǒ +237C4=>tuò +237C6=>liáo +237C7=>xiào +237C8=>chuì +237C9=>huài +237CA=>niǎo +237CB=>qiān +237CC=>lì +237CF=>pāo +237D0=>tiáo +237D1=>liú +237D2=>wú +237E4=>yǐng +237E6=>zhá +237F0=>yú +237F2=>xiǎn +237F3=>xuán +237F4=>shuān +237F5=>xī +237F8=>méi +237F9=>sēn +237FA=>liàn +237FC=>jiū +237FD=>lào +2380E=>xiāo +2380F=>zōu +2381A=>liú +2381C=>zhào +2381E=>zhé +23820=>lěi +2382D=>duǎn +23837=>jiǎn +23838=>shuān +23839=>zuó +2383A=>qiè +2383C=>lǎo +23849=>yù +2384A=>yì +2384B=>nǐ +2384E=>cén +23855=>yàn +23857=>ruǎn +2385E=>yán +2385F=>dié +23860=>mián +23867=>léi +23869=>wān +23870=>nǎ +23876=>yán +2387A=>lěi +2387D=>shā +2387E=>hū +23881=>xī +23882=>xī +23884=>yǒu +23885=>hān +23887=>hāi +23889=>wā +2388A=>xù +2388B=>pī +2388C=>tān +2388D=>xī +2388E=>xī +2388F=>bīn +23890=>qīn +23891=>xī +23892=>yú +23893=>xì +23895=>cì +23896=>qiàn +23897=>xiā +2389A=>wá +2389B=>è +2389C=>yǒu +2389D=>xìng +2389E=>ní +2389F=>hán +238A0=>bì +238A1=>shēng +238A4=>zhān +238A5=>diàn +238A6=>yǔ +238A8=>ǒu +238AA=>guǐ +238AB=>wǎng +238AC=>qiān +238AD=>yí +238B0=>zú +238B2=>qiān +238B3=>dìng +238B4=>kēng +238B6=>chù +238B7=>yī +238BA=>hān +238BB=>kuǎn +238C8=>diàn +238C9=>xì +238CA=>zī +238CB=>líng +238CC=>zì +238CE=>yù +238CF=>hūn +238D1=>sǐ +238D2=>kǎn +238DA=>àn +238DC=>yǒu +238DD=>jí +238DE=>hùn +238DF=>qiā +238E0=>hóu +238E1=>hóu +238E3=>diàn +238E9=>xiē +238ED=>shè +238EE=>shà +238F2=>xié +238F3=>yáo +238F4=>dà +238F6=>xiè +238F7=>chī +238F8=>yǒu +238F9=>hē +238FA=>shà +238FF=>tái +23901=>zhú +23903=>ǎi +23907=>què +23908=>zé +2390A=>lā +2390B=>lòu +2390C=>chuài +2390E=>yǒu +23916=>tì +23918=>shī +23921=>xiào +23922=>xì +23928=>huò +23929=>chì +2392A=>yì +2392F=>shú +23930=>yuè +23931=>chán +23932=>è +23933=>xī +23934=>xī +23935=>yǐng +23936=>zú +23937=>zā +2393A=>zā +23942=>tà +23943=>wàn +23947=>xìn +2394A=>wàng +2394B=>fǔ +23950=>lǔ +2395E=>jiǎn +23961=>yán +23963=>bì +23964=>kěn +23965=>guàn +23968=>zī +2396E=>kuǐ +2396F=>zhǒu +23970=>zhì +23973=>tú +23977=>tà +23979=>chù +2397A=>chēng +2397B=>chěng +2397C=>zhù +2397E=>dà +23987=>bì +23989=>jiǎ +2398C=>yì +2398F=>yuè +23990=>gāng +23996=>gān +2399C=>qiāo +239A0=>chú +239A1=>chú +239A2=>bì +239A6=>guì +239A9=>gǔ +239AA=>bǐng +239AB=>yìn +239AC=>zhuì +239AD=>gǔ +239AF=>lì +239B5=>è +239B6=>dǎi +239BC=>cán +239C2=>tì +239C3=>dù +239C4=>yì +239C8=>dié +239CA=>niǔ +239CC=>xuè +239CD=>nè +239CE=>guì +239CF=>kǎo +239D2=>chuǎn +239D6=>zhá +239D7=>yóu +239D9=>bài +239DA=>shí +239DB=>diàn +239DC=>pā +239DD=>qiú +239E1=>xuè +239E3=>mò +239E4=>kē +239E5=>yǒu +239E6=>jiǎo +239E7=>bó +239EC=>xiǔ +239F2=>mǐ +239F3=>luò +239F5=>xuè +239F7=>duò +239F9=>èr +239FA=>shān +239FC=>kuì +239FD=>nào +239FE=>miǎn +239FF=>lì +23A00=>luàn +23A02=>dié +23A04=>qià +23A05=>lèi +23A07=>mào +23A09=>hēng +23A0A=>chè +23A0B=>zhì +23A0D=>gǔ +23A0E=>cuō +23A13=>wù +23A14=>tào +23A17=>xī +23A18=>yāo +23A19=>wěi +23A1B=>zú +23A1C=>mà +23A1D=>yǔ +23A1E=>pěng +23A1F=>yì +23A20=>qìn +23A21=>yuè +23A22=>juè +23A23=>jiàng +23A24=>xù +23A25=>bēng +23A2A=>luǒ +23A2B=>zhuī +23A32=>dù +23A33=>xiàng +23A36=>huì +23A3A=>gǔ +23A3B=>kǎo +23A3E=>xīng +23A3F=>hún +23A40=>biān +23A44=>kè +23A45=>kǎo +23A48=>cuó +23A4F=>lù +23A51=>zuì +23A52=>zāo +23A53=>jiǎo +23A54=>guàn +23A59=>yān +23A5A=>ér +23A5C=>qíng +23A5F=>dèng +23A60=>sì +23A61=>suì +23A62=>liào +23A67=>shàn +23A69=>bì +23A6A=>wèi +23A6B=>yè +23A6D=>zhài +23A6F=>yé +23A70=>diào +23A71=>ài +23A74=>jiàng +23A77=>sū +23A79=>huài +23A7A=>yù +23A7D=>rǎng +23A80=>diān +23A81=>zuān +23A82=>bān +23A84=>qín +23A87=>jiā +23A89=>pí +23A8C=>tóu +23A90=>chóu +23A95=>guǐ +23AA0=>jī +23AA8=>xuè +23AAA=>diàn +23AAD=>biàn +23AAE=>zǎi +23AAF=>tóng +23AB6=>shǎn +23AB8=>gù +23AB9=>què +23AC0=>gǔ +23AC8=>hú +23AC9=>kuǎi +23ACC=>gòu +23ACE=>sù +23AD0=>chóu +23AD2=>kēng +23AD4=>dū +23AD9=>yì +23ADC=>dào +23ADD=>qiāng +23AE3=>lóng +23AE5=>lí +23AE7=>lì +23AE8=>qīng +23AEA=>wēi +23AEC=>móu +23AF1=>qì +23AF3=>jiǎng +23AF4=>xié +23AF9=>dài +23AFB=>lóu +23B02=>guàn +23B06=>péi +23B09=>pí +23B0B=>juàn +23B0D=>bēi +23B0E=>jué +23B0F=>juàn +23B10=>shì +23B15=>xiě +23B18=>ruí +23B19=>jìng +23B1A=>pò +23B1B=>sān +23B20=>jī +23B29=>fēn +23B2A=>bèi +23B2B=>jiè +23B2C=>sā +23B2E=>pī +23B34=>dì +23B35=>máo +23B36=>ba +23B37=>ba +23B38=>tiáo +23B39=>líng +23B3A=>shēng +23B3B=>zhěn +23B3C=>pī +23B3D=>wù +23B3F=>zè +23B40=>bào +23B47=>lǚ +23B56=>hāo +23B57=>dǒu +23B58=>fú +23B59=>ní +23B5D=>gé +23B60=>rú +23B61=>xiǎn +23B64=>bì +23B6E=>máo +23B72=>rǒng +23B73=>qiú +23B77=>bó +23B79=>hāo +23B7A=>nǎo +23B7B=>yán +23B83=>páo +23B84=>suī +23B86=>tuò +23B88=>qū +23B89=>lí +23B8A=>dé +23B8C=>jié +23B8D=>jié +23B8E=>gǔn +23B8F=>jiān +23B90=>bì +23BA0=>sàn +23BA1=>bāng +23BA2=>chún +23BA6=>nài +23BA7=>bǎng +23BAA=>róng +23BAB=>jiā +23BAC=>sōu +23BB0=>dé +23BBE=>xiān +23BBF=>zhān +23BC0=>mào +23BC3=>zī +23BC5=>jì +23BC6=>qí +23BCB=>rù +23BCC=>suō +23BCD=>rǒng +23BCE=>wù +23BCF=>róng +23BD0=>róng +23BDA=>tà +23BDC=>sōu +23BE4=>lí +23BE7=>cuǐ +23BE8=>zōng +23BE9=>mén +23BEA=>xǐ +23BEC=>mǎng +23BED=>niè +23BEF=>suī +23BF1=>péi +23BF4=>bì +23BF5=>dì +23BF8=>qú +23BF9=>qiáo +23BFB=>fēn +23BFC=>sù +23C03=>xū +23C07=>rǒng +23C08=>jī +23C0B=>qú +23C0C=>liè +23C15=>sào +23C18=>kùn +23C1A=>cuì +23C1B=>yè +23C1C=>bìng +23C1E=>jié +23C20=>qú +23C21=>qú +23C25=>méng +23C26=>rán +23C28=>bīn +23C29=>cháo +23C2C=>dú +23C36=>ráng +23C37=>xiān +23C3A=>táo +23C3B=>qú +23C3C=>niè +23C3F=>shū +23C40=>lǔ +23C42=>kùn +23C48=>mín +23C49=>mǐn +23C4D=>dàn +23C50=>yìn +23C53=>xiào +23C57=>jì +23C5C=>yīn +23C66=>fēn +23C67=>zhòng +23C6B=>gǔ +23C71=>chá +23C73=>liú +23C76=>bǔ +23C7A=>pā +23C7B=>sì +23C7C=>dāo +23C7D=>zhěn +23C80=>shān +23C82=>chuǎi +23C84=>jiǔ +23C8A=>kè +23C8B=>chí +23C91=>hù +23C92=>lì +23C93=>shā +23C96=>pài +23C97=>wéi +23C98=>wǔ +23C9C=>yíng +23CA1=>shā +23CA2=>dī +23CA5=>dān +23CB1=>tū +23CB2=>hé +23CB3=>pǒ +23CB5=>zhǐ +23CB6=>niǔ +23CB7=>nì +23CBD=>rǒng +23CBE=>guài +23CC0=>zhí +23CC3=>jí +23CDC=>fàn +23CDF=>jié +23CE0=>hǎi +23CE4=>zhàn +23CE6=>xì +23CE9=>zī +23CEC=>xí +23CED=>piào +23CF0=>bēn +23CF2=>jiǎn +23D13=>jiàn +23D16=>zá +23D1E=>bèn +23D1F=>mào +23D22=>zào +23D23=>zhuàng +23D25=>kuáng +23D28=>bí +23D2A=>pài +23D3C=>mào +23D3D=>tàn +23D5E=>tǔn +23D5F=>luǒ +23D62=>tān +23D71=>án +23D77=>hán +23D78=>zhú +23D7A=>duò +23D7B=>duò +23D7C=>gàn +23D86=>qiòng +23D88=>wǎng +23D8A=>mò +23D8B=>zhè +23D8C=>wěn +23D8D=>zhuàng +23D8F=>jiē +23D90=>pào +23D98=>sù +23D9D=>jù +23DA0=>qī +23DA1=>càn +23DA3=>tuán +23DA4=>shā +23DA6=>tuó +23DA9=>huà +23DAB=>yì +23DE0=>mín +23DE1=>zhōng +23DE5=>shuò +23DE9=>yì +23DEA=>wǎng +23DEB=>áo +23DF6=>sǔ +23DFE=>guǐ +23DFF=>tuǒ +23E00=>huǐ +23E03=>xù +23E04=>zǎn +23E06=>zǐ +23E07=>biàn +23E09=>dá +23E0A=>yīn +23E0B=>quǎn +23E0E=>huài +23E0F=>ná +23E10=>zá +23E12=>tí +23E18=>yí +23E19=>tān +23E1A=>shé +23E1B=>shuò +23E1D=>xíng +23E20=>yǒu +23E23=>fén +23E47=>kè +23E4B=>fú +23E52=>mǐn +23E5A=>pì +23E5C=>jí +23E5D=>qiào +23E5E=>zhǒng +23E5F=>gàn +23E60=>yuān +23E61=>chí +23E65=>qiàn +23E67=>zuó +23E69=>xié +23E6A=>máo +23E6C=>hú +23E6E=>pì +23E6F=>xùn +23E71=>xiá +23E72=>tí +23E75=>nà +23E76=>chuǎ +23E80=>wǔ +23EAC=>huāng +23EAD=>xuè +23EAE=>tào +23EB0=>qiào +23EB3=>jiāo +23EBC=>dǎng +23EBD=>bài +23ECD=>dàng +23ECE=>kòu +23ED0=>jū +23ED1=>shā +23ED2=>jīng +23ED5=>mó +23ED6=>nóu +23ED8=>shuò +23EDA=>shù +23EDB=>zhuāng +23EDC=>fú +23EDF=>zāng +23EE0=>xié +23EE1=>làng +23EE2=>tōng +23EE9=>zhé +23EEC=>càn +23EEE=>yuè +23EF1=>zhòu +23F1A=>tān +23F1E=>yán +23F1F=>lù +23F20=>yǎn +23F26=>zé +23F27=>shuài +23F45=>guō +23F46=>zhú +23F48=>rú +23F49=>rú +23F4C=>kǎn +23F4D=>jì +23F4E=>gāo +23F52=>xiè +23F55=>òu +23F56=>jiān +23F5A=>zhí +23F5B=>zhá +23F5D=>hǒng +23F5F=>kuǎn +23F61=>bó +23F64=>sè +23F65=>àn +23F66=>jiàn +23F68=>téng +23F6B=>sōng +23F6D=>mèng +23F6E=>yín +23F6F=>tān +23F70=>guō +23F73=>ruán +23F74=>wèi +23F77=>sì +23FA4=>qì +23FA6=>zhǎng +23FC5=>dǒng +23FC6=>fú +23FC7=>shěn +23FC8=>sù +23FC9=>yì +23FCA=>liàn +23FCC=>hé +23FCE=>zhēn +23FD0=>zé +23FD2=>cuǐ +23FD3=>cuǐ +23FDD=>fèng +23FDE=>lǐ +23FDF=>kòu +23FE3=>xiào +23FE4=>yǒu +24003=>háo +24009=>hàn +2400A=>kěn +2401D=>yù +24023=>huǎn +24024=>suō +24026=>là +24028=>dòu +24029=>jiàn +2402A=>pō +2402B=>biǎn +24030=>xuè +24032=>biàn +24037=>wèi +24061=>dàn +24062=>jié +24063=>bài +24065=>niǎn +24066=>xiàn +24067=>sè +2406A=>huá +2406B=>chuā +2406E=>òu +2406F=>liè +24070=>dí +24071=>cài +24073=>zhá +24075=>lǘ +24079=>huò +2407C=>lì +2407D=>yǐng +2407F=>wěi +24080=>bì +24081=>guó +24083=>pì +24086=>biāo +240A0=>yǎn +240A4=>zhuàn +240B2=>hóng +240B6=>lìn +240B7=>è +240B9=>yǐn +240BA=>làn +240BC=>yào +240BF=>xuàn +240C0=>lì +240E8=>làn +240E9=>líng +240EA=>xī +240EB=>hōng +240ED=>jiǎo +240EE=>zhuó +240F2=>zhí +240F5=>bó +240F6=>tēng +240F7=>ǎn +240FA=>xún +240FB=>lěi +240FC=>zāng +240FD=>huǐ +2410E=>xì +2410F=>hóng +24111=>fàn +24112=>jiǎn +24113=>cóng +24114=>zá +24116=>cā +24118=>yōu +2411B=>duì +2411C=>pān +24125=>tà +24127=>pàn +2412B=>fān +2412C=>xī +24136=>yào +24137=>luó +2413A=>biān +2413C=>jìn +2413D=>lì +2414A=>yàn +2414B=>dòu +2414E=>màn +24150=>gōng +24151=>rǎng +24152=>càn +24163=>mén +24171=>gǔ +24172=>shuàn +24178=>yán +24179=>bì +24180=>biāo +24181=>chéng +24182=>kuì +24184=>huǒ +2418D=>chì +2418F=>wò +24191=>còu +24192=>zhì +24199=>shuǐ +2419C=>guà +2419D=>pū +2419E=>xù +2419F=>sī +241A1=>wǔ +241AE=>fū +241B0=>shì +241B3=>huì +241B4=>huāng +241B5=>pā +241BC=>zhǔ +241BE=>yí +241C3=>lì +241C4=>shǎn +241DC=>mín +241DE=>gē +241E0=>hū +241EF=>ēn +241F0=>fá +241F3=>xù +241F4=>yí +241FE=>yìng +24214=>chí +24219=>yí +24225=>dí +24226=>huǐ +24227=>hé +24229=>zhǎ +24236=>yún +24237=>xiān +2424C=>xián +2424D=>lào +2424E=>shào +2424F=>shì +24250=>zhuó +24264=>biē +24265=>jiǔ +24266=>wō +24267=>jiǎo +24268=>fú +2426A=>xiāng +2426B=>kài +242B2=>nǎo +242B4=>huò +242B5=>jí +242B6=>là +242BB=>fōu +242BC=>shǎn +242BD=>liào +242BE=>miè +242BF=>chè +242C2=>mó +242CF=>lóu +242E8=>duò +242EB=>nǎo +242ED=>jī +242F0=>zhù +24302=>sù +24303=>duò +24307=>jiǒng +2430A=>zǎi +2430B=>huǐ +2430C=>yǐng +2430D=>hú +2430E=>lìn +2430F=>wěng +24310=>hàn +24314=>nán +24337=>xì +24339=>gàn +2433E=>hè +2433F=>jī +24340=>xiǎng +24341=>shā +24350=>tuì +24352=>zhāo +24353=>shù +24355=>yǒu +24356=>jiān +2435C=>zào +24364=>zhāng +2437D=>ruò +24384=>yān +2438B=>cuì +24397=>jí +24398=>shāng +243A3=>è +243A4=>láo +243A5=>tǎn +243A7=>zhù +243AD=>lǐn +243AF=>zēng +243B1=>juǎn +243B2=>hū +243D7=>shěn +243D8=>huò +243DC=>kuì +243F1=>chù +243F2=>zhòu +243F6=>āo +243F8=>zhuó +243FD=>xīng +243FF=>miè +24400=>hū +24414=>tán +24419=>bì +24423=>dǐng +24429=>kài +2442B=>biāo +24430=>huò +24431=>liè +24432=>cuàn +24443=>xiàn +24444=>rè +24453=>yuè +24455=>xūn +24457=>liǎo +24463=>shā +24466=>shì +2446A=>xiè +24473=>xiāo +24477=>yé +24478=>lǎn +24479=>yì +2447F=>liǎn +24494=>bó +24495=>cāo +2449D=>yào +244A6=>liàn +244BB=>tà +244D1=>jì +244D4=>xī +244D5=>zhì +244DA=>xī +244DD=>yuè +244E4=>xiǎn +244E6=>zhuò +244EF=>zhǎng +244F5=>zǔ +244F7=>ná +244FE=>dào +244FF=>liè +24500=>ná +24509=>páo +2450B=>jù +24516=>luǒ +24519=>shuǎ +2451A=>shàng +2451D=>luǒ +2451F=>fēn +24523=>bào +24528=>lì +2452B=>xiòng +24536=>dāng +24540=>chèng +24544=>zhǎng +24547=>sǒu +2454A=>shén +24552=>gě +24558=>yū +2455A=>huī +2455B=>chè +2455D=>jiào +2455E=>zhù +2455F=>shū +24562=>xiáo +24566=>níng +2456D=>jiāng +2456F=>jiāng +24577=>diào +2457D=>qiáng +2457E=>qiú +24580=>fēng +24586=>zhàn +24587=>kē +24592=>dié +24593=>zé +24596=>guāng +24597=>sè +24598=>fèn +2459B=>jiǎng +2459D=>yán +2459E=>zhì +245A2=>lì +245A6=>líng +245AA=>yí +245AC=>qǔ +245AD=>pán +245AE=>gōu +245B0=>jiǎ +245B1=>hé +245B3=>pèng +245B5=>jù +245B7=>chè +245BA=>liè +245BB=>shì +245BC=>pò +245BD=>xiàng +245BF=>pì +245C0=>luǒ +245C1=>cù +245C3=>yǔ +245C7=>kòng +245C8=>xiè +245CD=>wǎn +245CE=>yǎn +245CF=>péi +245D3=>chéng +245D8=>tí +245D9=>chè +245DA=>bì +245DB=>liàn +245DC=>jiǎ +245DE=>tíng +245E2=>tī +245E8=>dié +245EA=>shù +245EB=>lí +245EC=>lǘ +245ED=>xiā +245EF=>cuī +245F3=>bō +245F4=>tuí +245F5=>pú +245F7=>lìn +245F8=>fèn +245FA=>bó +245FB=>chàn +245FE=>dāng +245FF=>tǎi +24600=>dào +24603=>lì +24605=>yá +24606=>yá +24607=>zhān +2460A=>yí +2460C=>qī +24614=>hù +24616=>tīng +24618=>kǒu +2461B=>chún +2461C=>yóu +2461D=>fèn +2461F=>nuó +24620=>tiàn +24621=>jìn +24622=>pí +24623=>chén +24624=>pì +24626=>jiè +24627=>guǐ +24632=>zhuàng +24635=>hú +24636=>chǒu +24637=>shù +24638=>tāo +24639=>pí +2463A=>rǒng +2463B=>rǒng +2463D=>hǒu +2463E=>pēng +24645=>bài +24647=>xiá +2464B=>qǐn +2464C=>nǐ +2464E=>tāo +2464F=>qù +24652=>xié +24654=>zhào +24655=>huā +24656=>xīn +24658=>shōu +2465B=>tú +2465D=>liáng +2465E=>bì +2465F=>chū +24661=>xīng +24663=>xīn +24664=>fū +24669=>jiè +2466D=>fǔ +24670=>tè +24671=>shè +24674=>chāo +24675=>chuī +2467C=>rán +2467D=>hǒu +2467E=>bēng +24680=>cǎi +24685=>mú +24689=>xū +2468A=>dié +2468D=>chǎn +2468E=>yú +2468F=>zhòng +24693=>lí +24694=>shōu +2469A=>dú +2469C=>māo +2469D=>huáng +2469F=>táo +246A1=>dù +246A2=>tí +246A3=>shēng +246A4=>méi +246A8=>zhēn +246A9=>qín +246AA=>pì +246AB=>táng +246AC=>cāng +246AD=>yáo +246AF=>xiù +246B0=>bāng +246B1=>gǔ +246B5=>bù +246BC=>gòu +246BD=>bó +246C1=>wèn +246C4=>jì +246CA=>lā +246CD=>cuī +246CE=>mǐn +246CF=>cǔ +246D0=>ōu +246D1=>yōng +246D6=>máo +246D7=>kè +246D8=>māng +246D9=>dǐng +246DA=>huān +246DB=>duǒ +246DC=>jiāng +246DD=>sù +246E2=>céng +246E3=>tà +246E5=>huáng +246E6=>jué +246E7=>xún +246EA=>xiòng +246EC=>mì +246ED=>qún +246EE=>láo +246F1=>zhì +246F2=>wěi +246F7=>sè +246FB=>zāng +24701=>ǎn +24702=>wèi +24704=>huài +24707=>zhàn +24709=>yīng +2470A=>gē +2470B=>huì +2470D=>quán +24713=>liè +24714=>jú +24715=>bà +24716=>léi +24718=>mán +24719=>líng +2471C=>lì +2471D=>jǐ +24721=>huí +24722=>xìn +24723=>shì +24724=>zhé +24727=>bō +2472B=>chā +2472F=>chā +24730=>jīng +24731=>bā +24732=>bèi +24735=>yàn +24737=>hù +24739=>yú +2473B=>bì +2473C=>chuán +2473E=>jǐ +24742=>mù +24744=>máo +24745=>zhōng +24747=>yè +24748=>dōu +24749=>yě +2474D=>rì +2474E=>yīn +24750=>hào +24752=>nà +24753=>tiè +24754=>fù +24755=>mǔ +24756=>zǎi +24758=>hú +2475A=>chēn +2475B=>tuó +2475E=>chù +2475F=>fú +24767=>bào +2476C=>dǐ +2476D=>cǎi +2476E=>lù +2476F=>pǒ +24770=>dá +24771=>yè +24773=>yǐ +24777=>xiáng +24778=>bī +24779=>zhū +2477B=>yí +2477D=>lǜ +2477F=>kuāng +24782=>zhì +24787=>wá +24788=>dī +24789=>shù +2478A=>liè +2478B=>zǎo +2478C=>zhì +2478D=>náo +24797=>chái +2479A=>xiāo +2479B=>zàng +2479E=>yù +2479F=>dòu +247A0=>chà +247A1=>xié +247A2=>yáng +247A4=>xiǎn +247A5=>bǎo +247AE=>zhāi +247B0=>qiú +247B2=>hú +247B3=>zài +247B4=>jué +247B6=>hān +247BF=>àn +247C0=>zào +247C3=>shà +247C5=>xiàn +247C6=>chǐ +247C7=>yǎn +247C9=>àn +247CD=>zhé +247CE=>jué +247D1=>lì +247D3=>lè +247D6=>cǎi +247D8=>lù +247DA=>jiā +247DD=>xià +247DE=>xiào +247DF=>yān +247E0=>xū +247E2=>dùn +247E3=>yíng +247E4=>huī +247E5=>tí +247E6=>nóu +247E7=>xǐ +247EA=>tú +247F7=>wāi +247F8=>chēn +247FC=>hōng +247FE=>tí +247FF=>xuān +24800=>zá +24807=>gé +2480B=>lóu +2480C=>chái +2480D=>pán +2480E=>jí +24810=>tà +24813=>xī +24816=>xiāo +24818=>sāo +24819=>jiā +2481A=>sù +2481B=>huāng +2481D=>cuō +2481F=>tà +24820=>shuāi +2482A=>fú +2482B=>lì +2482D=>shè +2482F=>táng +24836=>diān +2483A=>bì +2483C=>gòu +2483D=>cù +2483F=>qiān +24842=>léi +24843=>sù +24846=>zòng +24847=>hāo +2484F=>chì +24850=>cáo +24853=>wò +24854=>xiāo +24855=>liè +24856=>yān +2485D=>bì +2485F=>huàn +24861=>xī +24862=>chī +24863=>xū +24864=>náo +24865=>yán +24867=>xiè +24868=>zhá +2486A=>suì +2486C=>xì +2486D=>bēng +2486E=>rán +2486F=>shuò +24870=>bān +24871=>guì +24872=>kāi +24873=>chēn +24876=>xù +2487E=>è +2487F=>lì +24880=>xī +24881=>huàn +24882=>sù +24884=>chǎng +2488A=>lù +2488B=>yán +2488E=>dāng +2488F=>dǎn +24890=>yāng +24892=>zhǎi +24893=>jù +24895=>duó +24896=>sāo +24897=>lái +24898=>sù +2489F=>zé +248A3=>bì +248A6=>yìn +248A8=>hāo +248AA=>liè +248AD=>háo +248AE=>yáng +248B4=>shuò +248B5=>ài +248B6=>qióng +248B9=>lěi +248BA=>xié +248BC=>shì +248C3=>lǔ +248C5=>què +248C6=>lián +248CC=>xiào +248CE=>yīng +248D1=>xié +248D8=>líng +248D9=>yōu +248DE=>dǎng +248DF=>lǎn +248E0=>xiāo +248E8=>yì +248EC=>wū +248EE=>yì +248EF=>tuō +248F0=>bǔ +248F2=>xìn +248F5=>sī +248F6=>jīn +248F8=>bā +248F9=>fǎ +248FB=>mò +248FC=>ruò +2490A=>dà +2490B=>jì +24910=>sù +24911=>qióng +24912=>bā +24926=>tián +24927=>yóu +24929=>tuó +2492B=>wài +2492C=>yòu +2492E=>dōng +24931=>xǐ +24932=>kǒng +24936=>qióng +24937=>duī +24938=>duò +2493A=>yì +24952=>xī +24953=>qīn +24954=>sù +24957=>liú +24959=>wán +2496D=>chē +2496E=>zhū +24970=>mào +24977=>quán +2497D=>yū +2497F=>yì +24980=>mí +24983=>lái +24984=>zhì +249A4=>ní +249A6=>bān +249AA=>dōng +249AE=>zhì +249D5=>yì +249D8=>líng +249D9=>yú +249DA=>cōng +249DB=>dì +249DC=>zhì +249E0=>ruǎn +249E3=>jiàn +249E9=>wàn +249EB=>jìn +249ED=>páng +24A0D=>lù +24A0E=>qú +24A10=>xǐ +24A11=>dá +24A16=>hù +24A17=>luǒ +24A19=>lè +24A36=>gǒng +24A3B=>lìng +24A42=>láo +24A44=>zhuàn +24A68=>zǎo +24A69=>hào +24A6A=>xiàng +24A6D=>hào +24A6E=>lì +24A71=>diàn +24A72=>gé +24A7D=>huán +24A84=>è +24A86=>xiá +24A8B=>jiān +24A8C=>qí +24A8D=>xiá +24A8E=>yǒu +24AA1=>zhēng +24AAA=>zhuàn +24AAE=>chàn +24AC9=>xiè +24AD5=>náo +24ADD=>jì +24ADE=>tián +24AE3=>yǎn +24AE7=>hǎo +24AE8=>xín +24AE9=>líng +24AEB=>bān +24AEC=>běng +24AF1=>gōu +24AF2=>líng +24AF5=>kuò +24AF6=>qià +24AF7=>jiào +24AF9=>ēn +24AFA=>yáo +24AFB=>dū +24B01=>huǒ +24B02=>dǔ +24B03=>pēi +24B0C=>yuán +24B0F=>lóu +24B10=>xíng +24B13=>lián +24B14=>yáo +24B15=>xī +24B16=>yáo +24B18=>xī +24B1B=>lú +24B1D=>yàn +24B20=>quán +24B25=>ráng +24B26=>wà +24B27=>zú +24B28=>fàn +24B29=>yì +24B2A=>dù +24B2B=>suì +24B2D=>pī +24B2F=>hán +24B31=>xù +24B33=>gǒng +24B35=>dì +24B37=>nà +24B3E=>duò +24B3F=>wā +24B42=>niè +24B48=>diào +24B49=>huāng +24B4C=>tí +24B4D=>fàn +24B51=>wú +24B52=>áng +24B54=>píng +24B59=>hán +24B5B=>gāng +24B5C=>lí +24B5E=>dūn +24B5F=>fù +24B60=>nà +24B62=>cèi +24B67=>jiē +24B69=>qìng +24B6B=>yīng +24B6C=>xiáng +24B71=>hú +24B74=>sù +24B7B=>gē +24B7C=>è +24B7D=>xù +24B86=>xī +24B8A=>kāng +24B8B=>guó +24B8C=>jiē +24B8D=>chuán +24B8E=>léi +24B8F=>héng +24B90=>zūn +24B95=>piè +24B98=>dēng +24B99=>xī +24B9A=>léi +24B9C=>shàn +24BA7=>lú +24BA9=>duì +24BAA=>jùn +24BAD=>chàn +24BAF=>xié +24BB0=>wā +24BB1=>zhé +24BB3=>zhuān +24BB7=>liù +24BB8=>léi +24BBC=>dài +24BBD=>gān +24BC4=>shì +24BC7=>yǎn +24BCC=>gān +24BD0=>yán +24BD6=>suī +24BDA=>zhōng +24BDC=>shì +24BE1=>shèng +24BE5=>chǎn +24BF7=>huáng +24BF8=>yìn +24BFB=>měng +24C02=>ráng +24C05=>xiáng +24C08=>bèi +24C0C=>chuán +24C11=>pú +24C19=>kē +24C1A=>lā +24C1D=>quǎn +24C1F=>hàng +24C20=>chì +24C21=>máng +24C26=>zhà +24C2A=>fèn +24C2C=>chào +24C33=>jǐng +24C43=>liè +24C45=>nà +24C46=>nà +24C47=>tóng +24C4B=>rán +24C4C=>zǔ +24C4D=>pī +24C4E=>yǒu +24C50=>shū +24C5B=>liè +24C5C=>shōu +24C5D=>tuǎn +24C5F=>gǎo +24C60=>sháo +24C61=>tuó +24C63=>nán +24C67=>tuǒ +24C68=>gōng +24C69=>diào +24C74=>měng +24C75=>bāng +24C77=>xié +24C78=>sì +24C79=>tǐng +24C7A=>guì +24C7D=>fú +24C7E=>guì +24C89=>guì +24C91=>zhǔ +24C93=>lái +24C95=>lǔn +24C96=>tiǎn +24C97=>rǎn +24C9A=>dōng +24CA8=>juàn +24CA9=>yán +24CAC=>ruán +24CAD=>dǎn +24CB0=>mào +24CB6=>luán +24CB8=>xù +24CBA=>xī +24CC2=>má +24CC3=>qī +24CC5=>chà +24CC8=>shāng +24CC9=>hàn +24CCA=>píng +24CCE=>jī +24CD3=>lì +24CD5=>yù +24CD6=>bān +24CD8=>tēng +24CDD=>chóu +24CE0=>chóu +24CE4=>qī +24CE5=>xī +24CE6=>bèi +24CEA=>yè +24CED=>guǎng +24CEF=>zhù +24CF3=>léi +24CF4=>léi +24CF5=>chā +24D00=>guǎng +24D0D=>dié +24D13=>yǎ +24D18=>niè +24D19=>shū +24D1B=>zhì +24D1F=>zhì +24D22=>zhì +24D23=>pǐ +24D25=>jiū +24D26=>jiū +24D27=>yì +24D28=>yòu +24D2A=>jiū +24D2F=>huàn +24D31=>dù +24D3B=>táo +24D3C=>qiè +24D3D=>qín +24D3E=>xìn +24D3F=>chān +24D40=>jì +24D42=>qìn +24D4A=>dù +24D4B=>zhī +24D4E=>ǒu +24D50=>wù +24D52=>wén +24D58=>bì +24D5B=>bēi +24D5D=>mǔ +24D5E=>jìn +24D5F=>táo +24D60=>liáo +24D65=>cáo +24D66=>zhá +24D6C=>chǐ +24D6D=>yā +24D6E=>kuí +24D6F=>yìn +24D78=>lóng +24D79=>qià +24D7B=>hāng +24D7C=>shàng +24D7D=>hài +24D7E=>chā +24D80=>jiǎo +24D81=>lǎo +24D88=>xī +24D8B=>bó +24D93=>zhǐ +24D95=>tùn +24D96=>fú +24D98=>hū +24D9A=>niè +24D9B=>yì +24D9C=>zhuàng +24DA0=>chá +24DA4=>suān +24DA7=>yùn +24DAE=>dù +24DB0=>xī +24DB1=>chuàn +24DB2=>xíng +24DB3=>jiǎo +24DB4=>shēn +24DC0=>wāng +24DC1=>bēi +24DC2=>féi +24DC3=>jiàn +24DC4=>quán +24DC5=>yì +24DC6=>dōng +24DC7=>xù +24DC8=>nà +24DC9=>jí +24DCC=>zhěn +24DCD=>qí +24DCE=>duī +24DCF=>yín +24DD1=>jiù +24DD2=>pí +24DD3=>xìn +24DD4=>lún +24DD5=>cǎi +24DD6=>lìng +24DD7=>biē +24DD8=>dào +24DD9=>dé +24DDF=>la +24DE1=>xī +24DE2=>jù +24DE4=>xiáo +24DE6=>jīng +24DF9=>wài +24DFB=>nǎo +24DFC=>xiāng +24DFD=>què +24DFE=>qiè +24DFF=>tū +24E00=>xǔ +24E01=>huì +24E05=>mín +24E06=>wěi +24E08=>yóu +24E09=>tuí +24E0A=>dài +24E0E=>kě +24E0F=>nà +24E11=>fù +24E12=>yù +24E13=>zhǐ +24E15=>hān +24E16=>āi +24E17=>fù +24E21=>yāng +24E24=>shí +24E26=>chán +24E2A=>chì +24E2B=>yùn +24E2C=>shuāi +24E2E=>sù +24E2F=>sǎng +24E31=>è +24E32=>zhěng +24E33=>ái +24E34=>suǒ +24E35=>bù +24E37=>qún +24E38=>yì +24E39=>yǎn +24E3B=>nà +24E3C=>wǔ +24E47=>lì +24E48=>lì +24E4A=>xī +24E4B=>jué +24E4C=>shī +24E4E=>yǎ +24E5B=>chén +24E5C=>yíng +24E5D=>bì +24E5E=>chè +24E61=>zhā +24E62=>tuǒ +24E63=>hù +24E64=>téng +24E65=>yìng +24E66=>bǐ +24E67=>níng +24E68=>liàn +24E69=>xìn +24E6A=>yǔ +24E72=>bèi +24E74=>mó +24E75=>duī +24E77=>dǎo +24E78=>qí +24E80=>shuāi +24E83=>xiāo +24E84=>zhǒng +24E85=>zhuì +24E87=>biàn +24E89=>wěi +24E8A=>xī +24E8C=>dēng +24E8E=>xiē +24E8F=>pān +24E90=>niè +24E93=>bié +24E94=>shè +24E95=>fèi +24E96=>mǐn +24E97=>qì +24EAA=>shàn +24EAB=>suǒ +24EB7=>jí +24EBA=>dǎn +24EBB=>juàn +24EBC=>lù +24EBE=>ào +24EC2=>yì +24EC3=>shǔ +24EC4=>suì +24EC5=>wèi +24EC6=>wán +24EC7=>chǔ +24ECC=>wò +24ED6=>bì +24ED8=>yǐn +24ED9=>huó +24EDC=>kài +24EDD=>níng +24EE2=>ài +24EE4=>lì +24EE6=>zhāi +24EF1=>lù +24EF6=>biàn +24EF7=>pán +24EFF=>guì +24F00=>sū +24F01=>méng +24F02=>xiǎn +24F03=>lòng +24F05=>qì +24F0B=>chàn +24F0C=>yì +24F0D=>háng +24F0F=>liǎn +24F10=>guàn +24F12=>wěi +24F17=>jué +24F18=>léi +24F19=>luán +24F1A=>lì +24F1C=>pí +24F22=>huǎn +24F2E=>guī +24F33=>jú +24F36=>dēng +24F3A=>fèi +24F41=>zhī +24F43=>mèi +24F45=>huàn +24F49=>pā +24F4A=>bǐ +24F4C=>pō +24F53=>ér +24F55=>huàn +24F63=>chàng +24F65=>luò +24F66=>fǒu +24F6F=>chóu +24F71=>zú +24F72=>nán +24F73=>xiǎo +24F79=>bài +24F7A=>lù +24F7C=>luò +24F7F=>niàn +24F80=>zé +24F84=>zhù +24F85=>hú +24F88=>huī +24F89=>tǎng +24F8A=>chóu +24F91=>huáng +24F92=>dōu +24F9B=>miào +24F9D=>bó +24FA0=>dì +24FA2=>děng +24FA3=>pū +24FA5=>sōng +24FA6=>chóu +24FAB=>yào +24FAC=>měng +24FAD=>lóng +24FB2=>lián +24FB5=>bié +24FBA=>lǚ +24FBF=>sè +24FC0=>zuó +24FC4=>cún +24FC5=>líng +24FC6=>zhěng +24FC7=>pǐ +24FC8=>báo +24FCB=>què +24FCE=>pī +24FCF=>nàn +24FD0=>pī +24FD1=>bǒ +24FD2=>bèi +24FD3=>fā +24FD5=>mǐn +24FD6=>mò +24FD7=>wà +24FD8=>zhāo +24FD9=>zhì +24FDA=>cū +24FDF=>xún +24FE0=>jí +24FE1=>guì +24FE3=>chéng +24FE7=>hàn +24FE8=>xiào +24FE9=>què +24FEB=>chuò +24FED=>fǔ +24FF3=>qǐn +24FF4=>lù +24FF5=>què +24FF6=>diǎn +24FF7=>qiān +24FFC=>chǎng +24FFD=>tà +24FFE=>bēi +25001=>dù +25002=>běng +25003=>hòu +25008=>zhǎ +25009=>zhǎ +2500E=>què +2500F=>má +25010=>hán +25013=>liú +25014=>lù +25016=>zī +25018=>pǐ +25019=>zhòu +2501B=>zāo +2501D=>niǔ +25020=>huì +25023=>xué +25025=>là +2502B=>nóu +2502C=>yǎn +2502D=>rǎn +2502E=>nǎo +25030=>là +25031=>guǎng +25032=>dú +25035=>lú +25039=>jiǎn +2503A=>xiè +2503B=>qì +2503E=>xiàng +25041=>guǒ +25042=>jié +25043=>màng +25046=>xiā +25047=>kuī +2504E=>yòng +25050=>hǎi +25051=>mì +25052=>yào +25055=>wēn +2505F=>lì +25060=>juàn +25061=>wū +25062=>qiáo +2506E=>diào +2506F=>chù +25072=>suō +25075=>chōng +25078=>quān +25079=>shè +25082=>měng +25083=>jù +2508B=>tú +25092=>nóng +25093=>mó +25099=>fèn +250A2=>áo +250A3=>guō +250A4=>hú +250A5=>cán +250A6=>dūn +250A7=>hǎi +250A8=>jiǎo +250B0=>gū +250B5=>jīn +250B8=>yáng +250C0=>chà +250CC=>huī +250D4=>qú +250D5=>kē +250DF=>qīng +250E0=>yì +250E3=>kǎi +250E4=>jiǎo +250E7=>chōu +250E8=>bǔ +250E9=>gèn +250EA=>jiāo +250EB=>zhī +250EE=>wèn +250F0=>bīn +250F4=>xiòng +250F5=>fàn +250F8=>yí +250F9=>chuàn +250FA=>yào +250FD=>yāng +250FE=>dù +250FF=>yǎn +25101=>méng +25107=>chī +25108=>mù +25109=>jiāo +2510B=>nǜ +2510D=>guó +2510E=>xuè +25111=>fú +25112=>xuē +25113=>fū +25114=>pèi +25115=>mò +25116=>xī +25117=>wò +25118=>shǎn +2511B=>xī +2511C=>qì +2511D=>miàn +25126=>dǎn +25128=>chǒu +25131=>fèi +25132=>mié +25134=>xuè +25135=>xù +25136=>sī +25137=>jǔ +25138=>mǎo +25139=>bào +2513B=>yí +2513C=>guā +2513D=>nì +2513F=>yí +25141=>zuò +25144=>nǔ +25151=>diàn +25152=>fàn +25153=>yì +25154=>shì +25157=>cū +25158=>zhěn +2515E=>shì +2515F=>jiǎo +25160=>hòu +25161=>ér +25166=>lèi +25167=>xuè +25168=>gèng +2516A=>shōu +2516C=>juān +25174=>jié +25175=>wéi +25177=>shǒu +25178=>jìng +2517A=>xú +2517B=>chòng +25185=>jiāng +25186=>mòu +25189=>yù +2518C=>jué +25191=>tìng +25194=>xiāo +25196=>dōu +25198=>guó +25199=>máng +2519A=>wāng +2519B=>xù +2519C=>wàng +2519D=>suō +2519E=>juàn +2519F=>yuè +251A1=>hán +251A3=>shēn +251A5=>xié +251A6=>liú +251A7=>rún +251AF=>bì +251B2=>nào +251B6=>wàn +251B7=>jiù +251B8=>quē +251C4=>nì +251C6=>mí +251C7=>suō +251C9=>qiǎng +251CC=>hàn +251CD=>zhuó +251CE=>mí +251CF=>xù +251D1=>lǎng +251D2=>jié +251D3=>dìng +251D4=>chàng +251D5=>zhì +251D6=>fēi +251D7=>jiá +251D8=>jùn +251D9=>huò +251DA=>qī +251DB=>jū +251DC=>zhūn +251DE=>diàn +251DF=>jiǎo +251E0=>yā +251E2=>zhǎn +251ED=>zhī +251EF=>mài +251F0=>hū +251F1=>xiè +251F2=>shí +251F3=>guī +251FF=>xù +25202=>jí +25204=>chuàng +25206=>mào +25207=>ruán +25208=>xū +25209=>huàn +2520A=>shà +2520B=>jǔ +2520F=>kuàng +25211=>hóu +25212=>guān +25213=>guā +25215=>mí +25216=>dié +25217=>bì +25218=>liǎng +25219=>là +2521A=>shǎn +2521B=>lù +2521C=>xì +2521F=>sǒu +2522C=>ōu +2522E=>léng +25237=>kū +25238=>guī +2523B=>xī +2523C=>pán +2523D=>sè +2523E=>juè +2523F=>hòng +25240=>guàn +25241=>jù +25243=>nài +25244=>huá +25245=>gé +25246=>lì +25247=>gòu +25248=>tì +2524A=>mà +2524B=>téng +2524C=>dá +25250=>qī +25251=>yù +25252=>jiǎo +25253=>miè +25254=>gěng +25255=>mèng +25256=>wèi +25258=>tí +25259=>qí +2525C=>chén +2525D=>dōu +2525F=>pán +25270=>hàn +25274=>mì +25275=>má +25276=>lù +25277=>qī +25278=>kēng +2527A=>dié +2527B=>qì +2527C=>jiāo +2527D=>kāng +2527E=>qiāo +2527F=>mì +25280=>shān +25287=>jiān +25288=>lí +25289=>kè +2528A=>xù +25291=>mán +25292=>fèng +25293=>chàn +25294=>huǐ +252A7=>kòu +252AA=>wěi +252AB=>guàn +252AC=>jí +252AD=>zùn +252AE=>huò +252AF=>xié +252B4=>suì +252B6=>ruǎn +252B8=>tè +252BC=>zhèng +252BD=>kūn +252BE=>xiǎng +252BF=>mián +252C1=>xì +252CC=>sā +252D9=>è +252DA=>miè +252DB=>zhǔ +252DC=>zōu +252DD=>měng +252DF=>xī +252E1=>táng +252E3=>jià +252E4=>cháng +252E5=>jí +252EE=>zhuó +252FF=>hè +25300=>chá +25301=>qì +25302=>mián +25303=>zhěn +25304=>kū +25305=>yè +25306=>zhōu +25308=>jiān +2530A=>pàn +2530D=>huī +2530F=>míng +25310=>liù +25318=>shuì +2531A=>mài +2531B=>lí +2531E=>shuò +2531F=>yí +25324=>lì +25328=>xiē +25329=>tè +2532A=>xiū +2532D=>xuàn +2532E=>lì +2532F=>méng +25330=>wéi +25331=>méng +2533A=>yào +2533B=>lán +2533C=>líng +2533D=>yīng +2533E=>yīng +2533F=>lì +25340=>jiǎn +25341=>guī +25345=>guān +25346=>xiè +25349=>shè +2534B=>zuī +25353=>kàn +25354=>léi +2535A=>biàn +2535D=>shǔ +2535E=>nǜ +2535F=>xù +25363=>hào +25368=>guǐ +2536A=>zhài +2536B=>láng +2536C=>cuān +2536D=>zhì +2536E=>féng +2536F=>qīn +25371=>zé +25372=>nà +25373=>niǔ +25374=>yì +25377=>cōng +25378=>shī +25379=>jiǎn +2537A=>zōng +2537B=>yǎn +2537C=>yīng +25380=>ruǎn +25382=>róng +25383=>xì +25385=>guān +25386=>kài +25388=>wù +2538A=>qín +2538B=>cōng +2538D=>zé +2538E=>xiè +25390=>yù +25391=>zàn +25392=>chuāng +25393=>lǐ +25394=>lǐ +25395=>xù +25396=>mí +25397=>xù +25398=>ruǎn +2539B=>guì +2539C=>rǒng +2539F=>máo +253A1=>qín +253A2=>cuàn +253A3=>cuàn +253A4=>cuàn +253AE=>wū +253B0=>fǎ +253B1=>bá +253B8=>qià +253B9=>zhì +253BA=>tiào +253C4=>zhì +253C5=>zhí +253C7=>huàn +253C8=>chóu +253CA=>zhì +253CE=>yǐng +253D2=>wù +253D3=>bēi +253D5=>hóng +253D6=>shěn +253D8=>jué +253D9=>kuì +253DC=>yǐ +253DD=>yà +253E0=>bī +253E4=>kuà +253E5=>qiān +253E8=>zhāo +253EA=>kǎi +253EB=>shāng +253EE=>àn +253EF=>zhé +253F0=>zhì +253F7=>zhì +253F9=>jiǎo +25400=>sī +25401=>pú +25402=>ǒu +2540A=>zhuó +25411=>yīng +25413=>huān +25415=>yà +25418=>shí +25419=>pā +2541A=>pǔ +2541E=>máng +2541F=>chāi +25429=>yún +2542C=>gǔ +25439=>dǎn +2543B=>náo +2543D=>zhé +2543F=>hú +25445=>kēng +25447=>dié +25448=>tīng +2544B=>guài +2544E=>qiōng +2544F=>shǐ +25450=>jiǎ +25451=>ào +25452=>nǎ +25453=>pǐn +25454=>jiá +25461=>zhè +25462=>bù +25463=>wǒ +25465=>chǎ +2546A=>náo +2546B=>kǎn +2546F=>dú +25470=>guài +25471=>qióng +25473=>róng +25474=>yǐ +25475=>duī +25476=>lěi +25478=>zhōu +25479=>kuā +2547A=>ē +2547B=>xiān +2547C=>diàn +2547D=>nuò +2547E=>è +2547F=>yōng +25480=>wù +25481=>kēng +25493=>zhì +25497=>zhǐ +25498=>xún +2549B=>zhèng +2549E=>yáng +254A0=>huò +254A1=>jí +254A2=>nǎo +254A7=>yà +254A8=>lù +254AB=>fū +254AC=>sǎn +254AD=>chù +254AE=>wěi +254B0=>fǔ +254B1=>kēng +254B2=>sì +254B3=>kàng +254B5=>yì +254B6=>huà +254BE=>yǔ +254C3=>lì +254C6=>lǐn +254C7=>dǔ +254C8=>è +254CC=>qiǎng +254CD=>dú +254D0=>jié +254D1=>chuò +254D2=>xiàn +254D6=>gǎo +254EC=>dào +254F0=>hōng +254FB=>zōng +254FE=>qì +254FF=>tuó +25500=>hōng +25501=>pǐ +25502=>gèng +25504=>niè +25507=>kōng +2550A=>zhǐ +25511=>xiǎo +25521=>shè +25522=>yú +25523=>jiāng +25529=>qǐ +2552A=>chěn +2552B=>sǎng +2552D=>suǒ +2552E=>qián +2552F=>huì +25531=>shàn +25532=>è +2553B=>qiū +2553D=>kè +25540=>wēng +25541=>zī +25542=>jí +25547=>dǎ +25549=>cuò +2554D=>lǒu +2554E=>kāng +2554F=>kuò +25550=>dí +25551=>qiē +25553=>mò +25556=>guǒ +25557=>hōng +25558=>cháo +25559=>hēi +25562=>cáo +25563=>zhé +25566=>gǔn +25570=>xū +25571=>péng +25572=>jué +25575=>gǎn +25576=>sī +25578=>suì +25579=>què +2557B=>wú +2557C=>yán +2557D=>pèng +2557E=>xiǎo +2557F=>pān +2558D=>là +25597=>bèng +25598=>zhěn +25599=>jí +2559C=>jǐn +2559D=>lián +2559E=>kěn +255A0=>zhóu +255A8=>zào +255AA=>lè +255AB=>qī +255AC=>bìng +255B5=>yǐn +255B6=>pīn +255BB=>sǒu +255BC=>lǜ +255BE=>dí +255BF=>dú +255C0=>liǎo +255C1=>zhuó +255CA=>chǎng +255D2=>chèn +255D3=>tà +255D9=>què +255DA=>dào +255DD=>rǎng +255DF=>pò +255E6=>zhōng +255E7=>xiē +255EA=>jiāng +255EB=>qú +255EC=>lěi +255ED=>cà +255EE=>quē +255F5=>xiàng +255F6=>lèi +255FA=>làn +255FF=>lǎ +25601=>lǎ +25604=>yù +2560A=>jiào +2560B=>qín +2560C=>jī +2560F=>gǎn +25612=>yì +25620=>yì +25621=>zhī +25624=>biǎo +25625=>shēng +25626=>jiù +2562B=>hē +2562C=>fú +2562E=>jū +25640=>zuǒ +25641=>yí +25646=>xiàn +25647=>yí +25649=>sì +2564B=>chuì +2564E=>mò +25661=>zhān +25663=>xún +25666=>rú +25668=>huò +2566C=>shāo +25670=>shòu +2567E=>yòu +2567F=>yù +25682=>jùn +25689=>zī +2568A=>lù +2569A=>chǐ +2569B=>kūn +256A0=>zhùn +256A6=>hóu +256A9=>xǔ +256BE=>zōng +256BF=>yìng +256C2=>zhū +256C5=>liù +256D1=>nù +256D8=>bì +256DA=>chì +256DC=>zǔ +256DD=>féng +256DE=>lù +256DF=>pǔ +256E5=>zhuàn +256E7=>zhé +256E8=>shī +256E9=>yǔ +256EA=>lù +256EB=>liáng +256EF=>jué +256F0=>liào +256F1=>bēng +25703=>yì +25704=>guān +2570C=>ǎo +2570F=>guì +25710=>mǐn +25712=>yǎn +25713=>lán +25716=>bó +25719=>zàn +2571A=>yǒu +25725=>yì +25726=>nǐ +2572C=>nǐ +2572D=>guǒ +2572E=>jùn +25730=>shī +25732=>xiǎn +25734=>qiān +25735=>què +25736=>kuí +25740=>shé +25742=>huò +25744=>wàn +2574A=>fèi +2574B=>fèi +2574D=>yù +25751=>zhī +25752=>guà +25754=>jié +25755=>máng +25756=>hé +25758=>yǒu +2575F=>dù +25760=>sī +25762=>lì +25765=>jié +25766=>niǔ +25767=>bà +25768=>yú +2576E=>zhī +25778=>hé +25779=>kē +2577E=>dù +2577F=>jiā +25781=>chēn +25783=>chuì +25784=>hé +25785=>zhǎi +2578A=>mèi +2578D=>hé +2578E=>zǐ +2578F=>zhú +25792=>tuó +25798=>zùn +2579A=>rú +2579B=>duò +2579C=>jiàng +257A7=>héng +257A9=>bēng +257AA=>mò +257AF=>zú +257B2=>biē +257B4=>kù +257B5=>jiá +257BA=>zhuō +257BC=>xiū +257C3=>hé +257C5=>qiāo +257CD=>fěi +257CE=>shēng +257D2=>zhuì +257D3=>kuǎn +257D4=>zè +257D5=>xiān +257D7=>bì +257D8=>yì +257DA=>chàng +257EA=>mào +257F6=>wǎn +257FD=>wū +257FE=>kū +257FF=>wǒ +25800=>xīng +25801=>kē +25803=>jiū +25804=>duān +25805=>huàn +25808=>zhì +25809=>cè +2580A=>róu +2580B=>jí +2580D=>yè +2581B=>jīng +2581C=>yàng +25821=>zǒng +25829=>cǎn +25831=>sī +25832=>lì +25833=>gǔ +25834=>chàng +25836=>fěi +25837=>liú +25839=>jié +2583A=>yūn +2583D=>zhì +25840=>chóu +25841=>biē +25852=>jī +2585C=>luó +2585D=>jiān +2585F=>chuāng +25860=>shuǎng +25862=>lǜ +25863=>jùn +25864=>jiào +25866=>tì +25867=>zhā +2586A=>yì +2586C=>cōng +2586D=>něi +2586E=>jiā +25874=>jì +2587D=>ài +25887=>jiǎn +2588A=>bèn +2588C=>fán +2588D=>suì +2588E=>zùn +25890=>gāo +25891=>gǎo +25892=>láo +25894=>zhuó +2589F=>hù +258A2=>tuí +258A6=>bì +258A7=>jú +258AE=>huá +258B2=>chéng +258B6=>kuài +258B7=>dāng +258B8=>gé +258B9=>xié +258BB=>jié +258BD=>cān +258C6=>zú +258C8=>pú +258CB=>shǔ +258CC=>bǔ +258D7=>níng +258D8=>yǎn +258D9=>zhòu +258DB=>méng +258DD=>biǎn +258DF=>xiàng +258E4=>lù +258E5=>lí +258E9=>jì +258EB=>miè +258EC=>lèi +258EE=>zhì +258EF=>yōu +258F0=>biǎn +258F8=>mù +258F9=>ràn +25902=>niǎo +2590A=>quán +2590B=>zhé +25910=>lèi +25917=>dǎng +25918=>jué +2591C=>líng +2591E=>líng +2591F=>yán +25923=>yǎo +25924=>zhèn +25925=>qī +25926=>ài +25928=>nú +25929=>mǎng +25931=>kǎn +25933=>jiū +25934=>yǎn +25935=>miàn +25937=>yín +25938=>wán +25939=>yào +2593A=>wā +2593B=>pí +2593C=>suì +25945=>kǒng +25948=>hóng +2594A=>mǐng +2594B=>líng +2594C=>yì +2594D=>shēn +2594F=>zuò +2595B=>tū +2595D=>yòng +2595F=>wà +25960=>guǐ +25961=>hòng +25965=>shì +25967=>xiòng +25969=>ā +25971=>chéng +25973=>kēng +25974=>yì +25975=>yàng +25976=>tíng +25977=>dòu +25978=>chá +25979=>liù +2597D=>qiú +2597E=>xuǎn +2597F=>shēn +25980=>kuān +25981=>tòng +25983=>qiǎn +25985=>chòu +2598A=>wěn +2598C=>lòng +2598D=>ǎn +25994=>kǎn +25996=>yǎo +25998=>fú +2599C=>bèng +2599D=>lǎn +2599E=>qià +2599F=>diàn +259A2=>jiào +259A3=>guī +259A5=>xiòng +259A8=>kè +259B6=>xiàn +259B7=>wòng +259C2=>gǒng +259C6=>ǒu +259C7=>kē +259CB=>kū +259D1=>tián +259D2=>gòu +259D3=>mǎ +259D5=>liù +259D9=>wèi +259DA=>wěn +259E1=>gòng +259E3=>tú +259E4=>níng +259E7=>mì +259EB=>láng +259EC=>qiǎn +259ED=>mán +259EE=>zhé +259F0=>huà +259F1=>yōng +259F2=>jìn +259F4=>mèi +259F7=>fú +259FB=>qú +25A0C=>liù +25A0D=>fù +25A0E=>dàn +25A10=>gǒng +25A12=>cuì +25A15=>xǐng +25A1C=>tū +25A1D=>shòu +25A2A=>qióng +25A33=>róng +25A3B=>lì +25A3F=>jī +25A40=>tuò +25A4C=>tóng +25A52=>tán +25A54=>líng +25A56=>yì +25A57=>ruǎn +25A59=>pǎ +25A5D=>cà +25A61=>yuè +25A62=>què +25A63=>zhù +25A64=>hài +25A71=>fá +25A72=>hài +25A80=>bū +25A81=>pīng +25A82=>liè +25A8A=>kuǐ +25A8B=>fú +25A8C=>tiǎn +25A8D=>wò +25A8F=>jū +25A98=>zhēn +25A9A=>fú +25AA2=>lóng +25AA6=>xì +25AA7=>tián +25AAB=>jì +25AAF=>yào +25AB1=>cù +25AB4=>pàng +25AB5=>qiè +25ABB=>lóng +25ABC=>jǐ +25AC2=>tóng +25AC3=>yí +25AC5=>chāng +25ACB=>gōng +25ACE=>dòng +25AD6=>xiāng +25AD9=>tǐng +25ADB=>zhuān +25ADC=>yǐ +25ADD=>yì +25ADE=>zǐ +25ADF=>qǐ +25AE2=>chǎ +25AEC=>dùn +25AEF=>chōng +25AF0=>lù +25AF1=>dùn +25AF3=>fāng +25AF4=>shì +25AF5=>tì +25AF6=>jī +25AF7=>qiū +25AF8=>shuǐ +25AF9=>chén +25AFC=>huàng +25AFD=>shi +25B00=>yún +25B06=>lóng +25B08=>mǎn +25B09=>gōu +25B0D=>xiān +25B0E=>mò +25B10=>shěn +25B12=>pō +25B13=>yào +25B14=>qū +25B15=>rǎn +25B19=>jù +25B1C=>yǐn +25B1D=>bái +25B1E=>niè +25B20=>chōu +25B2A=>róng +25B2B=>chuǎn +25B2C=>niè +25B2D=>lì +25B2E=>jiāng +25B2F=>kǎo +25B30=>cè +25B31=>chòng +25B32=>zhuā +25B33=>zǐ +25B34=>yáng +25B3C=>wěn +25B4B=>jì +25B4C=>jì +25B50=>lǜ +25B51=>qiú +25B52=>dùn +25B53=>báo +25B54=>chān +25B56=>bó +25B58=>chī +25B59=>zhè +25B5A=>màng +25B5C=>jì +25B5D=>miào +25B5E=>yuàn +25B60=>wú +25B61=>zhì +25B62=>pīng +25B65=>chōng +25B6B=>mí +25B6C=>féi +25B6D=>cuō +25B6E=>méng +25B8D=>yín +25B8E=>mǎng +25B8F=>diǎn +25B90=>diāo +25B92=>qián +25B95=>hàng +25B96=>zhí +25B97=>jú +25B98=>niàn +25B9C=>mí +25B9D=>gǔ +25BA3=>zhuā +25BA4=>niè +25BA5=>zhuó +25BA7=>yè +25BA8=>còng +25BAA=>xū +25BAC=>xì +25BAF=>bō +25BBE=>cǎn +25BC3=>yǎn +25BD1=>jǐn +25BD4=>jǔ +25BD5=>dàng +25BD6=>dù +25BD8=>yé +25BD9=>jìng +25BDA=>kè +25BDB=>luò +25BDC=>wěi +25BDD=>tū +25BDE=>yóu +25BDF=>pài +25BE1=>pí +25BE2=>dìng +25BE4=>wěi +25BE5=>chè +25BE6=>jiàn +25BE8=>sī +25BE9=>zhuó +25BEA=>sòu +25BEC=>ruǎn +25BEE=>yú +25BF3=>è +25BF6=>kǔ +25BF8=>zhù +25BFE=>xiá +25C1B=>fú +25C1C=>táo +25C1D=>xī +25C1E=>chōu +25C1F=>gǎn +25C20=>lǘ +25C21=>cè +25C22=>shàn +25C23=>liú +25C25=>xì +25C26=>jī +25C27=>yǐ +25C28=>tán +25C2A=>hú +25C2D=>cuō +25C2E=>gě +25C30=>shì +25C31=>sāo +25C32=>hòng +25C33=>xiàn +25C36=>xiá +25C3B=>mù +25C3C=>suǒ +25C3E=>zhài +25C40=>fū +25C41=>sè +25C42=>nú +25C43=>yì +25C67=>qín +25C68=>qìng +25C75=>huì +25C76=>shuǎng +25C77=>dǎn +25C78=>ōu +25C79=>mò +25C7A=>qiān +25C7B=>chì +25C7C=>pái +25C7D=>juàn +25C80=>cháo +25C81=>liè +25C82=>bīng +25C83=>kòu +25C84=>dàn +25C85=>chóu +25C86=>tōng +25C87=>dàn +25C88=>mǎn +25C89=>hù +25C8A=>liáo +25C8B=>xián +25C8D=>cáo +25C8E=>lù +25C8F=>chuàn +25C90=>wú +25C91=>mán +25C95=>zǐ +25C97=>dù +25C9A=>shuàng +25C9B=>fù +25C9C=>jù +25C9D=>zhòu +25C9F=>diào +25CA0=>wàng +25CA1=>chuāng +25CA2=>qiān +25CA3=>tuì +25CA5=>lián +25CA6=>biāo +25CA7=>lí +25CAA=>lí +25CC6=>bì +25CC7=>fù +25CC8=>cuì +25CC9=>dū +25CCB=>zàn +25CCC=>lóng +25CCD=>xún +25CCE=>qióng +25CCF=>jī +25CD0=>qiǎn +25CD2=>jiǎn +25CD3=>shāo +25CD4=>duò +25CD5=>shū +25CD6=>bù +25CD7=>xū +25CD8=>dǒng +25CDA=>rán +25CDC=>yáng +25CDD=>ruǐ +25CDE=>lìn +25CDF=>jiǎn +25CE0=>dì +25CE1=>fén +25CE2=>diàn +25CE3=>zuì +25CE5=>nǐng +25CEA=>suàn +25CEB=>tiǎn +25CEC=>àn +25CEF=>cè +25CF0=>dìng +25CF1=>shēn +25CF2=>dù +25CF3=>tí +25CF4=>jiǎo +25CF5=>zuì +25CF6=>zhǎng +25CF7=>jiǎn +25CF8=>dàn +25CF9=>dǎn +25CFA=>sǒng +25D10=>zhǎn +25D11=>tíng +25D12=>zhì +25D15=>yóu +25D16=>pái +25D21=>lǐ +25D24=>qián +25D26=>suì +25D27=>jǔ +25D28=>ài +25D29=>gé +25D2A=>jù +25D2B=>tún +25D2C=>bì +25D2D=>qià +25D2E=>bó +25D2F=>huì +25D31=>jiàn +25D34=>gōu +25D35=>suàn +25D3A=>cí +25D3B=>qiàng +25D3F=>yán +25D4F=>diàn +25D52=>miè +25D5C=>pò +25D5D=>lǐng +25D5E=>jié +25D5F=>zhù +25D60=>gǔ +25D63=>duān +25D64=>zhào +25D66=>shǎo +25D67=>qǐn +25D68=>mí +25D6A=>píng +25D6B=>cóng +25D6C=>chōu +25D6F=>sà +25D76=>tiǎn +25D85=>liú +25D86=>lǘ +25D87=>lǔ +25D88=>zōu +25D8C=>lǜ +25D8D=>huǎn +25D8F=>tiáo +25D90=>tuí +25D91=>qiǎng +25D92=>lìn +25D93=>bēi +25D94=>páo +25D95=>zhān +25D97=>lì +25D9B=>tí +25D9C=>hú +25DA2=>liè +25DB5=>huǐ +25DB6=>qū +25DB7=>xuǎn +25DB9=>jìng +25DBA=>dié +25DBB=>suí +25DBD=>wèi +25DBF=>yán +25DC0=>yān +25DC1=>bàn +25DC3=>jiǎng +25DC4=>nǐ +25DC5=>lì +25DC6=>hú +25DC7=>qì +25DC8=>zhōng +25DD1=>bì +25DD4=>yú +25DD5=>dié +25DD6=>lìn +25DD7=>lì +25DD8=>zhuó +25DD9=>jì +25DDA=>jū +25DDC=>fēng +25DDE=>yù +25DE8=>liè +25DE9=>zá +25DEA=>qián +25DEB=>jiē +25DEC=>guān +25DEE=>zhuó +25DF1=>fù +25DF9=>sè +25DFC=>cù +25E03=>huǐ +25E08=>dàng +25E09=>lóng +25E0A=>yì +25E17=>sǎ +25E18=>yuè +25E1A=>dí +25E21=>gǎn +25E22=>zān +25E23=>shàn +25E24=>yù +25E25=>bǒ +25E27=>dìng +25E28=>fán +25E2A=>yù +25E2C=>shēn +25E32=>gōng +25E34=>miè +25E35=>tún +25E38=>liè +25E41=>zhā +25E42=>pēi +25E44=>mí +25E46=>míng +25E47=>fàn +25E49=>nà +25E4A=>sì +25E4B=>yí +25E4C=>jiā +25E4D=>zhù +25E53=>bān +25E54=>yù +25E56=>pǒ +25E5A=>huān +25E5B=>càn +25E5C=>jiāo +25E60=>tán +25E69=>zhì +25E6B=>mǐ +25E6C=>kǎo +25E71=>yāo +25E72=>duì +25E73=>quǎn +25E74=>bù +25E75=>chù +25E76=>qiǎo +25E77=>liú +25E78=>bó +25E7A=>kāng +25E7B=>fèn +25E85=>dào +25E89=>dòu +25E8A=>gé +25E99=>líng +25E9A=>xí +25E9C=>nì +25E9D=>zhōu +25E9E=>zhōu +25EA3=>chōu +25EB4=>niān +25EB5=>jī +25EB7=>qū +25EC4=>kāi +25EC7=>xiàn +25EC9=>hé +25ECB=>lín +25ECD=>zī +25ED1=>ǒu +25ED2=>cù +25ED7=>chá +25EDD=>zhòng +25EDE=>bú +25EE4=>chōu +25EE5=>xì +25EE6=>sà +25EE7=>xián +25EE8=>sè +25EE9=>miàn +25EEB=>fán +25EEC=>zhī +25EEE=>cuì +25EF4=>xià +25EFE=>nuò +25EFF=>lí +25F00=>zú +25F02=>cuī +25F03=>zé +25F05=>lí +25F18=>qí +25F1A=>zhuō +25F1B=>cuì +25F1C=>pū +25F1E=>fán +25F1F=>tán +25F29=>zī +25F2A=>zǔ +25F2B=>zhōu +25F2C=>róng +25F2D=>lín +25F2E=>tán +25F36=>shì +25F3A=>cuǐ +25F3B=>zī +25F3C=>fū +25F41=>xiào +25F48=>fēng +25F4F=>xiàn +25F50=>jiàn +25F52=>fèn +25F57=>lì +25F58=>mò +25F5F=>yōu +25F65=>huò +25F67=>qū +25F6C=>niàng +25F70=>mí +25F73=>qì +25F76=>hé +25F78=>liàn +25F7F=>zuò +25F82=>líng +25F85=>zhú +25F87=>niǎo +25F8A=>jǐ +25F8B=>réng +25F8C=>jié +25F8D=>gǎn +25F90=>yì +25F93=>zhóu +25F95=>wù +25F9A=>gěng +25F9B=>cù +25F9D=>miè +25FA1=>xún +25FA3=>zhī +25FA4=>xiáo +25FA7=>fú +25FA8=>hú +25FAC=>dī +25FAE=>jué +25FAF=>diào +25FB9=>shǒu +25FBC=>wǎng +25FC3=>nà +25FC4=>dī +25FC5=>shì +25FC6=>cí +25FC7=>shū +25FC9=>wà +25FCA=>chè +25FCB=>fán +25FCD=>gū +25FCE=>yuān +25FD1=>guān +25FDA=>qiè +25FDC=>zhǎn +25FDD=>dài +25FDE=>shē +25FE6=>zhōu +25FE7=>xiǎng +25FE8=>míng +25FE9=>zì +25FEA=>huāng +25FEB=>mí +25FED=>xì +25FEE=>zhì +25FEF=>pài +25FF0=>duǒ +25FF4=>cì +25FF5=>móu +25FF7=>chào +25FF9=>yì +25FFA=>gōu +26007=>jīng +26013=>zēng +26014=>pīng +26015=>yè +26016=>jié +26018=>pī +2601B=>shā +2601C=>zhuàng +2601D=>jiǒng +26020=>liú +26021=>yǔ +26023=>jū +26028=>nuò +26038=>mào +26044=>chēn +26046=>zhuàn +26047=>niàn +26048=>kòng +26049=>jiē +2604A=>huà +2604D=>xīn +2604E=>zuó +2604F=>yàn +26050=>jué +26055=>hū +26056=>zhòu +26057=>shè +26059=>yǎn +2605B=>xiè +2605C=>dié +2605F=>chēn +26072=>jiǎn +26073=>jì +26076=>chuò +26077=>hóng +26080=>dá +26084=>kāi +26085=>xīng +26086=>huì +26087=>jiǎn +26088=>zhòu +26089=>zhǎ +2608A=>fù +2608B=>chì +2608C=>běng +2608D=>nuò +26091=>jì +26092=>qián +26094=>wàn +26095=>óu +26096=>bì +26097=>shuò +260A0=>jīng +260A1=>yè +260C4=>fěi +260C7=>lí +260CA=>lì +260CB=>pí +260D2=>suì +260D3=>liú +260D4=>hé +260D5=>hǔn +260D6=>tǎn +260D7=>shuò +260D8=>zhì +260D9=>bó +260DD=>xì +260E1=>pó +260E2=>qǔn +260E4=>mù +260FD=>yōng +26102=>dài +2610A=>qǐ +2610B=>diǎo +2610C=>niè +2610D=>shuǎng +2610F=>shāo +26110=>kǔn +26111=>suì +26113=>dōu +26114=>dié +2611C=>gōng +2612F=>zhuǎn +26130=>guó +2613C=>xū +2613D=>qú +26140=>xún +26143=>jiāo +26144=>zhé +26146=>diàn +26147=>sāng +26148=>bēng +2614A=>suǒ +2614B=>qiǎn +2614F=>xū +26151=>xún +26154=>mò +26175=>suì +26176=>là +26177=>zhǔ +26178=>zhòu +2617A=>lì +2617C=>dān +2617D=>jú +2617F=>yùn +26180=>chǎn +26181=>luó +26184=>sè +26186=>lián +26188=>zuǎn +2618B=>lài +2618C=>shuǎng +2618D=>qiè +26198=>dōu +2619E=>wù +2619F=>méng +261A1=>jì +261A4=>chī +261A6=>nǐ +261B8=>yáo +261BB=>là +261BE=>lǜ +261C0=>suì +261C1=>fū +261C4=>lěi +261C5=>wěi +261CE=>cōng +261D4=>lì +261D6=>pín +261D8=>jūn +261D9=>jǔ +261DB=>là +261E7=>jì +261EA=>miè +261EC=>yào +261ED=>biān +261F1=>cóng +261F2=>sī +261F5=>sī +261F8=>hé +26203=>nàng +26205=>dié +26208=>chè +26209=>yùn +2620B=>xiǔ +2620C=>shū +2620E=>chǎn +2620F=>mín +26210=>lián +26211=>yīn +26212=>xīng +26213=>wēi +26214=>gǔ +26215=>tóu +26216=>tā +26217=>fěi +26218=>dā +26219=>niè +2621A=>cù +2621B=>zuǒ +2621C=>jié +2621D=>xuàn +2621E=>bó +2621F=>jīn +26220=>yǐn +26221=>xū +26223=>yú +26224=>xiòng +26226=>qì +26227=>bēi +26228=>xíng +26229=>gǒng +2622C=>zuǐ +26230=>jiē +26232=>kāi +26235=>xíng +26236=>bēi +26237=>shū +26238=>yù +2623A=>zhǒu +2623B=>zhǎn +26242=>zhōng +26246=>chá +26248=>chuí +26249=>liù +2624E=>suī +26250=>zhǔ +26259=>biàn +2625D=>xìn +2625F=>yà +26262=>líng +26267=>yà +2626C=>tīng +26279=>dí +26281=>pí +26282=>hù +26283=>cén +2628A=>tiān +2628B=>mǒu +2628C=>juǎn +2628E=>mǒu +26290=>jù +26291=>liǔ +26293=>lǐng +26297=>liǔ +26298=>hù +262A6=>fú +262A7=>hú +262AA=>è +262AB=>gōng +262AC=>gū +262B1=>guà +262B9=>lüè +262BB=>fán +262BC=>lǜ +262BD=>méng +262BE=>fú +262BF=>liú +262C5=>xié +262C6=>gū +262C8=>xiàn +262C9=>bó +262CB=>jì +262D3=>quān +262D4=>lù +262DE=>shuò +262E1=>mǒu +262E2=>yù +262E3=>hàn +262E9=>yuè +262EA=>dàn +262EF=>yú +262F0=>jiān +262F3=>gāng +262FF=>cáo +26300=>shèn +26301=>liǔ +26306=>jiāo +26309=>sù +2630A=>sù +2630B=>zhòng +26312=>liào +26314=>xuǎn +26315=>lù +26317=>jì +2631A=>yán +2631F=>lù +26321=>mǐn +26322=>tí +26326=>huàn +26329=>yì +2632A=>tǎn +2632C=>wǔ +26330=>jī +26337=>dú +26338=>kūn +2633A=>jūn +2633F=>shī +26340=>nàn +26341=>pò +26344=>shū +26345=>quàn +2634C=>rèn +2634F=>fén +26352=>tà +26353=>tún +26355=>yáng +26366=>duō +26367=>cī +26369=>gǔ +2636A=>fén +2636D=>róu +26371=>gāo +26372=>xiáng +26374=>xiáng +26375=>hǒu +26377=>tāo +26378=>shàn +26379=>yáng +2637A=>zì +2637C=>yuán +26384=>sú +26387=>chuàn +26388=>xiáng +2638A=>bān +2638C=>mǎn +2638E=>fǔ +2638F=>lǎ +26390=>lǐ +26392=>jié +26393=>yōu +26398=>yù +2639A=>chì +2639C=>chuàn +2639D=>yì +2639E=>shān +263A2=>jí +263A3=>yān +263A6=>wù +263A7=>chún +263A8=>máng +263AD=>fú +263AE=>jiā +263AF=>gòu +263B0=>gú +263B1=>jiá +263B5=>xián +263B7=>jìn +263B8=>zì +263B9=>lóu +263BC=>gòu +263C0=>rén +263C2=>shān +263C5=>jué +263C6=>tóng +263C7=>yǒu +263D4=>jiān +263D5=>dú +263D7=>hú +263DB=>sāo +263DC=>yù +263E2=>mài +263E4=>zhī +263E5=>yān +263E6=>gāo +263E8=>huài +263EE=>quán +263F1=>yǎng +263F3=>zuǐ +263F7=>xiāo +263F8=>yì +263F9=>yǎn +263FA=>hóng +263FB=>yú +263FF=>chì +26401=>chí +26404=>háng +26405=>sè +26406=>pā +26407=>tà +26408=>fēn +26409=>chī +2640C=>hóng +2640D=>xuè +26416=>zhǐ +2641B=>qú +26420=>xī +26421=>fú +26423=>shū +26424=>hài +26426=>pò +26428=>cǐ +26430=>chài +26433=>hōng +26438=>pǎo +26439=>shēn +2643A=>xiāo +2643D=>xuān +2643E=>cǐ +2643F=>tíng +26440=>pò +26447=>tà +26448=>chā +2644B=>zú +2644C=>huò +2644D=>xù +2644E=>yàn +2644F=>chài +26451=>tuó +26458=>xián +26459=>xuān +2645A=>hóu +2645B=>huǎn +2645C=>gé +2645D=>chǒng +2645E=>bì +2645F=>hōng +26460=>hōng +26461=>chí +26463=>chá +2646F=>zhǎ +26471=>zhái +26472=>tà +26475=>pò +26476=>tà +26478=>yóu +26479=>fú +2647A=>cī +2647B=>dá +2647C=>tǎ +2647E=>liú +26481=>cī +26483=>hōng +26485=>hàn +26486=>lā +26488=>shī +2648D=>tóng +2648E=>huì +2648F=>hé +26490=>piē +26491=>yù +2649C=>xiān +2649D=>hǎn +2649F=>pò +264A6=>là +264A7=>huò +264B0=>tài +264B4=>lǎo +264B6=>shù +264BA=>dào +264BB=>diǎn +264C8=>xiòng +264CB=>wàng +264CD=>chě +264CE=>nài +264D0=>jué +264D3=>ér +264D4=>ér +264D5=>nǘ +264D6=>nǜ +264DD=>zhuǎn +264E2=>nuò +264E4=>liè +264E5=>lěi +264E7=>bā +264EC=>chēng +264EF=>guī +264F0=>quán +264F1=>gè +264F3=>gǒng +264F4=>shào +264F9=>lái +264FA=>zhēng +264FB=>yì +264FC=>gǔn +264FD=>wēi +264FE=>lǔn +26502=>shí +26503=>yīng +26504=>shěng +26505=>tú +26506=>bì +26508=>zé +26509=>zhòng +2650B=>rǒng +2650C=>qí +2650D=>fù +2650E=>cè +26513=>lí +26514=>mán +26516=>lián +26517=>biāo +2651B=>chuáng +2651C=>yì +26520=>pài +26525=>yì +26526=>kuài +26529=>biāo +2652B=>chì +2652C=>qú +2652D=>mò +2652E=>zhé +2652F=>shà +26530=>shà +26537=>yāo +26538=>gōng +26539=>nài +2653C=>xiè +2653F=>tiàn +26546=>yé +26549=>shā +2654F=>sào +26552=>diān +26553=>xù +26559=>qú +26560=>hōng +26561=>shèng +26562=>tìng +26570=>duo +26575=>liáo +26577=>hòng +26578=>lǐ +2657A=>xiǎng +2657D=>shèn +26580=>fū +26588=>yǎn +26589=>wǎng +2658A=>qī +2658B=>duǒ +2658D=>huà +2658E=>qiān +26590=>xiè +2659D=>cì +2659E=>shēng +265A2=>èr +265A4=>xīng +265A6=>tuì +265A7=>yàn +265A9=>liè +265AC=>mí +265B8=>zòng +265BA=>zī +265BC=>hú +265BD=>yíng +265BE=>lián +265BF=>dā +265C0=>tián +265C1=>tiàn +265CB=>róng +265CD=>ài +265D0=>ài +265D1=>zhé +265D2=>guō +265D3=>lù +265D4=>zhāo +265D5=>mí +265D6=>liáo +265D7=>zhé +265DB=>qǔ +265DC=>cōng +265DF=>tīng +265E1=>tán +265E2=>zhǎn +265E3=>hú +265E5=>piē +265E7=>dā +265E8=>róng +265EE=>nǎo +265F3=>náng +265F4=>dāng +265F5=>jiǎo +265FB=>jù +265FC=>ěr +2660A=>lì +2660C=>guō +2660D=>wài +26612=>niè +26614=>jīn +26629=>pǐ +2662A=>chì +26632=>pǐ +26633=>yì +26634=>dū +26635=>wǎ +26636=>xūn +26638=>qì +26639=>shàn +2663C=>xū +2663F=>hē +26640=>pàn +26642=>pēi +26644=>xiōng +26646=>chǐ +26647=>tān +26648=>zuì +26649=>zuǎn +2664A=>qì +2664B=>dū +26659=>shuǐ +2665C=>nǎ +2665D=>xī +26667=>chǎo +26668=>yì +2666B=>zhēng +2666E=>jú +2666F=>dài +26671=>sān +26674=>zhù +26675=>wàn +26678=>sān +26679=>bàn +2667A=>jià +2667B=>mài +26688=>tuò +2668A=>qì +2668F=>zhuāng +26690=>tuó +26693=>píng +2669D=>pēng +2669E=>kuāng +2669F=>yí +266A1=>xiè +266A2=>yuē +266A3=>hén +266A5=>hóu +266A6=>zhēng +266A7=>chǔn +266A8=>shì +266A9=>wǎ +266AB=>xié +266B8=>gèng +266C5=>è +266CF=>kú +266D0=>nà +266D3=>jū +266D4=>xuàn +266D5=>qū +266D6=>chè +266D7=>lǚ +266D8=>hé +266D9=>shèng +266DA=>nàn +266DC=>hé +266DD=>chá +266DE=>yān +266DF=>gěng +266E0=>niè +266E2=>guó +266E3=>yán +266E4=>guǎn +266E7=>zhì +266E8=>lao +266EF=>dǔ +266F0=>qì +266F1=>qū +266F2=>jué +26701=>fēng +26703=>xù +26704=>tuì +26706=>hán +26707=>kū +2670A=>shēn +2670B=>zhì +2670D=>pàng +2670E=>zhēng +2670F=>lì +26710=>wǎn +26712=>fǎn +26713=>xìn +26716=>yà +2671B=>jū +2671C=>shèn +2672D=>mǎng +2672F=>tǔn +26730=>zhuó +26731=>xī +26732=>yìn +26733=>jīng +26734=>tún +26737=>gèng +26738=>jì +2674F=>zhuǎn +26752=>tiē +26754=>zhī +26756=>jí +2675A=>yíng +2675B=>wèi +2675D=>huàn +2675E=>tíng +2675F=>chán +26762=>kuí +26763=>qià +26764=>bàn +26765=>chā +26766=>tuǒ +26767=>nǎn +26768=>jiē +2676A=>yān +2676C=>tú +2676E=>wěn +26770=>cōng +26773=>xù +26774=>yìn +26777=>bèng +2677C=>lǘ +26781=>zāi +26782=>dā +26786=>niè +26787=>jǔ +26788=>hóu +2678C=>gèng +26795=>hóu +26796=>kān +26797=>gōng +26799=>huǐ +2679A=>xiè +2679D=>xì +2679E=>hán +2679F=>mí +267A1=>wěng +267A2=>hùn +267A3=>sāo +267A4=>xìn +267A5=>zhé +267A6=>huò +267A8=>gōng +267AB=>sài +267AC=>jīn +267AD=>wā +267B1=>duǐ +267B2=>chī +267BD=>xī +267C2=>mí +267C3=>zāng +267C4=>sǎng +267D3=>tún +267D4=>zhì +267D5=>wěn +267D8=>yín +267D9=>tǔn +267DB=>chōng +267DC=>zé +267DE=>xiāo +267DF=>mó +267E0=>cù +267E3=>biǎn +267E4=>xiū +267E7=>yí +267EE=>huǎng +267F0=>zhā +267F1=>suō +267F2=>hún +267F3=>jù +26801=>cù +26804=>jī +26805=>xún +26806=>sǔn +26807=>céng +26809=>yì +2680E=>biāo +26812=>jué +26813=>lì +26816=>pào +2681B=>zā +2681C=>yè +2681E=>bì +2681F=>zhè +26820=>zhè +26822=>jiù +26823=>zhé +26826=>shù +2682A=>xī +26837=>xǔ +26838=>nǎi +26839=>xián +2683A=>gǔn +2683B=>wèi +2683E=>jí +2683F=>sà +26842=>dǒng +26843=>nuó +26844=>dù +26845=>zhēng +26846=>kū +26849=>míng +26855=>báo +26856=>huì +26859=>zōng +26868=>sàn +2686A=>tēng +2686B=>yí +2686D=>yù +26871=>yào +26872=>nǐng +26874=>chóu +26875=>hùn +26877=>duì +26879=>qì +2687A=>yǐng +2687B=>bìng +2687C=>níng +2687D=>huáng +26886=>yǐng +2688A=>báo +2688E=>guàng +2688F=>lěi +26890=>zǔn +26899=>chǎn +268A3=>jiǎn +268A7=>méng +268A9=>xiào +268AF=>xìn +268B1=>lí +268BA=>qiǎo +268BF=>wěi +268C0=>nà +268C2=>pāng +268C4=>léi +268C7=>luó +268CB=>luán +268CD=>gēng +268CF=>luán +268D2=>qú +268D6=>luó +268D8=>náng +268DB=>luó +268DC=>yuè +268E2=>shuì +268E5=>mì +268E6=>wáng +268E7=>cè +268E8=>jiān +268E9=>wǎng +268EF=>jiā +268F4=>huán +268F8=>liàn +268F9=>zì +268FA=>bái +268FB=>shǒu +268FE=>wǎn +26902=>shū +26907=>guī +26908=>xī +2690A=>rú +2690B=>yào +2690E=>gāo +26915=>yuè +26918=>yōng +26919=>wà +2691A=>bó +2691F=>xìn +26922=>pì +26923=>bó +26926=>hài +26927=>zhài +26928=>wò +2692A=>yè +2692B=>bì +2692C=>hài +26938=>chì +2693B=>zhì +2693D=>ní +26941=>wú +26942=>ǎi +26948=>ǎi +26949=>yǔ +2694A=>chì +2694D=>jìng +2694E=>zhì +2694F=>zhì +26950=>zhì +26951=>jú +26956=>hán +2695A=>pīng +2695D=>yǎo +26963=>yóu +26964=>pīng +26966=>mò +2696C=>zuò +2696D=>pò +2696F=>xué +26970=>kuáng +26971=>yì +26972=>pò +2697B=>zhuì +26983=>ní +26984=>qiǔ +26985=>còu +2698C=>yǎo +26991=>fén +26995=>xiá +26997=>jiāng +26998=>chā +2699B=>xiào +2699C=>chā +269A2=>chéng +269A3=>cuì +269A7=>qióng +269A9=>yù +269AB=>yú +269AF=>wèn +269B1=>chā +269B2=>yǔ +269B9=>zuó +269BA=>dǎo +269BD=>juàn +269BE=>dǎo +269BF=>yīng +269C1=>fěng +269C5=>wèng +269C8=>jìn +269C9=>qì +269CB=>qìn +269CD=>kuò +269CF=>tān +269D0=>xiān +269D2=>tiān +269D4=>kuò +269D6=>tiàn +269D8=>hú +269D9=>zhū +269DA=>zhān +269DB=>tà +269DD=>tiān +269DE=>tà +269DF=>tà +269E0=>huá +269E1=>yǎn +269E2=>tiè +269E4=>tiè +269E5=>tà +269EC=>huài +269EE=>jiá +269EF=>qì +269F1=>tà +269F4=>tān +269F5=>huà +269F8=>zhuàn +269F9=>huā +269FC=>lán +26A06=>zūn +26A07=>yì +26A08=>fú +26A09=>wù +26A0B=>fú +26A0D=>dīng +26A0E=>tà +26A16=>chào +26A19=>rì +26A1A=>quǎn +26A1C=>gē +26A21=>fú +26A22=>dì +26A23=>diāo +26A24=>yǒng +26A26=>jià +26A29=>lóng +26A2C=>yǒng +26A2D=>pí +26A2F=>huó +26A30=>qióng +26A32=>fán +26A33=>wú +26A34=>tóng +26A35=>háng +26A38=>tān +26A3E=>hēng +26A44=>tiāo +26A48=>zhōu +26A4B=>bài +26A4C=>xiè +26A4D=>dāo +26A4F=>jīn +26A55=>hū +26A56=>bēi +26A58=>dìng +26A5C=>nuó +26A5D=>wèi +26A5E=>yú +26A60=>xīng +26A61=>fú +26A62=>xiàn +26A63=>qì +26A64=>tū +26A67=>jí +26A69=>yìng +26A6B=>dèng +26A6C=>wēi +26A6D=>xī +26A6F=>pái +26A71=>shéng +26A72=>yǒu +26A74=>ái +26A75=>jiàn +26A77=>gōu +26A78=>ruò +26A7C=>gòng +26A7F=>shà +26A80=>táng +26A87=>lù +26A88=>áo +26A8A=>qì +26A8B=>xiū +26A8D=>dāi +26A91=>fá +26A92=>wèi +26A94=>dùn +26A95=>liáo +26A96=>fān +26A97=>huáng +26A98=>jué +26A99=>tà +26A9A=>zùn +26A9B=>ráo +26A9C=>cān +26A9D=>téng +26AA0=>huà +26AA1=>xū +26AA3=>zhān +26AA7=>gǎn +26AAA=>péng +26AAB=>cān +26AAC=>xiē +26AAD=>dá +26AB1=>jì +26AB6=>lǐ +26AB9=>pán +26ABD=>lóng +26ABE=>lì +26ABF=>xí +26AC0=>téng +26AC3=>líng +26AC8=>lǐ +26AC9=>rán +26ACA=>líng +26ACE=>gǔn +26AD4=>pō +26AD5=>mò +26AD6=>pāi +26AD9=>bà +26AE1=>qí +26AE4=>yán +26AEA=>wà +26AEB=>ǎng +26AED=>mìng +26AEE=>mǐn +26AEF=>xùn +26AF0=>méng +26AF3=>guǎi +26AF6=>jiāo +26AFB=>gǎi +26B01=>cái +26B02=>wù +26B03=>zhé +26B04=>rěn +26B05=>kōu +26B14=>zhǎo +26B15=>zhōng +26B16=>qiú +26B17=>guō +26B18=>gōng +26B19=>pū +26B1A=>hù +26B1B=>miǎn +26B1E=>tiān +26B23=>wǎng +26B38=>zhú +26B39=>dá +26B3A=>xiòng +26B3B=>ná +26B3E=>juān +26B41=>niǎn +26B48=>hù +26B49=>shā +26B5C=>zhī +26B5F=>tā +26B61=>sī +26B65=>yì +26B6D=>qióng +26B6E=>zhì +26B6F=>lǚ +26B70=>rú +26B72=>qí +26B73=>yǔ +26B74=>zhōu +26B75=>yáng +26B76=>xiǎn +26B77=>móu +26B78=>chóu +26B79=>huī +26B7A=>jiū +26B7B=>jiù +26B7C=>piǎo +26B81=>jiào +26B83=>guāi +26B85=>mò +26B90=>xī +26B91=>pú +26BAF=>jì +26BB6=>wěn +26BB7=>bèi +26BB8=>yǐ +26BB9=>fú +26BBA=>sī +26BBB=>juān +26BBC=>jì +26BBE=>nì +26BC0=>bèn +26BC5=>xù +26BC8=>qǐn +26BC9=>bó +26BCC=>wáng +26BCD=>zhè +26BCF=>wò +26BD0=>sháo +26BD1=>zào +26BD2=>yǎng +26BD5=>sòng +26BD6=>niè +26BDB=>bì +26BE3=>cú +26BE4=>qiāng +26BEA=>xiào +26BEB=>zhī +26BEC=>shé +26BEF=>zhì +26BF0=>pēng +26C0F=>diào +26C16=>wò +26C18=>zhǐ +26C19=>bì +26C1B=>fén +26C25=>bāng +26C2A=>qiú +26C2B=>nǐ +26C2C=>bó +26C2D=>dùn +26C2F=>shǐ +26C30=>xū +26C31=>cháng +26C32=>xū +26C33=>yé +26C34=>mí +26C38=>xīn +26C39=>zhuó +26C3A=>fù +26C3D=>pǐ +26C3E=>xuè +26C40=>yù +26C41=>xián +26C42=>yù +26C43=>yú +26C45=>jū +26C46=>tā +26C47=>kōng +26C4A=>zhēng +26C4B=>méng +26C4C=>gāng +26C52=>mù +26C53=>xǐ +26C54=>bì +26C56=>fù +26C5C=>xiào +26C60=>jiū +26C63=>gǒu +26C70=>chí +26C71=>jiū +26C72=>jiū +26C75=>shā +26C77=>fēi +26CAB=>fú +26CAF=>wàn +26CB0=>xū +26CB1=>bō +26CC1=>hào +26CC3=>xié +26CC4=>pián +26CC5=>yǔ +26CC7=>tián +26CC8=>pí +26CCA=>shǐ +26CCB=>kuǎi +26CCC=>jī +26CCF=>zhā +26CD0=>nài +26CD1=>mǒu +26CD3=>fú +26CD4=>dù +26CD7=>shěng +26CD8=>chá +26CDA=>chí +26CDB=>guǐ +26CDC=>mín +26CDD=>tāng +26CDE=>bài +26CDF=>qiāng +26CE1=>zhuó +26CE2=>wèi +26CE3=>xún +26CE5=>miǎo +26CE6=>zāi +26CE7=>yóu +26CE9=>yòu +26CEB=>shān +26CEC=>hé +26CED=>lǚ +26CEE=>zhí +26CF2=>jìng +26CF3=>zhēn +26CF6=>méng +26CF7=>yóu +26CF9=>wò +26CFA=>bá +26CFD=>juàn +26CFE=>rú +26CFF=>còu +26D00=>zhī +26D09=>hú +26D0A=>yāng +26D0C=>jùn +26D0D=>shé +26D0E=>kòu +26D11=>qián +26D14=>méng +26D1A=>tiáo +26D50=>niè +26D5F=>chí +26D61=>xiōng +26D63=>hùn +26D66=>dí +26D67=>láng +26D69=>zāo +26D6A=>cè +26D6B=>suǒ +26D6C=>zù +26D6D=>suī +26D6F=>xiá +26D71=>xiè +26D74=>jié +26D75=>yóu +26D77=>gòu +26D78=>gěng +26D7C=>jùn +26D7D=>huǎng +26D7E=>jí +26D7F=>pōu +26D80=>wū +26D82=>yì +26D85=>nǎi +26D87=>rǒng +26D88=>nán +26D8A=>píng +26D8B=>shàn +26D8C=>diāo +26D8D=>jí +26D8E=>huā +26D8F=>duì +26D90=>kǒng +26D91=>tà +26D93=>hòng +26D95=>shū +26D99=>héng +26D9A=>fěn +26DB2=>kòu +26DD9=>nián +26DDD=>chú +26DE6=>qiàng +26DF2=>xì +26DF3=>hú +26DF4=>sòng +26DF5=>wò +26DF7=>hài +26DF8=>rú +26DF9=>méng +26DFB=>sǎn +26DFD=>wú +26DFF=>yóu +26E01=>tān +26E02=>shēn +26E06=>qǐ +26E08=>guó +26E09=>qià +26E0A=>xiān +26E0F=>suī +26E10=>lù +26E13=>qī +26E14=>diāo +26E17=>qí +26E18=>jiá +26E19=>yóu +26E1A=>xí +26E1B=>cháo +26E21=>mì +26E22=>lòu +26E23=>bǐ +26E2A=>péi +26E2E=>zhēn +26E2F=>shēn +26E30=>chǎn +26E31=>fù +26E36=>qū +26E37=>sī +26E3A=>zuī +26E6B=>zhào +26E7D=>pí +26E80=>còu +26E86=>gāo +26E87=>dú +26E89=>fū +26E8A=>guān +26E8B=>sǎo +26E8C=>sǒu +26E8D=>jiǎn +26E8E=>póu +26E90=>cán +26E91=>bèng +26E92=>mòu +26E93=>zhāo +26E94=>xiáo +26E96=>jú +26E97=>shū +26E98=>jiǎn +26E99=>lí +26E9B=>chuàn +26E9C=>lào +26E9E=>hè +26E9F=>hú +26EA0=>gū +26EA1=>zhǎng +26EA2=>jié +26EA3=>xiàng +26EA5=>dū +26EA6=>hán +26EA7=>jiá +26EA8=>xiàng +26EA9=>jí +26EAA=>shǔ +26EAB=>làng +26EAC=>jī +26EAD=>shān +26EB0=>tāo +26EB1=>zī +26EB2=>shuàn +26EB4=>jí +26EB5=>chù +26EB6=>jì +26EB7=>shēn +26EB8=>lìn +26EB9=>liáo +26EBB=>sǎn +26EBD=>ǎn +26EBE=>ruǎn +26EC0=>tí +26EC1=>dàn +26EC3=>huán +26EC5=>sà +26F06=>ruí +26F07=>wū +26F08=>jù +26F09=>huán +26F0A=>léng +26F0B=>lù +26F0E=>tān +26F0F=>zēng +26F13=>qián +26F17=>xī +26F21=>cǐ +26F22=>shé +26F27=>sà +26F2A=>mào +26F2B=>qú +26F2D=>bó +26F2E=>gǎn +26F30=>qiè +26F31=>juàn +26F32=>dāng +26F33=>cháng +26F34=>yáng +26F35=>hé +26F37=>jī +26F39=>bǐng +26F3B=>méi +26F3F=>dūn +26F40=>ǎo +26F41=>jīng +26F42=>lù +26F43=>miàn +26F44=>diàn +26F45=>hè +26F47=>jiān +26F4A=>huá +26F4B=>gōu +26F4E=>lù +26F4F=>fú +26F50=>huǐ +26F52=>zéi +26F54=>jìn +26F55=>sī +26F56=>qūn +26F5C=>dàn +26F5E=>wàn +26F5F=>biǎn +26F64=>jiá +26F6B=>dǎn +26F6C=>jiū +26F6D=>xián +26F6E=>bó +26F8F=>xiá +26F91=>biāo +26F95=>pò +26F98=>sǎo +26F99=>bèi +26F9A=>shà +26F9B=>wěi +26F9D=>cāng +26F9E=>lù +26FA9=>dàn +26FAB=>gǔ +26FAC=>zā +26FAD=>bǎng +26FAE=>gàn +26FB1=>chāo +26FB2=>jì +26FB3=>liē +26FB5=>qióng +26FB6=>jiàn +26FB7=>lù +26FB8=>duān +26FB9=>suān +26FBA=>yáo +26FBB=>yǐn +26FBD=>tà +26FBE=>yáo +26FBF=>jīng +26FC0=>chú +26FC1=>fú +26FC2=>yuán +26FC3=>shǎo +26FC5=>bìng +26FC6=>dàng +26FC7=>shì +26FCA=>lú +26FCB=>qiè +26FCC=>luó +26FCD=>pò +26FCF=>méng +26FD0=>jié +26FD3=>jī +26FD6=>lù +27004=>chàng +27005=>miè +27006=>méng +27007=>jiǎn +2700A=>cǎi +2700C=>sù +27014=>hè +27015=>sà +27017=>zī +27018=>kēng +27019=>gěng +2701A=>sī +27020=>tí +27021=>zhàn +27022=>xiè +27023=>shuí +27024=>chǐ +27025=>yōu +27026=>lǔ +27027=>mèng +27028=>liè +27029=>sì +2702C=>xī +2702D=>fán +2702E=>fū +2702F=>shěn +27030=>tí +27031=>chài +27032=>yuè +27034=>fū +27035=>jiàn +27036=>dì +2703A=>xié +2703B=>dān +2703F=>zhí +27043=>xù +27048=>niè +27049=>fàn +2704A=>méng +2704B=>mǐn +2707E=>lóu +2707F=>dú +27081=>zhàn +27082=>jiàn +27083=>hàn +27084=>dàn +27085=>sēn +27086=>jiàn +27087=>tán +27088=>jiǎo +27089=>pó +2708B=>píng +2708D=>zhuàn +2708F=>liáo +27090=>zì +27092=>zhuó +27094=>hù +27099=>xì +2709B=>méng +2709C=>jù +2709D=>miè +2709E=>xián +270A0=>kuì +270A1=>méng +270A2=>jiān +270A6=>nóu +270A8=>dì +270A9=>sāo +270CF=>chù +270D0=>zhí +270D1=>qián +270D2=>lǚ +270D4=>zhuó +270D8=>zuò +270D9=>hán +270DA=>suǐ +270DB=>gòu +270DD=>chǒu +270DE=>jì +270DF=>yì +270E0=>yú +270E8=>nóu +270E9=>nǐ +270EA=>ruò +270EE=>lín +270F1=>níng +2710D=>qiáo +2710E=>yáo +2710F=>fù +27110=>shuāng +27111=>kuì +27112=>qú +27113=>dǒng +27114=>shǔ +2711A=>lí +2711B=>jú +2711C=>ruǐ +27120=>zhá +27124=>xiāo +27138=>mén +27139=>shí +2713A=>diān +2713B=>lì +2713C=>dèng +2713D=>zàn +2713F=>luó +27140=>cán +27143=>āo +27146=>jiǎn +27148=>diào +2714B=>yíng +27156=>yì +27157=>dǎng +27158=>nóu +2715A=>yuè +2716E=>lǐ +2716F=>lí +27170=>hù +27172=>yòu +2717A=>nàng +27182=>chèn +27189=>fēng +2718A=>biē +2718F=>mǎn +27190=>gàn +27191=>huò +27193=>cū +27195=>yǒu +27198=>yòu +2719C=>xū +271A1=>xù +271A2=>hǔ +271A3=>lú +271A5=>xiá +271A6=>yì +271AE=>hǔ +271AF=>hù +271B0=>zǐ +271B7=>gōng +271B8=>tuī +271B9=>wū +271BA=>líng +271BB=>gū +271BC=>zhōng +271C4=>lú +271C8=>zù +271CC=>tóng +271CD=>xiā +271CE=>hé +271D3=>yuè +271D9=>nán +271DA=>bó +271DB=>hū +271DC=>qì +271DD=>shú +271DE=>qiāng +271DF=>zhōu +271E0=>yào +271E1=>gū +271E5=>bān +271E6=>kǎn +271EE=>hé +271EF=>jì +271F0=>hú +271F1=>yán +271F6=>chūn +271F7=>dǐng +271F8=>qiū +271F9=>hóu +271FC=>hào +271FF=>zù +27201=>xián +27204=>xià +27205=>xì +27208=>sè +2720C=>gé +2720D=>xì +27211=>gé +27214=>lǚ +27216=>gé +27217=>kè +27219=>shòu +2721A=>zhù +2721C=>téng +2721D=>yà +2721E=>nì +27226=>luò +27227=>suī +2722A=>chǎn +2722D=>wù +2722F=>yū +27239=>zǎo +2723B=>yì +2723C=>xī +2723D=>hóng +2723E=>quán +2723F=>wǎng +27240=>chǐ +27241=>xì +27242=>tiǎn +27243=>yǔn +27245=>yī +27246=>jí +27247=>huī +27248=>fóu +2724A=>fǔ +2724D=>jí +2724E=>xuán +27251=>tài +27253=>dù +27257=>yuán +2725B=>dì +2725E=>zhǔ +2725F=>tāi +27261=>rǒng +27262=>xué +27263=>yù +27264=>fàn +27265=>běi +27267=>qǔ +27269=>bù +2726A=>jiā +2726B=>zhá +2726D=>nǔ +2726E=>shé +27272=>lì +27284=>guǐ +27285=>guǎi +27287=>dài +2728F=>gāi +27292=>cì +27294=>yǎn +27295=>sōng +27296=>shì +27298=>kù +27299=>zhǐ +2729A=>tóng +2729B=>qú +2729C=>è +2729E=>xíng +2729F=>rú +272A0=>yú +272A3=>yì +272A4=>yì +272A5=>xù +272A6=>fǒu +272A7=>gé +272AC=>hé +272AD=>yīn +272AF=>hòng +272B1=>duǒ +272BD=>xíng +272BE=>fán +272C9=>qī +272CA=>shā +272CC=>dù +272CD=>dì +272CE=>lí +272CF=>yì +272D0=>xí +272D1=>gěng +272D2=>tóng +272D3=>kào +272D4=>hòng +272D5=>kǔn +272D6=>niè +272D7=>chí +272D8=>tí +272DA=>tóng +272E0=>lí +272E1=>nà +272F1=>zhān +272F2=>běi +27301=>tiáo +27303=>zā +27304=>è +27305=>shòu +27306=>kōng +27307=>péng +27308=>fù +27309=>lù +2730A=>xiè +2730B=>xiè +2730C=>xiū +2730D=>lù +2730E=>tiǎn +2730F=>tà +27310=>cì +27311=>qū +27313=>fù +27314=>zhī +27316=>xiè +27317=>zǒu +27318=>fèi +27319=>mín +2731A=>xīng +2731D=>tóng +2731E=>qí +27320=>piāo +27322=>suì +27323=>ěr +27327=>hǔ +2733B=>sōng +2733D=>biē +2733E=>dīng +2733F=>bǎn +27340=>shī +27341=>xiè +27342=>xiáo +27343=>fěi +27352=>chuǎn +27353=>shuài +27354=>yāo +27355=>jué +27356=>shěng +27358=>yōu +27359=>fàn +2735C=>kuí +2735D=>dì +2735F=>máo +27360=>jié +27362=>yán +27365=>wēi +27368=>sāng +27369=>jié +2736A=>yú +2736B=>wèi +2736C=>è +2736D=>quán +2736E=>jiǒng +2736F=>féng +27370=>lóng +27371=>dié +27372=>pián +27374=>liàn +27375=>hú +27376=>lǜ +2737F=>diàn +27383=>cuì +27384=>móu +27395=>wáng +27396=>juān +27397=>kē +27398=>yán +27399=>jiǎo +273A1=>gōng +273A3=>róng +273A4=>sūn +273A5=>shàn +273A8=>chí +273AA=>qí +273AB=>suǒ +273AD=>yè +273AE=>zǎo +273AF=>quē +273B0=>zhǎn +273B1=>bā +273B2=>zú +273B3=>suǒ +273B4=>zhé +273B5=>xì +273B7=>chǔ +273B8=>jiǎo +273B9=>zuì +273BA=>gē +273BB=>wù +273BE=>lüè +273BF=>jí +273C2=>xié +273C3=>xié +273C6=>dǒu +273CB=>qiū +273D1=>píng +273D3=>liú +273E5=>jié +273E7=>huì +273EB=>shà +273F8=>zhí +273F9=>ài +273FA=>xù +273FB=>bì +273FD=>yē +273FE=>nì +273FF=>zhú +27401=>sù +27403=>xié +27404=>yù +27405=>qū +27408=>zú +27409=>zhī +2740A=>zhāng +2740B=>lüè +2740C=>wěi +2740D=>chōng +2740E=>mì +27410=>jī +27412=>sù +27413=>yě +27414=>xí +27415=>tuán +27416=>lián +27417=>xuán +27419=>wù +2741F=>máo +2742C=>hóng +2742F=>lüè +27430=>dú +27431=>cóng +27432=>chán +27433=>lù +27434=>sù +27440=>lüè +27446=>zhōng +27447=>lí +27448=>fèi +2744A=>jǐng +2744B=>kuì +2744C=>yì +2744D=>huá +2744E=>cuì +27450=>yù +27451=>běng +27452=>tūn +27453=>shǔ +27454=>dài +27455=>wū +27456=>cì +27457=>nìng +27458=>dàng +27459=>zú +2745A=>hán +2745C=>pí +2745D=>chuàn +27460=>dù +27461=>pá +27464=>zhū +27466=>xié +27467=>zhé +27468=>qiè +27469=>xuān +2746B=>sào +27480=>bì +27482=>fù +27488=>lì +2748E=>é +27490=>yē +27491=>shǔ +27493=>sè +27495=>qī +27496=>guò +27497=>sè +27499=>fù +2749A=>máo +2749C=>léi +2749D=>zhān +274A8=>chài +274AD=>wèi +274BD=>léi +274BF=>zéi +274C0=>yīng +274C1=>ài +274C2=>xiē +274C4=>bì +274CB=>chán +274CE=>pí +274CF=>cóng +274D0=>liè +274D1=>qí +274D3=>jì +274D4=>jīng +274D5=>dōng +274D6=>féi +274D7=>yí +274D8=>tuán +274E8=>měng +274E9=>cán +274EA=>yá +274F2=>yǎng +274F4=>tíng +274F8=>zhí +274FA=>xiè +274FB=>lǜ +274FD=>lì +274FF=>máo +27502=>xiá +27505=>sòu +27516=>sū +27517=>xuè +2751D=>lì +2751E=>yuán +27521=>zhǎn +27523=>tà +27524=>xuán +27525=>wèi +27526=>yè +27527=>páng +27528=>máo +27529=>tí +2752A=>pín +2752C=>dù +2752D=>qiú +2752E=>yǐ +27533=>tuó +27534=>chài +27537=>jìn +2753C=>é +27543=>chán +27544=>yīng +27545=>líng +27547=>xiǎn +27549=>qī +2754B=>yuè +2754C=>lüè +2754D=>yíng +2754E=>qú +27552=>fěi +27553=>zī +27559=>qīng +2755D=>níng +2755E=>wèi +2755F=>shuāng +27561=>fù +27564=>mò +27565=>mò +27566=>tuó +27567=>chài +27568=>zàng +2756E=>lí +2756F=>lí +27571=>xiá +27572=>juǎn +27574=>nán +27575=>mì +27578=>huáng +2757A=>shuàng +2757C=>xǔ +2757F=>fěi +27581=>xiè +27586=>tà +27587=>yǒng +27589=>zhǎn +27591=>qiáng +27592=>náng +27594=>lìn +27598=>luán +27599=>xiǎn +2759A=>fú +2759C=>líng +275A0=>sāo +275A2=>huì +275A8=>tíng +275AA=>qíng +275AC=>huāng +275AE=>àn +275B5=>mǎn +275B7=>nì +275BB=>guó +275BC=>ǒu +275BF=>xiàng +275C1=>jīn +275C6=>zhēng +275C8=>n +275CB=>sàn +275CC=>hù +275CE=>zú +275CF=>huǐ +275D2=>jī +275D6=>yè +275E6=>xíng +275E9=>là +275EA=>yù +275EB=>jué +275F1=>shù +275F2=>zhēng +275F4=>yǒng +275F6=>gē +275F8=>jiàn +275F9=>xìn +275FC=>huī +275FF=>shuài +27602=>chōng +27603=>háng +27608=>liǎo +2760D=>jiāng +2760F=>gōng +27611=>zhuó +27617=>qǐ +2761C=>qiān +2761E=>dǒu +2761F=>pō +27622=>hù +27625=>niǔ +27627=>qì +27628=>diāo +27629=>diāo +2762B=>lì +2762E=>xiōng +2763D=>ná +2763F=>zhēng +27640=>là +27641=>zhì +27643=>ě +27644=>bō +27645=>pō +27646=>xū +27647=>yòng +27648=>cí +27649=>lì +2764C=>páo +2764F=>xiù +2765B=>pù +2765D=>ché +2765E=>qì +27661=>yì +27663=>tí +27664=>duǒ +27665=>lóng +27667=>jiàn +2766D=>zhàn +2766E=>yuàn +27676=>yú +27678=>gēng +2767A=>hòu +2767E=>qǐ +27680=>mù +27681=>huàn +27682=>lòng +27683=>xì +27684=>é +27685=>lǎng +27686=>fèi +27687=>wǎn +27689=>cūn +2768B=>péng +2768F=>cuò +27690=>wēng +276A1=>gǎo +276A5=>cuì +276A8=>qì +276A9=>lí +276AA=>qiè +276AB=>qiàn +276AC=>kōng +276AD=>běng +276AF=>shòu +276B7=>wēi +276C4=>shān +276CF=>zī +276D2=>tì +276D3=>qiān +276D4=>dú +276D7=>tú +276DA=>wēi +276DE=>hú +276DF=>xīng +276E1=>shān +276E2=>zhǐ +276E7=>chǐ +276F8=>zhòu +276F9=>wēng +276FA=>chí +276FB=>suǒ +276FC=>xiè +276FE=>kè +27701=>shài +27702=>shī +27703=>shòu +27705=>jiè +27709=>gǎo +2770A=>lǚ +27714=>xiè +2771A=>zhǐ +2771E=>mán +27720=>shuài +27721=>kè +27723=>diǎo +27724=>yī +27726=>sù +27727=>chuāng +27731=>cuì +27732=>tuò +27735=>xiè +2773D=>xuán +27742=>hè +27743=>jué +27746=>tì +27747=>fèi +27749=>zhǐ +2774A=>shì +2774B=>tuí +2774E=>chōng +27750=>tì +27751=>zhàn +27752=>héng +27754=>qú +27755=>wéi +27757=>dūn +27758=>bào +2775C=>liáo +27764=>sī +2776A=>biǎo +2776B=>xiè +2776C=>bié +2776E=>cǒng +27772=>jù +27773=>hé +27777=>kuì +27778=>yōng +27780=>shù +2778D=>niè +2778F=>yú +27790=>zhuó +27791=>méng +27792=>hú +27795=>liè +2779D=>jiē +2779E=>xióng +277A3=>yǎn +277A9=>jié +277AA=>là +277AB=>shù +277AC=>jié +277AD=>léi +277B0=>zú +277B2=>shì +277B8=>wéi +277B9=>dū +277BA=>sù +277C3=>xié +277C4=>ráng +277CC=>luò +277D1=>qiān +277D8=>nàng +277D9=>líng +277DC=>jì +277E0=>mìng +277E3=>gǔ +277E8=>xuán +277EC=>xū +277F1=>bó +277FC=>wēi +27802=>kū +27806=>wǎn +27808=>chà +2780A=>mào +2780B=>kè +2780E=>cì +27812=>xiàn +27813=>mò +2781A=>hūn +2781B=>chàn +2781C=>shī +2781D=>zhěn +2781E=>è +2781F=>mí +27821=>shī +27822=>qū +27823=>shū +27825=>cī +27826=>yǎn +27829=>hū +2782A=>qī +2782B=>zhì +2782C=>huāng +27834=>zhǐ +27836=>yǒu +2783C=>gào +2783D=>yǎo +2783E=>pōu +27847=>yí +27848=>chèng +27849=>jì +2784B=>ǎi +2784D=>dòng +2784F=>suì +27851=>jiù +27858=>qì +27859=>lián +2785A=>xuǎn +2785C=>liǎo +27861=>yùn +27862=>xuǎn +27863=>cóu +27864=>piān +27866=>kuí +27868=>tí +27869=>huǎn +2786A=>dān +2786B=>guì +2786C=>chēn +2786E=>shǎng +2786F=>jì +27874=>liàn +27875=>kān +27876=>shèng +27878=>dōu +27879=>yóu +2787A=>qí +2787C=>xiǎo +27882=>yì +27883=>lóu +27886=>chuāng +2788B=>lào +2788C=>gāo +27890=>zēng +27892=>wéi +27896=>jiān +2789B=>yīng +2789C=>fán +2789D=>lì +2789E=>qiān +278A2=>yào +278A6=>kuī +278A7=>wéi +278A9=>què +278AC=>xiǎo +278AD=>què +278B0=>hū +278B5=>duō +278B6=>chù +278B9=>shēn +278BC=>zhuó +278BD=>é +278BE=>jì +278C1=>tán +278C3=>pā +278CB=>jiè +278CC=>qiào +278D1=>qián +278D2=>jù +278D5=>qiú +278D6=>tuó +278DA=>nuò +278DB=>sì +278DF=>yí +278E1=>gǔ +278E2=>hùn +278E3=>pá +278E4=>zī +278E6=>jiāo +278E9=>xǐ +278EA=>shǎo +278EC=>yí +278ED=>zhì +278F5=>lùn +278F7=>zhōu +278F8=>jué +278F9=>tán +278FA=>nuò +278FB=>jù +278FC=>hú +278FE=>zhì +27903=>bī +2790D=>chì +2790E=>xuān +2790F=>jí +27910=>guǎ +27911=>jú +27912=>wò +27913=>tuó +27915=>qiú +27916=>wēi +27917=>duān +27919=>shòu +2791B=>zhěn +2791C=>nè +2791F=>xì +27920=>zhé +27921=>zhì +27923=>ná +27928=>jiān +2792E=>yáo +2792F=>guó +27932=>dǐ +27934=>huò +27935=>jīng +2793C=>jué +2793D=>yuè +27944=>jí +27946=>sù +27948=>jiān +2794A=>kūn +2794B=>wò +2794C=>kuàng +2794D=>biāo +2794E=>jué +27951=>bì +27953=>chán +27955=>zī +27956=>lì +2795A=>fó +2795B=>qiǎn +2795C=>yǎn +2795E=>tàn +2795F=>mò +27963=>kòu +27964=>xī +2796E=>hù +2796F=>hù +27971=>fú +27974=>yàng +27975=>guò +27977=>rén +27978=>yìn +27979=>fēng +2797A=>jùn +2797C=>yún +2797F=>xùn +27981=>xì +2798E=>xiā +27991=>háng +2799A=>hù +2799D=>hū +2799E=>pù +2799F=>fān +279A4=>jiā +279A7=>yí +279AD=>tuō +279AE=>ná +279B8=>yín +279B9=>yìn +279C3=>jì +279C4=>wàng +279C5=>shì +279C6=>duī +279C7=>duò +279C9=>tuó +279CA=>wā +279CB=>lì +279CF=>rè +279D2=>cì +279D3=>xù +279D4=>zhōu +279D5=>zì +279DC=>wǎng +279DD=>yǎ +279DF=>jì +279E0=>chǎo +279E9=>jí +279F5=>shǎn +279F6=>tú +279F8=>bié +279F9=>xì +279FA=>pī +279FB=>zhà +279FE=>huì +27A00=>suō +27A02=>hè +27A04=>yuē +27A06=>wū +27A08=>líng +27A0A=>zhà +27A0B=>huá +27A17=>chán +27A1F=>è +27A21=>chén +27A27=>suì +27A29=>tiǎn +27A30=>zhì +27A31=>tì +27A32=>āo +27A33=>zhuó +27A34=>zì +27A35=>kē +27A37=>sè +27A38=>tián +27A39=>lù +27A3E=>shán +27A3F=>zhǎ +27A43=>chōng +27A45=>yàn +27A52=>mǔ +27A53=>hū +27A5A=>chī +27A5D=>sù +27A63=>nǎo +27A66=>jí +27A67=>duó +27A68=>hòu +27A6A=>còng +27A6B=>zhā +27A6C=>yín +27A6E=>xiǎo +27A70=>biàn +27A71=>bèng +27A72=>là +27A74=>chī +27A76=>qià +27A78=>ān +27A79=>shī +27A7C=>chì +27A85=>nù +27A87=>jì +27A93=>ǒu +27A95=>xiā +27A98=>chài +27A9A=>ái +27A9D=>shèng +27A9E=>hé +27AA0=>jí +27AA1=>chī +27AA2=>xì +27AA3=>zhēng +27AA6=>tā +27AA8=>mà +27AAB=>pī +27AAE=>xū +27AAF=>qiǎn +27AB9=>xià +27ACA=>yù +27AD1=>jié +27AD2=>xià +27AD3=>lǔ +27AD5=>qiè +27AD7=>chà +27ADB=>yàng +27ADC=>jì +27ADD=>shǎ +27ADE=>lòu +27AE0=>jī +27AE1=>zhì +27AE2=>wàng +27AE4=>bì +27AE5=>ān +27AE6=>yī +27AE7=>ān +27AEC=>lí +27AF9=>xiān +27AFE=>jiù +27AFF=>tǎn +27B01=>hào +27B02=>hè +27B05=>zhā +27B06=>zhǎn +27B07=>yì +27B08=>xì +27B0A=>xì +27B0B=>fà +27B0C=>yán +27B0F=>mǔ +27B15=>gū +27B1E=>yún +27B24=>zhòng +27B26=>chǎn +27B27=>chuáng +27B28=>huì +27B29=>zá +27B2A=>gùn +27B2B=>jiǎn +27B2C=>yá +27B30=>xiàng +27B31=>hè +27B43=>dàn +27B47=>mián +27B48=>níng +27B4A=>méng +27B4C=>liè +27B4D=>zhòu +27B4E=>pū +27B4F=>tāi +27B53=>yíng +27B54=>téng +27B55=>guó +27B5A=>qiáng +27B5C=>lǜ +27B5D=>sà +27B5E=>liè +27B5F=>chí +27B60=>xiě +27B63=>guó +27B64=>bào +27B65=>luò +27B66=>juàn +27B6A=>è +27B73=>hé +27B75=>mèi +27B78=>xiè +27B79=>pín +27B7B=>hān +27B7C=>chèn +27B7D=>shàn +27B7E=>huì +27B86=>yīng +27B88=>jiǎn +27B8D=>ān +27B91=>tà +27B92=>yī +27B93=>tuí +27B97=>liú +27B99=>zuó +27B9B=>lí +27B9D=>pín +27B9E=>xuè +27BA0=>nèn +27BA1=>dòu +27BA4=>lǎn +27BAA=>zhān +27BAB=>jué +27BAC=>zhēn +27BAD=>jí +27BAE=>qiān +27BB0=>hān +27BB1=>fén +27BB3=>hān +27BB4=>hóng +27BB5=>hé +27BB6=>hóu +27BBA=>zhàn +27BBB=>chóu +27BBC=>tài +27BBD=>qiàn +27BBF=>shè +27BC0=>yīng +27BC3=>qīn +27BC6=>huò +27BC8=>xì +27BC9=>hè +27BCA=>xì +27BCB=>xiā +27BCC=>hāo +27BCD=>lào +27BCF=>lì +27BD2=>chēng +27BD6=>jùn +27BD7=>xī +27BD8=>hǎn +27BDE=>dòu +27BE0=>dōu +27BE1=>wān +27BE4=>dōu +27BE5=>zài +27BE6=>juàn +27BE8=>lǒu +27BE9=>chù +27BEB=>zhēng +27BEF=>qí +27BF0=>kàn +27BF1=>huò +27BF2=>lái +27BFA=>gāi +27BFC=>shòu +27BFE=>dōng +27C03=>lóu +27C04=>tuān +27C07=>yú +27C08=>wù +27C0A=>tián +27C12=>guó +27C18=>tán +27C19=>qí +27C20=>liè +27C21=>lì +27C23=>xūn +27C28=>gèng +27C29=>tīng +27C2A=>hàn +27C2B=>chù +27C2D=>tún +27C2F=>xióng +27C30=>yóu +27C31=>mò +27C32=>chǐ +27C34=>hǔ +27C35=>dū +27C37=>mǔ +27C39=>nà +27C3B=>líng +27C3F=>ài +27C40=>xiān +27C44=>kǎn +27C45=>sì +27C46=>sān +27C4A=>yì +27C4F=>yì +27C50=>xiào +27C52=>zhī +27C53=>dòu +27C58=>mài +27C5C=>lún +27C5D=>jué +27C61=>qiāng +27C62=>líng +27C69=>pián +27C6A=>còu +27C6B=>duò +27C6C=>yǔ +27C70=>zhuō +27C72=>xì +27C73=>huài +27C74=>míng +27C75=>táng +27C79=>pū +27C7B=>mì +27C7C=>mán +27C7E=>guāi +27C80=>qiān +27C82=>lín +27C83=>mǐn +27C84=>wěi +27C85=>céng +27C87=>hù +27C88=>suí +27C8B=>jù +27C8C=>shà +27C8D=>méng +27C97=>wéi +27C98=>xī +27C99=>lìng +27C9C=>bì +27C9D=>wèi +27CA1=>lì +27CA2=>zhé +27CA4=>yóng +27CA5=>hú +27CA6=>wán +27CA7=>bā +27CA8=>jiān +27CAD=>zuǒ +27CAE=>zhǎn +27CAF=>bō +27CB0=>qiū +27CB1=>yāng +27CB4=>dōng +27CB5=>qú +27CBA=>pí +27CBB=>zhǎi +27CBE=>shān +27CBF=>gòu +27CC0=>biào +27CC1=>yí +27CC2=>fú +27CC4=>xìn +27CC5=>shì +27CC6=>tōng +27CC9=>dīng +27CCC=>tū +27CCD=>xiāo +27CCE=>wú +27CCF=>péi +27CD0=>huī +27CD5=>lái +27CD9=>sì +27CDA=>cuǐ +27CDB=>shà +27CDC=>zhǒu +27CDD=>zhào +27CDE=>wéi +27CDF=>lái +27CE0=>bì +27CE3=>dǒng +27CE6=>nǎo +27CE7=>xiē +27CE8=>rǎo +27CE9=>tuàn +27CEA=>wèi +27CEB=>yóu +27CEC=>méi +27CED=>yuán +27CEE=>zhòng +27CF6=>sōu +27CF8=>gú +27CF9=>shào +27CFB=>zhǎo +27CFC=>pí +27CFF=>tōng +27D01=>chī +27D02=>péng +27D03=>chán +27D04=>yōng +27D05=>shuǎng +27D07=>wǔ +27D09=>pí +27D0A=>huàn +27D0C=>fú +27D0E=>biào +27D13=>náo +27D15=>biào +27D16=>wèi +27D17=>yōng +27D19=>nǎo +27D1A=>guài +27D20=>lì +27D22=>xìn +27D23=>yán +27D24=>pò +27D25=>péi +27D2A=>suǒ +27D2C=>rèn +27D2D=>shǎn +27D32=>suǒ +27D38=>dān +27D3A=>mèn +27D43=>shǒu +27D48=>gòu +27D4A=>hān +27D4B=>shì +27D4C=>yǎng +27D4E=>gǔ +27D5B=>kē +27D5E=>jū +27D60=>pài +27D61=>cè +27D62=>bāo +27D63=>xiōng +27D64=>cái +27D67=>lǐn +27D68=>ài +27D6C=>mì +27D6D=>lǎi +27D71=>xiāo +27D73=>shé +27D7B=>huó +27D7C=>nì +27D84=>zhèng +27D86=>lìn +27D87=>zhá +27D8A=>yún +27D8D=>xù +27D94=>chéng +27D95=>wǒ +27D96=>xī +27D99=>bèi +27D9C=>shāng +27DA0=>yù +27DA1=>mì +27DB2=>duǎn +27DB5=>chà +27DB7=>zé +27DB8=>chèng +27DBA=>tíng +27DC5=>yí +27DCB=>yāo +27DCE=>kū +27DD0=>fén +27DD1=>xié +27DD2=>chèng +27DDB=>kuì +27DDF=>bīn +27DE1=>lóu +27DE5=>yì +27DE6=>mì +27DE7=>xiè +27DF1=>guī +27DF3=>luó +27DF6=>shàn +27DFE=>jú +27DFF=>dū +27E02=>xiān +27E05=>zhǐ +27E08=>bìn +27E15=>zhǐ +27E16=>zhuàn +27E17=>xué +27E18=>liàn +27E19=>suì +27E26=>làn +27E27=>jù +27E28=>mián +27E29=>xùn +27E2A=>zhàn +27E2B=>gùn +27E32=>zhì +27E3D=>wèi +27E3E=>quǎn +27E3F=>chài +27E48=>réng +27E4A=>yuè +27E4C=>zī +27E50=>luò +27E51=>guì +27E53=>chéng +27E55=>jū +27E56=>tiǎn +27E57=>wàn +27E5B=>zhī +27E5E=>nǎn +27E63=>hān +27E68=>xī +27E69=>lín +27E6C=>yān +27E6D=>xù +27E72=>hù +27E73=>gàn +27E74=>xù +27E76=>xì +27E7A=>cuì +27E7D=>xì +27E7E=>hú +27E85=>yān +27E8E=>yì +27E8F=>chí +27E90=>jué +27E92=>zú +27E9C=>jiào +27E9D=>yì +27E9F=>tǎn +27EA0=>chì +27EA1=>bá +27EA2=>tòu +27EA3=>zōng +27EA4=>qiú +27EA7=>chì +27EA8=>xǐ +27EB0=>nì +27EB2=>cū +27EB4=>wǔ +27EB6=>chù +27EB7=>sū +27EB8=>yóng +27EB9=>jǔ +27EBA=>bá +27EBC=>cǐ +27EBD=>dì +27EBE=>pǎn +27EBF=>chì +27EC1=>qiǔ +27EC3=>yán +27ECD=>zhǎi +27ED2=>xiàn +27ED3=>bèng +27ED4=>kuāng +27ED5=>qì +27ED6=>zhōu +27ED7=>jú +27ED8=>qiè +27ED9=>mò +27EDA=>yuán +27EDC=>guì +27EDD=>zuī +27EE7=>qiè +27EF0=>hú +27EF1=>qiú +27EF2=>hái +27EF3=>fù +27EF4=>làng +27EF5=>shà +27EF6=>xī +27EF7=>bū +27EF8=>shì +27EF9=>yǒng +27EFA=>guāng +27EFC=>niè +27EFF=>hǒu +27F0A=>mì +27F0E=>è +27F0F=>xián +27F10=>yǔn +27F11=>xù +27F12=>qǐn +27F13=>dōng +27F14=>léng +27F15=>qì +27F16=>lán +27F17=>fú +27F18=>qǐ +27F19=>chǒng +27F1C=>cù +27F1F=>mò +27F20=>bēi +27F24=>dào +27F28=>jié +27F29=>chòng +27F2A=>chì +27F2B=>yù +27F2C=>cuī +27F2D=>sù +27F2E=>tì +27F2F=>shù +27F30=>zhá +27F31=>fú +27F33=>chè +27F34=>fó +27F35=>hóu +27F36=>zhá +27F44=>jié +27F45=>zhá +27F46=>zhān +27F49=>yǎn +27F4A=>hái +27F4B=>wǔ +27F4C=>huá +27F4D=>diān +27F4E=>yáo +27F4F=>sōu +27F50=>qiān +27F51=>jí +27F52=>xiòng +27F53=>qì +27F54=>jūn +27F56=>hái +27F5E=>yǎn +27F5F=>jié +27F60=>cuī +27F62=>tuán +27F63=>zhāng +27F64=>piāo +27F65=>lù +27F66=>zhī +27F67=>chù +27F68=>mì +27F69=>qiāng +27F6B=>liàn +27F72=>lì +27F76=>é +27F77=>sù +27F78=>jué +27F7B=>jú +27F7C=>tán +27F7D=>liáo +27F7E=>sān +27F7F=>dòng +27F81=>zá +27F82=>zhí +27F86=>xuàn +27F87=>líng +27F8A=>dēng +27F8D=>zhān +27F8E=>xuān +27F8F=>qǐn +27F90=>jiào +27F91=>pì +27F94=>hǎn +27F9A=>yú +27F9B=>guó +27F9D=>xún +27FA0=>xún +27FA1=>chán +27FA2=>jié +27FA3=>jú +27FA4=>yǎn +27FA5=>dú +27FA7=>hòng +27FA8=>xiàn +27FA9=>xún +27FAE=>líng +27FAF=>jié +27FB0=>yì +27FB1=>qú +27FB2=>gān +27FB3=>fēng +27FB5=>jué +27FB6=>qū +27FBB=>jiù +27FBD=>jì +27FBE=>jǐ +27FC5=>xí +27FC6=>pāng +27FC8=>kuàng +27FC9=>kù +27FCB=>kù +27FCC=>zhà +27FCF=>bà +27FD2=>chěn +27FD3=>hù +27FD4=>nù +27FD5=>é +27FD6=>xiōng +27FD7=>dǔn +27FD8=>shēng +27FD9=>wán +27FDA=>fēn +27FDD=>xī +27FDE=>zī +27FE0=>hù +27FE5=>bié +27FE7=>tuò +27FE8=>bǎn +27FE9=>gé +27FEB=>kē +27FF2=>zhuì +27FF3=>fú +27FF4=>mò +27FF5=>jiá +27FF6=>tuó +27FF7=>yù +27FF9=>mǔ +27FFA=>jué +27FFB=>jú +27FFC=>guā +27FFD=>pǒ +28000=>nǐ +28004=>wǎ +28005=>yǎn +28014=>chǒu +28015=>kuāng +28016=>hài +28018=>xiáng +28019=>xī +2801B=>cún +2801C=>tōng +2801D=>ruò +2801F=>duó +28020=>chè +28024=>lèi +28025=>zī +28027=>zhěng +28028=>zuǒ +2802B=>kāng +2802C=>zài +2802E=>yuān +2802F=>qióng +28033=>fá +28034=>xún +28036=>jì +28038=>chā +28040=>shū +28041=>xuàn +28042=>xié +28043=>tī +28044=>hàn +28045=>xiān +28046=>shān +28047=>tùn +28048=>háng +28049=>kǔn +2804A=>cén +2804B=>dōu +2804C=>nuó +2804D=>yàn +2804E=>chéng +2804F=>pū +28050=>qì +28051=>yuè +28052=>fū +28057=>tǐng +2805F=>wǒ +28060=>shēng +28061=>tuǒ +28074=>tǎn +28076=>yǎ +28077=>zhì +28078=>lù +28079=>yǎn +2807A=>jū +2807D=>dé +2807F=>chù +28080=>zǔ +28081=>è +28082=>zhí +28083=>péng +28085=>biē +28087=>dǐ +28090=>lái +28092=>yè +2809C=>háo +2809D=>pán +2809E=>tàn +2809F=>kāng +280A0=>xū +280A1=>zòu +280A2=>jì +280A3=>wù +280A6=>chuàn +280A9=>pò +280AA=>yǎn +280AB=>tuò +280AD=>dú +280AF=>pián +280B0=>chì +280B1=>hùn +280B2=>pīng +280B4=>cōng +280B5=>zhǎ +280BA=>wān +280BF=>wǎi +280C3=>è +280C4=>wèi +280C5=>bāi +280C7=>jiāng +280D3=>chá +280D5=>chù +280D6=>kuà +280D7=>téng +280D8=>zōu +280D9=>lì +280DA=>tà +280DB=>sà +280DE=>pán +280DF=>pán +280E3=>sào +280E4=>qiāo +280ED=>zú +280EF=>zhì +280F0=>yǎn +280F2=>jié +280F3=>néng +28104=>luán +28105=>qū +28107=>dèng +28108=>liáng +28109=>chǎn +2810A=>qiè +2810B=>lòu +2810C=>dié +2810D=>cuī +28110=>jǐ +28113=>cháo +28114=>shuàn +28115=>zú +28117=>kāng +2811A=>qiāng +2811B=>lí +2812E=>shuāi +2812F=>yù +28130=>zhāng +28131=>lěi +28145=>pó +2814A=>zhé +2814B=>xiào +2814D=>tǎn +2814E=>cuì +2814F=>lán +28151=>xū +28152=>shù +28153=>zhǎ +28154=>cán +28157=>bǐ +28158=>pèng +2815D=>chéng +28163=>qiáo +28164=>jī +2816A=>zhāi +2816C=>lán +28181=>tiǎn +28182=>sà +28183=>jīn +28184=>zhù +28185=>duò +28187=>chà +28188=>juàn +28189=>táng +2818A=>bèng +2818C=>fán +2818D=>liè +2818E=>zéi +2818F=>suì +28199=>sè +281A7=>zhì +281A8=>tuí +281AA=>qīng +281AC=>chuò +281B0=>tà +281B1=>bìng +281B2=>wěn +281B5=>pǒ +281BD=>mó +281BE=>cā +281C1=>kuàng +281C3=>cuó +281C4=>rǎo +281C5=>bào +281C6=>lài +281CD=>niǎn +281CE=>lí +281D5=>jiǎo +281D6=>lú +281D7=>lì +281D8=>lóng +281D9=>guì +281DD=>chǎn +281E4=>xiān +281E6=>chàn +281E8=>xiè +281E9=>zhàn +281EF=>shuāng +281FB=>mǐ +281FC=>luán +281FD=>luò +28200=>diān +28208=>dié +2820A=>wān +2820B=>yuè +2820C=>luán +2820E=>luán +28213=>léng +28215=>wǎi +28216=>dìn +28217=>nèn +28218=>shǎo +28219=>xiè +2821A=>pí +28225=>máo +28227=>yǐn +28229=>bó +2822B=>zhù +2822E=>chōng +28236=>mǔ +28237=>tuó +28239=>tǒng +2823A=>yé +28241=>huàng +28243=>rèn +28245=>yè +2824B=>tuó +28256=>zuān +28257=>yù +2825A=>ā +2825C=>zhōu +2825D=>wān +28261=>duǒ +28262=>zhòng +28263=>hā +28264=>huáng +28265=>miàn +28269=>chūn +2826A=>qiè +2826B=>gōng +2826C=>tíng +2826D=>méi +28271=>tàng +28274=>róng +28277=>róng +28278=>qí +28279=>guó +2827D=>xiàng +2827E=>tián +28285=>xiāo +28288=>zhān +28289=>cuì +28294=>lán +28298=>shēn +2829A=>lěi +2829B=>lì +2829D=>chān +2829E=>niè +2829F=>luán +282A1=>tīng +282A2=>huì +282A7=>gōng +282B0=>qì +282B1=>yú +282B3=>xīn +282B8=>yuè +282B9=>bā +282BA=>dài +282BB=>jī +282BC=>xuàn +282BF=>jué +282C0=>niǔ +282C8=>dù +282C9=>jí +282D0=>pā +282D1=>gǒng +282D2=>bèn +282D4=>kēng +282D5=>yàng +282D6=>liǔ +282D7=>ní +282D8=>zhà +282D9=>yìn +282DA=>niǎn +282DB=>pào +282DD=>gōng +282DE=>bù +282DF=>hé +282E0=>rǒng +282E1=>guì +282E5=>bì +282E6=>xī +282E7=>jú +282E8=>hún +282E9=>bì +282EB=>tiāo +282EC=>zhěng +282EF=>yì +282F0=>cì +282F2=>bìng +282F7=>gōng +282FA=>fá +282FD=>yáng +282FE=>xǔ +28301=>hōng +28304=>zàng +28305=>chái +28306=>hóng +28308=>tián +2830C=>zhī +2830D=>xīng +2830E=>xú +28311=>zhèn +28314=>wǎn +28318=>jùn +2831D=>wò +28320=>lù +28322=>zhēng +28323=>rǒng +28324=>chéng +28325=>fú +28327=>è +28328=>tāo +28329=>táng +2832B=>juān +2832C=>chào +2832D=>tà +2832E=>dǐ +28330=>zōng +28333=>kēng +28334=>tuī +28336=>kēng +28345=>rǒng +28346=>yūn +28347=>hé +28348=>zǒng +28349=>cōng +2834A=>qiū +2834E=>mù +2834F=>duó +28350=>xǔ +28351=>kēng +28352=>xiàn +2835B=>dú +2835C=>kǎn +2835E=>yīng +28362=>zī +28367=>huáng +28369=>péng +2836B=>lì +2836D=>bó +2836E=>gé +2836F=>jú +28370=>kē +28372=>hú +28373=>yáo +28374=>táng +28376=>qióng +28377=>rǒng +28378=>liǔ +28379=>huì +2837A=>jī +28389=>zhì +2838B=>táng +2838C=>zhǐ +2838D=>kāng +28394=>yàng +28396=>tǎng +28397=>hōng +2839B=>liáng +2839D=>cáo +283A1=>nǎi +283A2=>zǒng +283A4=>dèng +283A6=>jiāo +283A7=>péng +283A9=>guāng +283AA=>ér +283AB=>jiàn +283AC=>jiào +283AD=>nuó +283AE=>zǎo +283B3=>péng +283B4=>dāng +283B6=>qú +283B7=>lián +283B8=>mù +283B9=>lǎn +283BE=>fén +283C2=>hún +283C6=>kuāng +283C8=>yǐn +283C9=>shuàn +283CA=>jiàn +283D2=>luò +283D4=>lù +283DA=>gé +283DB=>rǎng +283DE=>pín +283E0=>lóng +283E4=>zhěn +283E5=>xiàn +283E8=>lìn +283E9=>lián +283EA=>shān +283EB=>bó +283EC=>lì +283F3=>xié +283F4=>gé +283F5=>mǐn +283F6=>lián +283F9=>jué +283FA=>zhōu +283FF=>kē +28401=>dié +28403=>zhé +28405=>shū +28406=>jī +28407=>lóng +28408=>guāng +28409=>zǎo +2840A=>xiàn +2840B=>qiān +2840D=>shēn +28410=>yǐn +28411=>jiè +28414=>shēn +28415=>shēn +28416=>sǎ +2841B=>xì +28421=>kù +28423=>qú +28425=>gé +28426=>bàn +28428=>bì +28429=>qiān +28430=>bīn +28431=>bàn +28433=>zuò +28434=>pì +28436=>huò +2843E=>bàn +2844A=>nóng +2844C=>chén +2844E=>pēng +28451=>fǔ +28452=>tú +2845C=>pǐ +2845D=>pò +28460=>chǐ +28463=>xuè +28464=>qì +28465=>wù +28468=>zhì +28469=>dì +2846A=>cōng +2846B=>yóu +28479=>cōng +2847C=>dì +2847D=>zhuó +2847F=>zǒu +28480=>cóng +28483=>pàn +28484=>yǎn +28485=>qì +28486=>rǒng +28487=>jiá +28489=>zhì +2848A=>qiú +2848B=>yuè +2848D=>shì +28491=>háo +28499=>tuō +2849C=>bié +2849E=>kàn +284A2=>chuò +284A4=>cǐ +284A6=>yǐn +284A7=>shì +284A8=>nài +284A9=>ruǎn +284AB=>yáng +284AC=>chī +284AE=>cī +284B1=>gōng +284B2=>mí +284B4=>jǐ +284BC=>gèn +284BD=>zào +284C1=>běng +284C7=>xǐn +284C8=>kuò +284CA=>dié +284CD=>tíng +284DA=>shuì +284DE=>dài +284E6=>lǐ +284E8=>yǒng +284E9=>jiāo +284EC=>tá +284ED=>qǔ +284EE=>yín +284EF=>yuān +284F0=>jié +284F2=>qiān +284F3=>yāo +284F4=>yà +284F7=>qīng +284FF=>péi +28517=>jiā +28519=>tòu +2851B=>tī +28521=>dùn +28522=>chǎn +28523=>jiā +28524=>chì +28525=>jiān +28526=>shù +2852F=>tà +28555=>zhī +28557=>yuán +2855A=>hū +2855C=>liè +28560=>zé +28562=>chù +28566=>qiù +28567=>bēng +28579=>huán +2857A=>kuā +2857B=>shēng +2857D=>jié +2857F=>wǎng +28583=>hū +2858A=>zé +2858B=>zǎn +2858C=>yàng +2858E=>chǐ +2858F=>jiù +2859A=>liáo +2859B=>yū +285A0=>biǎn +285A2=>kuáng +285AC=>chòu +285AD=>yá +285AE=>zhuó +285B0=>qiè +285B1=>xiàn +285B3=>yuān +285B4=>wǔ +285B5=>jiǎo +285B6=>xiàng +285B7=>shà +285B9=>zhì +285BC=>chòng +285BE=>biān +285BF=>wēi +285D3=>dào +285DD=>yù +285DE=>tuí +285E1=>chào +285E5=>huì +285E6=>qiǎn +285E8=>wěi +285F0=>yóu +285FC=>dì +285FE=>dà +28601=>yóu +28602=>jiù +28603=>tuí +28604=>zǎn +28607=>huì +28609=>shà +2860C=>huò +28614=>yáo +28619=>xiàn +2861E=>xiàn +2862C=>dì +2862E=>jiù +28632=>huì +28634=>kào +28635=>yóu +28638=>lì +2863C=>chuán +2863E=>chí +28640=>huò +28642=>yóu +28644=>yuè +2864E=>tà +2864F=>zàn +28653=>niè +28654=>zhù +28661=>xiǎn +28669=>shí +2866B=>kǒu +2866C=>qǐ +2866D=>tǔ +2866E=>fán +2866F=>cūn +28672=>tún +28673=>chā +28674=>cái +28675=>xiàng +28676=>pèi +28677=>jǐng +28678=>qí +28679=>shǎo +2867A=>niǔ +2867B=>nà +2867D=>qín +2868D=>bì +28693=>bì +28694=>bāo +28695=>biàn +28696=>zī +28697=>nà +28698=>wèi +28699=>háo +286A1=>jǐn +286A3=>zhèng +286A7=>qié +286AE=>hào +286AF=>tóng +286B0=>zǎo +286B1=>shèng +286B2=>cún +286B3=>huāng +286B4=>rú +286B5=>zài +286B6=>nián +286BE=>xiān +286C8=>quán +286C9=>jì +286CA=>yín +286CB=>lǐ +286CC=>máng +286CD=>shào +286CE=>hàn +286CF=>cuò +286D0=>jùn +286D1=>jì +286D2=>bù +286D3=>lòng +286D4=>fǒu +286D5=>yóu +286D6=>kuài +286DC=>xiàng +286E1=>yún +286E3=>qín +286E4=>huí +286E5=>pú +286EB=>lí +286EC=>péi +286ED=>shū +286EE=>jū +286EF=>yí +286F0=>zhēng +286F1=>chóng +286F3=>xí +286F5=>hǔ +286F6=>róu +2870C=>huàn +2870D=>qiào +2870E=>zhī +2870F=>yíng +28710=>xǐ +28711=>qiāo +28712=>jì +28713=>zhēng +28714=>huáng +28716=>yú +28717=>zōu +28718=>méi +2871C=>shěng +28729=>quán +28730=>jiāng +28731=>hé +28733=>tóng +28734=>hé +28735=>wēn +28736=>yì +28737=>páng +2873A=>wēng +2873B=>qián +2873C=>lì +2873D=>yí +2873E=>chuàng +2873F=>xù +28740=>wěi +28746=>gē +28748=>yǔ +2874B=>zhài +2874C=>gān +2874D=>qiān +2874E=>kāng +2874F=>lí +28750=>shēn +28751=>guàn +28753=>piáo +28756=>lí +28758=>hǔ +2875B=>tú +2875C=>shùn +2875E=>hù +2875F=>lí +28762=>lòu +28766=>dàng +28768=>zuò +28769=>shān +2876B=>shè +2876D=>féng +2876E=>jù +2876F=>tóng +28770=>jiǎo +28771=>qiáo +28772=>gāo +28773=>zī +28774=>huáng +28775=>shān +28778=>tán +2878C=>tuō +2878E=>lìng +28790=>chéng +28791=>wèng +28792=>zuó +28793=>yù +28795=>zhú +28797=>qún +28798=>xǐ +28799=>qú +2879B=>gé +287A2=>qī +287A3=>xū +287A8=>gài +287A9=>què +287AA=>chóu +287AB=>méng +287B2=>shēn +287B3=>qú +287B6=>qiāo +287B7=>cán +287BA=>lì +287BC=>wàn +287BD=>léi +287BE=>xīng +287BF=>láng +287C2=>shì +287C3=>zhēng +287C4=>fán +287CA=>zhì +287CF=>yín +287D1=>lì +287D6=>mó +287D7=>wěi +287D9=>yīng +287DA=>ráng +287E0=>quān +287E5=>luǒ +287F2=>dài +287F4=>yìn +287F5=>bǐ +287F6=>gē +287F8=>wèn +287F9=>yǎn +287FA=>miǎn +287FC=>gǎng +287FD=>qiú +287FE=>zhī +2880B=>gū +2880C=>tóng +2880E=>líng +2880F=>tí +28810=>cí +28811=>yí +28812=>fàn +28813=>pō +28814=>bì +28816=>bào +2881F=>pēng +28821=>suān +28824=>sōng +28825=>wéi +28826=>xiáo +2882C=>hào +2882D=>yǎn +28836=>yí +28837=>zāo +28838=>yǐng +28839=>nǎn +2883F=>zā +28841=>tiǎn +28842=>xī +28843=>jiào +28844=>yán +2884C=>néi +2884D=>tǎn +2884E=>yàn +2884F=>tiǎn +28850=>zhì +28851=>chōu +28852=>táo +28857=>zhà +2885E=>miǎn +28861=>wǔ +28862=>yǐn +28863=>yàn +28864=>lǎo +28869=>pō +2886B=>hùn +2886C=>hǎi +2886D=>mú +2886E=>cōng +28871=>kù +28872=>chōu +28874=>yǒu +28878=>zhuó +2887B=>sōu +28882=>yìn +28885=>zuì +28886=>sāng +28887=>liù +28888=>hàn +28889=>wèi +2888A=>méng +2888B=>hú +2888C=>lì +2888E=>mì +28890=>bāng +28891=>jiǎn +2889C=>què +288A0=>méng +288A2=>mú +288A3=>hǒng +288A4=>hù +288A5=>mí +288A6=>shài +288A9=>shāng +288AA=>chào +288AC=>zhuó +288AE=>zhī +288AF=>niàn +288B5=>jì +288B8=>kē +288B9=>zhēng +288BF=>dān +288C0=>liǎo +288C1=>zhǎn +288C2=>gǒng +288C3=>láo +288C4=>huā +288C5=>chuài +288C7=>jiǎn +288C8=>kuì +288CD=>shē +288D4=>chěn +288D5=>tǎn +288D7=>hú +288D8=>méng +288D9=>pào +288DA=>zhǎn +288DB=>cháng +288DD=>gǎn +288E0=>yì +288E2=>suì +288E6=>xù +288E7=>jì +288E8=>làn +288EC=>yí +288EF=>mì +288F1=>miè +288F5=>cuán +288F8=>lǎn +288FB=>yān +288FE=>mí +28902=>yǒng +28903=>cáng +28904=>jiǎn +28907=>sōu +2890E=>yán +28911=>juàn +28915=>è +28918=>fèn +2891A=>fèn +28921=>guàng +28922=>mái +28924=>liě +28929=>chōng +2892B=>lí +28931=>zhí +28934=>xiè +28937=>chóu +28939=>jí +2893D=>pī +28942=>jié +28947=>zhǒu +2894D=>xiōng +28951=>kuàng +28959=>jǐng +2895B=>hù +2895E=>qián +28963=>cén +28966=>qí +28967=>wǎn +28968=>máo +2896A=>dǒu +28974=>kǒu +28976=>dài +28978=>náo +2897A=>hóng +28982=>lǎi +28983=>duǒ +28984=>qiān +28986=>yín +28996=>lòu +28997=>huī +2899B=>fù +2899C=>máo +2899E=>zhōu +289A1=>yóng +289AD=>láo +289AE=>jí +289AF=>yì +289B0=>liú +289B1=>cōng +289B3=>nǎn +289D0=>tūn +289D1=>xiàng +289D5=>biàn +289D6=>chuáng +289D7=>wù +289D9=>jū +289E5=>xiē +289E6=>pī +289E7=>zhuó +289E8=>ruì +289EA=>sào +289EB=>zì +289ED=>zhèng +289F0=>zú +289F1=>qū +289F3=>chì +289F5=>zhì +28A17=>quàn +28A18=>qiān +28A19=>yā +28A1A=>chào +28A1B=>hé +28A1C=>rǔ +28A20=>jū +28A21=>wù +28A2C=>chì +28A2D=>kuàng +28A2F=>còu +28A30=>ruàn +28A31=>kuò +28A32=>chí +28A33=>zú +28A34=>jiāo +28A36=>yú +28A37=>tú +28A38=>méng +28A39=>dā +28A3A=>shuò +28A65=>fēng +28A66=>gǒu +28A67=>dōng +28A68=>chǎ +28A69=>mào +28A6A=>chǎn +28A6B=>biān +28A6C=>yù +28A6F=>wán +28A70=>zú +28A72=>zī +28A74=>chuān +28A75=>wǎn +28A76=>wā +28A78=>quān +28A7B=>wǎn +28A7D=>xià +28A84=>yìng +28A85=>jiàn +28A88=>wěi +28A89=>tí +28A8A=>sāo +28A8C=>qí +28A8D=>shā +28A8E=>yù +28A8F=>jí +28A90=>dòu +28A91=>chǎn +28A92=>tuán +28A95=>liú +28A97=>zhuì +28AB3=>ruàn +28AB6=>yàn +28AB7=>gǔ +28AB9=>lì +28ABA=>chā +28ABE=>dì +28AC0=>zhǎn +28AC1=>pō +28AD2=>lòu +28AD4=>zhì +28B01=>lián +28B05=>luǒ +28B0D=>duò +28B10=>jué +28B11=>lì +28B12=>lán +28B14=>ruàn +28B15=>gū +28B16=>chán +28B17=>xū +28B1A=>zhǐ +28B41=>xuè +28B42=>bō +28B43=>chēng +28B45=>zhù +28B46=>hēi +28B49=>bān +28B53=>dié +28B56=>zhǎn +28B57=>guó +28B5A=>biāo +28B5B=>là +28B7A=>jīn +28B82=>gǎi +28B92=>mèng +28B94=>yù +28BAA=>xǐ +28BAC=>piāo +28BAD=>sī +28BB4=>dèng +28BB8=>chuō +28BB9=>dí +28BBA=>jī +28BBB=>chán +28BBF=>zhuó +28BD3=>cài +28BDE=>jiàng +28BF2=>tóu +28BFD=>lí +28C02=>qiàn +28C06=>chuō +28C0F=>tà +28C11=>diào +28C13=>jiǎn +28C1B=>zhǐ +28C1C=>jué +28C1E=>mó +28C20=>luó +28C26=>bǎo +28C2D=>zuǎn +28C35=>zhē +28C38=>yú +28C3B=>bǎo +28C3E=>mǎ +28C3F=>xì +28C40=>hù +28C41=>yì +28C42=>é +28C43=>gū +28C44=>tú +28C45=>zhēn +28C47=>qiú +28C48=>sù +28C49=>liàng +28C4A=>qū +28C4B=>líng +28C4C=>guàn +28C4D=>láng +28C4E=>tōu +28C4F=>dā +28C50=>lòu +28C51=>huáng +28C52=>shòu +28C53=>jiāo +28C54=>zūn +28C55=>gǎi +28C56=>wéi +28C59=>kūn +28C5A=>duàn +28C5B=>sōng +28C5C=>qí +28C5D=>yǎng +28C61=>shì +28C63=>gǎi +28C66=>dào +28C67=>yǎo +28C6B=>qián +28C6D=>shāo +28C6E=>cháng +28C6F=>miǔ +28C71=>mó +28C75=>nǎo +28C78=>cōng +28C7A=>niè +28C7B=>zhāo +28C7C=>cén +28C7F=>sōng +28C80=>niè +28C81=>cì +28C84=>jùn +28C86=>shāo +28C88=>zhú +28C89=>duǒ +28C8A=>àn +28C8B=>bī +28C8E=>tì +28C90=>pǐ +28C91=>xiá +28C92=>qiú +28C93=>shěng +28C97=>tāng +28C9B=>mán +28C9C=>piān +28C9E=>tì +28C9F=>róng +28CA7=>cōng +28CAA=>jī +28CAB=>féng +28CAC=>wù +28CAD=>jiào +28CAE=>láo +28CAF=>zēng +28CB0=>péng +28CB1=>cǎn +28CB3=>nóng +28CB5=>chǎn +28CBE=>mán +28CBF=>guì +28CC0=>niào +28CC1=>chōng +28CC2=>chàn +28CC6=>nàng +28CC9=>xiā +28CCA=>jiū +28CCB=>jǐ +28CCC=>zhèn +28CD1=>tǐng +28CD4=>mén +28CD5=>yuè +28CD7=>zhōng +28CD8=>tún +28CD9=>ruì +28CDA=>xiè +28CDB=>xī +28CDD=>tǐng +28CDE=>niǔ +28CE0=>wǎng +28CE1=>jiān +28CE3=>fēn +28CF2=>biàn +28CF7=>yí +28CFA=>dié +28CFB=>jī +28CFC=>gǎn +28CFF=>jiān +28D00=>jiōng +28D06=>kāi +28D0A=>què +28D0C=>nán +28D0D=>móu +28D0E=>xù +28D0F=>sǒng +28D10=>shèn +28D11=>kuāng +28D12=>què +28D13=>wéi +28D17=>dié +28D18=>nán +28D1A=>ruò +28D1B=>gōng +28D1C=>dòu +28D1E=>niǎn +28D21=>chāo +28D22=>hé +28D23=>yàn +28D29=>tú +28D2A=>bǔ +28D2C=>hú +28D2D=>yǒng +28D2F=>shǐ +28D30=>chù +28D39=>xiāo +28D3A=>mén +28D3B=>lǐ +28D3C=>tí +28D3E=>jiān +28D42=>zhǐ +28D43=>guā +28D44=>guǎn +28D46=>qì +28D48=>fēi +28D49=>yǔ +28D4A=>zhé +28D4B=>wěi +28D4C=>ě +28D4D=>chān +28D4E=>xī +28D50=>gǔ +28D57=>què +28D58=>huì +28D5A=>xié +28D5B=>yīng +28D5D=>tà +28D5E=>wāi +28D5F=>fú +28D60=>jiè +28D61=>pì +28D65=>shěng +28D66=>yú +28D67=>kuā +28D69=>pì +28D6A=>xié +28D6B=>nüè +28D6C=>xiàn +28D6D=>jiàn +28D6E=>xù +28D70=>bì +28D74=>nán +28D76=>liáng +28D78=>pián +28D7C=>jìng +28D80=>tǎ +28D81=>yàn +28D82=>ài +28D85=>xiāo +28D86=>qiāng +28D87=>wǔ +28D88=>táng +28D8A=>jùn +28D90=>kuò +28D97=>làng +28D99=>něng +28D9C=>dòu +28D9D=>shú +28D9F=>jiǎo +28DA0=>niè +28DA2=>yú +28DA8=>cè +28DAA=>jiǎo +28DAC=>huà +28DAD=>wén +28DAE=>yē +28DAF=>é +28DB0=>guāng +28DB1=>huā +28DB2=>jiāo +28DBA=>lèi +28DBC=>shāng +28DBD=>yòng +28DBF=>dēng +28DC0=>guān +28DC1=>niú +28DC3=>suì +28DC4=>xiàng +28DC6=>sà +28DC7=>chāng +28DCE=>rùn +28DD0=>yūn +28DD2=>fēn +28DD3=>jiàn +28DD4=>xù +28DD8=>xì +28DD9=>shú +28DE5=>xié +28DE6=>lì +28DE9=>tóu +28DEC=>mǐ +28DED=>chǎn +28DEE=>huō +28DF1=>zhuǎn +28DF2=>yuè +28DFB=>lán +28DFD=>yán +28DFE=>dàng +28DFF=>xiàng +28E00=>yuè +28E01=>tǐng +28E02=>bēng +28E03=>sàn +28E04=>xiàn +28E05=>dié +28E06=>pì +28E07=>pián +28E09=>tǎ +28E0B=>jiāo +28E0C=>yē +28E0E=>yuè +28E10=>réng +28E11=>qiǎo +28E12=>qí +28E13=>diāo +28E14=>qí +28E17=>hàn +28E18=>yuán +28E19=>yóu +28E1A=>jí +28E1B=>gài +28E1C=>hāi +28E1D=>shì +28E1F=>qū +28E29=>wèn +28E2C=>zhèn +28E2D=>pō +28E2E=>yán +28E2F=>gū +28E30=>jù +28E31=>tiàn +28E37=>è +28E3A=>yā +28E3B=>lìn +28E3C=>bì +28E40=>zǐ +28E41=>hóng +28E43=>duǒ +28E45=>duì +28E46=>xuàn +28E48=>shǎn +28E4A=>shǎn +28E4B=>yáo +28E4C=>rǎn +28E54=>tuó +28E57=>bīng +28E58=>xù +28E59=>tūn +28E5A=>chéng +28E5C=>dòu +28E5D=>yì +28E61=>chè +28E75=>juǎn +28E76=>jī +28E78=>zhào +28E79=>bēng +28E7B=>tiǎn +28E80=>pēng +28E85=>fù +28E96=>tuǒ +28E98=>xián +28E99=>nì +28E9A=>lóng +28E9D=>zhuó +28E9F=>zhēng +28EA0=>shǔn +28EA1=>zōng +28EA2=>fēng +28EA3=>duàn +28EA4=>pì +28EA5=>yǎn +28EA6=>sǒu +28EA7=>qiú +28EA8=>è +28EA9=>qián +28EAB=>qiǎn +28EAD=>cā +28EAE=>xùn +28EB5=>zhuì +28EB8=>mǎo +28EB9=>jiǎo +28EBF=>zhǎn +28EC0=>pí +28EC1=>xī +28EC2=>yàn +28EC3=>fèi +28EC4=>niè +28EC6=>zhì +28EC8=>suǒ +28ECA=>yì +28ECC=>lěi +28ECD=>xù +28ECF=>yì +28ED2=>wēi +28ED5=>jī +28ED6=>chēn +28ED7=>dié +28EE3=>yuán +28EE5=>xí +28EE7=>liú +28EE8=>suǒ +28EF1=>bēng +28EF2=>xià +28EF3=>yàn +28EF5=>cuī +28EF7=>kāng +28EFA=>qīng +28EFB=>lóu +28EFC=>bī +28F08=>zhàn +28F09=>cuàn +28F0A=>wú +28F0B=>xū +28F0C=>chēn +28F0D=>háo +28F0E=>jué +28F10=>chèn +28F11=>chá +28F12=>chǎn +28F13=>zhí +28F14=>xún +28F23=>gé +28F24=>chén +28F25=>yè +28F2A=>chǔ +28F2B=>qú +28F2C=>xiè +28F2E=>zhàn +28F2F=>kěn +28F31=>jué +28F3D=>qú +28F3F=>méng +28F40=>yè +28F41=>zōu +28F42=>pú +28F44=>shì +28F49=>shǔ +28F4A=>chán +28F4D=>dú +28F4F=>guō +28F50=>lù +28F51=>yān +28F56=>niǎo +28F57=>bīn +28F5F=>tuí +28F66=>nì +28F67=>huān +28F68=>qián +28F6F=>xià +28F72=>líng +28F77=>lián +28F79=>yì +28F7B=>lì +28F7C=>sì +28F7F=>dài +28F82=>wèi +28F85=>cì +28F89=>jiǔ +28F8A=>hóng +28F8C=>yú +28F8E=>kuí +28F92=>háng +28F93=>gē +28F94=>fàng +28F97=>kuí +28F9A=>guī +28F9B=>chǐ +28F9E=>jiǔ +28FA1=>suī +28FA4=>dié +28FAC=>suǐ +28FB0=>qín +28FB4=>guī +28FBB=>zhuī +28FBE=>tiào +28FC1=>yuè +28FC7=>zuǐ +28FCF=>wú +28FD0=>cuǐ +28FDB=>zhì +28FE0=>shuì +28FE2=>dōng +28FED=>wéi +28FFF=>chǒng +2900B=>rún +29016=>jí +2901C=>diāo +2901E=>cāng +29020=>kòu +29023=>wéi +29027=>cán +2902A=>má +2902B=>òu +29032=>sǎn +29036=>wéi +2903C=>sǎn +2903F=>jīn +2904C=>wéi +2905E=>cài +2905F=>lí +2906F=>yuè +29074=>yūn +29077=>chēng +2907A=>shān +29082=>hū +29083=>shài +29084=>tún +29086=>fǒu +29088=>qìn +29089=>xū +2908D=>chuān +2908E=>fù +29092=>yì +29093=>dōng +29094=>fú +29095=>fú +29096=>zé +29097=>pù +29099=>líng +2909D=>shài +2909E=>pào +290A2=>yín +290A3=>luò +290A4=>huà +290A5=>yìn +290A6=>bèng +290A7=>yū +290A8=>shè +290AA=>xiè +290AB=>chǔ +290B4=>shè +290B5=>diàn +290B9=>yì +290BB=>chè +290BC=>gěng +290BD=>lóng +290BE=>píng +290BF=>yǔn +290C0=>yàn +290C1=>mò +290C3=>suī +290CB=>jìng +290CD=>sòng +290CE=>páng +290D0=>yá +290D1=>sè +290D2=>duǒ +290D5=>chuáng +290D6=>xiè +290D8=>tuán +290D9=>gōng +290DA=>xuàn +290DC=>lā +290DE=>líng +290E0=>dài +290E1=>zhá +290EC=>yīn +290ED=>sōng +290EF=>yǔ +290F0=>tuó +290F1=>tuó +290F4=>bà +290F5=>rǎn +290F6=>bó +290F7=>dài +290F9=>zhá +290FA=>hóu +290FE=>huǐ +29105=>lú +2910A=>lìng +2910B=>rú +29115=>dàn +29116=>méng +29117=>xià +29118=>wěng +29119=>hán +2911A=>zī +2911B=>zhèn +2911C=>sè +2911D=>cuó +2911E=>lì +29120=>diān +29121=>lián +29122=>gòu +29126=>péng +2912A=>yīng +2912C=>hòu +2912E=>duì +2912F=>wù +29137=>piào +29138=>hè +2913A=>lóng +2913B=>mò +2913C=>fěi +2913D=>lǚ +2913E=>zé +2913F=>bó +29140=>diàn +29141=>mǎng +29143=>zhuàng +29144=>lù +29145=>pāng +29146=>duì +29147=>bù +2914C=>chēn +2914D=>màn +29156=>xī +2915D=>ǎn +2915E=>zhōng +29160=>nàn +29161=>tuò +29162=>hé +29165=>duì +29166=>wān +29167=>zhōng +29168=>cén +29169=>lì +2916A=>shuāng +2916E=>cén +29170=>sī +29172=>duì +29174=>hūn +2917C=>jiān +2917D=>nóng +2917E=>dàn +2917F=>fù +29180=>huò +29181=>huì +29182=>cí +29184=>yǒng +29185=>sà +29186=>tíng +2918E=>liù +29191=>suān +29192=>líng +29193=>mán +29194=>diàn +29198=>pāo +2919A=>líng +2919D=>lì +2919F=>nóu +291A3=>liè +291A4=>shǎn +291A6=>fèi +291AB=>shǎn +291AE=>líng +291AF=>zhàn +291B1=>bīn +291B2=>lí +291B5=>sī +291B6=>ráng +291B7=>jiān +291B8=>zhuó +291BB=>líng +291BC=>líng +291BD=>mèng +291BF=>shuāng +291C4=>líng +291C7=>hùn +291CE=>líng +291CF=>jiān +291D0=>qú +291D4=>nóng +291D5=>jìng +291D6=>chēn +291DC=>zhēn +291DD=>qìng +291DF=>qìng +291E0=>è +291E3=>sè +291E9=>bèi +291EB=>fēi +291EE=>fèi +291EF=>féi +291F4=>fāng +291F5=>kǔ +291FA=>zá +291FB=>huì +291FD=>féi +29201=>duì +29206=>pā +29207=>niǔ +29208=>pàng +29209=>dàn +2920A=>dān +2920B=>ài +2920D=>tiǎn +2920E=>chǎo +2920F=>ǎo +29210=>mèi +29211=>nǎn +29214=>bò +29215=>yù +29216=>xiān +29217=>mài +2921A=>pīng +2921C=>duī +2921E=>dào +29221=>xìng +29222=>nì +29223=>hān +29224=>chù +29225=>shuǎ +29226=>mǎn +2922C=>wàn +2922D=>yì +2922E=>diào +2922F=>yān +29231=>wò +29232=>suàn +29234=>ǎn +29235=>lán +29236=>nǎn +29238=>qiǔ +29239=>miàn +2923A=>nuǒ +2923B=>cán +2923C=>cǎn +29240=>làn +29241=>tiǎn +29242=>yè +29244=>niǎn +29246=>shuǎ +2924B=>cí +2924D=>jiǎn +29250=>gàn +29254=>jiàn +29255=>guó +29257=>zhān +29259=>luǒ +2925C=>jī +2925D=>guì +29261=>jiá +29262=>jǐ +29265=>xuàn +29267=>fēng +2926B=>bì +2926C=>qí +2926F=>yuán +29270=>àng +29271=>dī +29274=>è +29275=>fén +29278=>jù +29279=>nǐ +2927A=>tuó +2927C=>shēn +2927D=>fú +2927E=>xiá +2927F=>qú +29280=>pò +29281=>wǎn +29282=>líng +29283=>mà +29284=>zhòu +29285=>bào +29287=>yù +2928C=>běng +2928D=>mài +2928F=>jiā +29291=>yǎng +29293=>kuǎ +29294=>jiào +29296=>bǐng +2929A=>luò +2929B=>guǐ +2929C=>duò +2929D=>zhì +292A1=>zhèn +292A2=>è +292A3=>zhū +292A4=>bá +292A8=>zhèn +292A9=>fēng +292AA=>dòu +292AB=>niǎn +292AC=>bù +292AD=>duì +292AE=>shā +292AF=>sè +292B0=>bì +292B4=>zhì +292B5=>zhé +292B6=>bù +292BA=>jué +292BB=>xùn +292BF=>xì +292C1=>zhuó +292C2=>bài +292C3=>yáo +292C4=>chǒu +292C5=>tà +292C6=>qiān +292C8=>nào +292C9=>yù +292CA=>è +292CB=>jiān +292CC=>yì +292CD=>xiāo +292CF=>niè +292D2=>bīng +292D7=>guǒ +292D8=>xié +292D9=>diào +292DC=>jū +292DD=>suǒ +292DE=>dié +292DF=>fú +292E0=>miǎn +292E1=>shì +292E2=>xuàn +292E3=>tí +292E4=>yù +292E7=>xié +292E8=>fú +292E9=>zhì +292EA=>nǐ +292EB=>xuàn +292EC=>yáng +292EE=>fěng +292EF=>zòng +292F0=>zhòu +292F1=>xuān +292F5=>zhū +292F7=>la +292F9=>yìng +292FA=>gào +292FB=>kuò +292FD=>é +292FE=>wéi +292FF=>méi +29303=>huái +29304=>chǒu +29306=>suǒ +29307=>tà +29308=>suǒ +29309=>tà +2930A=>xuè +2930C=>gǒng +2930D=>jiǎ +2930F=>bó +29310=>tà +29311=>yuǎn +29318=>tà +2931D=>chuí +29320=>xiōng +29321=>hé +29322=>suō +29327=>mò +29328=>chóng +29329=>suī +2932A=>zé +2932B=>lù +2932C=>zhāng +2932D=>luò +2932E=>xù +2932F=>jiān +29330=>shān +29332=>xù +2933E=>jiǎng +29342=>bào +29343=>mái +29345=>tóng +29346=>xì +29349=>róng +2934B=>shéng +2934C=>zhòu +2934E=>jiān +2934F=>fù +29350=>dèng +29353=>yōng +29354=>jū +29356=>yì +29357=>bāng +29359=>sè +2935A=>suì +2935C=>duó +2935D=>xiè +29361=>huán +29365=>rǔ +29366=>nǐ +29367=>zhòu +29368=>guì +2936A=>luò +29372=>zhī +29373=>xù +29375=>zhī +29377=>jué +29378=>jū +2937B=>yuán +2937C=>lú +2937F=>bó +29382=>róng +29383=>xiè +29389=>xǐ +2938A=>luó +2938E=>gé +29391=>zuān +29392=>hàn +29394=>jiāo +29395=>sǎ +29396=>qín +29397=>qūn +29398=>páo +29399=>yuè +2939A=>chè +2939B=>fú +2939C=>pēi +2939F=>mèi +293A2=>tāo +293A4=>kēn +293A5=>xì +293AB=>duò +293AD=>yì +293B0=>suì +293B2=>xiá +293B3=>juān +293B5=>wéi +293B7=>yì +293B9=>yù +293BB=>bài +293BC=>tuó +293BD=>tà +293BE=>páo +293C2=>bǐng +293C5=>yùn +293C6=>yùn +293C7=>duàn +293C8=>ruǎn +293C9=>wéi +293CF=>wěi +293D0=>guì +293D2=>dá +293D3=>xiá +293D6=>hùn +293D7=>juǎn +293D8=>suī +293DA=>suì +293DD=>lóu +293DE=>bài +293DF=>yù +293E0=>zhèng +293E1=>guì +293E3=>kuī +293E4=>gāo +293E5=>dān +293E9=>xiǎn +293EA=>zhái +293EB=>sè +293ED=>kē +293EE=>bǔ +293EF=>bó +293F2=>suì +293F4=>yù +293F5=>bǔ +293F6=>jiū +293F7=>jiū +293F9=>juàn +293FA=>jué +293FC=>nà +293FD=>zhái +293FE=>tāo +293FF=>wěi +29400=>xiá +29401=>xiè +29405=>sà +29406=>jī +29409=>xiè +2940C=>duì +2940D=>zǐ +29418=>yuǎn +29419=>qìn +2941A=>fú +2941B=>péng +2941C=>páo +2941E=>yìn +29420=>hōng +29421=>zú +29423=>gōng +29424=>dòng +29425=>hē +29426=>wò +29428=>pāng +2942B=>sù +2942C=>kǎn +2942D=>niè +2942E=>háo +2942F=>fèng +29430=>è +29431=>yè +29434=>tíng +29435=>dòng +29436=>zhé +29437=>sāng +2943B=>mò +2943C=>sù +2943E=>lè +29440=>pǔ +29441=>é +29442=>zhuó +29443=>yè +29447=>xiāng +29448=>guàng +29449=>rěn +2944A=>líng +2944D=>ào +29450=>chāi +29452=>duó +29453=>qióng +29454=>kū +29455=>xū +29456=>huán +29457=>yāo +29458=>zhèn +29459=>tǐng +2945A=>běng +2945D=>áng +2945F=>kān +29461=>kū +29462=>péi +29463=>yòu +29464=>ǎo +29465=>mén +29466=>mò +2946C=>fǔ +2946D=>qīng +2946E=>là +2946F=>dǒu +29470=>tǎn +29473=>qiǎn +29474=>yào +29475=>wèi +29476=>hú +29477=>mò +29478=>hē +29479=>xuàn +2947B=>bì +2947C=>pō +2947E=>dī +29480=>zhěn +29482=>shī +29483=>kǎn +29484=>cè +29487=>xū +29488=>zhěn +2948A=>zhǔ +2948F=>huì +29490=>chǐ +29493=>hǒng +29494=>nóu +29495=>niè +29496=>yàn +29498=>chǒng +29499=>fǔ +2949A=>guāng +2949B=>qī +2949D=>gěn +2949E=>tǐng +294A2=>tǎn +294A3=>qiǎn +294A6=>jiù +294A7=>xū +294A8=>qǐ +294AA=>zhèn +294AE=>qiú +294B0=>ě +294B3=>huì +294B4=>hòng +294B5=>qǐng +294B7=>chē +294BA=>fù +294BC=>hōng +294BD=>xī +294BE=>wú +294BF=>máng +294C2=>tī +294C5=>hōng +294D0=>bó +294D2=>qǐn +294D3=>gěn +294D6=>fú +294D7=>kuǐ +294DD=>bié +294DE=>jìng +294DF=>kǎn +294E0=>guī +294E2=>gǎo +294E3=>xū +294E4=>àn +294E5=>yuè +294E6=>wù +294E7=>yí +294E8=>jīng +294EA=>lù +294EB=>quán +294EC=>tuí +294EE=>jì +294FA=>jiǒng +294FB=>jué +294FC=>piē +294FD=>kūn +29500=>wài +29501=>huì +29502=>dùn +29503=>yuǎn +29504=>jié +29506=>guì +29507=>gǎo +29508=>pò +29509=>mén +2950A=>zhuàn +2950B=>hàng +29514=>yóng +29515=>qiú +29517=>lèi +29518=>áng +29519=>pǐ +2951A=>wēng +2951D=>qìn +2951F=>qǐn +29520=>miè +29521=>dōu +29522=>mí +29523=>zhān +29525=>qǐng +29526=>yí +2952E=>bān +29531=>juān +29533=>zé +29534=>xù +29535=>lán +29536=>má +29537=>má +29538=>ōu +29539=>bēi +2953B=>póu +2953C=>xù +29540=>ào +29546=>hǒng +29549=>hǒng +2954A=>zhǎn +2954C=>sěn +2954D=>gǎo +2954F=>pó +29550=>liào +29555=>wài +29556=>xuān +2955C=>kuí +2955F=>è +29560=>hàn +29561=>sè +29564=>dàn +2956A=>xuān +2956C=>è +2956D=>gài +2956F=>dāo +29571=>měng +29572=>yī +29573=>nǐng +29575=>pín +29579=>cāng +2957E=>yuàn +29580=>è +29581=>niè +29584=>yǐn +29587=>qiāo +29589=>hōng +2958A=>líng +2958C=>chān +2958D=>yǐng +29592=>guān +29594=>niǎo +29595=>xū +29596=>tán +29597=>jìn +2959B=>péng +2959D=>liáo +295A0=>bèi +295A3=>xín +295A4=>tún +295A5=>chāo +295A6=>gān +295A8=>hū +295A9=>wǎng +295AC=>fú +295AD=>pèi +295AF=>náo +295B0=>xún +295B1=>xuè +295B4=>liǔ +295B5=>líng +295B6=>xuè +295B7=>qū +295B8=>háo +295B9=>yí +295BA=>hàn +295BC=>fú +295BD=>bá +295BE=>yí +295C0=>bó +295C4=>hōng +295C5=>lì +295C9=>sà +295CA=>xī +295CE=>shì +295CF=>piāo +295D0=>huà +295D1=>yí +295D2=>bó +295D3=>bó +295D4=>něi +295D5=>qiú +295D8=>wěi +295D9=>chè +295DA=>yóu +295DC=>wèi +295DD=>huǐ +295DE=>sà +295E2=>hòng +295E3=>sōu +295E4=>hàn +295E5=>páo +295E7=>fáng +295E9=>liú +295EA=>zhòu +295EB=>pí +295ED=>lì +295F0=>chuí +295F1=>xī +295F2=>zhēng +295F4=>bèng +295F5=>zhěng +295F6=>suì +295F7=>yǎn +295FC=>qīng +295FD=>wù +295FE=>liǎng +29600=>zhào +29601=>liáng +29605=>jiē +29607=>hōng +29608=>yōu +2960A=>là +2960B=>hòu +2960D=>yuàn +2960E=>hóng +2960F=>yè +29611=>yǐng +29612=>xuǎn +29613=>yóu +29618=>quán +2961C=>táng +2961D=>suǒ +2961F=>lì +29620=>sōu +29621=>lì +29624=>yù +29627=>yì +2962D=>xiū +2962E=>áo +2962F=>tuán +29630=>sù +29631=>shuài +29633=>yù +29635=>fēng +29639=>sù +2963A=>tuí +2963B=>yù +2963C=>zhēng +2963D=>zhēng +2963F=>táo +29644=>liú +29646=>chéng +29647=>suí +29648=>sāo +2964F=>gǔ +29650=>fēng +29651=>liè +29652=>piāo +29656=>lì +29658=>lóng +29659=>chū +2965A=>xiāo +2965B=>hōng +2965C=>xiè +2965D=>shè +29660=>lóng +29661=>hōu +29662=>xuán +29663=>fēng +29665=>bá +29666=>bó +29667=>táo +29668=>sù +29669=>zhào +2966A=>biāo +2966B=>sōu +2966C=>tuí +2966D=>suǒ +2966E=>xiāo +2966F=>héng +29670=>sāo +29672=>fēi +29677=>niù +29678=>mǎng +2967D=>huán +2967E=>zhī +29682=>yì +29684=>yù +29687=>yí +29688=>yuē +29689=>chí +29695=>yǐn +29696=>niù +29697=>rǒng +2969B=>nà +296A3=>tián +296A5=>bā +296AA=>ěr +296AB=>zhēng +296AC=>è +296AD=>póu +296AE=>jī +296AF=>ní +296B1=>jiǒng +296B2=>jiá +296B5=>gān +296B9=>líng +296BB=>zuì +296BE=>bèi +296C5=>shū +296C6=>yǐ +296C7=>pāi +296CB=>nǎo +296CC=>shì +296CE=>mǎn +296CF=>shì +296D1=>tí +296D8=>gōng +296DD=>lèi +296DE=>bǎo +296DF=>yuān +296E0=>zuō +296E1=>láng +296E2=>xiū +296E5=>zài +296E6=>chèng +296E7=>jiān +296E8=>mào +296E9=>jiá +296EA=>yù +296ED=>yù +296EE=>yí +296F2=>māng +296F3=>zài +296F5=>zhuì +296F6=>tí +296F9=>xì +296FA=>jú +296FB=>zàn +296FC=>lù +296FD=>táo +29700=>zhuì +29701=>líng +29703=>jù +29706=>jī +29707=>juǎn +2970A=>zī +2970C=>yuē +2970D=>dōng +29712=>nǎng +29716=>chóng +2971F=>àng +29723=>gēng +29725=>bō +29726=>dìng +29727=>wěi +2972C=>quán +2972D=>kē +29730=>pì +29731=>kǎn +29732=>fú +29733=>yǒng +29735=>tuán +29736=>tǒu +29737=>yòu +29738=>yāo +2973A=>yē +2973D=>yàn +29748=>xián +2974A=>tí +2974C=>suì +29750=>cí +29754=>xǔ +29755=>wù +29756=>cān +29757=>yù +2975A=>chǎn +2975B=>xiá +2975D=>kào +2975E=>cāng +2975F=>chā +29760=>qiǔ +29763=>dā +29765=>sù +29768=>huā +29777=>wū +29778=>yuān +2977D=>jiàng +2977E=>xiǎng +2977F=>zhāi +29780=>sǎn +29781=>mó +29783=>shǎng +29784=>cáo +29785=>suī +29786=>chuáng +29787=>mí +29788=>zhú +29789=>chóng +2978A=>jì +2978B=>chóng +29799=>lián +2979E=>hài +297A4=>dūn +297A5=>xiǎng +297A6=>chēng +297A7=>shǎng +297A8=>lì +297A9=>huáng +297AC=>dèng +297AF=>liáng +297B6=>zā +297BA=>huò +297BB=>lín +297BE=>dú +297BF=>hàn +297C0=>yōng +297C1=>yuàn +297C2=>guò +297C3=>líng +297C5=>liǎn +297C7=>ào +297C8=>dāng +297C9=>yì +297CA=>nóng +297CB=>shàn +297CD=>xìn +297D0=>dá +297D1=>yù +297D2=>cān +297D3=>wò +297D4=>chá +297D5=>bó +297D7=>jiǎn +297DE=>méng +297DF=>wěi +297E0=>mó +297E5=>shuì +297E6=>jié +297E7=>shuò +297E8=>huò +297EB=>chuò +297ED=>lóng +297EE=>huài +297F0=>tuō +297F3=>yú +297F6=>chàn +297F7=>yōng +297F8=>huò +297FA=>lǎn +297FF=>nà +29800=>bā +29801=>gān +29802=>yǐ +29803=>jiá +29805=>dá +29806=>dìng +29807=>xùn +29808=>rěn +29809=>juǎn +2980A=>tuán +2980B=>xǔ +2980C=>sòng +2980E=>cáo +2980F=>chēng +29811=>dǐng +2981A=>hái +2981F=>wǔ +29826=>qǐ +29828=>jī +2982E=>kuí +2982F=>wéi +29836=>shǒu +29837=>fú +29839=>tuán +2983B=>bié +2983D=>tán +2983E=>hāng +2983F=>piē +29843=>yú +29844=>tán +2984C=>xiāng +2984E=>xiū +29853=>wěng +29854=>hài +29855=>péng +2985D=>tán +2985F=>bié +29860=>xiāng +29863=>yǐ +29866=>piáo +29867=>huán +29868=>mǔ +29869=>bā +2986B=>fàn +2986F=>dīng +29877=>fēn +2987A=>jiè +2987E=>suó +29884=>wàn +29885=>gē +29888=>fēn +2988A=>tuó +2988C=>wén +2988D=>guā +2988E=>duō +29890=>zhé +29891=>cǐ +29892=>yǎo +29894=>bàn +29895=>bù +29896=>mò +29898=>pǒ +2989B=>gé +2989E=>liú +298A1=>rǎn +298A8=>gān +298AA=>hú +298AB=>móu +298AE=>xiū +298AF=>huāng +298B0=>fú +298B1=>huí +298B3=>qú +298B4=>jié +298B5=>tuō +298B6=>yú +298B7=>mò +298B8=>zhōu +298B9=>jiù +298BB=>shú +298BC=>kuāng +298BD=>qióng +298BE=>liè +298BF=>fù +298CA=>xù +298D6=>lìn +298D8=>niè +298DA=>pī +298DC=>fù +298DD=>bù +298DE=>yì +298E1=>bó +298E3=>é +298E9=>zhé +298EB=>lì +298EE=>tù +298EF=>dá +298F1=>lù +298F2=>yān +298F3=>dōng +298F4=>qiè +298F5=>wǎn +298F6=>mǐng +298F7=>zuī +298F8=>fù +298F9=>qū +298FA=>bēn +298FB=>ǎo +298FC=>qiāng +29901=>qūn +29908=>què +29909=>huá +2990A=>xiàn +2990B=>kùn +2990F=>cuì +29912=>yí +29916=>chī +29917=>zòng +29918=>nǎo +29919=>chéng +2991A=>duān +2991B=>yóng +2991C=>zhě +2991E=>tàn +2991F=>yáng +29920=>xié +29921=>xuān +29923=>duàn +29924=>shuǎ +29925=>xián +29926=>xián +29929=>é +29932=>lā +29938=>wèi +29939=>yōu +2993A=>yú +2993D=>tī +2993F=>jīn +29941=>táng +29942=>qí +29944=>diān +29945=>tāo +29946=>lǜ +29947=>zhàn +29948=>wēn +29949=>jì +2994A=>āo +2994B=>òu +2994C=>qià +29950=>shī +29951=>tǎ +29954=>mò +29958=>yóu +29960=>zhá +29963=>yáo +2996B=>chōng +2996C=>lí +2996D=>yú +2996E=>chǎn +2996F=>yī +29972=>chì +29974=>lí +2997D=>tú +2997F=>zú +29982=>xián +29987=>xì +29989=>bié +2998A=>hán +2998B=>qí +2998C=>sāng +2998E=>fēi +29990=>shàn +29998=>huān +299A0=>bàng +299A1=>yú +299A2=>yú +299A4=>jí +299B1=>kuǎi +299B2=>zōng +299B9=>xiàn +299BA=>méng +299C3=>lì +299C4=>zhì +299C5=>fán +299C6=>liè +299C7=>cài +299C8=>dú +299C9=>guāng +299CA=>xiòng +299CB=>lí +299CC=>qì +299CF=>jué +299D0=>tuō +299D2=>jù +299D3=>xiāo +299D8=>qú +299DC=>zhuǎn +299E1=>jué +299E6=>jiè +299E8=>zhòu +299E9=>xiàn +299EA=>lóng +299EB=>yǎng +299EC=>rǎn +299ED=>yì +299EE=>liè +299EF=>bō +299F0=>hún +299F1=>jì +299F2=>dòng +299F3=>zhōu +299F4=>quān +299F5=>jié +299FA=>jú +299FC=>bēn +299FF=>bī +29A00=>gé +29A01=>chǔn +29A03=>qián +29A04=>sōu +29A05=>wèi +29A06=>chéng +29A07=>lóu +29A08=>yú +29A09=>lā +29A0A=>qián +29A0B=>diān +29A0C=>tǎ +29A0D=>zhàn +29A0F=>fán +29A10=>liè +29A11=>tīng +29A12=>jī +29A13=>qiān +29A14=>hú +29A17=>yú +29A18=>qì +29A19=>yú +29A1A=>wā +29A1C=>bà +29A1D=>qí +29A1E=>sǎ +29A1F=>qiāo +29A20=>yà +29A21=>xiǎn +29A28=>cī +29A29=>fàn +29A2B=>kǔn +29A2C=>gǔn +29A2D=>quē +29A2E=>è +29A2F=>qióng +29A32=>mà +29A33=>kū +29A34=>yǎo +29A37=>quē +29A38=>chū +29A39=>jiǎ +29A3B=>zhǔ +29A3D=>duī +29A3E=>wá +29A40=>nǎo +29A44=>yán +29A45=>tóng +29A4B=>xíng +29A4C=>gǔn +29A4D=>pīng +29A51=>yǔ +29A52=>hè +29A54=>zhuó +29A57=>shē +29A58=>yǔ +29A5B=>jì +29A5D=>qiāng +29A5E=>shuì +29A5F=>chuò +29A60=>zú +29A61=>léng +29A62=>ní +29A64=>wā +29A65=>zhá +29A67=>dàn +29A6E=>dù +29A6F=>biàn +29A70=>jiē +29A71=>qià +29A72=>hé +29A73=>chòng +29A74=>yán +29A76=>yàn +29A7A=>sóng +29A7B=>téng +29A7C=>yǎo +29A7E=>kāo +29A80=>zhuī +29A81=>guì +29A82=>ái +29A83=>hài +29A88=>suǒ +29A89=>xù +29A8A=>biāo +29A8C=>fèng +29A8D=>qū +29A8E=>mǎng +29A90=>guó +29A96=>bì +29A97=>jué +29A98=>chuáng +29A9B=>pú +29A9F=>yì +29AA2=>qiān +29AA3=>yì +29AA4=>è +29AA5=>líng +29AA7=>bì +29AAD=>huò +29AAE=>mǒ +29AB1=>xūn +29AB4=>yàn +29AB8=>lì +29ABA=>tán +29ABE=>luán +29AC0=>kài +29AC1=>mào +29AC2=>xiāo +29AC7=>ǎi +29ACA=>tǎ +29ACD=>mèi +29ACF=>guō +29AD3=>gǎo +29AD4=>náo +29AD5=>háo +29AE0=>quē +29AE5=>cáo +29AE6=>sào +29AEB=>pí +29AF2=>xiē +29AF3=>xiāo +29AF4=>jú +29AF9=>chéng +29AFA=>nǎo +29B00=>nèi +29B0D=>mǔ +29B0F=>shāo +29B11=>diān +29B14=>líng +29B16=>zhěn +29B17=>yǎo +29B19=>fù +29B1A=>qián +29B1B=>qióng +29B1C=>jú +29B1D=>bìng +29B1E=>máo +29B1F=>zhà +29B20=>tāi +29B24=>chōng +29B2B=>zhǎi +29B2D=>shī +29B2E=>yòng +29B30=>qióng +29B31=>dào +29B32=>tì +29B33=>zhuǐ +29B35=>yìn +29B37=>nǎo +29B38=>bō +29B39=>kuāng +29B3A=>zhǐ +29B3B=>duǒ +29B3C=>cōng +29B3D=>bǎo +29B47=>lí +29B4A=>jú +29B4B=>wén +29B4C=>liè +29B4F=>wǒ +29B50=>shǐ +29B51=>niǎo +29B52=>máng +29B53=>jiū +29B58=>xiū +29B5D=>wō +29B5F=>dào +29B61=>xī +29B62=>àn +29B63=>dá +29B64=>zǒng +29B65=>hàn +29B66=>chuí +29B67=>bī +29B69=>dòng +29B6B=>zhǎng +29B6F=>yā +29B72=>dí +29B73=>huō +29B77=>mín +29B7A=>fù +29B7C=>bǎo +29B7D=>kè +29B7E=>máo +29B7F=>rè +29B80=>zōng +29B81=>qià +29B82=>xiā +29B83=>sōu +29B84=>xiū +29B85=>nà +29B89=>mán +29B8E=>zhā +29B8F=>chán +29B90=>shè +29B91=>wǒ +29B96=>ái +29B97=>bàng +29B98=>hāo +29B9A=>sāo +29B9B=>suǒ +29B9C=>tì +29B9D=>yà +29B9F=>bìng +29BA0=>róng +29BAB=>shā +29BAC=>wěng +29BAF=>áo +29BB1=>zhuāng +29BB3=>piào +29BB4=>suī +29BB5=>yī +29BB6=>sōu +29BB7=>dōu +29BB8=>sōu +29BB9=>luó +29BC3=>fèi +29BC4=>zùn +29BC6=>nào +29BC7=>dēng +29BC8=>zhí +29BC9=>cuō +29BCA=>liáo +29BCB=>jǐ +29BCC=>bō +29BCD=>cóng +29BCE=>chéng +29BCF=>bǔ +29BD1=>sān +29BD2=>zàn +29BD8=>jiào +29BDB=>yào +29BDC=>lǔ +29BDE=>càn +29BE8=>nǐ +29BF0=>jié +29BF1=>pú +29BF2=>zhuàng +29BF3=>zàn +29BFA=>lì +29BFD=>là +29C00=>chōng +29C03=>zhàn +29C0D=>biàn +29C0E=>wēng +29C13=>hòng +29C17=>pīn +29C19=>sè +29C1E=>nǐ +29C1F=>fēn +29C20=>xǔ +29C22=>shǐ +29C24=>jù +29C28=>jué +29C2A=>yù +29C2C=>guō +29C2D=>guō +29C2F=>hú +29C32=>lì +29C33=>xié +29C34=>ér +29C35=>yuán +29C36=>hái +29C39=>jìng +29C3B=>kè +29C3D=>zōng +29C3E=>fèi +29C40=>pēng +29C41=>gēng +29C43=>jiān +29C44=>ní +29C46=>xián +29C47=>lì +29C48=>chǎo +29C4A=>ér +29C4B=>gēng +29C4C=>yù +29C4D=>hú +29C4E=>fèi +29C4F=>áo +29C53=>ěr +29C58=>kè +29C59=>kù +29C5A=>bó +29C5D=>yè +29C5E=>jiào +29C66=>chǎo +29C67=>gēng +29C68=>rù +29C6A=>yuè +29C6C=>lín +29C71=>yù +29C72=>yuè +29C73=>zhāi +29C74=>xiāo +29C77=>miè +29C7B=>guǐ +29C7C=>jiū +29C7E=>tuò +29C81=>xí +29C82=>wěi +29C83=>zhuó +29C84=>wèi +29C85=>kuí +29C88=>mèi +29C8A=>hào +29C8B=>hāng +29C8C=>fāng +29C8D=>niú +29C8E=>yòu +29C8F=>huà +29C92=>làng +29CA0=>zhú +29CA1=>guǐ +29CA2=>bì +29CA3=>jiǎ +29CA4=>tiáo +29CA6=>lǜ +29CA7=>kǒng +29CA8=>zuǐ +29CA9=>líng +29CAA=>qí +29CAC=>zhú +29CB1=>gǔ +29CB2=>zù +29CB4=>yāng +29CB5=>sū +29CB7=>kuí +29CB9=>chāng +29CBB=>yáo +29CBE=>yù +29CC5=>shū +29CC6=>lài +29CC7=>yì +29CC8=>dōu +29CCC=>wú +29CCD=>yǐng +29CCE=>fú +29CCF=>zhuàn +29CD0=>fǔ +29CD2=>sù +29CD3=>lǐ +29CD4=>yào +29CD5=>tuì +29CDD=>guì +29CE1=>lǜ +29CE2=>yàn +29CE3=>qí +29CE4=>làng +29CE5=>zhú +29CE7=>guǐ +29CE8=>hū +29CEF=>jīng +29CF2=>chǐ +29CF5=>jú +29CF6=>zhá +29CF8=>miáo +29D00=>zhū +29D01=>gān +29D02=>xiōng +29D03=>jí +29D07=>shài +29D08=>mèi +29D09=>yùn +29D0D=>shòu +29D10=>lǜ +29D11=>yòu +29D12=>jiàng +29D13=>nuó +29D18=>jù +29D19=>yòu +29D1C=>yì +29D1D=>téng +29D1E=>wéi +29D1F=>chě +29D20=>lìn +29D21=>gù +29D23=>lì +29D24=>liào +29D27=>jiāo +29D28=>yáng +29D29=>biāo +29D2A=>qí +29D2E=>yì +29D31=>bīn +29D32=>méng +29D33=>chà +29D35=>gān +29D39=>qú +29D3A=>dí +29D3B=>léi +29D40=>líng +29D44=>huān +29D45=>qú +29D47=>luó +29D49=>kuí +29D4D=>qiú +29D4E=>yǔ +29D4F=>huà +29D53=>lèi +29D55=>rèn +29D56=>xiǎo +29D57=>sì +29D5A=>dù +29D5B=>biē +29D60=>niú +29D62=>hè +29D63=>pēi +29D65=>fèi +29D66=>mù +29D69=>fū +29D6C=>hú +29D6D=>wáng +29D6E=>shā +29D70=>jiāo +29D71=>wǔ +29D79=>fù +29D81=>bǐng +29D82=>zhù +29D84=>zhú +29D85=>chī +29D87=>shěn +29D88=>hū +29D89=>bū +29D8E=>rǎn +29D96=>mù +29D98=>lì +29D9B=>jiā +29D9E=>mà +29DA1=>méng +29DA2=>móu +29DA3=>zhōu +29DA4=>xiǎn +29DA5=>huǐ +29DA6=>guài +29DA7=>jiù +29DA9=>mù +29DAB=>rù +29DAD=>wú +29DAF=>rú +29DB1=>zhà +29DC1=>nuǒ +29DC2=>xié +29DC4=>jiàng +29DCB=>lǐ +29DCC=>shū +29DCD=>yì +29DCE=>dí +29DCF=>qíng +29DD0=>jú +29DD3=>zhì +29DD5=>láng +29DD6=>bù +29DD7=>kuáng +29DD8=>yì +29DDA=>bó +29DE7=>chì +29DED=>jiàng +29DEF=>wò +29DF0=>xùn +29DF5=>tūn +29DF6=>máng +29DF8=>fáng +29DF9=>zhuó +29DFB=>qià +29DFD=>tǎ +29DFE=>qí +29E00=>pèng +29E01=>biē +29E02=>fèn +29E03=>tù +29E04=>huà +29E07=>è +29E0B=>è +29E0E=>dìng +29E10=>rú +29E16=>è +29E1E=>yàn +29E1F=>sì +29E25=>yíng +29E26=>ní +29E27=>ní +29E28=>yí +29E39=>mí +29E3E=>yé +29E3F=>pō +29E40=>còu +29E42=>wèi +29E44=>hài +29E45=>yīng +29E47=>tíng +29E48=>zhì +29E49=>fēi +29E4A=>yóu +29E4D=>kuí +29E4E=>àn +29E4F=>bà +29E51=>hàn +29E5E=>nán +29E5F=>nài +29E62=>jīng +29E65=>wēi +29E71=>chù +29E73=>suǒ +29E74=>tāo +29E75=>qí +29E76=>táng +29E77=>wěi +29E78=>gǎn +29E7A=>gé +29E7C=>hàn +29E7E=>nà +29E7F=>gé +29E84=>zhēng +29E97=>tǎ +29E9B=>sī +29E9D=>nì +29E9E=>sǎng +29EAB=>xié +29EAF=>zú +29EB0=>yú +29EB1=>nì +29EB2=>qī +29EB5=>shēn +29EBC=>bū +29ECB=>kūn +29ECC=>lí +29ECE=>guā +29ED6=>yǎn +29ED7=>bù +29ED8=>jiàn +29EDA=>wú +29EDB=>cén +29EDC=>lín +29EDD=>zhuàn +29EDF=>huī +29EE1=>tóng +29EE2=>zhǎ +29EE4=>hēi +29EE7=>guǒ +29EF1=>jǐng +29EF5=>dié +29EF7=>yíng +29EFC=>zhì +29F02=>wěi +29F04=>jì +29F05=>rǒng +29F08=>ào +29F09=>dāng +29F0A=>luó +29F0B=>yè +29F0C=>wēi +29F12=>qiáng +29F19=>gé +29F1A=>jì +29F26=>zòu +29F28=>yí +29F2B=>zhǎ +29F2D=>liè +29F34=>yè +29F3C=>zhān +29F40=>chóu +29F41=>biāo +29F46=>xù +29F47=>yōu +29F4D=>xiè +29F4E=>wéi +29F4F=>lì +29F5B=>bó +29F5C=>jiǎn +29F5D=>chán +29F5E=>kūn +29F61=>qíng +29F67=>shuāng +29F68=>xī +29F69=>qú +29F70=>luó +29F73=>dǎng +29F74=>nián +29F75=>lǐ +29F77=>bà +29F79=>è +29F7A=>fū +29F7B=>fù +29F7C=>hǔn +29F7D=>zhà +29F7E=>ān +29F81=>qiú +29F82=>chóu +29F83=>miǎn +29F84=>xùn +29F85=>tù +29F86=>ní +29F87=>hu +29F88=>shū +29F8A=>xū +29F8B=>zhòng +29F8C=>kāng +29F92=>xiāo +29F93=>xiāo +29F94=>cì +29F95=>chì +29F97=>diāo +29F98=>yì +29F9A=>dīng +29F9D=>hàn +29F9E=>wán +29FA0=>yǐ +29FA1=>bào +29FA2=>yì +29FA7=>xùn +29FAC=>xiáng +29FB3=>bí +29FB6=>jié +29FB7=>gē +29FB8=>zè +29FBA=>zhèn +29FBB=>hú +29FBC=>xī +29FBD=>xīn +29FBE=>xiāo +29FBF=>fù +29FC0=>zhòng +29FC2=>mào +29FC3=>xīn +29FC4=>qiāng +29FC8=>fén +29FC9=>bān +29FCA=>huān +29FD1=>jiāo +29FD3=>bào +29FD4=>yā +29FD5=>yáo +29FDB=>xì +29FDD=>jù +29FDF=>qù +29FE0=>yuè +29FE1=>tái +29FE2=>tǒu +29FE3=>mò +29FE4=>zhá +29FE5=>qú +29FE7=>fū +29FE9=>qú +29FEA=>chì +29FEC=>yóu +29FF7=>tí +29FFA=>wā +29FFD=>tuó +29FFF=>chú +2A001=>gē +2A008=>yuān +2A009=>gē +2A00A=>qú +2A00F=>jù +2A012=>dié +2A013=>yí +2A014=>shī +2A015=>yì +2A017=>guǐ +2A018=>jiàng +2A01A=>sōng +2A01B=>qióng +2A01D=>è +2A01E=>huāng +2A01F=>huí +2A020=>xún +2A023=>jú +2A025=>zhái +2A026=>chì +2A027=>lǎo +2A029=>qí +2A02A=>xiū +2A02C=>huī +2A02D=>tóng +2A03A=>fù +2A03D=>xún +2A03E=>jié +2A03F=>mǐ +2A040=>yù +2A048=>zhuàng +2A049=>jiāo +2A04A=>zhì +2A04B=>chéng +2A04D=>jié +2A04E=>xiāo +2A04F=>chén +2A050=>lí +2A051=>yuè +2A053=>zhì +2A054=>láo +2A055=>wò +2A056=>qú +2A058=>wāng +2A05A=>yī +2A05B=>yì +2A05C=>láng +2A05E=>tóu +2A05F=>ān +2A060=>jué +2A061=>yàn +2A065=>jù +2A067=>zhèn +2A069=>zhì +2A06A=>mǎng +2A06E=>xiù +2A071=>chuáng +2A072=>chū +2A078=>qiāng +2A079=>fēi +2A07A=>cháng +2A07C=>mián +2A07D=>sù +2A07E=>ǎo +2A080=>fǔ +2A084=>wèi +2A085=>zhī +2A086=>mín +2A087=>chāng +2A088=>yán +2A089=>yù +2A08B=>fù +2A08C=>tà +2A08D=>jǐ +2A08F=>fèi +2A092=>hú +2A093=>jū +2A095=>yǔ +2A09B=>qí +2A09C=>méi +2A09F=>biē +2A0A0=>guǒ +2A0A4=>mìng +2A0A6=>wǎn +2A0A7=>wǎn +2A0B4=>jīng +2A0B5=>yù +2A0B6=>xián +2A0B9=>chūn +2A0BA=>jí +2A0BC=>xiāng +2A0BD=>pén +2A0BE=>fù +2A0C2=>liú +2A0C4=>sāi +2A0C5=>xuē +2A0C6=>zòu +2A0C8=>jié +2A0CB=>zhān +2A0CD=>yú +2A0CE=>yú +2A0CF=>méi +2A0D0=>miǎo +2A0D1=>mào +2A0D2=>duó +2A0D3=>fù +2A0DB=>jiàn +2A0E6=>miáo +2A0E8=>āo +2A0ED=>kè +2A0F6=>hóu +2A0FA=>gòu +2A0FC=>xī +2A0FE=>róng +2A0FF=>gē +2A100=>pán +2A101=>yuán +2A102=>xià +2A105=>shā +2A106=>pī +2A108=>qíng +2A109=>yōng +2A10A=>qú +2A10C=>gòng +2A10E=>gé +2A10F=>xiān +2A111=>sù +2A115=>bān +2A116=>qí +2A117=>hòu +2A11B=>xī +2A11D=>wū +2A12D=>qī +2A12E=>hù +2A12F=>guī +2A131=>dí +2A132=>shāng +2A133=>mài +2A134=>mǐn +2A135=>jì +2A136=>xí +2A137=>xiān +2A138=>jí +2A139=>cháng +2A13A=>kòu +2A13B=>chōng +2A142=>zhāng +2A143=>piǎo +2A144=>sù +2A145=>lüè +2A146=>lí +2A147=>mèng +2A148=>chōng +2A149=>tiān +2A14B=>líng +2A14D=>chì +2A156=>chōng +2A159=>chì +2A15D=>niǎo +2A15F=>yóng +2A16E=>mì +2A170=>shū +2A172=>xì +2A174=>è +2A175=>zī +2A178=>jié +2A179=>jī +2A17A=>hōu +2A17B=>shèng +2A17C=>lì +2A17E=>qī +2A180=>zhōu +2A181=>sī +2A182=>qú +2A18B=>xié +2A197=>sī +2A19B=>xū +2A1A0=>fù +2A1AF=>nóng +2A1B0=>yà +2A1B1=>liú +2A1B2=>jiǎ +2A1B3=>guī +2A1B4=>kuí +2A1B5=>chì +2A1B6=>càn +2A1B7=>chú +2A1B9=>guō +2A1BB=>dǎn +2A1BF=>jiàn +2A1C1=>dāng +2A1C2=>hòu +2A1C4=>kòu +2A1C6=>chù +2A1C7=>qiān +2A1C8=>ài +2A1CA=>pì +2A1D1=>xùn +2A1D2=>jīng +2A1D3=>mèng +2A1D5=>bīn +2A1D6=>lán +2A1D7=>gǔ +2A1D8=>chóu +2A1DB=>yōng +2A1DC=>guá +2A1DD=>yú +2A1DE=>zhòu +2A1ED=>cài +2A1EF=>liú +2A1F0=>bǔ +2A1F1=>luò +2A1F2=>jié +2A1F3=>zhēn +2A1F4=>miè +2A1F5=>guǎng +2A1F7=>jiá +2A1F9=>là +2A200=>shòu +2A203=>guō +2A206=>mèng +2A207=>qián +2A208=>lài +2A20A=>hé +2A20B=>tuán +2A211=>huī +2A218=>hōng +2A21C=>lǚ +2A21F=>jiá +2A225=>guī +2A228=>yī +2A229=>huān +2A230=>luó +2A234=>jué +2A238=>guàn +2A23B=>quán +2A23C=>niǎo +2A23F=>mán +2A242=>yùn +2A243=>wén +2A244=>chì +2A245=>chì +2A246=>zhī +2A248=>cí +2A249=>zhuàng +2A24A=>huá +2A24B=>jié +2A24C=>qú +2A24D=>tū +2A24E=>mín +2A24F=>méi +2A250=>yú +2A251=>áo +2A252=>bān +2A254=>pī +2A255=>zhēn +2A256=>lǔ +2A257=>chì +2A258=>tóu +2A25A=>jiē +2A25C=>zhān +2A262=>jīn +2A263=>lǔ +2A266=>jiàn +2A267=>tàn +2A268=>chāng +2A26A=>cì +2A26D=>wāi +2A26E=>còu +2A26F=>kàn +2A271=>biàn +2A278=>wēn +2A27B=>qiān +2A27F=>gàn +2A282=>huì +2A284=>gǎn +2A286=>jì +2A287=>gàn +2A289=>huái +2A28D=>sì +2A290=>fū +2A295=>pí +2A297=>cā +2A29C=>bèn +2A2A2=>shǐ +2A2A5=>huán +2A2A7=>guī +2A2AA=>ǒu +2A2B3=>páo +2A2B5=>yǐng +2A2B6=>tǐng +2A2B7=>xiào +2A2B9=>zhù +2A2BB=>yú +2A2C1=>jiàn +2A2C4=>qǔ +2A2C5=>wǎn +2A2C6=>kūn +2A2C7=>zhuī +2A2C9=>yù +2A2CA=>guǒ +2A2CB=>píng +2A2CC=>zuǐ +2A2CD=>zú +2A2CF=>zhū +2A2D0=>nuàn +2A2D1=>zhū +2A2D6=>piāo +2A2D7=>mí +2A2DC=>bì +2A2DD=>sù +2A2E1=>pú +2A2E2=>mí +2A2EB=>yè +2A2EC=>yǔ +2A2EE=>yù +2A2F0=>zhǔ +2A2F3=>líng +2A2FA=>nòu +2A2FE=>líng +2A300=>liǎo +2A302=>tuō +2A304=>bǐ +2A305=>nà +2A306=>qú +2A308=>pí +2A309=>dǒu +2A30A=>niè +2A30B=>tún +2A30D=>jī +2A30F=>líng +2A313=>kù +2A314=>sù +2A318=>tǒu +2A31E=>nái +2A31F=>zé +2A322=>tǒng +2A323=>gé +2A324=>duī +2A327=>jié +2A329=>tián +2A32A=>tiào +2A32B=>chí +2A32C=>qū +2A32E=>shā +2A330=>bó +2A331=>lí +2A333=>luò +2A335=>liáo +2A336=>shù +2A337=>děng +2A339=>chī +2A33A=>miè +2A33C=>táo +2A33D=>hún +2A33F=>nié +2A341=>jùn +2A342=>hù +2A344=>lù +2A345=>yè +2A347=>mò +2A348=>chào +2A34C=>suò +2A34E=>kē +2A34F=>fù +2A351=>chǎo +2A354=>suǒ +2A357=>qiū +2A35B=>sù +2A35D=>yùn +2A35F=>suǒ +2A360=>kū +2A361=>bó +2A363=>lǒu +2A364=>mò +2A366=>liǎn +2A367=>xuàn +2A368=>suǒ +2A369=>mán +2A36A=>bì +2A372=>tì +2A374=>lián +2A375=>tán +2A376=>shàn +2A378=>qú +2A379=>dú +2A37A=>huán +2A37B=>sào +2A37F=>kuàng +2A383=>niè +2A385=>niè +2A386=>luó +2A387=>zuó +2A388=>yì +2A389=>xiàn +2A38A=>chǎo +2A38B=>tiè +2A392=>shuò +2A394=>mǐ +2A397=>mí +2A39B=>wǎn +2A39D=>bèn +2A39E=>qiāng +2A3A0=>mǒ +2A3A3=>liú +2A3A4=>wò +2A3A6=>měi +2A3A8=>tóu +2A3AB=>mǔ +2A3AD=>méi +2A3B2=>zuò +2A3B4=>tún +2A3B5=>kàng +2A3B6=>tún +2A3BA=>chè +2A3BB=>zhèng +2A3BD=>chōng +2A3BE=>tiān +2A3C0=>zhì +2A3C1=>chán +2A3C2=>chán +2A3C5=>qīng +2A3C6=>tūn +2A3C7=>huǐ +2A3C8=>què +2A3C9=>zhān +2A3CA=>jiān +2A3CB=>chán +2A3CD=>huáng +2A3CF=>huī +2A3D0=>chí +2A3D2=>huáng +2A3D3=>héng +2A3D4=>yǔn +2A3D6=>tuān +2A3D7=>biān +2A3D9=>huáng +2A3DA=>yǔn +2A3DF=>mò +2A3E0=>gōng +2A3E2=>gōng +2A3E4=>guì +2A3E6=>chán +2A3E8=>què +2A3E9=>ruì +2A3EA=>kuàng +2A3EB=>piào +2A3EE=>rǔ +2A3F2=>niǔ +2A3F3=>hù +2A3F4=>jǐn +2A3F5=>nì +2A3F6=>bào +2A3F8=>nǐ +2A3FA=>bì +2A3FB=>hú +2A3FC=>lí +2A3FF=>zhū +2A400=>nǎ +2A402=>quǎn +2A403=>fěng +2A404=>bǐ +2A405=>lí +2A406=>bié +2A407=>nián +2A408=>dǒng +2A40B=>lián +2A40C=>nì +2A40D=>lián +2A40E=>má +2A40F=>zhé +2A413=>jiā +2A414=>yí +2A416=>lǒng +2A418=>yì +2A41D=>dài +2A41E=>dù +2A423=>yǐ +2A425=>tài +2A426=>hāng +2A427=>shù +2A42C=>wán +2A42E=>sù +2A42F=>yǎo +2A430=>èr +2A432=>zhèn +2A43A=>dòu +2A43B=>jiān +2A43F=>pāng +2A440=>huī +2A442=>chà +2A443=>shān +2A444=>lú +2A446=>yù +2A448=>yàn +2A449=>wǎn +2A44A=>qiào +2A44B=>luō +2A44C=>yù +2A44F=>tú +2A450=>wèi +2A452=>tùn +2A455=>hǔn +2A456=>bēn +2A457=>qiè +2A459=>jīn +2A45A=>lái +2A45C=>zhǐ +2A45D=>yú +2A45F=>cì +2A466=>yè +2A467=>dié +2A468=>chà +2A469=>diàn +2A46A=>mán +2A46C=>dèng +2A46D=>wēi +2A46E=>niǎn +2A46F=>lèi +2A470=>bīng +2A471=>wū +2A473=>zhěn +2A476=>róu +2A477=>wài +2A478=>mì +2A479=>jiè +2A47B=>hóu +2A47D=>zhài +2A47E=>rǔ +2A47F=>zī +2A480=>pán +2A482=>mò +2A484=>mì +2A486=>qī +2A487=>mò +2A48A=>zhī +2A48B=>bān +2A48D=>miè +2A48F=>lù +2A491=>qī +2A492=>chōng +2A494=>lí +2A495=>yì +2A498=>dèng +2A499=>cuō +2A49B=>duì +2A49C=>mà +2A49D=>yǎn +2A49F=>zèng +2A4A0=>yǎn +2A4A1=>duì +2A4A2=>pū +2A4A5=>yuè +2A4A9=>huò +2A4AA=>mài +2A4AB=>jiǎn +2A4AC=>nóng +2A4AD=>qín +2A4AF=>qín +2A4B2=>yè +2A4B4=>tái +2A4B9=>jiān +2A4BC=>chá +2A4BE=>dàn +2A4BF=>téng +2A4C0=>lì +2A4C3=>niǎng +2A4C4=>chán +2A4C5=>zāng +2A4CA=>yù +2A4CC=>zuì +2A4CD=>biān +2A4D0=>chǔ +2A4D8=>rán +2A4DA=>rán +2A4DB=>yāng +2A4DC=>bǒ +2A4E1=>cù +2A4EC=>mí +2A4EE=>kě +2A4F0=>cù +2A4F7=>xí +2A4F9=>má +2A4FB=>shī +2A4FC=>diān +2A4FF=>shī +2A502=>dǐng +2A503=>jiōng +2A505=>yuán +2A506=>gān +2A50A=>huì +2A50B=>jī +2A50D=>péng +2A50F=>dēng +2A511=>bèng +2A514=>pāng +2A515=>tà +2A517=>yuān +2A518=>gāo +2A519=>yuān +2A51F=>jiā +2A523=>kōng +2A526=>dòng +2A529=>xián +2A52A=>qì +2A52C=>sāng +2A530=>yìn +2A533=>lóng +2A536=>tēng +2A537=>lóng +2A53A=>rèn +2A53D=>yìn +2A53E=>píng +2A53F=>pū +2A540=>yuán +2A541=>rǒng +2A543=>fāng +2A547=>hāng +2A548=>mí +2A549=>hú +2A54A=>zī +2A54C=>líng +2A54D=>jiōng +2A54E=>rǒng +2A552=>píng +2A553=>guāng +2A554=>ěr +2A55D=>cù +2A55E=>jùn +2A566=>xiǔ +2A568=>ér +2A569=>tì +2A56B=>yáng +2A56D=>ài +2A56E=>hú +2A56F=>xí +2A571=>hú +2A573=>sī +2A574=>lǐ +2A576=>yì +2A577=>gǔ +2A579=>táng +2A580=>què +2A581=>zōng +2A582=>lí +2A584=>jiào +2A587=>fán +2A588=>pú +2A589=>sī +2A58B=>jié +2A58C=>lú +2A58D=>lì +2A58E=>chán +2A590=>yào +2A595=>huī +2A599=>hōu +2A59A=>diān +2A59B=>qiù +2A59C=>jué +2A59E=>pì +2A5A2=>kuī +2A5A5=>xǐ +2A5A6=>tī +2A5A9=>xù +2A5AF=>biǎn +2A5B2=>hē +2A5B3=>lián +2A5B6=>sù +2A5B7=>liào +2A5BC=>jīn +2A5C1=>lì +2A5C2=>chán +2A5C5=>qí +2A5C6=>qí +2A5C9=>zī +2A5CB=>zī +2A5CD=>qí +2A5CF=>qí +2A5D0=>zī +2A5D2=>zhāi +2A5D3=>zhāi +2A5D4=>pà +2A5D6=>jū +2A5D9=>yǎn +2A5DC=>háng +2A5DD=>nà +2A5E4=>yǎn +2A5E6=>zhàn +2A5E7=>shǐ +2A5E8=>zhí +2A5ED=>zhā +2A5F4=>rǒng +2A5F5=>zhā +2A5F7=>yì +2A5F8=>míng +2A5F9=>yá +2A5FB=>zhì +2A5FD=>kuò +2A5FE=>xiá +2A600=>pián +2A601=>tà +2A603=>yǐ +2A606=>xiū +2A607=>zhāi +2A609=>duǒ +2A60A=>è +2A60E=>yín +2A610=>è +2A611=>suān +2A612=>ān +2A613=>cuó +2A615=>tuó +2A617=>tuó +2A618=>xiá +2A61B=>chuò +2A61D=>suān +2A625=>jì +2A626=>qiǎn +2A627=>zú +2A628=>zhāi +2A629=>yǔn +2A62A=>zhàn +2A62C=>yí +2A632=>yá +2A633=>yuē +2A639=>hé +2A63A=>qià +2A63E=>chā +2A643=>óu +2A648=>hú +2A64A=>yàn +2A64C=>qiè +2A64D=>bó +2A64E=>qiāng +2A64F=>jiè +2A65B=>nì +2A65E=>chǎn +2A65F=>qǐn +2A661=>zāo +2A664=>yǐn +2A665=>xiè +2A667=>qí +2A668=>jiàn +2A66B=>xū +2A66D=>zèng +2A66F=>è +2A673=>zū +2A674=>yǐ +2A679=>zhí +2A67A=>lì +2A67D=>lì +2A67E=>yín +2A681=>lián +2A683=>chán +2A685=>jué +2A687=>zá +2A68E=>zhāi +2A68F=>pián +2A691=>lóng +2A693=>lóng +2A698=>lóng +2A69D=>lóng +2A6A0=>lóng +2A6A2=>mǎng +2A6A5=>zhé +2A6AC=>gàn +2A6AD=>gōu +2A6AE=>rán +2A6AF=>cù +2A6B0=>jiāo +2A6B7=>bǒ +2A6B9=>zhù +2A6BA=>qiū +2A6BB=>yāng +2A6C0=>xiào +2A6C2=>huí +2A6C3=>qū +2A6C8=>líng +2A6CA=>yín +2A6CE=>pì +2A6D2=>lián +2A79D=>duó +2A848=>bái +2A84F=>zhān +2A8AE=>luán +2AA0A=>sóng +2AA17=>juē +2AA9D=>yōng +2AEB9=>nǔ +2AED0=>cōng +2AFA2=>xiàn +2B061=>lì +2B088=>fèi +2B099=>sù +2B0DC=>kòu +2B128=>chī +2B138=>xūn +2B230=>qià +2B2D0=>gǒng +2B300=>jī +2B328=>luó +2B359=>yì +2B35F=>yí +2B362=>náo +2B370=>xǐ +2B372=>xiǎo +2B3CB=>juē +2B404=>yuè +2B406=>kuài +2B409=>líng +2B410=>ní +2B413=>bù +2B4B6=>hán +2B4E7=>fū +2B4E9=>cōng +2B50E=>jué +2B5E0=>zhāng +2B5E6=>bù +2B5E7=>sù +2B5EE=>huáng +2B5F4=>zhān +2B61D=>jué +2B623=>hàn +2B624=>ái +2B628=>tí +2B688=>xù +2B689=>hóng +2B692=>fú +2B694=>huí +2B695=>shī +2B699=>pū +2B6DB=>zhī +2B6DE=>jué +2B6E2=>níng +2B6F6=>chì +2B6F8=>tí \ No newline at end of file diff --git a/vendor/github.com/Chain-Zhang/pinyin/resource.go b/vendor/github.com/Chain-Zhang/pinyin/resource.go new file mode 100644 index 0000000..0ec94c5 --- /dev/null +++ b/vendor/github.com/Chain-Zhang/pinyin/resource.go @@ -0,0 +1,41212 @@ +package pinyin + +var resource = map[string]string{ + "3400": "qiū", + "3401": "tiàn", + "3404": "kuà", + "3405": "wǔ", + "3406": "yǐn", + "340C": "yí", + "3416": "xié", + "341C": "chóu", + "3421": "nuò", + "3424": "dān", + "3428": "xù", + "3429": "xíng", + "342B": "xiōng", + "342C": "liú", + "342D": "lǐn", + "342E": "xiāng", + "342F": "yōng", + "3430": "xìn", + "3431": "zhěn", + "3432": "dài", + "3433": "wù", + "3434": "pān", + "3437": "mǎ", + "3438": "qiàn", + "3439": "yì", + "343A": "yín", + "343B": "nèi", + "343C": "chèng", + "343D": "fēng", + "3441": "zhuō", + "3442": "fǎng", + "3443": "ǎo", + "3444": "wǔ", + "3445": "zuò", + "3447": "zhòu", + "3448": "dòng", + "3449": "sù", + "344A": "yì", + "344B": "qióng", + "344C": "kuāng", + "344D": "lèi", + "344E": "nǎo", + "344F": "zhù", + "3450": "shū", + "3454": "xǔ", + "3457": "shēn", + "3458": "jiè", + "3459": "dié", + "345A": "nuó", + "345B": "sù", + "345C": "yì", + "345D": "lòng", + "345E": "yìng", + "345F": "běng", + "3463": "lán", + "3464": "miáo", + "3465": "yì", + "3466": "lì", + "3467": "jì", + "3468": "yǔ", + "3469": "luó", + "346A": "chái", + "346E": "hún", + "346F": "xǔ", + "3470": "huì", + "3471": "rǎo", + "3473": "zhòu", + "3475": "hàn", + "3476": "xì", + "3477": "tài", + "3478": "yáo", + "3479": "huì", + "347A": "jùn", + "347B": "mà", + "347C": "lüè", + "347D": "táng", + "347E": "yáo", + "347F": "zhào", + "3480": "zhāi", + "3481": "yǔ", + "3482": "zhuó", + "3483": "èr", + "3484": "rǎn", + "3485": "qǐ", + "3486": "chì", + "3487": "wǔ", + "3488": "hàn", + "3489": "tǎng", + "348A": "sè", + "348C": "qióng", + "348D": "léi", + "348E": "sà", + "3491": "kuǐ", + "3492": "pú", + "3493": "tà", + "3494": "shú", + "3495": "yāng", + "3496": "ǒu", + "3497": "tái", + "3499": "mián", + "349A": "yìn", + "349B": "diào", + "349C": "yǔ", + "349D": "miè", + "349E": "jùn", + "349F": "niǎo", + "34A0": "xiè", + "34A1": "yóu", + "34A4": "chè", + "34A5": "fēng", + "34A6": "lěi", + "34A7": "lì", + "34A9": "luǒ", + "34AB": "jì", + "34B0": "quán", + "34B2": "cái", + "34B3": "liǎng", + "34B4": "gǔ", + "34B5": "mào", + "34B7": "guǎ", + "34B8": "suì", + "34BB": "mào", + "34BC": "mán", + "34BD": "quān", + "34BE": "shì", + "34BF": "lí", + "34C1": "wǎng", + "34C2": "kòu", + "34C3": "dù", + "34C4": "zhèn", + "34C5": "tīng", + "34C8": "bìng", + "34C9": "huò", + "34CA": "dòng", + "34CB": "gòng", + "34CC": "chēng", + "34CE": "qīn", + "34CF": "jiǒng", + "34D0": "lù", + "34D1": "xìng", + "34D3": "nán", + "34D4": "xiè", + "34D6": "bì", + "34D7": "jié", + "34D8": "sù", + "34DA": "gōng", + "34DC": "yòu", + "34DD": "xíng", + "34DE": "qià", + "34DF": "pí", + "34E0": "diàn", + "34E1": "fǔ", + "34E2": "luò", + "34E3": "qià", + "34E4": "qià", + "34E5": "tāng", + "34E6": "bāi", + "34E7": "gān", + "34E8": "cí", + "34E9": "xuān", + "34EA": "lǎng", + "34ED": "shé", + "34EF": "lí", + "34F0": "huà", + "34F1": "tóu", + "34F2": "piān", + "34F3": "dī", + "34F4": "ruǎn", + "34F5": "è", + "34F6": "qiè", + "34F7": "yì", + "34F8": "zhuō", + "34F9": "ruì", + "34FA": "jiān", + "34FC": "chì", + "34FD": "chóng", + "34FE": "xī", + "3500": "lüè", + "3501": "dēng", + "3502": "lín", + "3503": "jué", + "3504": "sù", + "3505": "xiào", + "3506": "zàn", + "3509": "zhǔ", + "350A": "zhǎn", + "350B": "jiān", + "350C": "zòu", + "350D": "chuā", + "350E": "xiè", + "350F": "lì", + "3511": "chì", + "3512": "xí", + "3513": "jiǎn", + "3515": "jí", + "3517": "fèi", + "3518": "chù", + "3519": "bēng", + "351A": "jié", + "351C": "bá", + "351D": "liǎng", + "351E": "kuài", + "3520": "xiā", + "3521": "biē", + "3522": "jué", + "3523": "léi", + "3524": "xìn", + "3525": "bài", + "3526": "yǎng", + "3527": "lǜ", + "3528": "bèi", + "3529": "è", + "352A": "lǔ", + "352D": "chè", + "352E": "nuó", + "352F": "xuán", + "3530": "héng", + "3531": "yǔ", + "3533": "guǐ", + "3534": "yì", + "3535": "xuǎn", + "3536": "gòng", + "3537": "lòu", + "3538": "tī", + "3539": "lè", + "353A": "shì", + "353C": "sǔn", + "353D": "yào", + "353E": "xiān", + "353F": "zòu", + "3541": "què", + "3542": "yín", + "3543": "xī", + "3544": "zhǐ", + "3545": "jiá", + "3546": "hù", + "3547": "lā", + "3548": "yǐ", + "3549": "kè", + "354A": "fū", + "354B": "qín", + "354C": "ài", + "354E": "kè", + "354F": "chú", + "3550": "xiě", + "3551": "chú", + "3552": "wēi", + "3555": "huàn", + "3556": "sù", + "3557": "yòu", + "3559": "jùn", + "355A": "zhǎo", + "355B": "xù", + "355C": "shǐ", + "355E": "shuā", + "355F": "kuì", + "3560": "shuāng", + "3561": "hé", + "3562": "gài", + "3563": "yǎn", + "3564": "qiú", + "3565": "shēn", + "3566": "huà", + "3567": "xī", + "3568": "fàn", + "3569": "pàng", + "356A": "dǎn", + "356B": "fǎng", + "356C": "gōng", + "356D": "āo", + "356E": "fǔ", + "356F": "nè", + "3570": "xuè", + "3571": "yóu", + "3572": "huá", + "3574": "chén", + "3575": "guó", + "3576": "ň", + "3577": "huà", + "3578": "lì", + "3579": "fá", + "357A": "xiāo", + "357B": "pǒu", + "357D": "sì", + "3580": "lè", + "3581": "lìn", + "3582": "yì", + "3583": "hǒu", + "3585": "xù", + "3586": "qú", + "3587": "ér", + "358A": "xún", + "358F": "niè", + "3590": "wěi", + "3591": "xiè", + "3592": "tí", + "3593": "hóng", + "3594": "tǔn", + "3595": "niè", + "3596": "niè", + "3597": "yín", + "3598": "zhēn", + "359E": "wāi", + "359F": "shòu", + "35A0": "nuò", + "35A1": "yè", + "35A2": "qí", + "35A3": "tòu", + "35A4": "hán", + "35A5": "jùn", + "35A6": "dǒng", + "35A7": "hūn", + "35A8": "lù", + "35A9": "jū", + "35AA": "huò", + "35AB": "líng", + "35AD": "tiǎn", + "35AE": "lún", + "35B5": "gé", + "35B6": "yān", + "35B7": "shí", + "35B8": "xué", + "35B9": "pēn", + "35BA": "chǔn", + "35BB": "niú", + "35BC": "duǒ", + "35BD": "zé", + "35BE": "è", + "35BF": "xié", + "35C0": "yōu", + "35C1": "è", + "35C2": "shěng", + "35C3": "wěn", + "35C4": "kū", + "35C5": "hú", + "35C6": "gé", + "35C7": "xiá", + "35C8": "màn", + "35C9": "lüè", + "35CA": "jí", + "35CB": "hóu", + "35CC": "zhì", + "35CF": "wāi", + "35D1": "bai", + "35D2": "ài", + "35D3": "zhuī", + "35D4": "qiān", + "35D5": "gòu", + "35D6": "dàn", + "35D7": "bēi", + "35D8": "bó", + "35D9": "chū", + "35DA": "lì", + "35DB": "xiào", + "35DC": "xiù", + "35E2": "hóng", + "35E3": "tì", + "35E4": "cù", + "35E5": "kuò", + "35E6": "láo", + "35E7": "zhì", + "35E8": "xiē", + "35E9": "xī", + "35EB": "qiè", + "35EC": "zhā", + "35ED": "xī", + "35F0": "cóng", + "35F1": "jí", + "35F2": "huò", + "35F3": "tǎ", + "35F4": "yán", + "35F5": "xù", + "35F6": "pō", + "35F7": "sǎi", + "35FB": "guō", + "35FC": "yè", + "35FD": "xiǎng", + "35FE": "xuē", + "35FF": "hé", + "3600": "zuò", + "3601": "yì", + "3602": "cí", + "3604": "lēng", + "3605": "xián", + "3606": "tǎi", + "3607": "róng", + "3608": "yì", + "3609": "zhì", + "360A": "xī", + "360B": "xián", + "360C": "jù", + "360D": "jí", + "360E": "hǎn", + "3610": "pào", + "3611": "lì", + "3613": "lán", + "3614": "sǎi", + "3615": "hǎn", + "3616": "yán", + "3617": "qū", + "3619": "yán", + "361A": "hǎn", + "361B": "kān", + "361C": "chǐ", + "361D": "niè", + "361E": "huò", + "3620": "bì", + "3621": "xiá", + "3622": "wěng", + "3623": "xuán", + "3624": "wān", + "3625": "yóu", + "3626": "qín", + "3627": "xù", + "3628": "niè", + "3629": "bì", + "362A": "hào", + "362B": "jǐng", + "362C": "ào", + "362D": "ào", + "3630": "zhēn", + "3631": "tān", + "3632": "jú", + "3634": "zuò", + "3635": "bù", + "3636": "jié", + "3637": "ài", + "3638": "zàng", + "3639": "cí", + "363A": "fá", + "363F": "niè", + "3640": "liù", + "3641": "méi", + "3642": "duì", + "3643": "bāng", + "3644": "bì", + "3645": "bǎo", + "3647": "chù", + "3648": "xià", + "3649": "tiǎn", + "364A": "cháng", + "364D": "duō", + "364E": "wēi", + "364F": "fù", + "3650": "duǒ", + "3651": "yǔ", + "3652": "yě", + "3653": "kuí", + "3654": "wěi", + "3655": "kuài", + "3657": "wēi", + "3658": "yāo", + "3659": "lǒng", + "365A": "xīng", + "365B": "zhuān", + "365C": "chí", + "365D": "xié", + "365E": "niè", + "365F": "lǎng", + "3660": "yī", + "3661": "zōng", + "3662": "mán", + "3663": "zhàng", + "3664": "xià", + "3665": "gùn", + "3666": "xié", + "3668": "jì", + "3669": "liáo", + "366A": "yì", + "366B": "jí", + "366C": "yín", + "366E": "dā", + "366F": "yì", + "3670": "xiè", + "3671": "hào", + "3672": "yǒng", + "3673": "kǎn", + "3674": "chàn", + "3675": "tái", + "3676": "táng", + "3677": "zhí", + "3678": "bào", + "3679": "méng", + "367A": "kuí", + "367B": "chán", + "367C": "lěi", + "367E": "xì", + "3680": "xī", + "3681": "qiào", + "3682": "nàng", + "3683": "yūn", + "3685": "lóng", + "3686": "fù", + "3687": "zōng", + "3689": "gǔ", + "368A": "kāi", + "368B": "diāo", + "368C": "huà", + "368D": "kuǐ", + "368F": "gǎo", + "3690": "tào", + "3692": "shǎn", + "3693": "lǎi", + "3694": "niè", + "3695": "fú", + "3696": "gǎo", + "3697": "qié", + "3698": "bàn", + "3699": "jiā", + "369A": "kōng", + "369B": "xì", + "369C": "yù", + "369D": "zhuī", + "369E": "shěn", + "369F": "chuò", + "36A0": "xiāo", + "36A1": "jǐ", + "36A2": "nú", + "36A3": "xiáo", + "36A4": "yì", + "36A5": "yú", + "36A6": "yí", + "36A7": "yǎn", + "36A8": "shěn", + "36A9": "rǎn", + "36AA": "hào", + "36AB": "sà", + "36AC": "jūn", + "36AD": "yóu", + "36AF": "xín", + "36B0": "pēi", + "36B1": "qiū", + "36B2": "chān", + "36B4": "bù", + "36B5": "dōng", + "36B6": "sì", + "36B7": "ěr", + "36B9": "mǎo", + "36BA": "yùn", + "36BB": "jī", + "36BD": "qiǎo", + "36BE": "xiōng", + "36BF": "páo", + "36C0": "chú", + "36C1": "pēng", + "36C2": "nuǒ", + "36C3": "jié", + "36C4": "yī", + "36C5": "èr", + "36C6": "duò", + "36CA": "duǒ", + "36CD": "qiè", + "36CE": "lǚ", + "36CF": "qiú", + "36D0": "sǒu", + "36D1": "càn", + "36D2": "dòu", + "36D3": "xī", + "36D4": "fēng", + "36D5": "yì", + "36D6": "suō", + "36D7": "qiē", + "36D8": "pò", + "36D9": "xīn", + "36DA": "tǒng", + "36DB": "xìn", + "36DC": "yóu", + "36DD": "bèi", + "36DE": "lòng", + "36E3": "yún", + "36E4": "lí", + "36E5": "tà", + "36E6": "lǎn", + "36E7": "mǎn", + "36E8": "qiǎng", + "36E9": "zhóu", + "36EA": "yàn", + "36EB": "xī", + "36EC": "lù", + "36ED": "xī", + "36EE": "sǎo", + "36EF": "fàn", + "36F1": "wěi", + "36F2": "fà", + "36F3": "yì", + "36F4": "nǎo", + "36F5": "chēng", + "36F6": "tàn", + "36F7": "jī", + "36F8": "shù", + "36F9": "pián", + "36FA": "ān", + "36FB": "kuā", + "36FC": "chā", + "36FE": "xián", + "36FF": "zhì", + "3702": "fēng", + "3703": "liàn", + "3704": "xún", + "3705": "xù", + "3706": "mì", + "3707": "huì", + "3708": "mù", + "3709": "yōng", + "370A": "zhǎn", + "370B": "yì", + "370C": "nǒu", + "370D": "táng", + "370E": "xī", + "370F": "yún", + "3710": "shù", + "3711": "fú", + "3712": "yì", + "3713": "dá", + "3715": "lián", + "3716": "cáo", + "3717": "cān", + "3718": "jù", + "3719": "lù", + "371A": "sù", + "371B": "nèn", + "371C": "ào", + "371D": "ǎn", + "371E": "qiàn", + "3720": "cuī", + "3721": "cōng", + "3723": "rán", + "3724": "niǎn", + "3725": "mái", + "3726": "xín", + "3727": "yuè", + "3728": "nái", + "3729": "ào", + "372A": "shēn", + "372B": "mà", + "372E": "làn", + "372F": "xī", + "3730": "yuè", + "3731": "zhì", + "3732": "wěng", + "3733": "huái", + "3734": "mèng", + "3735": "niǎo", + "3736": "wǎn", + "3737": "mí", + "3738": "niè", + "3739": "qú", + "373A": "zàn", + "373B": "liàn", + "373C": "zhí", + "373D": "zǐ", + "373E": "hái", + "373F": "xù", + "3740": "hào", + "3741": "xuān", + "3742": "zhì", + "3743": "miǎn", + "3744": "chún", + "3745": "gòu", + "3747": "chún", + "3748": "luán", + "3749": "zhù", + "374A": "shǒu", + "374B": "liǎo", + "374C": "jiù", + "374D": "xiě", + "374E": "dìng", + "374F": "jiè", + "3750": "róng", + "3751": "máng", + "3753": "kè", + "3754": "yǎo", + "3755": "níng", + "3756": "yí", + "3757": "láng", + "3758": "yóng", + "3759": "yín", + "375A": "yán", + "375B": "sù", + "375D": "lín", + "375E": "yā", + "375F": "máo", + "3760": "míng", + "3761": "zuì", + "3762": "yǔ", + "3763": "yì", + "3764": "gòu", + "3765": "mǐ", + "3766": "jùn", + "3767": "wěn", + "3769": "kāng", + "376A": "diàn", + "376B": "lóng", + "376D": "xǐng", + "376E": "cuì", + "376F": "qiáo", + "3770": "mián", + "3771": "mèng", + "3772": "qǐn", + "3774": "wán", + "3775": "dé", + "3776": "ài", + "3778": "biàn", + "3779": "nóu", + "377A": "lián", + "377B": "jǐn", + "377C": "yū", + "377D": "chuí", + "377E": "zuǒ", + "377F": "bǒ", + "3780": "huī", + "3781": "yào", + "3782": "tuǐ", + "3783": "jì", + "3784": "ān", + "3785": "luò", + "3786": "jǐ", + "3787": "wěi", + "3788": "bō", + "3789": "zā", + "378A": "xù", + "378B": "niǎn", + "378C": "yùn", + "378E": "bǎ", + "378F": "zhé", + "3790": "jū", + "3791": "wěi", + "3792": "xiè", + "3793": "qì", + "3794": "yí", + "3795": "xiè", + "3796": "cí", + "3797": "qiú", + "3798": "dū", + "3799": "niào", + "379A": "qì", + "379B": "jǐ", + "379C": "tuī", + "379E": "sóng", + "379F": "diàn", + "37A0": "láo", + "37A1": "zhǎn", + "37A4": "yín", + "37A5": "cén", + "37A6": "jǐ", + "37A7": "huì", + "37A8": "zǐ", + "37A9": "lán", + "37AA": "náo", + "37AB": "jù", + "37AC": "qìn", + "37AD": "dài", + "37AF": "jié", + "37B0": "xǔ", + "37B1": "cōng", + "37B2": "yòng", + "37B3": "dǒu", + "37B4": "chí", + "37B6": "mǐn", + "37B7": "huáng", + "37B8": "suì", + "37B9": "kě", + "37BA": "zú", + "37BB": "hào", + "37BC": "chéng", + "37BD": "xuè", + "37BE": "ní", + "37BF": "chì", + "37C0": "lián", + "37C1": "àn", + "37C2": "mǔ", + "37C3": "sī", + "37C4": "xiáng", + "37C5": "yáng", + "37C6": "huá", + "37C7": "cuò", + "37C8": "qiú", + "37C9": "láo", + "37CA": "fú", + "37CB": "duì", + "37CC": "máng", + "37CD": "láng", + "37CE": "tuǒ", + "37CF": "hán", + "37D0": "mǎng", + "37D1": "bó", + "37D2": "qūn", + "37D3": "qí", + "37D4": "hán", + "37D6": "lòng", + "37D8": "tiáo", + "37D9": "zé", + "37DA": "qí", + "37DB": "zàn", + "37DC": "mí", + "37DD": "péi", + "37DE": "zhàn", + "37DF": "xiàng", + "37E0": "gǎng", + "37E2": "qí", + "37E4": "lù", + "37E6": "yùn", + "37E7": "è", + "37E8": "duān", + "37E9": "mín", + "37EA": "wēi", + "37EB": "quán", + "37EC": "sǒu", + "37ED": "mín", + "37EE": "tū", + "37F0": "mǐng", + "37F1": "yǎo", + "37F2": "jué", + "37F3": "lì", + "37F4": "kuài", + "37F5": "gǎng", + "37F6": "yuán", + "37F7": "da", + "37F9": "láo", + "37FA": "lóu", + "37FB": "qiàn", + "37FC": "áo", + "37FD": "biǎo", + "37FE": "yōng", + "37FF": "mǎng", + "3800": "dǎo", + "3802": "áo", + "3804": "xí", + "3805": "fú", + "3806": "dān", + "3807": "jiù", + "3808": "rùn", + "3809": "tóng", + "380A": "qū", + "380B": "è", + "380C": "qī", + "380D": "jí", + "380E": "jí", + "380F": "huá", + "3810": "jiào", + "3811": "zuì", + "3812": "biǎo", + "3813": "méng", + "3814": "bài", + "3815": "wěi", + "3816": "yǐ", + "3817": "ào", + "3818": "yǔ", + "3819": "háo", + "381A": "duì", + "381B": "wò", + "381C": "nì", + "381D": "cuán", + "381F": "lí", + "3820": "lú", + "3821": "niǎo", + "3822": "huái", + "3823": "lì", + "3825": "lǜ", + "3826": "fēng", + "3827": "mǐ", + "3828": "yù", + "382A": "jù", + "382D": "zhǎn", + "382E": "pēng", + "382F": "yǐ", + "3831": "jì", + "3832": "bǐ", + "3834": "rèn", + "3835": "huāng", + "3836": "fán", + "3837": "gé", + "3838": "kù", + "3839": "jiè", + "383A": "shā", + "383C": "sī", + "383D": "tóng", + "383E": "yuān", + "383F": "zī", + "3840": "bì", + "3841": "kuǎ", + "3842": "lì", + "3843": "huāng", + "3844": "xún", + "3845": "nuǒ", + "3847": "zhé", + "3848": "wèn", + "3849": "xián", + "384A": "qià", + "384B": "yé", + "384C": "mào", + "384F": "shù", + "3851": "qiāo", + "3852": "zhūn", + "3853": "kūn", + "3854": "wù", + "3855": "yīng", + "3856": "chuáng", + "3857": "tí", + "3858": "lián", + "3859": "bī", + "385A": "gōu", + "385B": "máng", + "385C": "xiè", + "385D": "fèng", + "385E": "lóu", + "385F": "zāo", + "3860": "zhèng", + "3861": "chú", + "3862": "màn", + "3863": "lóng", + "3865": "yìn", + "3866": "pīn", + "3867": "zhèng", + "3868": "jiān", + "3869": "luán", + "386A": "nié", + "386B": "yì", + "386D": "jì", + "386E": "jí", + "386F": "zhái", + "3870": "yǔ", + "3871": "jiǔ", + "3872": "huán", + "3873": "zhǐ", + "3874": "lā", + "3875": "líng", + "3876": "zhǐ", + "3877": "běn", + "3878": "zhà", + "3879": "jū", + "387A": "dàn", + "387B": "liào", + "387C": "yì", + "387D": "zhào", + "387E": "xiàn", + "387F": "chì", + "3880": "cì", + "3881": "chǐ", + "3882": "yǎn", + "3883": "láng", + "3884": "dòu", + "3885": "lòng", + "3886": "chán", + "3888": "tuí", + "3889": "chá", + "388A": "ǎi", + "388B": "chǐ", + "388D": "yǐng", + "388E": "zhé", + "388F": "tóu", + "3891": "tuí", + "3892": "chá", + "3893": "yǎo", + "3894": "zǒng", + "3896": "pān", + "3897": "qiào", + "3898": "lián", + "3899": "qín", + "389A": "lǔ", + "389B": "yàn", + "389C": "kàng", + "389D": "sū", + "389E": "yì", + "389F": "chān", + "38A0": "jiǒng", + "38A1": "jiǎng", + "38A3": "jìng", + "38A5": "dòng", + "38A7": "juàn", + "38A8": "hàn", + "38A9": "dì", + "38AC": "hóng", + "38AE": "chí", + "38AF": "diāo", + "38B0": "bì", + "38B2": "xùn", + "38B3": "lú", + "38B5": "xié", + "38B6": "bì", + "38B8": "bì", + "38BA": "xián", + "38BB": "ruì", + "38BC": "biè", + "38BD": "ěr", + "38BE": "juàn", + "38C0": "zhèn", + "38C1": "bèi", + "38C2": "è", + "38C3": "yǔ", + "38C4": "qú", + "38C5": "zàn", + "38C6": "mí", + "38C7": "yì", + "38C8": "sì", + "38CC": "shàn", + "38CD": "tái", + "38CE": "mù", + "38CF": "jìng", + "38D0": "biàn", + "38D1": "róng", + "38D2": "cèng", + "38D3": "càn", + "38D4": "dīng", + "38D9": "dí", + "38DA": "tǒng", + "38DB": "tà", + "38DC": "xíng", + "38DD": "sōng", + "38DE": "duó", + "38DF": "xì", + "38E0": "tāo", + "38E2": "tí", + "38E3": "shàn", + "38E4": "jiàn", + "38E5": "zhì", + "38E6": "wēi", + "38E7": "yìn", + "38EA": "huǎn", + "38EB": "zhǒng", + "38EC": "qì", + "38ED": "zōng", + "38EF": "xiè", + "38F0": "xiè", + "38F1": "zé", + "38F2": "wéi", + "38F5": "tà", + "38F6": "zhān", + "38F7": "nìng", + "38FB": "yì", + "38FC": "rěn", + "38FD": "shù", + "38FE": "chà", + "38FF": "zhuó", + "3901": "miǎn", + "3902": "jí", + "3903": "fáng", + "3904": "pèi", + "3905": "ài", + "3906": "fàn", + "3907": "ǎo", + "3908": "qìn", + "3909": "qiā", + "390A": "xiào", + "390B": "fēn", + "390C": "gān", + "390D": "qiāo", + "390E": "gē", + "390F": "tóng", + "3910": "chān", + "3911": "yòu", + "3912": "gāo", + "3913": "bèn", + "3914": "fù", + "3915": "chù", + "3916": "zhù", + "3918": "zhòu", + "391A": "háng", + "391B": "nín", + "391C": "jué", + "391D": "chōng", + "391E": "chà", + "391F": "kǒng", + "3920": "liè", + "3921": "lì", + "3922": "yù", + "3924": "yú", + "3925": "hài", + "3926": "lì", + "3927": "hóu", + "3928": "gǒng", + "3929": "kè", + "392A": "yuàn", + "392B": "dé", + "392C": "huì", + "392E": "guàng", + "392F": "jiǒng", + "3930": "zuò", + "3931": "fù", + "3932": "qiè", + "3933": "běi", + "3934": "chè", + "3935": "cí", + "3936": "máng", + "3937": "hān", + "3938": "xì", + "3939": "qiú", + "393A": "huǎng", + "393D": "chóu", + "393E": "sàn", + "393F": "yān", + "3940": "zhí", + "3941": "dé", + "3942": "tè", + "3943": "mèn", + "3944": "líng", + "3945": "shòu", + "3946": "tuì", + "3947": "cán", + "3948": "dié", + "3949": "chè", + "394A": "péng", + "394B": "yī", + "394C": "jú", + "394D": "jì", + "394E": "lái", + "394F": "tiǎn", + "3950": "yuàn", + "3952": "cǎi", + "3953": "qī", + "3954": "yù", + "3955": "lián", + "3956": "cōng", + "395A": "yú", + "395B": "jí", + "395C": "wèi", + "395D": "mǐ", + "395E": "suì", + "395F": "xié", + "3960": "xū", + "3961": "chì", + "3962": "qiú", + "3963": "huì", + "3965": "yú", + "3966": "qiè", + "3967": "shùn", + "3968": "shuì", + "3969": "duǒ", + "396A": "lóu", + "396C": "páng", + "396D": "tài", + "396E": "zhòu", + "396F": "yǐn", + "3970": "sāo", + "3971": "fěi", + "3972": "chēn", + "3973": "yuán", + "3974": "yí", + "3975": "hùn", + "3976": "sè", + "3977": "yè", + "3978": "mǐn", + "3979": "fěn", + "397A": "hé", + "397C": "yìn", + "397D": "cè", + "397E": "nì", + "397F": "ào", + "3980": "féng", + "3981": "lián", + "3982": "cháng", + "3983": "chǎn", + "3984": "má", + "3985": "diē", + "3986": "hū", + "3987": "lù", + "3989": "yì", + "398A": "huá", + "398B": "zhā", + "398C": "hū", + "398D": "è", + "398E": "huò", + "398F": "sǔn", + "3990": "nì", + "3991": "xiàn", + "3992": "lí", + "3993": "xiàn", + "3994": "yàn", + "3995": "lóng", + "3996": "mèn", + "3997": "jīn", + "3998": "jī", + "399A": "biǎn", + "399B": "yǔ", + "399C": "huò", + "399D": "miǎo", + "399E": "chóu", + "399F": "mái", + "39A1": "lè", + "39A2": "jié", + "39A3": "wèi", + "39A4": "yì", + "39A5": "xuān", + "39A6": "xì", + "39A7": "cǎn", + "39A8": "lán", + "39A9": "yǐn", + "39AA": "xiè", + "39AB": "zā", + "39AC": "luǒ", + "39AD": "líng", + "39AE": "qián", + "39AF": "huò", + "39B0": "jiān", + "39B1": "wǒ", + "39B4": "gé", + "39B5": "zhū", + "39B6": "dié", + "39B7": "yǒng", + "39B8": "jǐ", + "39B9": "yáng", + "39BA": "rù", + "39BB": "xí", + "39BC": "shuàng", + "39BD": "yù", + "39BE": "yí", + "39BF": "qiǎn", + "39C0": "jí", + "39C1": "qù", + "39C2": "tián", + "39C3": "shōu", + "39C4": "qiǎn", + "39C5": "mù", + "39C6": "jīn", + "39C7": "mǎo", + "39C8": "yǐn", + "39C9": "gài", + "39CA": "pō", + "39CB": "xuǎn", + "39CC": "mào", + "39CD": "fǎng", + "39CE": "yá", + "39CF": "gāng", + "39D0": "sǒng", + "39D1": "huī", + "39D2": "yù", + "39D3": "guā", + "39D4": "guài", + "39D5": "liǔ", + "39D6": "è", + "39D7": "zǐ", + "39D8": "zì", + "39D9": "bì", + "39DA": "wǎ", + "39DC": "liè", + "39DF": "kuǎi", + "39E1": "hài", + "39E2": "yīn", + "39E3": "zhū", + "39E4": "chòng", + "39E5": "xiǎn", + "39E6": "xuàn", + "39E8": "qiú", + "39E9": "pèi", + "39EA": "guǐ", + "39EB": "ér", + "39EC": "gǒng", + "39ED": "qióng", + "39EE": "hū", + "39EF": "lǎo", + "39F0": "lì", + "39F1": "chèn", + "39F2": "sǎn", + "39F3": "zhuò", + "39F4": "wǒ", + "39F5": "póu", + "39F6": "kēng", + "39F7": "tùn", + "39F8": "pēng", + "39F9": "tè", + "39FA": "tà", + "39FB": "zhuó", + "39FC": "biào", + "39FD": "gù", + "39FE": "hū", + "3A00": "bǐng", + "3A01": "zhì", + "3A02": "dǒng", + "3A03": "duǐ", + "3A04": "zhōu", + "3A05": "nèi", + "3A06": "lǐn", + "3A07": "pó", + "3A08": "jǐ", + "3A09": "mín", + "3A0A": "wěi", + "3A0B": "chě", + "3A0C": "gòu", + "3A0D": "bāng", + "3A0E": "rú", + "3A0F": "tān", + "3A10": "bǔ", + "3A11": "zōng", + "3A12": "kuī", + "3A13": "láo", + "3A14": "hàn", + "3A15": "yíng", + "3A16": "zhì", + "3A17": "jié", + "3A18": "xǐng", + "3A19": "xié", + "3A1A": "xún", + "3A1B": "shǎn", + "3A1C": "qián", + "3A1D": "xiē", + "3A1E": "sù", + "3A1F": "hāi", + "3A20": "mì", + "3A21": "hún", + "3A22": "pī", + "3A24": "huì", + "3A25": "nà", + "3A26": "sǒng", + "3A27": "bèn", + "3A28": "chōu", + "3A29": "jié", + "3A2A": "huàng", + "3A2B": "lǎn", + "3A2D": "hù", + "3A2E": "dōu", + "3A2F": "huò", + "3A30": "gǔn", + "3A31": "yáo", + "3A32": "cè", + "3A33": "guǐ", + "3A34": "jiàn", + "3A35": "jiǎn", + "3A36": "dǎo", + "3A37": "jìn", + "3A38": "mà", + "3A39": "huì", + "3A3A": "miǎn", + "3A3B": "cán", + "3A3C": "lüè", + "3A3D": "pì", + "3A3E": "yàng", + "3A3F": "jù", + "3A40": "jù", + "3A41": "què", + "3A43": "qiān", + "3A44": "shāi", + "3A46": "jiù", + "3A47": "huò", + "3A48": "yǔn", + "3A49": "dá", + "3A4A": "xuān", + "3A4B": "xiāo", + "3A4C": "fèi", + "3A4D": "cè", + "3A4E": "yè", + "3A50": "dèn", + "3A52": "qín", + "3A53": "huǐ", + "3A54": "tún", + "3A56": "qiáng", + "3A57": "xí", + "3A58": "nǐ", + "3A59": "sāi", + "3A5A": "méng", + "3A5B": "tuán", + "3A5C": "lǎn", + "3A5D": "háo", + "3A5E": "cì", + "3A5F": "zhài", + "3A60": "āo", + "3A61": "luǒ", + "3A62": "miè", + "3A64": "fū", + "3A66": "xié", + "3A67": "bó", + "3A68": "huì", + "3A69": "qǐng", + "3A6A": "xié", + "3A6D": "bó", + "3A6E": "qián", + "3A6F": "pó", + "3A70": "jiǎo", + "3A71": "jué", + "3A72": "kǔn", + "3A73": "sǒng", + "3A74": "jú", + "3A75": "è", + "3A76": "niè", + "3A77": "qiān", + "3A78": "dié", + "3A79": "dié", + "3A7B": "qī", + "3A7C": "zhī", + "3A7D": "qí", + "3A7E": "zhuì", + "3A7F": "kū", + "3A80": "yú", + "3A81": "qín", + "3A82": "kū", + "3A83": "hé", + "3A84": "fú", + "3A86": "dǐ", + "3A87": "xiàn", + "3A88": "guì", + "3A89": "hé", + "3A8A": "qún", + "3A8B": "hàn", + "3A8C": "tǒng", + "3A8D": "bó", + "3A8E": "shǎn", + "3A8F": "bǐ", + "3A90": "lù", + "3A91": "yè", + "3A92": "ní", + "3A93": "chuái", + "3A94": "sàn", + "3A95": "diào", + "3A96": "lù", + "3A97": "tǒu", + "3A98": "liǎn", + "3A99": "kě", + "3A9A": "sàn", + "3A9B": "zhěn", + "3A9C": "chuǎi", + "3A9D": "liàn", + "3A9E": "mào", + "3AA0": "qiān", + "3AA1": "kài", + "3AA2": "shǎo", + "3AA3": "xiāo", + "3AA4": "bì", + "3AA5": "zhā", + "3AA6": "yìn", + "3AA7": "xī", + "3AA8": "shàn", + "3AA9": "sù", + "3AAA": "sà", + "3AAB": "ruì", + "3AAC": "chuō", + "3AAD": "lú", + "3AAE": "líng", + "3AAF": "chá", + "3AB1": "huàn", + "3AB4": "jiá", + "3AB5": "bàn", + "3AB6": "hú", + "3AB7": "dǒu", + "3AB9": "lǒu", + "3ABA": "jū", + "3ABB": "juàn", + "3ABC": "kě", + "3ABD": "suǒ", + "3ABE": "luò", + "3ABF": "zhé", + "3AC0": "dǐng", + "3AC1": "duàn", + "3AC2": "zhù", + "3AC3": "yǎn", + "3AC4": "páng", + "3AC5": "chá", + "3ACA": "yǐ", + "3ACD": "yóu", + "3ACE": "huī", + "3ACF": "yǎo", + "3AD0": "yǎo", + "3AD1": "zhǐ", + "3AD2": "gǒng", + "3AD3": "qǐ", + "3AD4": "gèn", + "3AD7": "hòu", + "3AD8": "mì", + "3AD9": "fú", + "3ADA": "hū", + "3ADB": "guàng", + "3ADC": "tǎn", + "3ADD": "dī", + "3ADF": "yán", + "3AE2": "qù", + "3AE4": "chǎng", + "3AE5": "mǐng", + "3AE6": "tāo", + "3AE7": "bào", + "3AE8": "ān", + "3AEB": "xiǎn", + "3AEF": "mào", + "3AF0": "làng", + "3AF1": "nǎn", + "3AF2": "bèi", + "3AF3": "chén", + "3AF5": "fēi", + "3AF6": "zhǒu", + "3AF7": "jī", + "3AF8": "jiē", + "3AF9": "shù", + "3AFB": "kùn", + "3AFC": "dié", + "3AFD": "lù", + "3B02": "yú", + "3B03": "tái", + "3B04": "chàn", + "3B05": "màn", + "3B06": "mǐn", + "3B07": "huàn", + "3B08": "wēn", + "3B09": "nuǎn", + "3B0A": "huàn", + "3B0B": "hóu", + "3B0C": "jìng", + "3B0D": "bó", + "3B0E": "xiǎn", + "3B0F": "lì", + "3B10": "jìn", + "3B12": "mǎng", + "3B13": "piào", + "3B14": "háo", + "3B15": "yáng", + "3B17": "xiàn", + "3B18": "sù", + "3B19": "wěi", + "3B1A": "chè", + "3B1B": "xī", + "3B1C": "jìn", + "3B1D": "céng", + "3B1E": "hè", + "3B1F": "fēn", + "3B20": "shài", + "3B21": "líng", + "3B23": "duì", + "3B24": "qī", + "3B25": "pù", + "3B26": "yuè", + "3B27": "bó", + "3B29": "huì", + "3B2A": "dié", + "3B2B": "yàn", + "3B2C": "jù", + "3B2D": "jiào", + "3B2E": "nàn", + "3B2F": "liè", + "3B30": "yú", + "3B31": "tì", + "3B32": "tiān", + "3B33": "wǔ", + "3B34": "hǒng", + "3B35": "xiáo", + "3B36": "hào", + "3B38": "tiāo", + "3B39": "zhēng", + "3B3B": "huāng", + "3B3C": "fù", + "3B3F": "tūn", + "3B41": "réng", + "3B42": "jiǎo", + "3B44": "xìn", + "3B47": "yuàn", + "3B48": "jué", + "3B49": "huá", + "3B4B": "bàng", + "3B4C": "móu", + "3B4E": "gāng", + "3B4F": "wěi", + "3B51": "mèi", + "3B52": "sì", + "3B53": "biàn", + "3B54": "lú", + "3B55": "qū", + "3B58": "gé", + "3B59": "zhé", + "3B5A": "lǚ", + "3B5B": "pài", + "3B5C": "róng", + "3B5D": "qiú", + "3B5E": "liè", + "3B5F": "gǒng", + "3B60": "xiǎn", + "3B61": "xì", + "3B62": "xīn", + "3B64": "niǎo", + "3B68": "xié", + "3B69": "liè", + "3B6A": "fū", + "3B6B": "cuó", + "3B6C": "zhuó", + "3B6D": "bā", + "3B6E": "zuò", + "3B6F": "zhé", + "3B70": "zuī", + "3B71": "hé", + "3B72": "jí", + "3B74": "jiān", + "3B78": "tú", + "3B79": "xián", + "3B7A": "yǎn", + "3B7B": "táng", + "3B7C": "tà", + "3B7D": "dǐ", + "3B7E": "jué", + "3B7F": "áng", + "3B80": "hán", + "3B81": "xiáo", + "3B82": "jú", + "3B83": "wēi", + "3B84": "bǎng", + "3B85": "zhuī", + "3B86": "niè", + "3B87": "tiàn", + "3B88": "nài", + "3B8B": "yǒu", + "3B8C": "mián", + "3B8F": "nài", + "3B90": "shěng", + "3B91": "chā", + "3B92": "yān", + "3B93": "gèn", + "3B94": "chòng", + "3B95": "ruǎn", + "3B96": "jiá", + "3B97": "qín", + "3B98": "máo", + "3B99": "è", + "3B9A": "lì", + "3B9B": "chí", + "3B9C": "zāng", + "3B9D": "hé", + "3B9E": "jié", + "3B9F": "niǎn", + "3BA1": "guàn", + "3BA2": "hóu", + "3BA3": "gài", + "3BA5": "bèn", + "3BA6": "suǒ", + "3BA7": "wū", + "3BA8": "jì", + "3BA9": "xī", + "3BAA": "qióng", + "3BAB": "hé", + "3BAC": "wēng", + "3BAD": "xián", + "3BAE": "jié", + "3BAF": "hún", + "3BB0": "pí", + "3BB1": "shēn", + "3BB2": "chōu", + "3BB3": "zhèn", + "3BB5": "zhān", + "3BB6": "shuò", + "3BB7": "jī", + "3BB8": "sòng", + "3BB9": "zhǐ", + "3BBA": "běn", + "3BBE": "lǎng", + "3BBF": "bì", + "3BC0": "xuàn", + "3BC1": "péi", + "3BC2": "dài", + "3BC4": "zhī", + "3BC5": "pí", + "3BC6": "chǎn", + "3BC7": "bì", + "3BC8": "sù", + "3BC9": "huò", + "3BCA": "hén", + "3BCB": "jiǒng", + "3BCC": "chuán", + "3BCD": "jiǎng", + "3BCE": "nèn", + "3BCF": "gǔ", + "3BD0": "fǎng", + "3BD3": "tà", + "3BD4": "cuì", + "3BD5": "xī", + "3BD6": "dé", + "3BD7": "xián", + "3BD8": "kuǎn", + "3BD9": "zhé", + "3BDA": "tā", + "3BDB": "hú", + "3BDC": "cuì", + "3BDD": "lù", + "3BDE": "juàn", + "3BDF": "lù", + "3BE0": "qiàn", + "3BE1": "pào", + "3BE2": "zhèn", + "3BE4": "lì", + "3BE5": "cáo", + "3BE6": "qí", + "3BE9": "tì", + "3BEA": "líng", + "3BEB": "qú", + "3BEC": "liǎn", + "3BED": "lǔ", + "3BEE": "shú", + "3BEF": "gòng", + "3BF0": "zhé", + "3BF1": "pāo", + "3BF2": "jìn", + "3BF3": "qíng", + "3BF6": "zōng", + "3BF7": "pú", + "3BF8": "jǐn", + "3BF9": "biǎo", + "3BFA": "jiàn", + "3BFB": "gǔn", + "3BFE": "zāo", + "3BFF": "liè", + "3C00": "lí", + "3C01": "luǒ", + "3C02": "shěn", + "3C03": "mián", + "3C04": "jiàn", + "3C05": "dí", + "3C06": "bèi", + "3C08": "liǎn", + "3C0A": "xián", + "3C0B": "pín", + "3C0C": "què", + "3C0D": "lóng", + "3C0E": "zuì", + "3C10": "jué", + "3C11": "shān", + "3C12": "xué", + "3C14": "xiè", + "3C16": "lǎn", + "3C17": "qí", + "3C18": "yí", + "3C19": "nuó", + "3C1A": "lí", + "3C1B": "yuè", + "3C1D": "yǐ", + "3C1E": "chī", + "3C1F": "jì", + "3C20": "hāng", + "3C21": "xiè", + "3C22": "kēng", + "3C23": "zī", + "3C24": "hē", + "3C25": "xì", + "3C26": "qù", + "3C27": "hāi", + "3C28": "xiā", + "3C29": "hāi", + "3C2A": "guī", + "3C2B": "chān", + "3C2C": "xún", + "3C2D": "xū", + "3C2E": "shèn", + "3C2F": "kòu", + "3C30": "xiā", + "3C31": "shà", + "3C32": "yū", + "3C33": "yà", + "3C34": "pǒu", + "3C35": "zú", + "3C36": "yǒu", + "3C37": "zì", + "3C38": "liǎn", + "3C39": "xiān", + "3C3A": "xià", + "3C3B": "yǐ", + "3C3C": "shà", + "3C3D": "yàn", + "3C3E": "jiào", + "3C3F": "xī", + "3C40": "chǐ", + "3C41": "shì", + "3C42": "kāng", + "3C43": "yǐn", + "3C44": "hēi", + "3C45": "yì", + "3C46": "xī", + "3C47": "sè", + "3C48": "jìn", + "3C49": "yè", + "3C4A": "yōu", + "3C4B": "què", + "3C4C": "yé", + "3C4D": "luán", + "3C4E": "kūn", + "3C4F": "zhèng", + "3C54": "xiē", + "3C56": "cuì", + "3C57": "xiū", + "3C58": "àn", + "3C59": "xiǔ", + "3C5A": "cán", + "3C5B": "chuǎn", + "3C5C": "zhá", + "3C5E": "yì", + "3C5F": "pī", + "3C60": "kū", + "3C61": "shēng", + "3C62": "láng", + "3C63": "tuǐ", + "3C64": "xī", + "3C65": "líng", + "3C66": "qī", + "3C67": "wò", + "3C68": "liàn", + "3C69": "dú", + "3C6A": "mèn", + "3C6B": "làn", + "3C6C": "wěi", + "3C6D": "duàn", + "3C6E": "kuài", + "3C6F": "ái", + "3C70": "zǎi", + "3C71": "huì", + "3C72": "yì", + "3C73": "mò", + "3C74": "zì", + "3C75": "fèn", + "3C76": "péng", + "3C78": "bì", + "3C79": "lì", + "3C7A": "lú", + "3C7B": "luò", + "3C7C": "hāi", + "3C7D": "zhěn", + "3C7E": "gāi", + "3C7F": "què", + "3C80": "zhēn", + "3C81": "kōng", + "3C82": "chéng", + "3C83": "jiù", + "3C84": "jué", + "3C85": "jì", + "3C86": "líng", + "3C88": "sháo", + "3C89": "què", + "3C8A": "ruì", + "3C8B": "chuò", + "3C8C": "nèng", + "3C8D": "zhī", + "3C8E": "lóu", + "3C8F": "pāo", + "3C92": "bào", + "3C93": "róng", + "3C94": "xiān", + "3C95": "lèi", + "3C96": "xiāo", + "3C97": "fū", + "3C98": "qú", + "3C9A": "shā", + "3C9B": "zhǐ", + "3C9C": "tán", + "3C9D": "rǒng", + "3C9E": "sū", + "3C9F": "yǐng", + "3CA0": "máo", + "3CA1": "nài", + "3CA2": "biàn", + "3CA4": "shuāi", + "3CA5": "táng", + "3CA6": "hàn", + "3CA7": "sào", + "3CA8": "róng", + "3CAA": "dēng", + "3CAB": "pú", + "3CAC": "jiāo", + "3CAD": "tǎn", + "3CAF": "rán", + "3CB0": "níng", + "3CB1": "liè", + "3CB2": "dié", + "3CB3": "dié", + "3CB4": "zhòng", + "3CB6": "lǜ", + "3CB7": "dàn", + "3CB8": "xī", + "3CB9": "guǐ", + "3CBA": "jí", + "3CBB": "nì", + "3CBC": "yì", + "3CBD": "niàn", + "3CBE": "yǔ", + "3CBF": "wǎng", + "3CC0": "guò", + "3CC1": "zè", + "3CC2": "yán", + "3CC3": "cuì", + "3CC4": "xián", + "3CC5": "jiǎo", + "3CC6": "tǒu", + "3CC7": "fù", + "3CC8": "pèi", + "3CCA": "yōu", + "3CCB": "qiū", + "3CCC": "yā", + "3CCD": "bù", + "3CCE": "biàn", + "3CCF": "shì", + "3CD0": "zhá", + "3CD1": "yì", + "3CD2": "biàn", + "3CD4": "duì", + "3CD5": "lán", + "3CD6": "yī", + "3CD7": "chài", + "3CD8": "chōng", + "3CD9": "xuàn", + "3CDA": "xù", + "3CDB": "yú", + "3CDC": "xiū", + "3CE0": "tà", + "3CE1": "guō", + "3CE5": "lòng", + "3CE6": "xiè", + "3CE7": "chè", + "3CE8": "jiǎn", + "3CE9": "tān", + "3CEA": "pì", + "3CEB": "zǎn", + "3CEC": "xuán", + "3CED": "xián", + "3CEE": "niào", + "3CF4": "mì", + "3CF5": "jì", + "3CF6": "nǒu", + "3CF7": "hū", + "3CF8": "huā", + "3CF9": "wǎng", + "3CFA": "yóu", + "3CFB": "zé", + "3CFC": "bì", + "3CFD": "mǐ", + "3CFE": "qiāng", + "3CFF": "xiè", + "3D00": "fàn", + "3D01": "yì", + "3D02": "tān", + "3D03": "lèi", + "3D04": "yǒng", + "3D06": "jìn", + "3D07": "shè", + "3D08": "yìn", + "3D09": "jǐ", + "3D0B": "sù", + "3D0F": "wǎng", + "3D10": "miàn", + "3D11": "sù", + "3D12": "yì", + "3D13": "shāi", + "3D14": "xī", + "3D15": "jí", + "3D16": "luò", + "3D17": "yōu", + "3D18": "mào", + "3D19": "zhǎ", + "3D1A": "suì", + "3D1B": "zhì", + "3D1C": "biàn", + "3D1D": "lí", + "3D25": "qiào", + "3D26": "guàn", + "3D27": "xī", + "3D28": "zhèn", + "3D29": "yōng", + "3D2A": "niè", + "3D2B": "jùn", + "3D2C": "xiè", + "3D2D": "yǎo", + "3D2E": "xiè", + "3D2F": "zhī", + "3D30": "néng", + "3D32": "sī", + "3D33": "lǒng", + "3D34": "chén", + "3D35": "mì", + "3D36": "què", + "3D37": "dān", + "3D38": "shǎn", + "3D3C": "sù", + "3D3D": "xiè", + "3D3E": "bó", + "3D3F": "dǐng", + "3D40": "zú", + "3D42": "shù", + "3D43": "shé", + "3D44": "hàn", + "3D45": "tān", + "3D46": "gǎo", + "3D4A": "nà", + "3D4B": "mì", + "3D4C": "xún", + "3D4D": "mèn", + "3D4E": "jiàn", + "3D4F": "cuǐ", + "3D50": "jué", + "3D51": "hè", + "3D52": "fèi", + "3D53": "shí", + "3D54": "chě", + "3D55": "shèn", + "3D56": "nǜ", + "3D57": "píng", + "3D58": "màn", + "3D5D": "yì", + "3D5E": "chóu", + "3D60": "kū", + "3D61": "báo", + "3D62": "léi", + "3D63": "kě", + "3D64": "shà", + "3D65": "bì", + "3D66": "suí", + "3D67": "gé", + "3D68": "pì", + "3D69": "yì", + "3D6A": "xián", + "3D6B": "nì", + "3D6C": "yíng", + "3D6D": "zhǔ", + "3D6E": "chún", + "3D6F": "féng", + "3D70": "xù", + "3D71": "piǎo", + "3D72": "wǔ", + "3D73": "liáo", + "3D74": "cáng", + "3D75": "zòu", + "3D76": "zuō", + "3D77": "biàn", + "3D78": "yào", + "3D79": "huán", + "3D7A": "pài", + "3D7B": "xiū", + "3D7D": "lěi", + "3D7E": "qìng", + "3D7F": "xiào", + "3D80": "jiāo", + "3D81": "guó", + "3D84": "yán", + "3D85": "xué", + "3D86": "zhū", + "3D87": "héng", + "3D88": "yíng", + "3D89": "xī", + "3D8C": "lián", + "3D8D": "xiǎn", + "3D8E": "huán", + "3D8F": "yīn", + "3D91": "liàn", + "3D92": "shǎn", + "3D93": "cáng", + "3D94": "bèi", + "3D95": "jiǎn", + "3D96": "shù", + "3D97": "fàn", + "3D98": "diàn", + "3D9A": "bà", + "3D9B": "yú", + "3D9E": "nǎng", + "3D9F": "lěi", + "3DA0": "yì", + "3DA1": "dài", + "3DA3": "chán", + "3DA4": "chǎo", + "3DA5": "gān", + "3DA6": "jìn", + "3DA7": "nèn", + "3DAB": "liǎo", + "3DAC": "mò", + "3DAD": "yǒu", + "3DAF": "liù", + "3DB0": "hán", + "3DB2": "yòng", + "3DB3": "jìn", + "3DB4": "chǐ", + "3DB5": "rèn", + "3DB6": "nóng", + "3DB9": "hòng", + "3DBA": "tiàn", + "3DBC": "āi", + "3DBD": "guā", + "3DBE": "biāo", + "3DBF": "bó", + "3DC0": "qióng", + "3DC2": "shù", + "3DC3": "chuǐ", + "3DC4": "huǐ", + "3DC5": "chǎo", + "3DC6": "fù", + "3DC7": "huī", + "3DC8": "è", + "3DC9": "wèi", + "3DCA": "fén", + "3DCB": "tán", + "3DCD": "lún", + "3DCE": "hè", + "3DCF": "yǒng", + "3DD0": "huǐ", + "3DD2": "yú", + "3DD3": "zǒng", + "3DD4": "yàn", + "3DD5": "qiú", + "3DD6": "zhào", + "3DD7": "jiǒng", + "3DD8": "tái", + "3DDF": "tuì", + "3DE0": "lín", + "3DE1": "jiǒng", + "3DE2": "zhǎ", + "3DE3": "xīng", + "3DE4": "hù", + "3DE6": "xù", + "3DEA": "cuì", + "3DEB": "qǐng", + "3DEC": "mò", + "3DEE": "zāo", + "3DEF": "bèng", + "3DF0": "chī", + "3DF3": "yàn", + "3DF4": "gé", + "3DF5": "mò", + "3DF6": "bèi", + "3DF7": "juǎn", + "3DF8": "dié", + "3DF9": "zhào", + "3DFB": "wú", + "3DFC": "yàn", + "3DFE": "jué", + "3DFF": "xiān", + "3E00": "tái", + "3E01": "hǎn", + "3E03": "diǎn", + "3E04": "jì", + "3E05": "jié", + "3E07": "zuǎn", + "3E09": "xiè", + "3E0A": "lài", + "3E0B": "fán", + "3E0C": "huò", + "3E0D": "xì", + "3E0E": "niè", + "3E0F": "mí", + "3E10": "rán", + "3E11": "cuàn", + "3E12": "yín", + "3E13": "mì", + "3E15": "jué", + "3E16": "qū", + "3E17": "tóng", + "3E18": "wàn", + "3E19": "zhē", + "3E1A": "lǐ", + "3E1B": "sháo", + "3E1C": "kòng", + "3E1D": "xiān", + "3E1E": "zhé", + "3E1F": "zhī", + "3E20": "tiǎo", + "3E21": "shū", + "3E22": "bèi", + "3E23": "yè", + "3E24": "piàn", + "3E25": "chàn", + "3E26": "hù", + "3E27": "kèn", + "3E28": "jiū", + "3E29": "ān", + "3E2A": "chún", + "3E2B": "qián", + "3E2C": "bèi", + "3E2D": "bā", + "3E2E": "fén", + "3E2F": "kē", + "3E30": "tuó", + "3E31": "tuó", + "3E32": "zuó", + "3E33": "líng", + "3E35": "guǐ", + "3E36": "yān", + "3E37": "shì", + "3E38": "hǒu", + "3E39": "liè", + "3E3A": "shā", + "3E3B": "sì", + "3E3D": "bèi", + "3E3E": "rèn", + "3E3F": "dú", + "3E40": "bó", + "3E41": "liáng", + "3E42": "qiǎn", + "3E43": "fèi", + "3E44": "jì", + "3E45": "zǒng", + "3E46": "huī", + "3E47": "hé", + "3E48": "lí", + "3E49": "yuán", + "3E4A": "yuè", + "3E4B": "xiū", + "3E4C": "chǎn", + "3E4D": "dí", + "3E4E": "léi", + "3E4F": "jǐn", + "3E50": "chóng", + "3E51": "sì", + "3E52": "pǔ", + "3E53": "yǎo", + "3E54": "jiāng", + "3E55": "huān", + "3E56": "huàn", + "3E57": "tāo", + "3E58": "rù", + "3E59": "wěng", + "3E5A": "yíng", + "3E5B": "ráo", + "3E5C": "yín", + "3E5D": "shì", + "3E5E": "yín", + "3E5F": "jué", + "3E60": "tún", + "3E61": "xuán", + "3E62": "jiā", + "3E63": "zhōng", + "3E64": "qiè", + "3E65": "zhù", + "3E66": "diāo", + "3E68": "yòu", + "3E6B": "yí", + "3E6C": "shǐ", + "3E6D": "yì", + "3E6E": "mò", + "3E71": "què", + "3E72": "xiāo", + "3E73": "wú", + "3E74": "gēng", + "3E75": "yǐng", + "3E76": "tíng", + "3E77": "shǐ", + "3E78": "ní", + "3E79": "gēng", + "3E7A": "tà", + "3E7B": "wō", + "3E7C": "jú", + "3E7D": "chǎn", + "3E7E": "piǎo", + "3E7F": "zhuó", + "3E80": "hū", + "3E81": "nǎo", + "3E82": "yán", + "3E83": "gǒu", + "3E84": "yǔ", + "3E85": "hóu", + "3E87": "sī", + "3E88": "chī", + "3E89": "hù", + "3E8A": "yàng", + "3E8B": "wēng", + "3E8C": "xiàn", + "3E8D": "pín", + "3E8E": "róng", + "3E8F": "lóu", + "3E90": "lǎo", + "3E91": "shān", + "3E92": "xiāo", + "3E93": "zé", + "3E94": "hài", + "3E95": "fán", + "3E96": "hǎn", + "3E97": "chān", + "3E98": "zhàn", + "3E9A": "tǎ", + "3E9B": "zhù", + "3E9C": "nóng", + "3E9D": "hàn", + "3E9E": "yú", + "3E9F": "zhuó", + "3EA0": "yòu", + "3EA1": "lì", + "3EA2": "huò", + "3EA3": "xī", + "3EA4": "xiān", + "3EA5": "chán", + "3EA6": "lián", + "3EA8": "sī", + "3EA9": "jiù", + "3EAA": "pú", + "3EAB": "qiú", + "3EAC": "gǒng", + "3EAD": "zǐ", + "3EAE": "yú", + "3EB1": "réng", + "3EB2": "niǔ", + "3EB3": "méi", + "3EB4": "bā", + "3EB5": "jiú", + "3EB7": "xù", + "3EB8": "píng", + "3EB9": "biàn", + "3EBA": "mào", + "3EBF": "yí", + "3EC0": "yú", + "3EC2": "píng", + "3EC3": "qū", + "3EC4": "bǎo", + "3EC5": "huì", + "3EC9": "bù", + "3ECA": "máng", + "3ECB": "là", + "3ECC": "tú", + "3ECD": "wú", + "3ECE": "lì", + "3ECF": "líng", + "3ED1": "jì", + "3ED2": "jùn", + "3ED3": "zōu", + "3ED4": "duǒ", + "3ED5": "jué", + "3ED6": "dài", + "3ED7": "bèi", + "3EDD": "là", + "3EDE": "bīn", + "3EDF": "suí", + "3EE0": "tú", + "3EE1": "xuē", + "3EE7": "duò", + "3EEA": "suì", + "3EEB": "bì", + "3EEC": "tū", + "3EED": "sè", + "3EEE": "càn", + "3EEF": "tú", + "3EF0": "miǎn", + "3EF1": "jīn", + "3EF2": "lǚ", + "3EF5": "zhàn", + "3EF6": "bǐ", + "3EF7": "jí", + "3EF8": "zēn", + "3EF9": "xuān", + "3EFA": "lì", + "3EFD": "suì", + "3EFE": "yōng", + "3EFF": "shǔ", + "3F02": "é", + "3F07": "qióng", + "3F08": "luó", + "3F09": "zhèn", + "3F0A": "tún", + "3F0B": "gū", + "3F0C": "yǔ", + "3F0D": "lěi", + "3F0E": "bó", + "3F0F": "něi", + "3F10": "pián", + "3F11": "liàn", + "3F12": "tǎng", + "3F13": "lián", + "3F14": "wēn", + "3F15": "dāng", + "3F16": "lì", + "3F17": "tíng", + "3F18": "wǎ", + "3F19": "zhòu", + "3F1A": "gāng", + "3F1B": "xíng", + "3F1C": "àng", + "3F1D": "fàn", + "3F1E": "pèng", + "3F1F": "bó", + "3F20": "tuó", + "3F21": "shū", + "3F22": "yí", + "3F23": "bó", + "3F24": "qiè", + "3F25": "tǒu", + "3F26": "gǒng", + "3F27": "tóng", + "3F28": "hán", + "3F29": "chéng", + "3F2A": "jié", + "3F2B": "huàn", + "3F2C": "xìng", + "3F2D": "diàn", + "3F2E": "chāi", + "3F2F": "dòng", + "3F30": "pí", + "3F31": "ruǎn", + "3F32": "liè", + "3F33": "shěng", + "3F34": "ǒu", + "3F35": "dì", + "3F36": "yú", + "3F37": "chuán", + "3F38": "róng", + "3F39": "kāng", + "3F3A": "táng", + "3F3B": "cóng", + "3F3C": "piáo", + "3F3D": "chuǎng", + "3F3E": "lù", + "3F3F": "tóng", + "3F40": "zhèng", + "3F41": "lì", + "3F42": "sà", + "3F43": "pān", + "3F44": "sī", + "3F46": "dāng", + "3F47": "hú", + "3F48": "yì", + "3F49": "xiàn", + "3F4A": "xiè", + "3F4B": "luó", + "3F4C": "liù", + "3F4E": "tán", + "3F4F": "gàn", + "3F51": "tán", + "3F55": "yóu", + "3F56": "nán", + "3F58": "gǎng", + "3F59": "jùn", + "3F5A": "chì", + "3F5B": "gōu", + "3F5C": "wǎn", + "3F5D": "lì", + "3F5E": "liú", + "3F5F": "liè", + "3F60": "xiá", + "3F61": "bēi", + "3F62": "ǎn", + "3F63": "yù", + "3F64": "jú", + "3F65": "róu", + "3F66": "xún", + "3F67": "zī", + "3F68": "cuó", + "3F69": "càn", + "3F6A": "zěng", + "3F6B": "yōng", + "3F6C": "fù", + "3F6D": "ruǎn", + "3F6F": "xí", + "3F70": "shù", + "3F71": "jiǎo", + "3F72": "jiǎo", + "3F73": "xū", + "3F74": "zhàng", + "3F77": "shuì", + "3F78": "chén", + "3F79": "fǎn", + "3F7A": "jí", + "3F7B": "zhī", + "3F7D": "gù", + "3F7E": "wù", + "3F80": "qiè", + "3F81": "shù", + "3F82": "hāi", + "3F83": "tuó", + "3F84": "dú", + "3F85": "zǐ", + "3F86": "rán", + "3F87": "mù", + "3F88": "fù", + "3F89": "líng", + "3F8A": "jí", + "3F8B": "xiū", + "3F8C": "xuǎn", + "3F8D": "nái", + "3F8E": "yā", + "3F8F": "jiè", + "3F90": "lì", + "3F91": "dá", + "3F92": "rú", + "3F93": "yuān", + "3F94": "lǚ", + "3F95": "shěn", + "3F96": "lǐ", + "3F97": "liàng", + "3F98": "gěng", + "3F99": "xìn", + "3F9A": "xiē", + "3F9B": "qǐn", + "3F9C": "qiè", + "3F9D": "chè", + "3F9E": "yóu", + "3F9F": "bù", + "3FA0": "kuáng", + "3FA1": "què", + "3FA2": "ài", + "3FA3": "qīn", + "3FA4": "qiāng", + "3FA5": "chù", + "3FA6": "pèi", + "3FA7": "kuò", + "3FA8": "yī", + "3FA9": "guāi", + "3FAA": "shěng", + "3FAB": "piān", + "3FAD": "zhòu", + "3FAE": "huáng", + "3FAF": "huī", + "3FB0": "hú", + "3FB1": "bèi", + "3FB4": "zhā", + "3FB5": "jì", + "3FB6": "gǔ", + "3FB7": "xī", + "3FB8": "gǎo", + "3FB9": "chái", + "3FBA": "mà", + "3FBB": "zhù", + "3FBC": "tuǐ", + "3FBD": "zhuì", + "3FBE": "xiān", + "3FBF": "láng", + "3FC3": "zhì", + "3FC4": "ài", + "3FC5": "xiǎn", + "3FC6": "guō", + "3FC7": "xí", + "3FC9": "tuǐ", + "3FCA": "cǎn", + "3FCB": "sào", + "3FCC": "xiān", + "3FCD": "jiè", + "3FCE": "fèn", + "3FCF": "qún", + "3FD1": "yào", + "3FD2": "dǎo", + "3FD3": "jiá", + "3FD4": "lěi", + "3FD5": "yán", + "3FD6": "lú", + "3FD7": "tuí", + "3FD8": "yíng", + "3FD9": "pì", + "3FDA": "luò", + "3FDB": "lì", + "3FDC": "biě", + "3FDE": "mào", + "3FDF": "bái", + "3FE0": "huàng", + "3FE2": "yào", + "3FE3": "hē", + "3FE4": "chǔn", + "3FE5": "hé", + "3FE6": "nìng", + "3FE7": "chóu", + "3FE8": "lì", + "3FE9": "tǎng", + "3FEA": "huán", + "3FEB": "bì", + "3FEC": "bā", + "3FED": "chè", + "3FEE": "yàng", + "3FEF": "dá", + "3FF0": "áo", + "3FF1": "xué", + "3FF3": "zī", + "3FF4": "dā", + "3FF5": "rǎn", + "3FF6": "bāng", + "3FF7": "cuó", + "3FF8": "wǎn", + "3FF9": "tà", + "3FFA": "báo", + "3FFB": "gān", + "3FFC": "yán", + "3FFD": "xī", + "3FFE": "zhù", + "3FFF": "yǎ", + "4000": "fàn", + "4001": "yòu", + "4002": "ān", + "4003": "tuí", + "4004": "méng", + "4005": "shè", + "4006": "jìn", + "4007": "gǔ", + "4008": "jì", + "4009": "qiáo", + "400A": "jiǎo", + "400B": "yán", + "400C": "xì", + "400D": "kàn", + "400E": "miǎn", + "400F": "xuàn", + "4010": "shān", + "4011": "wò", + "4012": "qiān", + "4013": "huàn", + "4014": "rèn", + "4015": "zhèn", + "4016": "tiān", + "4017": "jué", + "4018": "xié", + "4019": "qì", + "401A": "áng", + "401B": "mèi", + "401C": "gǔ", + "401E": "tāo", + "401F": "fán", + "4020": "jù", + "4021": "chàn", + "4022": "shùn", + "4023": "bì", + "4024": "mào", + "4025": "shuò", + "4026": "gǔ", + "4027": "hǒng", + "4028": "huà", + "4029": "luò", + "402A": "háng", + "402B": "jiá", + "402C": "quán", + "402D": "gāi", + "402E": "huāng", + "402F": "bǔ", + "4030": "gǔ", + "4031": "fēng", + "4032": "mù", + "4033": "ài", + "4034": "yǐng", + "4035": "shùn", + "4036": "liàng", + "4037": "jié", + "4038": "chì", + "4039": "jié", + "403A": "chōu", + "403B": "pìng", + "403C": "chēn", + "403D": "yán", + "403E": "dǔ", + "403F": "dì", + "4041": "liàng", + "4042": "xiàn", + "4043": "biāo", + "4044": "xìng", + "4045": "měng", + "4046": "yè", + "4047": "mì", + "4048": "qì", + "4049": "qì", + "404A": "wò", + "404B": "xiè", + "404C": "yù", + "404D": "qià", + "404E": "chéng", + "404F": "yǎo", + "4050": "yīng", + "4051": "yáng", + "4052": "jí", + "4053": "zōng", + "4054": "xuān", + "4055": "mín", + "4056": "lōu", + "4057": "kǎi", + "4058": "yǎo", + "4059": "yǎn", + "405A": "sǔn", + "405B": "guì", + "405C": "huàng", + "405D": "yíng", + "405E": "shěng", + "405F": "chá", + "4060": "lián", + "4062": "xuán", + "4063": "chuán", + "4064": "chè", + "4065": "nì", + "4066": "qù", + "4067": "miáo", + "4068": "huò", + "4069": "yú", + "406A": "zhǎn", + "406B": "hú", + "406C": "céng", + "406D": "biāo", + "406E": "qián", + "406F": "xī", + "4070": "jiǎng", + "4071": "kōu", + "4072": "mái", + "4073": "mǎng", + "4074": "zhǎn", + "4075": "biǎn", + "4076": "jī", + "4077": "jué", + "4078": "náng", + "4079": "bì", + "407A": "shì", + "407B": "shuò", + "407C": "mò", + "407D": "liè", + "407E": "miè", + "407F": "mò", + "4080": "xī", + "4081": "chán", + "4082": "qú", + "4083": "jiào", + "4084": "huò", + "4085": "xiān", + "4086": "xù", + "4087": "niǔ", + "4088": "tóng", + "4089": "hóu", + "408A": "yù", + "408C": "chōng", + "408D": "bó", + "408E": "zuǎn", + "408F": "diāo", + "4090": "zhuō", + "4091": "jī", + "4092": "qià", + "4094": "xìng", + "4095": "huì", + "4096": "shí", + "4097": "kū", + "4099": "duī", + "409A": "yáo", + "409B": "yú", + "409C": "bàng", + "409D": "jié", + "409E": "zhè", + "409F": "jiā", + "40A0": "shǐ", + "40A1": "dǐ", + "40A2": "dǒng", + "40A3": "cí", + "40A4": "fù", + "40A5": "mín", + "40A6": "zhēn", + "40A7": "zhěn", + "40A9": "yàn", + "40AA": "qiǎo", + "40AB": "hāng", + "40AC": "gǒng", + "40AD": "qiāo", + "40AE": "lüè", + "40AF": "guài", + "40B0": "là", + "40B1": "ruì", + "40B2": "fǎ", + "40B3": "cuǒ", + "40B4": "yán", + "40B5": "gōng", + "40B6": "jié", + "40B7": "guāi", + "40B8": "guó", + "40B9": "suǒ", + "40BA": "wǒ", + "40BB": "zhèng", + "40BC": "niè", + "40BD": "diào", + "40BE": "lǎi", + "40BF": "tà", + "40C0": "cuì", + "40C1": "yā", + "40C2": "gǔn", + "40C5": "dī", + "40C7": "mián", + "40C8": "jiē", + "40C9": "mín", + "40CA": "jǔ", + "40CB": "yú", + "40CC": "zhēn", + "40CD": "zhào", + "40CE": "zhà", + "40CF": "xīng", + "40D1": "bān", + "40D2": "hé", + "40D3": "gòu", + "40D4": "hóng", + "40D5": "láo", + "40D6": "wù", + "40D7": "bō", + "40D8": "kēng", + "40D9": "lù", + "40DA": "cù", + "40DB": "lián", + "40DC": "yī", + "40DD": "qiào", + "40DE": "shú", + "40E0": "xuàn", + "40E1": "jīn", + "40E2": "qīn", + "40E3": "huǐ", + "40E4": "sù", + "40E5": "chuáng", + "40E6": "dūn", + "40E7": "lóng", + "40E9": "náo", + "40EA": "tán", + "40EB": "dǎn", + "40EC": "wěi", + "40ED": "gǎn", + "40EE": "dá", + "40EF": "lì", + "40F0": "cā", + "40F1": "xiàn", + "40F2": "pán", + "40F3": "là", + "40F4": "zhū", + "40F5": "niǎo", + "40F6": "huái", + "40F7": "yíng", + "40F8": "xiàn", + "40F9": "làn", + "40FA": "mó", + "40FB": "bà", + "40FD": "guǐ", + "40FE": "bǐ", + "40FF": "fū", + "4100": "huò", + "4101": "yì", + "4102": "liù", + "4104": "yīn", + "4105": "juàn", + "4106": "huó", + "4107": "chéng", + "4108": "dòu", + "4109": "é", + "410B": "yǎn", + "410C": "zhuì", + "410D": "zhà", + "410E": "qǐ", + "410F": "yú", + "4110": "quàn", + "4111": "huó", + "4112": "niè", + "4113": "huáng", + "4114": "jǔ", + "4115": "shè", + "4118": "péng", + "4119": "míng", + "411A": "cáo", + "411B": "lóu", + "411C": "lí", + "411D": "chuāng", + "411F": "cuī", + "4120": "shàn", + "4121": "dān", + "4122": "qí", + "4124": "lài", + "4125": "líng", + "4126": "liǎo", + "4127": "réng", + "4128": "yú", + "4129": "yì", + "412A": "diǎo", + "412B": "qǐ", + "412C": "yí", + "412D": "nián", + "412E": "fū", + "412F": "jiǎn", + "4130": "yá", + "4131": "fāng", + "4132": "ruì", + "4133": "xiān", + "4136": "bì", + "4137": "shí", + "4138": "pò", + "4139": "nián", + "413A": "zhì", + "413B": "táo", + "413C": "tiǎn", + "413D": "tiǎn", + "413E": "rù", + "413F": "yì", + "4140": "liè", + "4141": "àn", + "4142": "hé", + "4143": "qióng", + "4144": "lì", + "4145": "guī", + "4146": "zì", + "4147": "sù", + "4148": "yuàn", + "4149": "yà", + "414A": "chá", + "414B": "wǎn", + "414C": "juān", + "414D": "tǐng", + "414E": "yǒu", + "414F": "huì", + "4150": "jiǎn", + "4151": "ruí", + "4152": "máng", + "4153": "jǔ", + "4154": "zī", + "4155": "jū", + "4156": "ān", + "4157": "suì", + "4158": "lái", + "4159": "hùn", + "415A": "quǎn", + "415B": "chāng", + "415C": "duò", + "415D": "kōng", + "415E": "nè", + "415F": "cǎn", + "4160": "tí", + "4161": "xǔ", + "4162": "jiù", + "4163": "huáng", + "4164": "qì", + "4165": "jié", + "4166": "máo", + "4167": "yān", + "4169": "zhǐ", + "416A": "tuí", + "416C": "ài", + "416D": "páng", + "416E": "càng", + "416F": "táng", + "4170": "ěn", + "4171": "hùn", + "4172": "qí", + "4173": "chú", + "4174": "suǒ", + "4175": "zhuó", + "4176": "nòu", + "4177": "tú", + "4178": "shēn", + "4179": "lǒu", + "417A": "biāo", + "417B": "lí", + "417C": "mán", + "417D": "xīn", + "417E": "cén", + "417F": "huáng", + "4180": "měi", + "4181": "gāo", + "4182": "lián", + "4183": "dào", + "4184": "zhǎn", + "4185": "zī", + "4188": "zhì", + "4189": "bà", + "418A": "cuì", + "418B": "qiū", + "418D": "lóng", + "418E": "xiān", + "418F": "fèi", + "4190": "guó", + "4191": "chéng", + "4192": "jiù", + "4193": "è", + "4194": "chōng", + "4195": "yuè", + "4196": "hóng", + "4197": "yǎo", + "4198": "yā", + "4199": "yáo", + "419A": "tóng", + "419B": "zhà", + "419C": "yòu", + "419D": "xuè", + "419E": "yǎo", + "419F": "kè", + "41A0": "huàn", + "41A1": "láng", + "41A2": "yuè", + "41A3": "chén", + "41A6": "shèn", + "41A8": "níng", + "41A9": "míng", + "41AA": "hōng", + "41AB": "chuāng", + "41AC": "yǔn", + "41AD": "xuān", + "41AE": "jìn", + "41AF": "zhuó", + "41B0": "yū", + "41B1": "tān", + "41B2": "kāng", + "41B3": "qióng", + "41B5": "chéng", + "41B6": "jiū", + "41B7": "xuè", + "41B8": "zhēng", + "41B9": "chōng", + "41BA": "pān", + "41BB": "qiào", + "41BD": "qú", + "41BE": "lán", + "41BF": "yì", + "41C0": "róng", + "41C1": "sī", + "41C2": "qiān", + "41C3": "sì", + "41C5": "fá", + "41C7": "méng", + "41C8": "huà", + "41CB": "hài", + "41CC": "qiào", + "41CD": "chù", + "41CE": "què", + "41CF": "duì", + "41D0": "lì", + "41D1": "bà", + "41D2": "jiè", + "41D3": "xū", + "41D4": "luò", + "41D6": "yǔn", + "41D7": "zhōng", + "41D8": "hù", + "41D9": "yǐn", + "41DB": "zhǐ", + "41DC": "qiǎn", + "41DE": "gān", + "41DF": "jiàn", + "41E0": "zhù", + "41E1": "zhù", + "41E2": "kǔ", + "41E3": "niè", + "41E4": "ruì", + "41E5": "zé", + "41E6": "ǎng", + "41E7": "zhì", + "41E8": "gòng", + "41E9": "yì", + "41EA": "chī", + "41EB": "jī", + "41EC": "zhū", + "41ED": "lǎo", + "41EE": "rèn", + "41EF": "róng", + "41F0": "zhēng", + "41F1": "nà", + "41F2": "cè", + "41F5": "yí", + "41F6": "jué", + "41F7": "bié", + "41F8": "chéng", + "41F9": "jùn", + "41FA": "dòu", + "41FB": "wěi", + "41FC": "yì", + "41FD": "zhé", + "41FE": "yán", + "4200": "sān", + "4201": "lún", + "4202": "píng", + "4203": "zhǎo", + "4204": "hán", + "4205": "yù", + "4206": "dài", + "4207": "zhào", + "4208": "féi", + "4209": "shà", + "420A": "líng", + "420B": "tà", + "420C": "qū", + "420D": "máng", + "420E": "yè", + "420F": "báo", + "4210": "guì", + "4211": "guǎ", + "4212": "nǎn", + "4213": "gé", + "4215": "shí", + "4216": "kē", + "4217": "suǒ", + "4218": "cí", + "4219": "zhòu", + "421A": "tái", + "421B": "kuài", + "421C": "qìn", + "421D": "xū", + "421E": "dǔ", + "421F": "cè", + "4220": "huǎn", + "4221": "cōng", + "4222": "sǎi", + "4223": "zhèng", + "4224": "qián", + "4225": "jīn", + "4226": "zōng", + "4227": "wěi", + "422A": "xì", + "422B": "nà", + "422C": "pú", + "422D": "sōu", + "422E": "jù", + "422F": "zhēn", + "4230": "shāo", + "4231": "tāo", + "4232": "bān", + "4233": "tà", + "4234": "qiàn", + "4235": "wēng", + "4236": "róng", + "4237": "luò", + "4238": "hú", + "4239": "sǒu", + "423A": "zhōng", + "423B": "pú", + "423C": "miè", + "423D": "jīn", + "423E": "shāo", + "423F": "mì", + "4240": "shù", + "4241": "líng", + "4242": "lěi", + "4243": "jiǎng", + "4244": "léng", + "4245": "zhì", + "4246": "diǎo", + "4248": "sǎn", + "4249": "gū", + "424A": "fàn", + "424B": "mèi", + "424C": "suì", + "424D": "jiǎn", + "424E": "táng", + "424F": "xiè", + "4250": "kū", + "4251": "wú", + "4252": "fán", + "4253": "luò", + "4254": "cān", + "4255": "céng", + "4256": "líng", + "4257": "yī", + "4258": "cóng", + "4259": "yún", + "425A": "méng", + "425B": "yù", + "425C": "zhì", + "425D": "yǐ", + "425E": "dǎn", + "425F": "huò", + "4260": "wéi", + "4261": "tán", + "4262": "sè", + "4263": "xiè", + "4264": "sǒu", + "4265": "sǒng", + "4266": "qiān", + "4267": "liú", + "4268": "yì", + "426A": "lèi", + "426B": "lí", + "426C": "fèi", + "426D": "liè", + "426E": "lìn", + "426F": "xiàn", + "4270": "xiào", + "4271": "ōu", + "4272": "mí", + "4273": "xiān", + "4274": "ráng", + "4275": "zhuàn", + "4276": "shuāng", + "4277": "yán", + "4278": "biàn", + "4279": "líng", + "427A": "hóng", + "427B": "qí", + "427C": "liào", + "427D": "bǎn", + "427E": "bì", + "427F": "hú", + "4280": "hú", + "4282": "cè", + "4283": "pèi", + "4284": "qióng", + "4285": "míng", + "4286": "jiù", + "4287": "bù", + "4288": "méi", + "4289": "sǎn", + "428A": "wèi", + "428D": "lí", + "428E": "quǎn", + "4290": "hún", + "4291": "xiǎng", + "4293": "shì", + "4294": "yíng", + "4296": "nǎn", + "4297": "huáng", + "4298": "jiù", + "4299": "yān", + "429B": "sà", + "429C": "tuán", + "429D": "xiè", + "429E": "zhé", + "429F": "mén", + "42A0": "xì", + "42A1": "mán", + "42A3": "huáng", + "42A4": "tán", + "42A5": "xiào", + "42A6": "yè", + "42A7": "bì", + "42A8": "luó", + "42A9": "fán", + "42AA": "lì", + "42AB": "cuǐ", + "42AC": "chuā", + "42AD": "dào", + "42AE": "dí", + "42AF": "kuàng", + "42B0": "chú", + "42B1": "xiān", + "42B2": "chàn", + "42B3": "mí", + "42B4": "qiàn", + "42B5": "qiú", + "42B6": "zhèn", + "42BA": "hù", + "42BB": "gān", + "42BC": "chǐ", + "42BD": "guài", + "42BE": "mù", + "42BF": "bó", + "42C0": "huà", + "42C1": "gěng", + "42C2": "yáo", + "42C3": "mào", + "42C4": "wǎng", + "42C8": "rú", + "42C9": "xué", + "42CA": "zhēng", + "42CB": "mín", + "42CC": "jiǎng", + "42CE": "zhàn", + "42CF": "zuó", + "42D0": "yuè", + "42D1": "liè", + "42D3": "zhòu", + "42D4": "bì", + "42D5": "rèn", + "42D6": "yù", + "42D8": "chuò", + "42D9": "ěr", + "42DA": "yì", + "42DB": "mǐ", + "42DC": "qìng", + "42DE": "wǎng", + "42DF": "jì", + "42E0": "bǔ", + "42E2": "biē", + "42E3": "fán", + "42E4": "yuè", + "42E5": "lí", + "42E6": "fán", + "42E7": "qú", + "42E8": "fǔ", + "42E9": "ér", + "42EA": "ē", + "42EB": "zhēng", + "42EC": "tiān", + "42ED": "yù", + "42EE": "jìn", + "42EF": "qǐ", + "42F0": "jú", + "42F1": "lái", + "42F2": "chě", + "42F3": "běi", + "42F4": "niù", + "42F5": "yì", + "42F6": "xǔ", + "42F7": "móu", + "42F8": "xún", + "42F9": "fú", + "42FB": "nín", + "42FC": "tīng", + "42FD": "běng", + "42FE": "zhǎ", + "42FF": "wēi", + "4300": "kē", + "4301": "yāo", + "4302": "òu", + "4303": "xiāo", + "4304": "gěng", + "4305": "táng", + "4306": "guì", + "4307": "huì", + "4308": "tā", + "430A": "yáo", + "430B": "dā", + "430C": "qì", + "430D": "jǐn", + "430E": "lüè", + "430F": "mì", + "4310": "mì", + "4311": "jiān", + "4312": "lù", + "4313": "fán", + "4314": "ōu", + "4315": "mí", + "4316": "jié", + "4317": "fǔ", + "4318": "biè", + "4319": "huàng", + "431A": "sū", + "431B": "yáo", + "431C": "niè", + "431D": "jīn", + "431E": "liǎn", + "431F": "bó", + "4320": "jiān", + "4321": "tǐ", + "4322": "líng", + "4323": "zuǎn", + "4324": "shī", + "4325": "yǐn", + "4326": "dào", + "4327": "chóu", + "4328": "cā", + "4329": "miè", + "432A": "yǎn", + "432B": "lǎn", + "432C": "chóng", + "432D": "jiāo", + "432E": "shuāng", + "432F": "quān", + "4330": "niè", + "4331": "luò", + "4333": "shī", + "4334": "luò", + "4335": "zhú", + "4337": "chōu", + "4338": "juàn", + "4339": "jiǒng", + "433A": "ěr", + "433B": "yì", + "433C": "ruì", + "433D": "cǎi", + "433E": "rén", + "433F": "fú", + "4340": "lán", + "4341": "suì", + "4342": "yú", + "4343": "yóu", + "4344": "diǎn", + "4345": "líng", + "4346": "zhù", + "4347": "tà", + "4348": "píng", + "4349": "zhǎi", + "434A": "jiāo", + "434B": "chuí", + "434C": "bù", + "434D": "kòu", + "434E": "cùn", + "4350": "hǎn", + "4351": "hǎn", + "4352": "mǒu", + "4353": "hù", + "4354": "gōng", + "4355": "dī", + "4356": "fú", + "4357": "xuàn", + "4358": "mí", + "4359": "méi", + "435A": "làng", + "435B": "gù", + "435C": "zhào", + "435D": "tà", + "435E": "yù", + "435F": "zòng", + "4360": "lí", + "4361": "lù", + "4362": "wú", + "4363": "léi", + "4364": "jǐ", + "4365": "lì", + "4366": "lí", + "4368": "pō", + "4369": "yǎng", + "436A": "wà", + "436B": "tuó", + "436C": "pēng", + "436E": "zhào", + "436F": "guǐ", + "4371": "xú", + "4372": "nái", + "4373": "què", + "4374": "wěi", + "4375": "zhēng", + "4376": "dōng", + "4377": "wěi", + "4378": "bó", + "437A": "huàn", + "437B": "xuàn", + "437C": "zān", + "437D": "lì", + "437E": "yǎn", + "437F": "huáng", + "4380": "xuè", + "4381": "hú", + "4382": "bǎo", + "4383": "rǎn", + "4384": "xiāo", + "4385": "pò", + "4386": "liào", + "4387": "zhōu", + "4388": "yì", + "4389": "xù", + "438A": "luò", + "438B": "kào", + "438C": "chù", + "438E": "nà", + "438F": "hán", + "4390": "chǎo", + "4391": "lù", + "4392": "zhǎn", + "4393": "tà", + "4394": "fū", + "4395": "hōng", + "4396": "zēng", + "4397": "qiáo", + "4398": "sù", + "4399": "pīn", + "439A": "guàn", + "439C": "hūn", + "439D": "chú", + "439F": "ér", + "43A0": "ér", + "43A1": "ruǎn", + "43A2": "qǐ", + "43A3": "sì", + "43A4": "jú", + "43A6": "yǎn", + "43A7": "bàng", + "43A8": "yè", + "43A9": "zī", + "43AA": "nè", + "43AB": "chuàng", + "43AC": "bà", + "43AD": "cāo", + "43AE": "tì", + "43AF": "hàn", + "43B0": "zuó", + "43B1": "bà", + "43B2": "zhé", + "43B3": "wà", + "43B4": "gēng", + "43B5": "bì", + "43B6": "èr", + "43B7": "zhù", + "43B8": "wù", + "43B9": "wén", + "43BA": "zhì", + "43BB": "zhòu", + "43BC": "lù", + "43BD": "wén", + "43BE": "gǔn", + "43BF": "qiú", + "43C0": "là", + "43C1": "zǎi", + "43C2": "sǒu", + "43C3": "mián", + "43C4": "dǐ", + "43C5": "qì", + "43C6": "cáo", + "43C7": "piào", + "43C8": "lián", + "43C9": "shī", + "43CA": "lóng", + "43CB": "sù", + "43CC": "qì", + "43CD": "yuàn", + "43CE": "féng", + "43CF": "xū", + "43D0": "jué", + "43D1": "dì", + "43D2": "piàn", + "43D3": "guǎn", + "43D4": "niǔ", + "43D5": "rèn", + "43D6": "zhèn", + "43D7": "gài", + "43D8": "pì", + "43D9": "tǎn", + "43DA": "chǎo", + "43DB": "chǔn", + "43DC": "hē", + "43DD": "zhuān", + "43DE": "mò", + "43DF": "bié", + "43E0": "qì", + "43E1": "shì", + "43E2": "bǐ", + "43E3": "jué", + "43E4": "sì", + "43E6": "guā", + "43E7": "nà", + "43E8": "huǐ", + "43E9": "xī", + "43EA": "èr", + "43EB": "xiū", + "43EC": "móu", + "43EE": "xí", + "43EF": "zhì", + "43F0": "rùn", + "43F1": "jú", + "43F2": "dié", + "43F3": "zhè", + "43F4": "shào", + "43F5": "měng", + "43F6": "bì", + "43F7": "hàn", + "43F8": "yú", + "43F9": "xiàn", + "43FA": "pāng", + "43FB": "néng", + "43FC": "cán", + "43FD": "bù", + "43FF": "qǐ", + "4400": "jì", + "4401": "zhuó", + "4402": "lù", + "4403": "jùn", + "4404": "xiàn", + "4405": "xī", + "4406": "cǎi", + "4407": "wěn", + "4408": "zhí", + "4409": "zì", + "440A": "kūn", + "440B": "cōng", + "440C": "tiǎn", + "440D": "chù", + "440E": "dī", + "440F": "chǔn", + "4410": "qiū", + "4411": "zhé", + "4412": "zhā", + "4413": "róu", + "4414": "bǐn", + "4415": "jí", + "4416": "xī", + "4417": "zhū", + "4418": "jué", + "4419": "gé", + "441A": "jī", + "441B": "dā", + "441C": "chēn", + "441D": "suò", + "441E": "ruò", + "441F": "xiǎng", + "4420": "huǎng", + "4421": "qí", + "4422": "zhù", + "4423": "sǔn", + "4424": "chāi", + "4425": "wěng", + "4426": "kē", + "4427": "kào", + "4428": "gǔ", + "4429": "gāi", + "442A": "fàn", + "442B": "cōng", + "442C": "cáo", + "442D": "zhì", + "442E": "chǎn", + "442F": "léi", + "4430": "xiū", + "4431": "zhài", + "4432": "zhé", + "4433": "yú", + "4434": "guì", + "4435": "gōng", + "4436": "zān", + "4437": "dān", + "4438": "huò", + "4439": "sōu", + "443A": "tàn", + "443B": "gū", + "443C": "xì", + "443D": "mán", + "443E": "duó", + "443F": "ào", + "4440": "pì", + "4441": "wù", + "4442": "ǎi", + "4443": "méng", + "4444": "pì", + "4445": "méng", + "4446": "yǎng", + "4447": "zhì", + "4448": "bó", + "4449": "yíng", + "444A": "wéi", + "444B": "rǎng", + "444C": "lán", + "444D": "yān", + "444E": "chǎn", + "444F": "quán", + "4450": "zhěn", + "4451": "pú", + "4453": "tái", + "4454": "fèi", + "4455": "shǔ", + "4457": "dàng", + "4458": "cuó", + "4459": "tān", + "445A": "tián", + "445B": "chǐ", + "445C": "tà", + "445D": "jiǎ", + "445E": "shùn", + "445F": "huáng", + "4460": "liǎo", + "4463": "chēn", + "4464": "jìn", + "4465": "è", + "4466": "gōu", + "4467": "fú", + "4468": "duò", + "446A": "è", + "446B": "bēng", + "446C": "tāo", + "446D": "dì", + "446F": "dì", + "4470": "bù", + "4471": "wǎn", + "4472": "zhào", + "4473": "lún", + "4474": "qí", + "4475": "mù", + "4476": "qiàn", + "4478": "zōng", + "4479": "sōu", + "447B": "yóu", + "447C": "zhōu", + "447D": "tà", + "447F": "sù", + "4480": "bù", + "4481": "xí", + "4482": "jiǎng", + "4483": "cào", + "4484": "fù", + "4485": "téng", + "4486": "chè", + "4487": "fù", + "4488": "fèi", + "4489": "wǔ", + "448A": "xī", + "448B": "yǎng", + "448C": "mìng", + "448D": "pǎng", + "448E": "mǎng", + "448F": "sēng", + "4490": "méng", + "4491": "cǎo", + "4492": "tiáo", + "4493": "kǎi", + "4494": "bài", + "4495": "xiǎo", + "4496": "xìn", + "4497": "qì", + "449A": "shǎo", + "449B": "huàn", + "449C": "niú", + "449D": "xiáo", + "449E": "chén", + "449F": "dān", + "44A0": "fēng", + "44A1": "yǐn", + "44A2": "áng", + "44A3": "rǎn", + "44A4": "rì", + "44A5": "mán", + "44A6": "fàn", + "44A7": "qū", + "44A8": "shǐ", + "44A9": "hé", + "44AA": "biàn", + "44AB": "dài", + "44AC": "mò", + "44AD": "děng", + "44B0": "kuāng", + "44B2": "chà", + "44B3": "duǒ", + "44B4": "yǒu", + "44B5": "hào", + "44B7": "guā", + "44B8": "xuè", + "44B9": "lèi", + "44BA": "jǐn", + "44BB": "qǐ", + "44BC": "qū", + "44BD": "wǎng", + "44BE": "yī", + "44BF": "liáo", + "44C2": "yán", + "44C3": "yì", + "44C4": "yín", + "44C5": "qí", + "44C6": "zhé", + "44C7": "xì", + "44C8": "yì", + "44C9": "yé", + "44CA": "wú", + "44CB": "zhī", + "44CC": "zhì", + "44CD": "hǎn", + "44CE": "chuò", + "44CF": "fū", + "44D0": "chún", + "44D1": "píng", + "44D2": "kuǎi", + "44D3": "chóu", + "44D5": "tuǒ", + "44D6": "qióng", + "44D7": "cōng", + "44D8": "gāo", + "44D9": "kuā", + "44DA": "qū", + "44DB": "qū", + "44DC": "zhī", + "44DD": "mèng", + "44DE": "lì", + "44DF": "zhōu", + "44E0": "tà", + "44E1": "zhī", + "44E2": "gù", + "44E3": "liǎng", + "44E4": "hū", + "44E5": "là", + "44E6": "diǎn", + "44E7": "cì", + "44E8": "yīng", + "44EB": "qí", + "44ED": "chà", + "44EE": "mào", + "44EF": "dú", + "44F0": "yīn", + "44F1": "chái", + "44F2": "ruì", + "44F3": "hěn", + "44F4": "ruǎn", + "44F5": "fū", + "44F6": "lài", + "44F7": "xìng", + "44F8": "jiān", + "44F9": "yì", + "44FA": "měi", + "44FC": "máng", + "44FD": "jì", + "44FE": "suō", + "44FF": "hàn", + "4501": "lì", + "4502": "zǐ", + "4503": "zǔ", + "4504": "yáo", + "4505": "gē", + "4506": "lí", + "4507": "qǐ", + "4508": "gòng", + "4509": "lì", + "450A": "bīng", + "450B": "suō", + "450E": "sù", + "450F": "chòu", + "4510": "jiān", + "4511": "xié", + "4512": "bèi", + "4513": "xǔ", + "4514": "jìng", + "4515": "pú", + "4516": "líng", + "4517": "xiáng", + "4518": "zuò", + "4519": "diào", + "451A": "chún", + "451B": "qǐng", + "451C": "nán", + "451D": "zhāi", + "451E": "lǜ", + "451F": "yí", + "4520": "shǎo", + "4521": "yú", + "4522": "huá", + "4523": "lí", + "4524": "pā", + "4527": "lí", + "452A": "shuǎng", + "452C": "yì", + "452D": "nìng", + "452E": "sī", + "452F": "kù", + "4530": "fù", + "4531": "yī", + "4532": "dēng", + "4533": "rán", + "4534": "cè", + "4536": "tí", + "4537": "qín", + "4538": "biǎo", + "4539": "suì", + "453A": "wéi", + "453B": "dūn", + "453C": "sè", + "453D": "ài", + "453E": "qì", + "453F": "zǔn", + "4540": "kuǎn", + "4541": "fěi", + "4543": "yìn", + "4545": "sǎo", + "4546": "dòu", + "4547": "huì", + "4548": "xiè", + "4549": "zé", + "454A": "tán", + "454B": "táng", + "454C": "zhì", + "454D": "yì", + "454E": "fú", + "454F": "é", + "4551": "jùn", + "4552": "jiā", + "4553": "chá", + "4554": "xián", + "4555": "màn", + "4557": "bì", + "4558": "líng", + "4559": "jié", + "455A": "kuì", + "455B": "jiá", + "455D": "chēng", + "455E": "làng", + "455F": "xīng", + "4560": "fèi", + "4561": "lǘ", + "4562": "zhǎ", + "4563": "hé", + "4564": "jī", + "4565": "nǐ", + "4566": "yíng", + "4567": "xiào", + "4568": "téng", + "4569": "lǎo", + "456A": "zé", + "456B": "kuí", + "456D": "qián", + "456E": "jú", + "456F": "piáo", + "4570": "fán", + "4571": "tóu", + "4572": "lǐn", + "4573": "mí", + "4574": "zhuó", + "4575": "xié", + "4576": "hù", + "4577": "mí", + "4578": "jiē", + "4579": "zá", + "457A": "cóng", + "457B": "lì", + "457C": "rán", + "457D": "zhú", + "457E": "yín", + "457F": "hàn", + "4581": "yì", + "4582": "luán", + "4583": "yuè", + "4584": "rán", + "4585": "líng", + "4586": "niàng", + "4587": "yù", + "4588": "nüè", + "458A": "yì", + "458B": "nüè", + "458C": "yì", + "458D": "qián", + "458E": "xiá", + "458F": "chǔ", + "4590": "yín", + "4591": "mì", + "4592": "xī", + "4593": "nà", + "4594": "kǎn", + "4595": "zǔ", + "4596": "xiá", + "4597": "yán", + "4598": "tú", + "4599": "tī", + "459A": "wū", + "459B": "suǒ", + "459C": "yín", + "459D": "chóng", + "459E": "zhǒu", + "459F": "mǎng", + "45A0": "yuán", + "45A1": "nǜ", + "45A2": "miáo", + "45A3": "zǎo", + "45A4": "wǎn", + "45A5": "lí", + "45A6": "qū", + "45A7": "nà", + "45A8": "shí", + "45A9": "bì", + "45AA": "zī", + "45AB": "bàng", + "45AD": "juàn", + "45AE": "xiǎng", + "45AF": "kuí", + "45B0": "pài", + "45B1": "kuāng", + "45B2": "xún", + "45B3": "zhà", + "45B4": "yáo", + "45B5": "kūn", + "45B6": "huī", + "45B7": "xī", + "45B8": "é", + "45B9": "yáng", + "45BA": "tiáo", + "45BB": "yóu", + "45BC": "jué", + "45BD": "lí", + "45BF": "lí", + "45C0": "chēng", + "45C1": "jì", + "45C2": "hǔ", + "45C3": "zhàn", + "45C4": "fǔ", + "45C5": "cháng", + "45C6": "guǎn", + "45C7": "jú", + "45C8": "méng", + "45C9": "chāng", + "45CA": "tàn", + "45CB": "móu", + "45CC": "xīng", + "45CD": "lǐ", + "45CE": "yān", + "45CF": "sōu", + "45D0": "shī", + "45D1": "yì", + "45D2": "bìng", + "45D3": "cōng", + "45D4": "hóu", + "45D5": "wǎn", + "45D6": "dì", + "45D7": "jī", + "45D8": "gé", + "45D9": "hán", + "45DA": "bó", + "45DB": "xiū", + "45DC": "liú", + "45DD": "cán", + "45DE": "cán", + "45DF": "yì", + "45E0": "xuán", + "45E1": "yán", + "45E2": "zǎo", + "45E3": "hàn", + "45E4": "yóng", + "45E5": "zōng", + "45E7": "kāng", + "45E8": "yú", + "45E9": "qī", + "45EA": "zhè", + "45EB": "má", + "45EE": "shuǎng", + "45EF": "jìn", + "45F0": "guàn", + "45F1": "pú", + "45F2": "lìn", + "45F4": "tíng", + "45F5": "jiāng", + "45F6": "là", + "45F7": "yì", + "45F8": "yōng", + "45F9": "cì", + "45FA": "yǎn", + "45FB": "jié", + "45FC": "xūn", + "45FD": "wèi", + "45FE": "xiǎn", + "45FF": "níng", + "4600": "fù", + "4601": "gé", + "4603": "mò", + "4604": "zhù", + "4605": "nái", + "4606": "xiǎn", + "4607": "wén", + "4608": "lì", + "4609": "cán", + "460A": "miè", + "460B": "jiān", + "460C": "nì", + "460D": "chài", + "460E": "wān", + "460F": "xù", + "4610": "nǜ", + "4611": "mài", + "4612": "zuī", + "4613": "kàn", + "4614": "kā", + "4615": "háng", + "4618": "yù", + "4619": "wèi", + "461A": "zhú", + "461D": "yì", + "461F": "diāo", + "4620": "fú", + "4621": "bǐ", + "4622": "zhǔ", + "4623": "zǐ", + "4624": "shù", + "4625": "xiá", + "4626": "ní", + "4628": "jiǎo", + "4629": "xún", + "462A": "chōng", + "462B": "nòu", + "462C": "róng", + "462D": "zhì", + "462E": "sāng", + "4630": "shān", + "4631": "yù", + "4633": "jīn", + "4635": "lù", + "4636": "hān", + "4637": "biē", + "4638": "yì", + "4639": "zuì", + "463A": "zhàn", + "463B": "yù", + "463C": "wǎn", + "463D": "ní", + "463E": "guǎn", + "463F": "jué", + "4640": "běng", + "4641": "cán", + "4643": "duò", + "4644": "qì", + "4645": "yāo", + "4646": "kuì", + "4647": "ruán", + "4648": "hóu", + "4649": "xún", + "464A": "xiè", + "464C": "kuì", + "464E": "xié", + "464F": "bó", + "4650": "kè", + "4651": "cuī", + "4652": "xù", + "4653": "bǎi", + "4654": "ōu", + "4655": "zǒng", + "4657": "tì", + "4658": "chǔ", + "4659": "chí", + "465A": "niǎo", + "465B": "guàn", + "465C": "féng", + "465D": "xiè", + "465E": "dēng", + "465F": "wéi", + "4660": "jué", + "4661": "kuì", + "4662": "zèng", + "4663": "sà", + "4664": "duǒ", + "4665": "líng", + "4666": "méng", + "4668": "guǒ", + "4669": "méng", + "466A": "lóng", + "466C": "yìng", + "466E": "guàn", + "466F": "cù", + "4670": "lí", + "4671": "dú", + "4673": "biāo", + "4675": "xī", + "4677": "dé", + "4678": "dé", + "4679": "xiàn", + "467A": "lián", + "467C": "shào", + "467D": "xié", + "467E": "shī", + "467F": "wèi", + "4682": "hè", + "4683": "yóu", + "4684": "lù", + "4685": "lài", + "4686": "yǐng", + "4687": "shěng", + "4688": "juàn", + "4689": "qì", + "468A": "jiǎn", + "468B": "yùn", + "468D": "qì", + "468F": "lìn", + "4690": "jí", + "4691": "mái", + "4692": "chuáng", + "4693": "niǎn", + "4694": "bīn", + "4695": "lì", + "4696": "líng", + "4697": "gāng", + "4698": "chéng", + "4699": "xuān", + "469A": "xiǎn", + "469B": "hú", + "469C": "bī", + "469D": "zú", + "469E": "dǎi", + "469F": "dǎi", + "46A0": "hùn", + "46A1": "sāi", + "46A2": "chè", + "46A3": "tí", + "46A5": "nuò", + "46A6": "zhì", + "46A7": "liú", + "46A8": "fèi", + "46A9": "jiǎo", + "46AA": "guān", + "46AB": "xí", + "46AC": "lín", + "46AD": "xuān", + "46AE": "réng", + "46AF": "tǎo", + "46B0": "pǐ", + "46B1": "xìn", + "46B2": "shàn", + "46B3": "zhì", + "46B4": "wà", + "46B5": "tǒu", + "46B6": "tiān", + "46B7": "yī", + "46B8": "xiè", + "46B9": "pǐ", + "46BA": "yáo", + "46BB": "yáo", + "46BC": "nǜ", + "46BD": "hào", + "46BE": "nín", + "46BF": "yìn", + "46C0": "fǎn", + "46C1": "nán", + "46C2": "yāo", + "46C3": "wàn", + "46C4": "yuǎn", + "46C5": "xiá", + "46C6": "zhòu", + "46C7": "yuǎn", + "46C8": "shì", + "46C9": "miàn", + "46CA": "xī", + "46CB": "jì", + "46CC": "táo", + "46CD": "fèi", + "46CE": "xuè", + "46CF": "ní", + "46D0": "cí", + "46D1": "mì", + "46D2": "biàn", + "46D4": "ná", + "46D5": "yù", + "46D6": "è", + "46D7": "zhǐ", + "46D8": "rén", + "46D9": "xù", + "46DA": "lüè", + "46DB": "huì", + "46DC": "xùn", + "46DD": "náo", + "46DE": "hàn", + "46DF": "jiá", + "46E0": "dòu", + "46E1": "huà", + "46E2": "tū", + "46E3": "pīng", + "46E4": "cù", + "46E5": "xī", + "46E6": "sòng", + "46E7": "mí", + "46E8": "xìn", + "46E9": "wù", + "46EA": "qióng", + "46EB": "zhāng", + "46EC": "táo", + "46ED": "xìng", + "46EE": "jiù", + "46EF": "jù", + "46F0": "hùn", + "46F1": "tí", + "46F2": "mán", + "46F3": "yàn", + "46F4": "jī", + "46F5": "shòu", + "46F6": "lěi", + "46F7": "wǎn", + "46F8": "chè", + "46F9": "càn", + "46FA": "jiè", + "46FB": "yòu", + "46FC": "huǐ", + "46FD": "zhǎ", + "46FE": "sù", + "46FF": "gé", + "4700": "nǎo", + "4701": "xì", + "4703": "duī", + "4704": "chí", + "4705": "wéi", + "4706": "zhé", + "4707": "gǔn", + "4708": "chāo", + "4709": "chī", + "470A": "zāo", + "470B": "huì", + "470C": "luán", + "470D": "liáo", + "470E": "láo", + "470F": "tuō", + "4710": "huī", + "4711": "wù", + "4712": "ào", + "4713": "shè", + "4714": "suí", + "4715": "mài", + "4716": "tàn", + "4717": "xìn", + "4718": "jǐng", + "4719": "án", + "471A": "tà", + "471B": "chán", + "471C": "wèi", + "471D": "tuǎn", + "471E": "jì", + "471F": "chén", + "4720": "chè", + "4721": "yù", + "4722": "xiǎn", + "4723": "xīn", + "4727": "nǎo", + "4729": "yàn", + "472A": "qiú", + "472B": "jiāng", + "472C": "sǒng", + "472D": "jùn", + "472E": "liáo", + "472F": "jú", + "4731": "mǎn", + "4732": "liè", + "4734": "chù", + "4735": "chǐ", + "4736": "xiáng", + "4737": "qīn", + "4738": "měi", + "4739": "shù", + "473A": "chǎi", + "473B": "chǐ", + "473C": "gú", + "473D": "yú", + "473E": "yīn", + "4740": "liú", + "4741": "láo", + "4742": "shù", + "4743": "zhé", + "4744": "shuāng", + "4745": "huī", + "4748": "è", + "474A": "shà", + "474B": "zòng", + "474C": "jué", + "474D": "jùn", + "474E": "tuān", + "474F": "lóu", + "4750": "wéi", + "4751": "chōng", + "4752": "zhù", + "4753": "liè", + "4755": "zhé", + "4756": "zhǎo", + "4758": "yì", + "4759": "chū", + "475A": "ní", + "475B": "bō", + "475C": "suān", + "475D": "yǐ", + "475E": "hào", + "475F": "yà", + "4760": "huán", + "4761": "màn", + "4762": "màn", + "4763": "qú", + "4764": "lǎo", + "4765": "háo", + "4766": "zhōng", + "4767": "mín", + "4768": "xián", + "4769": "zhèn", + "476A": "shǔ", + "476B": "zuó", + "476C": "zhù", + "476D": "gòu", + "476E": "xuàn", + "476F": "yì", + "4770": "zhì", + "4771": "xié", + "4772": "jìn", + "4773": "cán", + "4775": "bù", + "4776": "liáng", + "4777": "zhī", + "4778": "jì", + "4779": "wǎn", + "477A": "guàn", + "477B": "jū", + "477C": "jìng", + "477D": "ài", + "477E": "fù", + "477F": "guì", + "4780": "hòu", + "4781": "yàn", + "4782": "ruǎn", + "4783": "zhì", + "4784": "biào", + "4785": "yí", + "4786": "suǒ", + "4787": "dié", + "4788": "guì", + "4789": "shèng", + "478A": "xùn", + "478B": "chèn", + "478C": "shé", + "478D": "qíng", + "4790": "chǔn", + "4791": "hóng", + "4792": "dòng", + "4793": "chēng", + "4794": "wěi", + "4795": "rú", + "4796": "shǔ", + "4797": "cāi", + "4798": "jí", + "4799": "zá", + "479A": "qí", + "479B": "yān", + "479C": "fù", + "479D": "yù", + "479E": "fú", + "479F": "pò", + "47A0": "zhī", + "47A1": "tǎn", + "47A2": "zuó", + "47A3": "chě", + "47A4": "qú", + "47A5": "yòu", + "47A6": "hé", + "47A7": "hòu", + "47A8": "guǐ", + "47A9": "è", + "47AA": "jiàng", + "47AB": "yǔn", + "47AC": "tòu", + "47AD": "cūn", + "47AE": "tū", + "47AF": "fù", + "47B0": "zuó", + "47B1": "hú", + "47B3": "bó", + "47B4": "zhāo", + "47B5": "juě", + "47B6": "tāng", + "47B7": "jué", + "47B8": "fù", + "47B9": "huáng", + "47BA": "chūn", + "47BB": "yǒng", + "47BC": "chuǐ", + "47BD": "suǒ", + "47BE": "chí", + "47BF": "qiān", + "47C0": "cāi", + "47C1": "xiáo", + "47C2": "mán", + "47C3": "cān", + "47C4": "qì", + "47C5": "jiàn", + "47C6": "bì", + "47C7": "jī", + "47C8": "zhí", + "47C9": "zhú", + "47CA": "qú", + "47CB": "zhǎn", + "47CC": "jí", + "47CD": "biān", + "47CF": "lì", + "47D0": "lì", + "47D1": "yuè", + "47D2": "quán", + "47D3": "chēng", + "47D4": "fù", + "47D5": "chà", + "47D6": "tàng", + "47D7": "shì", + "47D8": "hàng", + "47D9": "qiè", + "47DA": "qí", + "47DB": "bó", + "47DC": "nà", + "47DD": "tòu", + "47DE": "chú", + "47DF": "cù", + "47E0": "yuè", + "47E1": "zhī", + "47E2": "chén", + "47E3": "chù", + "47E4": "bì", + "47E5": "méng", + "47E6": "bá", + "47E7": "tián", + "47E8": "mín", + "47E9": "liě", + "47EA": "fěng", + "47EB": "chēng", + "47EC": "qiù", + "47ED": "tiáo", + "47EE": "fú", + "47EF": "kuò", + "47F0": "jiǎn", + "47F4": "zhèn", + "47F5": "qiú", + "47F6": "zuò", + "47F7": "chì", + "47F8": "kuí", + "47F9": "liè", + "47FA": "bèi", + "47FB": "dù", + "47FC": "wǔ", + "47FE": "zhuó", + "47FF": "lù", + "4800": "tāng", + "4802": "chú", + "4803": "liǎng", + "4804": "tiǎn", + "4805": "kǔn", + "4806": "cháng", + "4807": "jué", + "4808": "tú", + "4809": "huàn", + "480A": "fèi", + "480B": "bì", + "480D": "xiā", + "480E": "wò", + "480F": "jì", + "4810": "qù", + "4811": "kuǐ", + "4812": "hú", + "4813": "qiū", + "4814": "suì", + "4815": "cāi", + "4817": "qiù", + "4818": "pì", + "4819": "páng", + "481A": "wà", + "481B": "yáo", + "481C": "róng", + "481D": "xūn", + "481E": "cù", + "481F": "dié", + "4820": "chì", + "4821": "cuó", + "4822": "mèng", + "4823": "xuǎn", + "4824": "duǒ", + "4825": "bié", + "4826": "zhè", + "4827": "chú", + "4828": "chàn", + "4829": "guì", + "482A": "duàn", + "482B": "zòu", + "482C": "dèng", + "482D": "lái", + "482E": "téng", + "482F": "yuè", + "4830": "quán", + "4831": "zhú", + "4832": "líng", + "4833": "chēn", + "4834": "zhěn", + "4835": "fù", + "4836": "shè", + "4837": "tiǎo", + "4838": "kuā", + "4839": "ái", + "483B": "qióng", + "483C": "shù", + "483D": "hái", + "483E": "shǎn", + "483F": "wài", + "4840": "zhǎn", + "4841": "lǒng", + "4842": "jiū", + "4843": "lì", + "4845": "chūn", + "4846": "róng", + "4847": "yuè", + "4848": "jué", + "4849": "kǎng", + "484A": "fǎn", + "484B": "qí", + "484C": "hóng", + "484D": "fú", + "484E": "lú", + "484F": "hóng", + "4850": "tuó", + "4851": "mín", + "4852": "tián", + "4853": "juàn", + "4854": "qǐ", + "4855": "zhěng", + "4856": "qìng", + "4857": "gǒng", + "4858": "tián", + "4859": "láng", + "485A": "mào", + "485B": "yìn", + "485C": "lù", + "485D": "yuān", + "485E": "jú", + "485F": "pì", + "4861": "xié", + "4862": "biàn", + "4863": "hūn", + "4864": "zhū", + "4865": "róng", + "4866": "sǎng", + "4867": "wū", + "4868": "chà", + "4869": "kēng", + "486A": "shàn", + "486B": "péng", + "486C": "màn", + "486D": "xiū", + "486F": "cōng", + "4870": "kēng", + "4871": "zhuǎn", + "4872": "chán", + "4873": "sī", + "4874": "chōng", + "4875": "suì", + "4876": "bèi", + "4877": "kài", + "4879": "zhì", + "487A": "wèi", + "487B": "mín", + "487C": "líng", + "487D": "zuān", + "487E": "niè", + "487F": "líng", + "4880": "qì", + "4881": "yuè", + "4883": "yì", + "4884": "xǐ", + "4885": "chén", + "4887": "rǒng", + "4888": "chén", + "4889": "nóng", + "488A": "yóu", + "488B": "jì", + "488C": "bó", + "488D": "fǎng", + "4890": "cú", + "4891": "dǐ", + "4892": "jiāo", + "4893": "yú", + "4894": "hé", + "4895": "xù", + "4896": "yù", + "4897": "qū", + "4899": "bài", + "489A": "gēng", + "489B": "jiǒng", + "489D": "yà", + "489E": "shù", + "489F": "yóu", + "48A0": "sòng", + "48A1": "yè", + "48A2": "càng", + "48A3": "yáo", + "48A4": "shù", + "48A5": "yán", + "48A6": "shuài", + "48A7": "liào", + "48A8": "cōng", + "48A9": "yù", + "48AA": "bó", + "48AB": "suí", + "48AD": "yàn", + "48AE": "lèi", + "48AF": "lín", + "48B0": "tī", + "48B1": "dú", + "48B2": "yuè", + "48B3": "jǐ", + "48B5": "yún", + "48B8": "jū", + "48B9": "jǔ", + "48BA": "chū", + "48BB": "chén", + "48BC": "gōng", + "48BD": "xiàng", + "48BE": "xiǎn", + "48BF": "ān", + "48C0": "guǐ", + "48C1": "yǔ", + "48C2": "lěi", + "48C4": "tú", + "48C5": "chén", + "48C6": "xíng", + "48C7": "qiú", + "48C8": "hàng", + "48CA": "dǎng", + "48CB": "cǎi", + "48CC": "dǐ", + "48CD": "yǎn", + "48CE": "zī", + "48D0": "yīng", + "48D1": "chán", + "48D3": "lí", + "48D4": "suǒ", + "48D5": "mǎ", + "48D6": "mǎ", + "48D8": "táng", + "48D9": "péi", + "48DA": "lóu", + "48DB": "qī", + "48DC": "cuó", + "48DD": "tú", + "48DE": "è", + "48DF": "cán", + "48E0": "jié", + "48E1": "yí", + "48E2": "jí", + "48E3": "dǎng", + "48E4": "jué", + "48E5": "bǐ", + "48E6": "lèi", + "48E7": "yì", + "48E8": "chún", + "48E9": "chún", + "48EA": "pò", + "48EB": "lí", + "48EC": "zǎi", + "48ED": "tài", + "48EE": "pò", + "48EF": "cú", + "48F0": "jù", + "48F1": "xù", + "48F2": "fàn", + "48F4": "xù", + "48F5": "èr", + "48F6": "huó", + "48F7": "zhū", + "48F8": "rǎn", + "48F9": "fá", + "48FA": "juān", + "48FB": "hān", + "48FC": "liáng", + "48FD": "zhī", + "48FE": "mì", + "48FF": "yū", + "4901": "cén", + "4902": "méi", + "4903": "yīn", + "4904": "miǎn", + "4905": "tú", + "4906": "kuí", + "4909": "mì", + "490A": "róng", + "490B": "yù", + "490C": "qiāng", + "490D": "mí", + "490E": "jú", + "490F": "pǐ", + "4910": "jǐn", + "4911": "wàng", + "4912": "jì", + "4913": "méng", + "4914": "jiàn", + "4915": "xuè", + "4916": "bào", + "4917": "gǎn", + "4918": "chǎn", + "4919": "lì", + "491A": "lǐ", + "491B": "qiú", + "491C": "dùn", + "491D": "yìng", + "491E": "yǔn", + "491F": "chén", + "4920": "zhǐ", + "4921": "rǎn", + "4923": "lüè", + "4924": "kāi", + "4925": "guǐ", + "4926": "yuè", + "4927": "huì", + "4928": "pì", + "4929": "chá", + "492A": "duǒ", + "492B": "chán", + "492C": "shā", + "492D": "shì", + "492E": "shè", + "492F": "xíng", + "4930": "yíng", + "4931": "shì", + "4932": "chì", + "4933": "yè", + "4934": "hán", + "4935": "fèi", + "4936": "yè", + "4937": "yǎn", + "4938": "zuàn", + "4939": "sōu", + "493A": "jīn", + "493B": "duò", + "493C": "xiàn", + "493D": "guān", + "493E": "tāo", + "493F": "qiè", + "4940": "chǎn", + "4941": "hán", + "4942": "mèng", + "4943": "yuè", + "4944": "cù", + "4945": "qiàn", + "4946": "jǐn", + "4947": "shàn", + "4948": "mǔ", + "4949": "yuān", + "494B": "pēng", + "494C": "zhèng", + "494D": "zhì", + "494E": "chún", + "494F": "yǔ", + "4950": "móu", + "4951": "wàn", + "4952": "jiàng", + "4953": "qī", + "4954": "sù", + "4955": "piě", + "4956": "tián", + "4957": "kuǎn", + "4958": "cù", + "4959": "suì", + "495B": "jiē", + "495C": "jiàn", + "495D": "áo", + "495E": "jiǎo", + "495F": "yè", + "4961": "yè", + "4962": "lóng", + "4963": "záo", + "4964": "báo", + "4965": "lián", + "4967": "huán", + "4968": "lǜ", + "4969": "wéi", + "496A": "xiǎn", + "496B": "tiě", + "496C": "bó", + "496D": "zhèng", + "496E": "zhú", + "496F": "bēi", + "4970": "méng", + "4971": "xiě", + "4972": "ōu", + "4973": "yōu", + "4975": "xiǎo", + "4976": "lì", + "4977": "zhá", + "4978": "mí", + "497A": "yé", + "497D": "pō", + "497E": "xiě", + "4982": "shàn", + "4983": "zhuō", + "4985": "shàn", + "4986": "jué", + "4987": "jì", + "4988": "jiē", + "498A": "niǎo", + "498B": "áo", + "498C": "chù", + "498D": "wù", + "498E": "guǎn", + "498F": "xiè", + "4990": "tǐng", + "4991": "xuè", + "4992": "dàng", + "4993": "zhān", + "4994": "tǎn", + "4995": "pēng", + "4996": "xié", + "4997": "xù", + "4998": "xiàn", + "4999": "sì", + "499A": "kuà", + "499B": "zhèng", + "499C": "wú", + "499D": "huō", + "499E": "rùn", + "499F": "wěn", + "49A0": "dū", + "49A1": "huán", + "49A2": "kuò", + "49A3": "fù", + "49A4": "chuài", + "49A5": "xián", + "49A6": "qín", + "49A7": "qié", + "49A8": "lán", + "49AA": "yà", + "49AB": "yīng", + "49AC": "què", + "49AD": "hāng", + "49AE": "chǔn", + "49AF": "zhì", + "49B1": "wěi", + "49B2": "yán", + "49B3": "xiàng", + "49B4": "yì", + "49B5": "nǐ", + "49B6": "zhèng", + "49B7": "chuài", + "49B9": "shí", + "49BA": "dīng", + "49BB": "zǐ", + "49BC": "jué", + "49BD": "xù", + "49BE": "yuán", + "49C1": "xǔ", + "49C2": "dào", + "49C3": "tián", + "49C4": "gè", + "49C5": "yí", + "49C6": "hóng", + "49C7": "yī", + "49C9": "lǐ", + "49CA": "kū", + "49CB": "xiǎn", + "49CC": "suī", + "49CD": "xì", + "49CE": "xuàn", + "49D1": "dī", + "49D2": "lái", + "49D3": "zhōu", + "49D4": "niàn", + "49D5": "chéng", + "49D6": "jiàn", + "49D7": "bì", + "49D8": "zhuàn", + "49D9": "líng", + "49DA": "hào", + "49DB": "bàng", + "49DC": "táng", + "49DD": "chī", + "49DE": "mà", + "49DF": "xiàn", + "49E0": "shuàn", + "49E1": "yōng", + "49E2": "qū", + "49E4": "pú", + "49E5": "huì", + "49E6": "wéi", + "49E7": "yǐ", + "49E8": "yè", + "49EA": "chè", + "49EB": "háo", + "49EC": "bīn", + "49EE": "xiàn", + "49EF": "chán", + "49F0": "hùn", + "49F2": "hàn", + "49F3": "cí", + "49F4": "zhī", + "49F5": "qí", + "49F6": "kuí", + "49F7": "róu", + "49F9": "yīng", + "49FA": "xióng", + "49FC": "hú", + "49FD": "cuǐ", + "49FF": "què", + "4A00": "dí", + "4A01": "wù", + "4A02": "qiū", + "4A04": "yàn", + "4A05": "liáo", + "4A06": "bí", + "4A08": "bīn", + "4A0A": "yuān", + "4A0B": "nüè", + "4A0C": "báo", + "4A0D": "yǐng", + "4A0E": "hóng", + "4A0F": "cí", + "4A10": "qià", + "4A11": "tí", + "4A12": "yù", + "4A13": "léi", + "4A14": "báo", + "4A16": "jì", + "4A17": "fú", + "4A18": "xiàn", + "4A19": "cén", + "4A1A": "hū", + "4A1B": "sè", + "4A1C": "bēng", + "4A1D": "qīng", + "4A1E": "yǔ", + "4A1F": "wā", + "4A20": "ǎi", + "4A21": "hán", + "4A22": "dàn", + "4A23": "gé", + "4A24": "dí", + "4A25": "huò", + "4A26": "pāng", + "4A28": "zhuī", + "4A29": "líng", + "4A2A": "mái", + "4A2B": "mài", + "4A2C": "lián", + "4A2D": "xiāo", + "4A2E": "xuě", + "4A2F": "zhèn", + "4A30": "pò", + "4A31": "fù", + "4A32": "nóu", + "4A33": "xì", + "4A34": "duì", + "4A35": "dàn", + "4A36": "yǔn", + "4A37": "xiàn", + "4A38": "yǐn", + "4A39": "shū", + "4A3A": "duì", + "4A3B": "bèng", + "4A3C": "hù", + "4A3D": "fěi", + "4A3E": "fèi", + "4A3F": "zá", + "4A40": "bèi", + "4A41": "fēi", + "4A42": "xiān", + "4A43": "shì", + "4A44": "miǎn", + "4A45": "zhǎn", + "4A46": "zhǎn", + "4A47": "zhān", + "4A48": "huì", + "4A49": "fǔ", + "4A4A": "wǎn", + "4A4B": "mǒ", + "4A4C": "qiáo", + "4A4D": "liǎo", + "4A4F": "miè", + "4A50": "hū", + "4A51": "hóng", + "4A52": "yú", + "4A53": "qí", + "4A54": "duò", + "4A55": "áng", + "4A57": "bà", + "4A58": "dì", + "4A59": "xuàn", + "4A5A": "dì", + "4A5B": "bì", + "4A5C": "zhòu", + "4A5D": "páo", + "4A5E": "tié", + "4A5F": "yí", + "4A61": "jiá", + "4A62": "zhì", + "4A63": "tú", + "4A64": "xié", + "4A65": "dàn", + "4A66": "tiáo", + "4A67": "xiè", + "4A68": "chàng", + "4A69": "yuǎn", + "4A6A": "guǎn", + "4A6B": "liǎng", + "4A6C": "běng", + "4A6E": "lù", + "4A6F": "jí", + "4A70": "xuàn", + "4A71": "shù", + "4A72": "dū", + "4A73": "sōu", + "4A74": "hú", + "4A75": "yùn", + "4A76": "chǎn", + "4A77": "bāng", + "4A78": "róng", + "4A79": "é", + "4A7A": "wēng", + "4A7B": "bà", + "4A7C": "féng", + "4A7D": "yū", + "4A7E": "zhè", + "4A7F": "fén", + "4A80": "guǎn", + "4A81": "bǔ", + "4A82": "gé", + "4A83": "dūn", + "4A84": "huáng", + "4A85": "dú", + "4A86": "tǐ", + "4A87": "bó", + "4A88": "qiàn", + "4A89": "liè", + "4A8A": "lóng", + "4A8B": "wèi", + "4A8C": "zhàn", + "4A8D": "lán", + "4A8E": "suī", + "4A8F": "nà", + "4A90": "bì", + "4A91": "tuó", + "4A92": "zhù", + "4A93": "diē", + "4A94": "bǔ", + "4A95": "jú", + "4A96": "pò", + "4A97": "xiá", + "4A98": "wěi", + "4A99": "pò", + "4A9A": "dā", + "4A9B": "fān", + "4A9C": "chān", + "4A9D": "hù", + "4A9E": "zá", + "4AA4": "fán", + "4AA5": "xiè", + "4AA6": "hóng", + "4AA7": "chí", + "4AA8": "báo", + "4AA9": "yín", + "4AAB": "jīng", + "4AAC": "bó", + "4AAD": "ruǎn", + "4AAE": "chǒu", + "4AAF": "yīng", + "4AB0": "yī", + "4AB1": "gǎi", + "4AB2": "kūn", + "4AB3": "yǔn", + "4AB4": "zhěn", + "4AB5": "yǎ", + "4AB6": "jū", + "4AB7": "hòu", + "4AB8": "mín", + "4AB9": "bāi", + "4ABA": "gé", + "4ABB": "biàn", + "4ABC": "zhuō", + "4ABD": "hào", + "4ABE": "zhěn", + "4ABF": "shěng", + "4AC0": "gěn", + "4AC1": "bì", + "4AC2": "duǒ", + "4AC3": "chún", + "4AC4": "chuà", + "4AC5": "sàn", + "4AC6": "chéng", + "4AC7": "rán", + "4AC8": "chěn", + "4AC9": "mào", + "4ACA": "péi", + "4ACB": "wēi", + "4ACC": "pǐ", + "4ACD": "fǔ", + "4ACE": "zhuō", + "4ACF": "qī", + "4AD0": "lín", + "4AD1": "yī", + "4AD2": "mén", + "4AD3": "wú", + "4AD4": "qì", + "4AD5": "dié", + "4AD6": "chěn", + "4AD7": "xiá", + "4AD8": "hé", + "4AD9": "sǎng", + "4ADA": "guā", + "4ADB": "hóu", + "4ADC": "āo", + "4ADD": "fǔ", + "4ADE": "qiāo", + "4ADF": "hùn", + "4AE0": "pī", + "4AE1": "yán", + "4AE2": "sī", + "4AE3": "xí", + "4AE4": "míng", + "4AE5": "kuǐ", + "4AE6": "gé", + "4AE8": "ào", + "4AE9": "sǎn", + "4AEA": "shuǎng", + "4AEB": "lóu", + "4AEC": "zhěn", + "4AED": "huì", + "4AEE": "chán", + "4AF0": "lìn", + "4AF1": "ná", + "4AF2": "hàn", + "4AF3": "dú", + "4AF4": "jìn", + "4AF5": "mián", + "4AF6": "fán", + "4AF7": "è", + "4AF8": "chāo", + "4AF9": "hóng", + "4AFA": "hóng", + "4AFB": "yù", + "4AFC": "xuè", + "4AFD": "pāo", + "4AFE": "bī", + "4AFF": "chāo", + "4B00": "yǒu", + "4B01": "yí", + "4B02": "xuè", + "4B03": "sà", + "4B04": "xù", + "4B05": "lì", + "4B06": "lì", + "4B07": "yuàn", + "4B08": "duì", + "4B09": "huò", + "4B0A": "shà", + "4B0B": "léng", + "4B0C": "pōu", + "4B0D": "hū", + "4B0E": "guó", + "4B0F": "bù", + "4B10": "ruí", + "4B11": "wèi", + "4B12": "sōu", + "4B13": "àn", + "4B14": "yú", + "4B15": "xiāng", + "4B16": "héng", + "4B17": "yáng", + "4B18": "xiāo", + "4B19": "yáo", + "4B1B": "bì", + "4B1D": "héng", + "4B1E": "táo", + "4B1F": "liú", + "4B21": "zhù", + "4B23": "xì", + "4B24": "zàn", + "4B25": "yì", + "4B26": "dòu", + "4B27": "yuán", + "4B28": "jiù", + "4B2A": "bó", + "4B2B": "tí", + "4B2C": "yǐng", + "4B2E": "yí", + "4B2F": "nián", + "4B30": "shào", + "4B31": "bèn", + "4B32": "gōu", + "4B33": "bǎn", + "4B34": "mò", + "4B35": "gāi", + "4B36": "èn", + "4B37": "shě", + "4B39": "zhì", + "4B3A": "yàng", + "4B3B": "jiàn", + "4B3C": "yuàn", + "4B3D": "shuì", + "4B3E": "tí", + "4B3F": "wěi", + "4B40": "xùn", + "4B41": "zhì", + "4B42": "yì", + "4B43": "rěn", + "4B44": "shì", + "4B45": "hú", + "4B46": "nè", + "4B47": "yē", + "4B48": "jiàn", + "4B49": "suǐ", + "4B4A": "yǐng", + "4B4B": "bǎo", + "4B4C": "hú", + "4B4D": "hú", + "4B4E": "yè", + "4B50": "yàng", + "4B51": "lián", + "4B52": "xī", + "4B53": "èn", + "4B54": "duī", + "4B55": "zǎn", + "4B56": "zhù", + "4B57": "yǐng", + "4B58": "yǐng", + "4B59": "jǐn", + "4B5A": "chuáng", + "4B5B": "dàn", + "4B5D": "kuài", + "4B5E": "yì", + "4B5F": "yè", + "4B60": "jiǎn", + "4B61": "èn", + "4B62": "níng", + "4B63": "cí", + "4B64": "qiǎn", + "4B65": "xuè", + "4B66": "bō", + "4B67": "mǐ", + "4B68": "shuì", + "4B69": "mó", + "4B6A": "liáng", + "4B6B": "qǐ", + "4B6C": "qǐ", + "4B6D": "shǒu", + "4B6E": "fú", + "4B6F": "bó", + "4B70": "bèng", + "4B71": "bié", + "4B72": "yǐ", + "4B73": "wèi", + "4B74": "huán", + "4B75": "fán", + "4B76": "qí", + "4B77": "máo", + "4B78": "bǎo", + "4B79": "áng", + "4B7A": "ǎng", + "4B7B": "fù", + "4B7C": "qí", + "4B7D": "qún", + "4B7E": "tuó", + "4B7F": "yì", + "4B80": "bó", + "4B81": "pián", + "4B82": "bá", + "4B84": "xuán", + "4B87": "yù", + "4B88": "chí", + "4B89": "lú", + "4B8A": "yí", + "4B8B": "lì", + "4B8D": "niǎo", + "4B8E": "xì", + "4B8F": "wú", + "4B91": "lèi", + "4B92": "pū", + "4B93": "zhuō", + "4B94": "zuī", + "4B95": "zhuó", + "4B96": "chāng", + "4B97": "àn", + "4B98": "ér", + "4B99": "yù", + "4B9A": "lèng", + "4B9B": "fù", + "4B9C": "zhá", + "4B9D": "hún", + "4B9E": "chǔn", + "4B9F": "sōu", + "4BA0": "bī", + "4BA1": "bì", + "4BA2": "zhá", + "4BA4": "hé", + "4BA5": "lì", + "4BA7": "hàn", + "4BA8": "zǎi", + "4BA9": "gú", + "4BAA": "chéng", + "4BAB": "lóu", + "4BAC": "mò", + "4BAD": "mì", + "4BAE": "mài", + "4BAF": "ào", + "4BB0": "zhé", + "4BB1": "zhú", + "4BB2": "huáng", + "4BB3": "fán", + "4BB4": "dèng", + "4BB5": "tóng", + "4BB7": "dú", + "4BB8": "wò", + "4BB9": "wèi", + "4BBA": "jì", + "4BBB": "chì", + "4BBC": "lín", + "4BBD": "biāo", + "4BBE": "lóng", + "4BBF": "jiǎn", + "4BC0": "niè", + "4BC1": "luó", + "4BC2": "shēn", + "4BC4": "guā", + "4BC5": "niè", + "4BC6": "yì", + "4BC7": "kū", + "4BC8": "wán", + "4BC9": "wā", + "4BCA": "qià", + "4BCB": "bó", + "4BCC": "kāo", + "4BCD": "líng", + "4BCE": "gàn", + "4BCF": "guā", + "4BD0": "hái", + "4BD1": "kuāng", + "4BD2": "héng", + "4BD3": "kuī", + "4BD4": "zé", + "4BD5": "tīng", + "4BD6": "láng", + "4BD7": "bì", + "4BD8": "huàn", + "4BD9": "pò", + "4BDA": "yǎo", + "4BDB": "wàn", + "4BDC": "tì", + "4BDD": "suǐ", + "4BDE": "kuā", + "4BDF": "duì", + "4BE0": "ǎo", + "4BE1": "jiàn", + "4BE2": "mó", + "4BE3": "kuì", + "4BE4": "kuài", + "4BE5": "àn", + "4BE6": "mà", + "4BE7": "qǐng", + "4BE8": "qiāo", + "4BEA": "kǎo", + "4BEB": "hào", + "4BEC": "duǒ", + "4BED": "xiān", + "4BEE": "nái", + "4BEF": "suō", + "4BF0": "jiè", + "4BF1": "pī", + "4BF2": "pā", + "4BF3": "sōng", + "4BF4": "cháng", + "4BF5": "niè", + "4BF6": "mán", + "4BF7": "sōng", + "4BF8": "cì", + "4BF9": "xiān", + "4BFA": "kuò", + "4BFC": "dí", + "4BFD": "póu", + "4BFE": "tiáo", + "4BFF": "zú", + "4C00": "wǒ", + "4C01": "fèi", + "4C02": "cài", + "4C03": "péng", + "4C04": "sāi", + "4C06": "róu", + "4C07": "qí", + "4C08": "cuó", + "4C09": "pán", + "4C0A": "bó", + "4C0B": "mán", + "4C0C": "zǒng", + "4C0D": "cì", + "4C0E": "kuì", + "4C0F": "jì", + "4C10": "lán", + "4C12": "méng", + "4C13": "mián", + "4C14": "pán", + "4C15": "lú", + "4C16": "zuǎn", + "4C18": "liú", + "4C19": "yǐ", + "4C1A": "wén", + "4C1B": "lì", + "4C1C": "lì", + "4C1D": "zèng", + "4C1E": "zhǔ", + "4C1F": "hún", + "4C20": "shén", + "4C21": "chì", + "4C22": "xìng", + "4C23": "wǎng", + "4C24": "dōng", + "4C25": "huò", + "4C26": "pǐ", + "4C27": "hū", + "4C28": "mèi", + "4C29": "chě", + "4C2A": "mèi", + "4C2B": "chāo", + "4C2C": "jú", + "4C2D": "nòu", + "4C2F": "yì", + "4C30": "rú", + "4C31": "líng", + "4C32": "yà", + "4C34": "qì", + "4C35": "zī", + "4C37": "bàng", + "4C38": "gōng", + "4C39": "zé", + "4C3A": "jiè", + "4C3B": "yú", + "4C3C": "qín", + "4C3D": "bèi", + "4C3E": "bā", + "4C3F": "tuó", + "4C40": "yāng", + "4C41": "qiáo", + "4C42": "yǒu", + "4C43": "zhì", + "4C44": "jiè", + "4C45": "mò", + "4C46": "shéng", + "4C47": "shàn", + "4C48": "qí", + "4C49": "shàn", + "4C4A": "mǐ", + "4C4B": "gǒng", + "4C4C": "yí", + "4C4D": "gèng", + "4C4E": "gèng", + "4C4F": "tǒu", + "4C50": "fū", + "4C51": "xué", + "4C52": "yè", + "4C53": "tíng", + "4C54": "tiáo", + "4C55": "móu", + "4C56": "liú", + "4C57": "cān", + "4C58": "lí", + "4C59": "shū", + "4C5A": "lù", + "4C5B": "huò", + "4C5C": "cuò", + "4C5D": "pái", + "4C5E": "liú", + "4C5F": "jù", + "4C60": "zhàn", + "4C61": "jú", + "4C62": "zhēng", + "4C63": "zú", + "4C64": "xiàn", + "4C65": "zhì", + "4C68": "là", + "4C6B": "là", + "4C6C": "xū", + "4C6D": "gèng", + "4C6E": "é", + "4C6F": "mú", + "4C70": "zhòng", + "4C71": "tí", + "4C72": "yuán", + "4C73": "zhān", + "4C74": "gèng", + "4C75": "wēng", + "4C76": "láng", + "4C77": "yú", + "4C78": "sōu", + "4C79": "zhǎ", + "4C7A": "hái", + "4C7B": "huá", + "4C7C": "zhǎn", + "4C7E": "lóu", + "4C7F": "chàn", + "4C80": "zhì", + "4C81": "wèi", + "4C82": "xuán", + "4C83": "zǎo", + "4C84": "mín", + "4C85": "guī", + "4C86": "sū", + "4C89": "sī", + "4C8A": "duò", + "4C8B": "cén", + "4C8C": "kuǎn", + "4C8D": "téng", + "4C8E": "něi", + "4C8F": "láo", + "4C90": "lǔ", + "4C91": "yí", + "4C92": "xiè", + "4C93": "yǎn", + "4C94": "qíng", + "4C95": "pū", + "4C96": "chóu", + "4C97": "xián", + "4C98": "guǎn", + "4C99": "jié", + "4C9A": "lài", + "4C9B": "méng", + "4C9C": "yè", + "4C9E": "lì", + "4C9F": "yìn", + "4CA0": "chūn", + "4CA1": "qiū", + "4CA2": "téng", + "4CA3": "yú", + "4CA6": "dài", + "4CA7": "dù", + "4CA8": "hóng", + "4CAA": "xì", + "4CAC": "qí", + "4CAE": "yuán", + "4CAF": "jí", + "4CB0": "yùn", + "4CB1": "fǎng", + "4CB2": "gōng", + "4CB3": "háng", + "4CB4": "zhèn", + "4CB5": "què", + "4CB8": "jiè", + "4CB9": "pí", + "4CBA": "gàn", + "4CBB": "xuán", + "4CBC": "shēng", + "4CBD": "shí", + "4CBE": "qiǎo", + "4CBF": "cí", + "4CC0": "dié", + "4CC1": "bó", + "4CC2": "diāo", + "4CC3": "wǎn", + "4CC4": "cí", + "4CC5": "zhǐ", + "4CC6": "bái", + "4CC7": "wǔ", + "4CC8": "bǎo", + "4CC9": "dàn", + "4CCA": "bá", + "4CCB": "tóng", + "4CCD": "gōng", + "4CCE": "jiù", + "4CCF": "guì", + "4CD0": "cì", + "4CD1": "yǒu", + "4CD2": "yuán", + "4CD3": "lǎo", + "4CD4": "jú", + "4CD5": "fú", + "4CD6": "niè", + "4CD7": "é", + "4CD8": "é", + "4CD9": "xǐng", + "4CDA": "kàn", + "4CDB": "yàn", + "4CDC": "tú", + "4CDD": "pǒu", + "4CDE": "běng", + "4CDF": "míng", + "4CE0": "shuì", + "4CE1": "yàn", + "4CE2": "qí", + "4CE3": "yuán", + "4CE4": "biē", + "4CE6": "xuān", + "4CE7": "hóu", + "4CE8": "huáng", + "4CE9": "yāo", + "4CEA": "juàn", + "4CEB": "kuí", + "4CEC": "è", + "4CED": "jí", + "4CEE": "mò", + "4CEF": "chóng", + "4CF0": "bǎo", + "4CF1": "wù", + "4CF2": "zhèn", + "4CF3": "xù", + "4CF4": "tà", + "4CF5": "chì", + "4CF6": "xī", + "4CF7": "cóng", + "4CF8": "má", + "4CF9": "kòu", + "4CFA": "yàn", + "4CFB": "cán", + "4CFD": "hè", + "4CFE": "dēng", + "4CFF": "rán", + "4D00": "tóng", + "4D01": "yù", + "4D02": "xiàng", + "4D03": "náo", + "4D04": "shùn", + "4D05": "fén", + "4D06": "pú", + "4D07": "líng", + "4D08": "ǎo", + "4D09": "huán", + "4D0A": "yí", + "4D0B": "huán", + "4D0C": "méng", + "4D0D": "yīng", + "4D0E": "lěi", + "4D0F": "yàn", + "4D10": "bǎo", + "4D11": "dié", + "4D12": "líng", + "4D13": "shī", + "4D14": "jiāo", + "4D15": "liè", + "4D16": "jīng", + "4D17": "jú", + "4D18": "tī", + "4D19": "pì", + "4D1A": "gǎng", + "4D1B": "xiāo", + "4D1C": "wāi", + "4D1D": "chuài", + "4D1E": "dí", + "4D1F": "huán", + "4D20": "yǎo", + "4D21": "lì", + "4D22": "mí", + "4D23": "hū", + "4D24": "shēng", + "4D25": "jiā", + "4D26": "yín", + "4D27": "wēi", + "4D29": "piáo", + "4D2A": "lù", + "4D2B": "líng", + "4D2C": "yì", + "4D2D": "cái", + "4D2E": "shàn", + "4D2F": "hū", + "4D30": "shú", + "4D31": "tuō", + "4D32": "mò", + "4D33": "huá", + "4D34": "tiè", + "4D35": "bǐng", + "4D36": "péng", + "4D37": "hún", + "4D38": "fū", + "4D39": "guǒ", + "4D3A": "bù", + "4D3B": "lí", + "4D3C": "chàn", + "4D3D": "pí", + "4D3E": "cuó", + "4D3F": "méng", + "4D40": "suǒ", + "4D41": "qiàng", + "4D42": "zhí", + "4D43": "kuàng", + "4D44": "bí", + "4D45": "áo", + "4D46": "méng", + "4D47": "xiàn", + "4D48": "kù", + "4D49": "tóu", + "4D4A": "tuān", + "4D4B": "wěi", + "4D4C": "xiān", + "4D4E": "tuān", + "4D4F": "lǎo", + "4D50": "chǎn", + "4D51": "nì", + "4D52": "nì", + "4D53": "lí", + "4D54": "dǒng", + "4D55": "jù", + "4D56": "qiàn", + "4D57": "bó", + "4D58": "shài", + "4D59": "zhā", + "4D5A": "tǎo", + "4D5B": "qiàn", + "4D5C": "nǒng", + "4D5D": "yì", + "4D5E": "jìng", + "4D5F": "gǎn", + "4D60": "dí", + "4D61": "jiǎn", + "4D62": "mèi", + "4D63": "dá", + "4D64": "jiǎn", + "4D65": "yù", + "4D66": "xiè", + "4D67": "zài", + "4D68": "máng", + "4D69": "lí", + "4D6A": "gùn", + "4D6B": "xūn", + "4D6C": "tà", + "4D6D": "zhè", + "4D6E": "yàng", + "4D6F": "tuǎn", + "4D70": "shāng", + "4D71": "xì", + "4D72": "qiāo", + "4D73": "wèi", + "4D74": "yìng", + "4D75": "chuā", + "4D76": "qú", + "4D77": "wā", + "4D79": "zhī", + "4D7A": "tǐng", + "4D7B": "gǔ", + "4D7C": "shāng", + "4D7D": "cà", + "4D7E": "fú", + "4D7F": "tiè", + "4D80": "tà", + "4D81": "tà", + "4D82": "zhuó", + "4D83": "hán", + "4D84": "píng", + "4D85": "hé", + "4D86": "zhuī", + "4D87": "zhòu", + "4D88": "bó", + "4D89": "liú", + "4D8A": "nǜ", + "4D8B": "xī", + "4D8C": "pào", + "4D8D": "dì", + "4D8E": "hē", + "4D8F": "tì", + "4D90": "wài", + "4D91": "tì", + "4D92": "qí", + "4D93": "jì", + "4D94": "chí", + "4D95": "bà", + "4D96": "jìn", + "4D97": "kè", + "4D98": "lì", + "4D99": "jù", + "4D9A": "qǔ", + "4D9B": "là", + "4D9C": "gǔ", + "4D9D": "qià", + "4D9E": "qí", + "4D9F": "xiàn", + "4DA0": "jiǎn", + "4DA1": "shí", + "4DA2": "jiān", + "4DA3": "ái", + "4DA4": "huá", + "4DA5": "zhā", + "4DA6": "zé", + "4DA7": "yǎo", + "4DA8": "zhān", + "4DA9": "jì", + "4DAA": "chà", + "4DAB": "yàn", + "4DAC": "jiān", + "4DAE": "yǎn", + "4DB0": "jiāo", + "4DB1": "tóng", + "4DB2": "nán", + "4DB3": "yuè", + "4DB5": "chí", + "4E00": "yī", + "4E01": "dīng", + "4E02": "kǎo", + "4E03": "qī", + "4E04": "shàng", + "4E05": "xià", + "4E06": "hǎn", + "4E07": "wàn", + "4E08": "zhàng", + "4E09": "sān", + "4E0A": "shàng", + "4E0B": "xià", + "4E0C": "jī", + "4E0D": "bù", + "4E0E": "yǔ", + "4E0F": "miǎn", + "4E10": "gài", + "4E11": "chǒu", + "4E12": "chǒu", + "4E13": "zhuān", + "4E14": "qiě", + "4E15": "pī", + "4E16": "shì", + "4E17": "shì", + "4E18": "qiū", + "4E19": "bǐng", + "4E1A": "yè", + "4E1B": "cóng", + "4E1C": "dōng", + "4E1D": "sī", + "4E1E": "chéng", + "4E1F": "diū", + "4E20": "qiū", + "4E21": "liǎng", + "4E22": "diū", + "4E23": "yǒu", + "4E24": "liǎng", + "4E25": "yán", + "4E26": "bìng", + "4E27": "sàng", + "4E28": "gǔn", + "4E29": "jiū", + "4E2A": "gè", + "4E2B": "yā", + "4E2C": "qiáng", + "4E2D": "zhōng", + "4E2E": "jǐ", + "4E2F": "jiè", + "4E30": "fēng", + "4E31": "guàn", + "4E32": "chuàn", + "4E33": "chǎn", + "4E34": "lín", + "4E35": "zhuó", + "4E36": "zhǔ", + "4E37": "ha", + "4E38": "wán", + "4E39": "dān", + "4E3A": "wèi", + "4E3B": "zhǔ", + "4E3C": "jǐng", + "4E3D": "lì", + "4E3E": "jǔ", + "4E3F": "piě", + "4E40": "fú", + "4E41": "yí", + "4E42": "yì", + "4E43": "nǎi", + "4E44": "wu", + "4E45": "jiǔ", + "4E46": "jiǔ", + "4E47": "tuō", + "4E48": "me", + "4E49": "yì", + "4E4A": "yī", + "4E4B": "zhī", + "4E4C": "wū", + "4E4D": "zhà", + "4E4E": "hu", + "4E4F": "fá", + "4E50": "lè", + "4E51": "yín", + "4E52": "pīng", + "4E53": "pāng", + "4E54": "qiáo", + "4E55": "hǔ", + "4E56": "guāi", + "4E57": "chéng", + "4E58": "chéng", + "4E59": "yǐ", + "4E5A": "yǐn", + "4E5B": "ya", + "4E5C": "miē", + "4E5D": "jiǔ", + "4E5E": "qǐ", + "4E5F": "yě", + "4E60": "xí", + "4E61": "xiāng", + "4E62": "gài", + "4E63": "jiǔ", + "4E64": "xià", + "4E65": "hù", + "4E66": "shū", + "4E67": "dou", + "4E68": "shǐ", + "4E69": "jī", + "4E6A": "náng", + "4E6B": "jiā", + "4E6C": "jù", + "4E6D": "shí", + "4E6E": "mǎo", + "4E6F": "hū", + "4E70": "mǎi", + "4E71": "luàn", + "4E72": "zī", + "4E73": "rǔ", + "4E74": "xué", + "4E75": "yǎn", + "4E76": "fǔ", + "4E77": "shā", + "4E78": "nǎ", + "4E79": "gān", + "4E7A": "suǒ", + "4E7B": "yú", + "4E7C": "cui", + "4E7D": "zhě", + "4E7E": "gān", + "4E7F": "zhì", + "4E80": "guī", + "4E81": "gān", + "4E82": "luàn", + "4E83": "lǐn", + "4E84": "yì", + "4E85": "jué", + "4E86": "le", + "4E87": "ma", + "4E88": "yǔ", + "4E89": "zhēng", + "4E8A": "shì", + "4E8B": "shì", + "4E8C": "èr", + "4E8D": "chù", + "4E8E": "yú", + "4E8F": "kuī", + "4E90": "yú", + "4E91": "yún", + "4E92": "hù", + "4E93": "qí", + "4E94": "wǔ", + "4E95": "jǐng", + "4E96": "sì", + "4E97": "suì", + "4E98": "gèn", + "4E99": "gèn", + "4E9A": "yà", + "4E9B": "xiē", + "4E9C": "yà", + "4E9D": "qí", + "4E9E": "yà", + "4E9F": "jí", + "4EA0": "tóu", + "4EA1": "wáng", + "4EA2": "kàng", + "4EA3": "tà", + "4EA4": "jiāo", + "4EA5": "hài", + "4EA6": "yì", + "4EA7": "chǎn", + "4EA8": "hēng", + "4EA9": "mǔ", + "4EAA": "ye", + "4EAB": "xiǎng", + "4EAC": "jīng", + "4EAD": "tíng", + "4EAE": "liàng", + "4EAF": "xiǎng", + "4EB0": "jīng", + "4EB1": "yè", + "4EB2": "qīn", + "4EB3": "bó", + "4EB4": "yòu", + "4EB5": "xiè", + "4EB6": "dǎn", + "4EB7": "lián", + "4EB8": "duǒ", + "4EB9": "mén", + "4EBA": "rén", + "4EBB": "rén", + "4EBC": "jí", + "4EBD": "ji", + "4EBE": "wáng", + "4EBF": "yì", + "4EC0": "shén", + "4EC1": "rén", + "4EC2": "lè", + "4EC3": "dīng", + "4EC4": "zè", + "4EC5": "jǐn", + "4EC6": "pū", + "4EC7": "chóu", + "4EC8": "bā", + "4EC9": "zhǎng", + "4ECA": "jīn", + "4ECB": "jiè", + "4ECC": "bīng", + "4ECD": "réng", + "4ECE": "cóng", + "4ECF": "fó", + "4ED0": "sǎn", + "4ED1": "lún", + "4ED2": "bīng", + "4ED3": "cāng", + "4ED4": "zǐ", + "4ED5": "shì", + "4ED6": "tā", + "4ED7": "zhàng", + "4ED8": "fù", + "4ED9": "xian", + "4EDA": "xiān", + "4EDB": "tuō", + "4EDC": "hóng", + "4EDD": "tóng", + "4EDE": "rèn", + "4EDF": "qiān", + "4EE0": "gǎn", + "4EE1": "gē", + "4EE2": "bó", + "4EE3": "dài", + "4EE4": "lìng", + "4EE5": "yǐ", + "4EE6": "chào", + "4EE7": "cháng", + "4EE8": "sā", + "4EE9": "shang", + "4EEA": "yí", + "4EEB": "mù", + "4EEC": "men", + "4EED": "rèn", + "4EEE": "jiǎ", + "4EEF": "chào", + "4EF0": "yǎng", + "4EF1": "qián", + "4EF2": "zhòng", + "4EF3": "pǐ", + "4EF4": "wò", + "4EF5": "wǔ", + "4EF6": "jiàn", + "4EF7": "jià", + "4EF8": "yǎo", + "4EF9": "fēng", + "4EFA": "cāng", + "4EFB": "rèn", + "4EFC": "wáng", + "4EFD": "fèn", + "4EFE": "dī", + "4EFF": "fǎng", + "4F00": "zhōng", + "4F01": "qǐ", + "4F02": "pèi", + "4F03": "yú", + "4F04": "diào", + "4F05": "dùn", + "4F06": "wù", + "4F07": "yì", + "4F08": "xǐn", + "4F09": "kàng", + "4F0A": "yī", + "4F0B": "jí", + "4F0C": "ài", + "4F0D": "wu", + "4F0E": "jì", + "4F0F": "fú", + "4F10": "fá", + "4F11": "xiū", + "4F12": "jìn", + "4F13": "pī", + "4F14": "dǎn", + "4F15": "fū", + "4F16": "tǎng", + "4F17": "zhòng", + "4F18": "yōu", + "4F19": "huǒ", + "4F1A": "huì", + "4F1B": "yǔ", + "4F1C": "cuì", + "4F1D": "chuán", + "4F1E": "sǎn", + "4F1F": "wěi", + "4F20": "chuán", + "4F21": "chē", + "4F22": "yá", + "4F23": "xiàn", + "4F24": "shāng", + "4F25": "chāng", + "4F26": "lún", + "4F27": "cāng", + "4F28": "xùn", + "4F29": "xìn", + "4F2A": "wěi", + "4F2B": "zhù", + "4F2C": "ze", + "4F2D": "xián", + "4F2E": "nǔ", + "4F2F": "bó", + "4F30": "gū", + "4F31": "nǐ", + "4F32": "nì", + "4F33": "xiè", + "4F34": "bàn", + "4F35": "xù", + "4F36": "ling", + "4F37": "zhòu", + "4F38": "shēn", + "4F39": "qū", + "4F3A": "cì", + "4F3B": "bēng", + "4F3C": "shì", + "4F3D": "jiā", + "4F3E": "pī", + "4F3F": "yì", + "4F40": "sì", + "4F41": "yǐ", + "4F42": "zhēng", + "4F43": "diàn", + "4F44": "hān", + "4F45": "mài", + "4F46": "dàn", + "4F47": "zhù", + "4F48": "bù", + "4F49": "qū", + "4F4A": "bǐ", + "4F4B": "zhāo", + "4F4C": "cǐ", + "4F4D": "wèi", + "4F4E": "dī", + "4F4F": "zhù", + "4F50": "zuǒ", + "4F51": "yòu", + "4F52": "yǎng", + "4F53": "tǐ", + "4F54": "zhàn", + "4F55": "hé", + "4F56": "bì", + "4F57": "tuó", + "4F58": "shé", + "4F59": "yú", + "4F5A": "yì", + "4F5B": "fú", + "4F5C": "zuò", + "4F5D": "gōu", + "4F5E": "nìng", + "4F5F": "tóng", + "4F60": "nǐ", + "4F61": "xiān", + "4F62": "qú", + "4F63": "yōng", + "4F64": "wǎ", + "4F65": "qiān", + "4F66": "shi", + "4F67": "kǎ", + "4F68": "bao", + "4F69": "pèi", + "4F6A": "huí", + "4F6B": "hè", + "4F6C": "lǎo", + "4F6D": "xiáng", + "4F6E": "gé", + "4F6F": "yáng", + "4F70": "bǎi", + "4F71": "fǎ", + "4F72": "mǐng", + "4F73": "jiā", + "4F74": "èr", + "4F75": "bìng", + "4F76": "jí", + "4F77": "hěn", + "4F78": "huó", + "4F79": "guǐ", + "4F7A": "quán", + "4F7B": "tiāo", + "4F7C": "jiǎo", + "4F7D": "cì", + "4F7E": "yì", + "4F7F": "shǐ", + "4F80": "xíng", + "4F81": "shēn", + "4F82": "tuō", + "4F83": "kǎn", + "4F84": "zhí", + "4F85": "gāi", + "4F86": "lái", + "4F87": "yí", + "4F88": "chǐ", + "4F89": "kuǎ", + "4F8A": "guāng", + "4F8B": "lì", + "4F8C": "yīn", + "4F8D": "shì", + "4F8E": "mǐ", + "4F8F": "zhū", + "4F90": "xù", + "4F91": "yòu", + "4F92": "ān", + "4F93": "lù", + "4F94": "móu", + "4F95": "ér", + "4F96": "lún", + "4F97": "dòng", + "4F98": "chà", + "4F99": "chī", + "4F9A": "xùn", + "4F9B": "gōng", + "4F9C": "zhōu", + "4F9D": "yī", + "4F9E": "rú", + "4F9F": "cún", + "4FA0": "xiá", + "4FA1": "sì", + "4FA2": "zài", + "4FA3": "lǚ", + "4FA4": "ta", + "4FA5": "jiǎo", + "4FA6": "zhēn", + "4FA7": "cè", + "4FA8": "qiáo", + "4FA9": "kuài", + "4FAA": "chái", + "4FAB": "nìng", + "4FAC": "nóng", + "4FAD": "jǐn", + "4FAE": "wǔ", + "4FAF": "hóu", + "4FB0": "jiǒng", + "4FB1": "chěng", + "4FB2": "zhèn", + "4FB3": "zuò", + "4FB4": "chǒu", + "4FB5": "qīn", + "4FB6": "lǚ", + "4FB7": "jú", + "4FB8": "shù", + "4FB9": "tǐng", + "4FBA": "shèn", + "4FBB": "tuì", + "4FBC": "bó", + "4FBD": "nán", + "4FBE": "xiāo", + "4FBF": "biàn", + "4FC0": "tuǐ", + "4FC1": "yǔ", + "4FC2": "xì", + "4FC3": "cù", + "4FC4": "é", + "4FC5": "qiú", + "4FC6": "xú", + "4FC7": "guàng", + "4FC8": "kù", + "4FC9": "wǔ", + "4FCA": "jùn", + "4FCB": "yì", + "4FCC": "fǔ", + "4FCD": "liáng", + "4FCE": "zǔ", + "4FCF": "qiào", + "4FD0": "lì", + "4FD1": "yǒng", + "4FD2": "hùn", + "4FD3": "jìng", + "4FD4": "qiàn", + "4FD5": "sàn", + "4FD6": "pěi", + "4FD7": "sú", + "4FD8": "fú", + "4FD9": "xī", + "4FDA": "lǐ", + "4FDB": "fǔ", + "4FDC": "pīng", + "4FDD": "bǎo", + "4FDE": "yú", + "4FDF": "qí", + "4FE0": "xiá", + "4FE1": "xìn", + "4FE2": "xiū", + "4FE3": "yǔ", + "4FE4": "dì", + "4FE5": "chē", + "4FE6": "chóu", + "4FE7": "zhi", + "4FE8": "yǎn", + "4FE9": "liǎ", + "4FEA": "lì", + "4FEB": "lái", + "4FEC": "si", + "4FED": "jiǎn", + "4FEE": "xiū", + "4FEF": "fǔ", + "4FF0": "huò", + "4FF1": "jù", + "4FF2": "xiào", + "4FF3": "pái", + "4FF4": "jiàn", + "4FF5": "biào", + "4FF6": "chù", + "4FF7": "fèi", + "4FF8": "fèng", + "4FF9": "yà", + "4FFA": "ǎn", + "4FFB": "bèi", + "4FFC": "yù", + "4FFD": "xīn", + "4FFE": "bǐ", + "4FFF": "hǔ", + "5000": "chāng", + "5001": "zhī", + "5002": "bìng", + "5003": "jiù", + "5004": "yáo", + "5005": "cuì", + "5006": "liǎ", + "5007": "wǎn", + "5008": "lái", + "5009": "cāng", + "500A": "zòng", + "500B": "gè", + "500C": "guān", + "500D": "bèi", + "500E": "tiǎn", + "500F": "shū", + "5010": "shū", + "5011": "men", + "5012": "dào", + "5013": "tán", + "5014": "jué", + "5015": "chuí", + "5016": "xìng", + "5017": "péng", + "5018": "tǎng", + "5019": "hou", + "501A": "yǐ", + "501B": "qī", + "501C": "tì", + "501D": "gàn", + "501E": "jìng", + "501F": "jiè", + "5020": "suī", + "5021": "chàng", + "5022": "jié", + "5023": "fǎng", + "5024": "zhí", + "5025": "kōng", + "5026": "juàn", + "5027": "zōng", + "5028": "jù", + "5029": "qiàn", + "502A": "ní", + "502B": "lún", + "502C": "zhuō", + "502D": "wō", + "502E": "luǒ", + "502F": "sōng", + "5030": "lèng", + "5031": "hùn", + "5032": "dōng", + "5033": "zì", + "5034": "bèn", + "5035": "wǔ", + "5036": "jù", + "5037": "nǎi", + "5038": "cǎi", + "5039": "jiǎn", + "503A": "zhài", + "503B": "yē", + "503C": "zhí", + "503D": "shà", + "503E": "qīng", + "503F": "qie", + "5040": "yīng", + "5041": "chēng", + "5042": "jiān", + "5043": "yǎn", + "5044": "ruǎn", + "5045": "zhòng", + "5046": "chǔn", + "5047": "jiǎ", + "5048": "jì", + "5049": "wěi", + "504A": "yǔ", + "504B": "bìng", + "504C": "ruò", + "504D": "tí", + "504E": "wēi", + "504F": "piān", + "5050": "yàn", + "5051": "fēng", + "5052": "tǎng", + "5053": "wò", + "5054": "è", + "5055": "xié", + "5056": "chě", + "5057": "shěng", + "5058": "kǎn", + "5059": "dì", + "505A": "zuò", + "505B": "chā", + "505C": "tíng", + "505D": "bèi", + "505E": "xiè", + "505F": "huáng", + "5060": "yǎo", + "5061": "zhàn", + "5062": "chǒu", + "5063": "yān", + "5064": "yóu", + "5065": "jiàn", + "5066": "xǔ", + "5067": "zhā", + "5068": "cī", + "5069": "fù", + "506A": "bī", + "506B": "zhì", + "506C": "zǒng", + "506D": "miǎn", + "506E": "jí", + "506F": "yǐ", + "5070": "xiè", + "5071": "xún", + "5072": "cāi", + "5073": "duān", + "5074": "cè", + "5075": "zhēn", + "5076": "ǒu", + "5077": "tōu", + "5078": "tōu", + "5079": "bèi", + "507A": "zá", + "507B": "lóu", + "507C": "jié", + "507D": "wěi", + "507E": "fèn", + "507F": "cháng", + "5080": "guī", + "5081": "sǒu", + "5082": "zhì", + "5083": "sù", + "5084": "xiā", + "5085": "fu", + "5086": "yuàn", + "5087": "rǒng", + "5088": "lì", + "5089": "nù", + "508A": "yùn", + "508B": "jiǎng", + "508C": "mà", + "508D": "bàng", + "508E": "diān", + "508F": "táng", + "5090": "hào", + "5091": "jié", + "5092": "xī", + "5093": "shàn", + "5094": "qiàn", + "5095": "jué", + "5096": "cāng", + "5097": "chù", + "5098": "sǎn", + "5099": "bèi", + "509A": "xiào", + "509B": "yǒng", + "509C": "yáo", + "509D": "tàn", + "509E": "suō", + "509F": "yǎng", + "50A0": "fā", + "50A1": "bìng", + "50A2": "jiā", + "50A3": "dǎi", + "50A4": "zài", + "50A5": "tǎng", + "50A6": "gu", + "50A7": "bīn", + "50A8": "chǔ", + "50A9": "nuó", + "50AA": "cān", + "50AB": "lěi", + "50AC": "cuī", + "50AD": "yōng", + "50AE": "zāo", + "50AF": "zǒng", + "50B0": "bēng", + "50B1": "sǒng", + "50B2": "ào", + "50B3": "chuán", + "50B4": "yǔ", + "50B5": "zhài", + "50B6": "zú", + "50B7": "shāng", + "50B8": "chuǎng", + "50B9": "jìng", + "50BA": "chì", + "50BB": "shǎ", + "50BC": "hàn", + "50BD": "zhāng", + "50BE": "qīng", + "50BF": "yàn", + "50C0": "dì", + "50C1": "xiè", + "50C2": "lóu", + "50C3": "bèi", + "50C4": "piào", + "50C5": "jǐn", + "50C6": "liàn", + "50C7": "lù", + "50C8": "mán", + "50C9": "qiān", + "50CA": "xiān", + "50CB": "tàn", + "50CC": "yíng", + "50CD": "dòng", + "50CE": "zhuàn", + "50CF": "xiàng", + "50D0": "shàn", + "50D1": "qiáo", + "50D2": "jiǒng", + "50D3": "tuǐ", + "50D4": "zǔn", + "50D5": "pú", + "50D6": "xī", + "50D7": "láo", + "50D8": "chǎng", + "50D9": "guāng", + "50DA": "liáo", + "50DB": "qī", + "50DC": "chēng", + "50DD": "chán", + "50DE": "wěi", + "50DF": "jī", + "50E0": "bō", + "50E1": "huì", + "50E2": "chuǎn", + "50E3": "tiě", + "50E4": "dàn", + "50E5": "jiǎo", + "50E6": "jiù", + "50E7": "sēng", + "50E8": "fèn", + "50E9": "xiàn", + "50EA": "jú", + "50EB": "è", + "50EC": "jiāo", + "50ED": "jiàn", + "50EE": "tóng", + "50EF": "lìn", + "50F0": "bó", + "50F1": "gù", + "50F2": "xian", + "50F3": "sù", + "50F4": "xiàn", + "50F5": "jiāng", + "50F6": "mǐn", + "50F7": "yè", + "50F8": "jìn", + "50F9": "jià", + "50FA": "qiào", + "50FB": "pì", + "50FC": "fēng", + "50FD": "zhòu", + "50FE": "ài", + "50FF": "sài", + "5100": "yí", + "5101": "jùn", + "5102": "nóng", + "5103": "chán", + "5104": "yì", + "5105": "dàng", + "5106": "jǐng", + "5107": "xuān", + "5108": "kuài", + "5109": "jiǎn", + "510A": "chù", + "510B": "dān", + "510C": "jiǎo", + "510D": "shǎ", + "510E": "zài", + "510F": "can", + "5110": "bīn", + "5111": "án", + "5112": "rú", + "5113": "tái", + "5114": "chóu", + "5115": "chái", + "5116": "lán", + "5117": "nǐ", + "5118": "jǐn", + "5119": "qiàn", + "511A": "méng", + "511B": "wǔ", + "511C": "níng", + "511D": "qióng", + "511E": "nǐ", + "511F": "cháng", + "5120": "liè", + "5121": "lěi", + "5122": "lǚ", + "5123": "kuǎng", + "5124": "bào", + "5125": "yù", + "5126": "biāo", + "5127": "zǎn", + "5128": "zhí", + "5129": "sì", + "512A": "yōu", + "512B": "háo", + "512C": "chèn", + "512D": "chèn", + "512E": "lì", + "512F": "téng", + "5130": "wěi", + "5131": "lǒng", + "5132": "chǔ", + "5133": "chán", + "5134": "ráng", + "5135": "shū", + "5136": "huì", + "5137": "lì", + "5138": "luó", + "5139": "zǎn", + "513A": "nuó", + "513B": "tǎng", + "513C": "yǎn", + "513D": "léi", + "513E": "nàng", + "513F": "ér", + "5140": "wù", + "5141": "yǔn", + "5142": "zān", + "5143": "yuán", + "5144": "xiōng", + "5145": "chōng", + "5146": "zhào", + "5147": "xiōng", + "5148": "xiān", + "5149": "guāng", + "514A": "duì", + "514B": "kè", + "514C": "duì", + "514D": "miǎn", + "514E": "tù", + "514F": "cháng", + "5150": "ér", + "5151": "duì", + "5152": "ér", + "5153": "jīn", + "5154": "tù", + "5155": "sì", + "5156": "yǎn", + "5157": "yǎn", + "5158": "shǐ", + "515A": "dǎng", + "515B": "qiān", + "515C": "dōu", + "515D": "fēn", + "515E": "máo", + "515F": "shēn", + "5160": "dōu", + "5162": "jīng", + "5163": "lǐ", + "5164": "huáng", + "5165": "rù", + "5166": "wáng", + "5167": "nèi", + "5168": "quán", + "5169": "liǎng", + "516A": "yú", + "516B": "bā", + "516C": "gōng", + "516D": "liù", + "516E": "xī", + "516F": "han", + "5170": "lán", + "5171": "gòng", + "5172": "tiān", + "5173": "guān", + "5174": "xìng", + "5175": "bīng", + "5176": "qí", + "5177": "jù", + "5178": "diǎn", + "5179": "zī", + "517A": "fēn", + "517B": "yǎng", + "517C": "jiān", + "517D": "shòu", + "517E": "jì", + "517F": "yì", + "5180": "jì", + "5181": "chǎn", + "5182": "jiōng", + "5183": "mào", + "5184": "rǎn", + "5185": "nèi", + "5186": "yuán", + "5187": "mǎo", + "5188": "gāng", + "5189": "rǎn", + "518A": "cè", + "518B": "jiōng", + "518C": "cè", + "518D": "zài", + "518E": "guǎ", + "518F": "jiǒng", + "5190": "mào", + "5191": "zhòu", + "5192": "mào", + "5193": "gòu", + "5194": "xǔ", + "5195": "miǎn", + "5196": "mì", + "5197": "rǒng", + "5198": "yín", + "5199": "xiě", + "519A": "kǎn", + "519B": "jūn", + "519C": "nóng", + "519D": "yí", + "519E": "mí", + "519F": "shì", + "51A0": "guān", + "51A1": "méng", + "51A2": "zhǒng", + "51A3": "jù", + "51A4": "yuān", + "51A5": "míng", + "51A6": "kòu", + "51A7": "lín", + "51A8": "fù", + "51A9": "xiě", + "51AA": "mì", + "51AB": "bīng", + "51AC": "dōng", + "51AD": "tái", + "51AE": "gāng", + "51AF": "féng", + "51B0": "bīng", + "51B1": "hù", + "51B2": "chōng", + "51B3": "jué", + "51B4": "hù", + "51B5": "kuàng", + "51B6": "yě", + "51B7": "lěng", + "51B8": "pàn", + "51B9": "fú", + "51BA": "mǐn", + "51BB": "dòng", + "51BC": "xiǎn", + "51BD": "liè", + "51BE": "qià", + "51BF": "jiān", + "51C0": "jìng", + "51C1": "shù", + "51C2": "měi", + "51C3": "tú", + "51C4": "qī", + "51C5": "gù", + "51C6": "zhǔn", + "51C7": "sōng", + "51C8": "jìng", + "51C9": "liáng", + "51CA": "qìng", + "51CB": "diāo", + "51CC": "líng", + "51CD": "dòng", + "51CE": "gàn", + "51CF": "jiǎn", + "51D0": "yīn", + "51D1": "còu", + "51D2": "yí", + "51D3": "lì", + "51D4": "chuàng", + "51D5": "mǐng", + "51D6": "zhun", + "51D7": "cuī", + "51D8": "sī", + "51D9": "duó", + "51DA": "jìn", + "51DB": "lǐn", + "51DC": "lǐn", + "51DD": "níng", + "51DE": "xī", + "51DF": "dú", + "51E0": "jǐ", + "51E1": "fán", + "51E2": "fán", + "51E3": "fán", + "51E4": "fèng", + "51E5": "jū", + "51E6": "chǔ", + "51E7": "zheng", + "51E8": "fēng", + "51E9": "mu", + "51EA": "zhi", + "51EB": "fú", + "51EC": "fēng", + "51ED": "píng", + "51EE": "fēng", + "51EF": "kǎi", + "51F0": "huáng", + "51F1": "kǎi", + "51F2": "gān", + "51F3": "dèng", + "51F4": "píng", + "51F5": "qiǎn", + "51F6": "xiōng", + "51F7": "kuài", + "51F8": "tū", + "51F9": "āo", + "51FA": "chū", + "51FB": "jī", + "51FC": "dàng", + "51FD": "hán", + "51FE": "hán", + "51FF": "záo", + "5200": "dāo", + "5201": "diāo", + "5202": "dāo", + "5203": "rèn", + "5204": "rèn", + "5205": "chuāng", + "5206": "fēn", + "5207": "qiè", + "5208": "yì", + "5209": "jī", + "520A": "kān", + "520B": "qiàn", + "520C": "cǔn", + "520D": "chú", + "520E": "wěn", + "520F": "jī", + "5210": "dǎn", + "5211": "xíng", + "5212": "huà", + "5213": "wán", + "5214": "jué", + "5215": "lí", + "5216": "yuè", + "5217": "liè", + "5218": "liú", + "5219": "zé", + "521A": "gāng", + "521B": "chuàng", + "521C": "fú", + "521D": "chū", + "521E": "qù", + "521F": "jū", + "5220": "shān", + "5221": "mǐn", + "5222": "líng", + "5223": "zhōng", + "5224": "pàn", + "5225": "bié", + "5226": "jié", + "5227": "jié", + "5228": "páo", + "5229": "lì", + "522A": "shān", + "522B": "bié", + "522C": "chǎn", + "522D": "jǐng", + "522E": "guā", + "522F": "gēng", + "5230": "dào", + "5231": "chuàng", + "5232": "kuī", + "5233": "kū", + "5234": "duò", + "5235": "èr", + "5236": "zhì", + "5237": "shuā", + "5238": "quàn", + "5239": "shā", + "523A": "cì", + "523B": "kè", + "523C": "jié", + "523D": "guì", + "523E": "cì", + "523F": "guì", + "5240": "kǎi", + "5241": "duò", + "5242": "jì", + "5243": "tì", + "5244": "jǐng", + "5245": "lóu", + "5246": "luǒ", + "5247": "zé", + "5248": "yuān", + "5249": "cuò", + "524A": "xuē", + "524B": "kè", + "524C": "lá", + "524D": "qián", + "524E": "shā", + "524F": "chuàng", + "5250": "guǎ", + "5251": "jiàn", + "5252": "cuò", + "5253": "lí", + "5254": "tī", + "5255": "fèi", + "5256": "pōu", + "5257": "chǎn", + "5258": "qí", + "5259": "chuàng", + "525A": "zì", + "525B": "gāng", + "525C": "wān", + "525D": "bō", + "525E": "jī", + "525F": "duō", + "5260": "qíng", + "5261": "shàn", + "5262": "dū", + "5263": "jiàn", + "5264": "jì", + "5265": "bō", + "5266": "yān", + "5267": "jù", + "5268": "huō", + "5269": "shèng", + "526A": "jiǎn", + "526B": "duó", + "526C": "duān", + "526D": "wū", + "526E": "guǎ", + "526F": "fù", + "5270": "shèng", + "5271": "jiàn", + "5272": "gē", + "5273": "dá", + "5274": "kǎi", + "5275": "chuàng", + "5276": "chuān", + "5277": "chǎn", + "5278": "tuán", + "5279": "lù", + "527A": "lí", + "527B": "pěng", + "527C": "shān", + "527D": "piāo", + "527E": "kōu", + "527F": "jiǎo", + "5280": "guā", + "5281": "qiāo", + "5282": "jué", + "5283": "huà", + "5284": "zhā", + "5285": "zhuò", + "5286": "lián", + "5287": "jù", + "5288": "pī", + "5289": "liú", + "528A": "guì", + "528B": "jiǎo", + "528C": "guì", + "528D": "jiàn", + "528E": "jiàn", + "528F": "tāng", + "5290": "huō", + "5291": "jì", + "5292": "jiàn", + "5293": "yì", + "5294": "jiàn", + "5295": "zhì", + "5296": "chán", + "5297": "jiǎn", + "5298": "mó", + "5299": "lí", + "529A": "zhǔ", + "529B": "lì", + "529C": "yà", + "529D": "quàn", + "529E": "bàn", + "529F": "gōng", + "52A0": "jiā", + "52A1": "wu", + "52A2": "mài", + "52A3": "liè", + "52A4": "jìn", + "52A5": "kēng", + "52A6": "xié", + "52A7": "zhǐ", + "52A8": "dòng", + "52A9": "zhù", + "52AA": "nǔ", + "52AB": "jié", + "52AC": "qú", + "52AD": "shào", + "52AE": "yì", + "52AF": "zhū", + "52B0": "mò", + "52B1": "lì", + "52B2": "jìn", + "52B3": "láo", + "52B4": "láo", + "52B5": "juàn", + "52B6": "kǒu", + "52B7": "yáng", + "52B8": "wā", + "52B9": "xiào", + "52BA": "móu", + "52BB": "kuāng", + "52BC": "jié", + "52BD": "liè", + "52BE": "hé", + "52BF": "shì", + "52C0": "kè", + "52C1": "jìn", + "52C2": "gào", + "52C3": "bó", + "52C4": "mǐn", + "52C5": "chì", + "52C6": "láng", + "52C7": "yǒng", + "52C8": "yǒng", + "52C9": "miǎn", + "52CA": "kè", + "52CB": "xūn", + "52CC": "juàn", + "52CD": "qíng", + "52CE": "lù", + "52CF": "bù", + "52D0": "měng", + "52D1": "chì", + "52D2": "lēi", + "52D3": "kài", + "52D4": "miǎn", + "52D5": "dòng", + "52D6": "xù", + "52D7": "xù", + "52D8": "kān", + "52D9": "wu", + "52DA": "yì", + "52DB": "xūn", + "52DC": "wěng", + "52DD": "shèng", + "52DE": "láo", + "52DF": "mù", + "52E0": "lù", + "52E1": "piào", + "52E2": "shì", + "52E3": "jī", + "52E4": "qín", + "52E5": "jiàng", + "52E6": "chāo", + "52E7": "quàn", + "52E8": "xiàng", + "52E9": "yì", + "52EA": "jué", + "52EB": "fān", + "52EC": "juān", + "52ED": "tóng", + "52EE": "jù", + "52EF": "dān", + "52F0": "xié", + "52F1": "mài", + "52F2": "xūn", + "52F3": "xūn", + "52F4": "lǜ", + "52F5": "lì", + "52F6": "chè", + "52F7": "ráng", + "52F8": "quàn", + "52F9": "bāo", + "52FA": "sháo", + "52FB": "yún", + "52FC": "jiū", + "52FD": "bào", + "52FE": "gōu", + "52FF": "wù", + "5300": "yún", + "5301": "wén", + "5302": "bi", + "5303": "gài", + "5304": "gài", + "5305": "bāo", + "5306": "cōng", + "5307": "yi", + "5308": "xiōng", + "5309": "pēng", + "530A": "jū", + "530B": "táo", + "530C": "gé", + "530D": "pú", + "530E": "è", + "530F": "páo", + "5310": "fú", + "5311": "gōng", + "5312": "dá", + "5313": "jiù", + "5314": "qiōng", + "5315": "bǐ", + "5316": "huà", + "5317": "běi", + "5318": "nǎo", + "5319": "shi", + "531A": "fāng", + "531B": "jiù", + "531C": "yí", + "531D": "zā", + "531E": "jiàng", + "531F": "kàng", + "5320": "jiang", + "5321": "kuāng", + "5322": "hū", + "5323": "xiá", + "5324": "qū", + "5325": "biàn", + "5326": "guǐ", + "5327": "qiè", + "5328": "zāng", + "5329": "kuāng", + "532A": "fěi", + "532B": "hū", + "532C": "yǔ", + "532D": "guǐ", + "532E": "kuì", + "532F": "huì", + "5330": "dān", + "5331": "guì", + "5332": "lián", + "5333": "lián", + "5334": "suǎn", + "5335": "dú", + "5336": "jiù", + "5337": "jué", + "5338": "xì", + "5339": "pǐ", + "533A": "qū", + "533B": "yī", + "533C": "kē", + "533D": "yǎn", + "533E": "biǎn", + "533F": "nì", + "5340": "qū", + "5341": "shí", + "5342": "xùn", + "5343": "qiān", + "5344": "niàn", + "5345": "sà", + "5346": "zú", + "5347": "shēng", + "5348": "wǔ", + "5349": "huì", + "534A": "bàn", + "534B": "shì", + "534C": "xì", + "534D": "wàn", + "534E": "huá", + "534F": "xié", + "5350": "wàn", + "5351": "bēi", + "5352": "zú", + "5353": "zhuō", + "5354": "xié", + "5355": "dān", + "5356": "mài", + "5357": "nán", + "5358": "dān", + "5359": "jí", + "535A": "bó", + "535B": "shuài", + "535C": "bo", + "535D": "kuàng", + "535E": "biàn", + "535F": "bǔ", + "5360": "zhàn", + "5361": "kǎ", + "5362": "lú", + "5363": "yǒu", + "5364": "lǔ", + "5365": "xī", + "5366": "guà", + "5367": "wò", + "5368": "xiè", + "5369": "jié", + "536A": "jié", + "536B": "wèi", + "536C": "áng", + "536D": "qióng", + "536E": "zhī", + "536F": "mǎo", + "5370": "yìn", + "5371": "wēi", + "5372": "shào", + "5373": "jí", + "5374": "què", + "5375": "luǎn", + "5376": "chǐ", + "5377": "juǎn", + "5378": "xiè", + "5379": "xù", + "537A": "jǐn", + "537B": "què", + "537C": "wù", + "537D": "jí", + "537E": "è", + "537F": "qīng", + "5380": "xī", + "5381": "san", + "5382": "chǎng", + "5383": "wěi", + "5384": "è", + "5385": "tīng", + "5386": "lì", + "5387": "zhé", + "5388": "hǎn", + "5389": "lì", + "538A": "yǎ", + "538B": "yā", + "538C": "yàn", + "538D": "shè", + "538E": "dǐ", + "538F": "zhǎ", + "5390": "páng", + "5391": "yá", + "5392": "hé", + "5393": "yá", + "5394": "zhì", + "5395": "cè", + "5396": "páng", + "5397": "tí", + "5398": "lí", + "5399": "shè", + "539A": "hòu", + "539B": "tīng", + "539C": "zuī", + "539D": "cuò", + "539E": "fèi", + "539F": "yuán", + "53A0": "cè", + "53A1": "yuán", + "53A2": "xiāng", + "53A3": "yǎn", + "53A4": "lì", + "53A5": "jué", + "53A6": "shà", + "53A7": "diān", + "53A8": "chú", + "53A9": "jiù", + "53AA": "jǐn", + "53AB": "áo", + "53AC": "guǐ", + "53AD": "yàn", + "53AE": "sī", + "53AF": "lì", + "53B0": "chǎng", + "53B1": "lán", + "53B2": "lì", + "53B3": "yán", + "53B4": "yǎn", + "53B5": "yuán", + "53B6": "sī", + "53B7": "gōng", + "53B8": "lín", + "53B9": "róu", + "53BA": "qù", + "53BB": "qù", + "53BC": "ěr", + "53BD": "lěi", + "53BE": "dū", + "53BF": "xiàn", + "53C0": "zhuān", + "53C1": "sān", + "53C2": "cān", + "53C3": "cān", + "53C4": "cān", + "53C5": "cān", + "53C6": "ài", + "53C7": "dài", + "53C8": "yòu", + "53C9": "chā", + "53CA": "jí", + "53CB": "you", + "53CC": "shuāng", + "53CD": "fǎn", + "53CE": "shōu", + "53CF": "guài", + "53D0": "bá", + "53D1": "fā", + "53D2": "ruò", + "53D3": "shì", + "53D4": "shū", + "53D5": "zhuó", + "53D6": "qǔ", + "53D7": "shòu", + "53D8": "biàn", + "53D9": "xù", + "53DA": "jiǎ", + "53DB": "pàn", + "53DC": "sǒu", + "53DD": "gào", + "53DE": "wèi", + "53DF": "sǒu", + "53E0": "dié", + "53E1": "ruì", + "53E2": "cóng", + "53E3": "kǒu", + "53E4": "gǔ", + "53E5": "jù", + "53E6": "lìng", + "53E7": "guǎ", + "53E8": "dāo", + "53E9": "kòu", + "53EA": "zhǐ", + "53EB": "jiào", + "53EC": "zhào", + "53ED": "ba", + "53EE": "dīng", + "53EF": "kě", + "53F0": "tái", + "53F1": "chì", + "53F2": "shǐ", + "53F3": "yòu", + "53F4": "qiú", + "53F5": "pǒ", + "53F6": "yè", + "53F7": "hào", + "53F8": "sī", + "53F9": "tàn", + "53FA": "chǐ", + "53FB": "lè", + "53FC": "diāo", + "53FD": "jī", + "53FE": "liǎo", + "53FF": "hōng", + "5400": "miē", + "5401": "xū", + "5402": "máng", + "5403": "chī", + "5404": "gè", + "5405": "xuān", + "5406": "yāo", + "5407": "zǐ", + "5408": "hé", + "5409": "jí", + "540A": "diào", + "540B": "cùn", + "540C": "tóng", + "540D": "míng", + "540E": "hòu", + "540F": "lì", + "5410": "tǔ", + "5411": "xiàng", + "5412": "zhā", + "5413": "xià", + "5414": "yě", + "5415": "lǚ", + "5416": "yā", + "5417": "ma", + "5418": "ǒu", + "5419": "huō", + "541A": "yī", + "541B": "jūn", + "541C": "chǒu", + "541D": "lìn", + "541E": "tūn", + "541F": "yín", + "5420": "fèi", + "5421": "bǐ", + "5422": "qìn", + "5423": "qìn", + "5424": "jiè", + "5425": "bù", + "5426": "fǒu", + "5427": "ba", + "5428": "dūn", + "5429": "fēn", + "542A": "é", + "542B": "hán", + "542C": "tīng", + "542D": "kēng", + "542E": "shǔn", + "542F": "qǐ", + "5430": "hóng", + "5431": "zhī", + "5432": "yǐn", + "5433": "wú", + "5434": "wú", + "5435": "chǎo", + "5436": "ne", + "5437": "xuè", + "5438": "xī", + "5439": "chuī", + "543A": "dōu", + "543B": "wěn", + "543C": "hǒu", + "543D": "hōng", + "543E": "wú", + "543F": "gào", + "5440": "ya", + "5441": "jùn", + "5442": "lǚ", + "5443": "è", + "5444": "gé", + "5445": "méi", + "5446": "dāi", + "5447": "qǐ", + "5448": "chéng", + "5449": "wú", + "544A": "gào", + "544B": "fū", + "544C": "jiào", + "544D": "hōng", + "544E": "chǐ", + "544F": "shēng", + "5450": "ne", + "5451": "tūn", + "5452": "fǔ", + "5453": "yì", + "5454": "dāi", + "5455": "ǒu", + "5456": "lì", + "5457": "bei", + "5458": "yuán", + "5459": "guō", + "545A": "wen", + "545B": "qiāng", + "545C": "wū", + "545D": "è", + "545E": "shī", + "545F": "juǎn", + "5460": "pěn", + "5461": "wěn", + "5462": "ne", + "5463": "ḿ", + "5464": "lìng", + "5465": "rán", + "5466": "yōu", + "5467": "dǐ", + "5468": "zhōu", + "5469": "shì", + "546A": "zhòu", + "546B": "tiè", + "546C": "xì", + "546D": "yì", + "546E": "qì", + "546F": "píng", + "5470": "zǐ", + "5471": "gū", + "5472": "cī", + "5473": "wèi", + "5474": "xǔ", + "5475": "ā", + "5476": "náo", + "5477": "gā", + "5478": "pēi", + "5479": "yì", + "547A": "xiāo", + "547B": "shēn", + "547C": "hū", + "547D": "mìng", + "547E": "dá", + "547F": "qù", + "5480": "jǔ", + "5481": "hán", + "5482": "zā", + "5483": "tuō", + "5484": "duō", + "5485": "pǒu", + "5486": "páo", + "5487": "bié", + "5488": "fú", + "5489": "yāng", + "548A": "hé", + "548B": "zǎ", + "548C": "hé", + "548D": "hāi", + "548E": "jiù", + "548F": "yǒng", + "5490": "fu", + "5491": "dā", + "5492": "zhòu", + "5493": "wǎ", + "5494": "kā", + "5495": "gu", + "5496": "kā", + "5497": "zuo", + "5498": "bù", + "5499": "lóng", + "549A": "dōng", + "549B": "níng", + "549C": "ta", + "549D": "sī", + "549E": "xiàn", + "549F": "huò", + "54A0": "qì", + "54A1": "èr", + "54A2": "è", + "54A3": "guāng", + "54A4": "zhà", + "54A5": "xì", + "54A6": "yí", + "54A7": "lie", + "54A8": "zī", + "54A9": "miē", + "54AA": "mī", + "54AB": "zhǐ", + "54AC": "yǎo", + "54AD": "jī", + "54AE": "zhòu", + "54AF": "gē", + "54B0": "shù", + "54B1": "zán", + "54B2": "xiào", + "54B3": "hāi", + "54B4": "huī", + "54B5": "kuǎ", + "54B6": "huài", + "54B7": "táo", + "54B8": "xián", + "54B9": "è", + "54BA": "xuǎn", + "54BB": "xiū", + "54BC": "guō", + "54BD": "yàn", + "54BE": "lǎo", + "54BF": "yī", + "54C0": "āi", + "54C1": "pǐn", + "54C2": "shěn", + "54C3": "tóng", + "54C4": "hōng", + "54C5": "xiōng", + "54C6": "duō", + "54C7": "wa", + "54C8": "hā", + "54C9": "zāi", + "54CA": "yòu", + "54CB": "diè", + "54CC": "pài", + "54CD": "xiǎng", + "54CE": "āi", + "54CF": "gén", + "54D0": "kuāng", + "54D1": "yǎ", + "54D2": "dā", + "54D3": "xiāo", + "54D4": "bì", + "54D5": "huì", + "54D6": "nian", + "54D7": "huā", + "54D8": "xing", + "54D9": "kuài", + "54DA": "duǒ", + "54DB": "fēn", + "54DC": "jì", + "54DD": "nóng", + "54DE": "mōu", + "54DF": "yō", + "54E0": "hào", + "54E1": "yuán", + "54E2": "lòng", + "54E3": "pǒu", + "54E4": "máng", + "54E5": "gē", + "54E6": "ó", + "54E7": "chī", + "54E8": "shào", + "54E9": "li", + "54EA": "nǎ", + "54EB": "zú", + "54EC": "hé", + "54ED": "kū", + "54EE": "xiāo", + "54EF": "xiàn", + "54F0": "láo", + "54F1": "bō", + "54F2": "zhé", + "54F3": "zhā", + "54F4": "liàng", + "54F5": "bā", + "54F6": "miē", + "54F7": "liè", + "54F8": "suī", + "54F9": "fú", + "54FA": "bǔ", + "54FB": "hàn", + "54FC": "hēng", + "54FD": "gěng", + "54FE": "shuō", + "54FF": "gě", + "5500": "yòu", + "5501": "yàn", + "5502": "gū", + "5503": "gǔ", + "5504": "bei", + "5505": "hán", + "5506": "suō", + "5507": "chún", + "5508": "yì", + "5509": "āi", + "550A": "jiá", + "550B": "tū", + "550C": "xián", + "550D": "wǎn", + "550E": "lì", + "550F": "xī", + "5510": "táng", + "5511": "zuò", + "5512": "qiú", + "5513": "chē", + "5514": "wú", + "5515": "zào", + "5516": "yǎ", + "5517": "dōu", + "5518": "qǐ", + "5519": "dí", + "551A": "qìn", + "551B": "mà", + "551C": "mò", + "551D": "gòng", + "551E": "dǒu", + "551F": "qù", + "5520": "láo", + "5521": "liǎng", + "5522": "suǒ", + "5523": "zào", + "5524": "huàn", + "5525": "lang", + "5526": "shā", + "5527": "jī", + "5528": "zuǒ", + "5529": "wō", + "552A": "fěng", + "552B": "jìn", + "552C": "hu", + "552D": "qì", + "552E": "shòu", + "552F": "wéi", + "5530": "shuā", + "5531": "chàng", + "5532": "ér", + "5533": "lì", + "5534": "qiàng", + "5535": "ǎn", + "5536": "zé", + "5537": "yō", + "5538": "niàn", + "5539": "yū", + "553A": "tiǎn", + "553B": "lài", + "553C": "shà", + "553D": "xī", + "553E": "tuò", + "553F": "hū", + "5540": "ái", + "5541": "zhāo", + "5542": "nǒu", + "5543": "kěn", + "5544": "zhuó", + "5545": "zhuó", + "5546": "shāng", + "5547": "dì", + "5548": "hēng", + "5549": "lín", + "554A": "a", + "554B": "cǎi", + "554C": "xiāng", + "554D": "tūn", + "554E": "wǔ", + "554F": "wèn", + "5550": "cuì", + "5551": "shà", + "5552": "gǔ", + "5553": "qǐ", + "5554": "qǐ", + "5555": "táo", + "5556": "dàn", + "5557": "dàn", + "5558": "yè", + "5559": "zǐ", + "555A": "bǐ", + "555B": "cuì", + "555C": "chuài", + "555D": "hé", + "555E": "yǎ", + "555F": "qǐ", + "5560": "zhé", + "5561": "fēi", + "5562": "liǎng", + "5563": "xián", + "5564": "pí", + "5565": "shà", + "5566": "la", + "5567": "zé", + "5568": "yīng", + "5569": "guà", + "556A": "pā", + "556B": "zhě", + "556C": "sè", + "556D": "zhuàn", + "556E": "niè", + "556F": "guo", + "5570": "luō", + "5571": "yān", + "5572": "dì", + "5573": "quán", + "5574": "chǎn", + "5575": "bo", + "5576": "dìng", + "5577": "lāng", + "5578": "xiào", + "5579": "jú", + "557A": "táng", + "557B": "chì", + "557C": "tí", + "557D": "án", + "557E": "jiū", + "557F": "dàn", + "5580": "kā", + "5581": "yóng", + "5582": "wèi", + "5583": "nán", + "5584": "shàn", + "5585": "yù", + "5586": "zhé", + "5587": "lǎ", + "5588": "jiē", + "5589": "hóu", + "558A": "hǎn", + "558B": "dié", + "558C": "zhōu", + "558D": "chái", + "558E": "wāi", + "558F": "nuò", + "5590": "yù", + "5591": "yīn", + "5592": "zá", + "5593": "yāo", + "5594": "ō", + "5595": "miǎn", + "5596": "hú", + "5597": "yǔn", + "5598": "chuǎn", + "5599": "huì", + "559A": "huàn", + "559B": "huàn", + "559C": "xǐ", + "559D": "hē", + "559E": "jī", + "559F": "kuì", + "55A0": "zhǒng", + "55A1": "wéi", + "55A2": "shà", + "55A3": "xù", + "55A4": "huáng", + "55A5": "duó", + "55A6": "niè", + "55A7": "xuān", + "55A8": "liàng", + "55A9": "yù", + "55AA": "sàng", + "55AB": "chī", + "55AC": "qiáo", + "55AD": "yàn", + "55AE": "dān", + "55AF": "pèn", + "55B0": "cān", + "55B1": "lí", + "55B2": "yō", + "55B3": "zhā", + "55B4": "wēi", + "55B5": "miāo", + "55B6": "yíng", + "55B7": "pēn", + "55B8": "bǔ", + "55B9": "kuí", + "55BA": "xì", + "55BB": "yù", + "55BC": "jié", + "55BD": "lou", + "55BE": "kù", + "55BF": "zào", + "55C0": "hù", + "55C1": "tí", + "55C2": "yáo", + "55C3": "hè", + "55C4": "á", + "55C5": "xiù", + "55C6": "qiāng", + "55C7": "sè", + "55C8": "yōng", + "55C9": "sù", + "55CA": "hǒng", + "55CB": "xié", + "55CC": "ài", + "55CD": "suō", + "55CE": "ma", + "55CF": "chā", + "55D0": "hài", + "55D1": "kē", + "55D2": "dā", + "55D3": "sǎng", + "55D4": "chēn", + "55D5": "rù", + "55D6": "sōu", + "55D7": "wā", + "55D8": "jī", + "55D9": "pǎng", + "55DA": "wū", + "55DB": "qiǎn", + "55DC": "shì", + "55DD": "gé", + "55DE": "zī", + "55DF": "jiē", + "55E0": "luò", + "55E1": "wēng", + "55E2": "wà", + "55E3": "sì", + "55E4": "chī", + "55E5": "háo", + "55E6": "suo", + "55E8": "hāi", + "55E9": "suǒ", + "55EA": "qín", + "55EB": "niè", + "55EC": "hē", + "55ED": "zhí", + "55EE": "sài", + "55EF": "ń", + "55F0": "gè", + "55F1": "ná", + "55F2": "diǎ", + "55F3": "āi", + "55F4": "qiang", + "55F5": "tōng", + "55F6": "bì", + "55F7": "áo", + "55F8": "áo", + "55F9": "lián", + "55FA": "zuī", + "55FB": "zhē", + "55FC": "mò", + "55FD": "sou", + "55FE": "sǒu", + "55FF": "tǎn", + "5600": "dí", + "5601": "qī", + "5602": "jiào", + "5603": "chōng", + "5604": "jiāo", + "5605": "kǎi", + "5606": "tàn", + "5607": "shān", + "5608": "cáo", + "5609": "jiā", + "560A": "ái", + "560B": "xiāo", + "560C": "piào", + "560D": "lou", + "560E": "gā", + "560F": "gǔ", + "5610": "xiāo", + "5611": "hū", + "5612": "huì", + "5613": "guō", + "5614": "ǒu", + "5615": "xiān", + "5616": "zé", + "5617": "cháng", + "5618": "xū", + "5619": "pó", + "561A": "dē", + "561B": "ma", + "561C": "mà", + "561D": "hú", + "561E": "lei", + "561F": "dū", + "5620": "gā", + "5621": "tāng", + "5622": "yě", + "5623": "bēng", + "5624": "yīng", + "5625": "sai", + "5626": "jiào", + "5627": "mì", + "5628": "xiào", + "5629": "huā", + "562A": "mǎi", + "562B": "rán", + "562C": "chuài", + "562D": "pēng", + "562E": "láo", + "562F": "xiào", + "5630": "jī", + "5631": "zhǔ", + "5632": "cháo", + "5633": "kuì", + "5634": "zuǐ", + "5635": "xiāo", + "5636": "sī", + "5637": "háo", + "5638": "fǔ", + "5639": "liáo", + "563A": "qiáo", + "563B": "xī", + "563C": "chù", + "563D": "chǎn", + "563E": "dàn", + "563F": "hēi", + "5640": "xùn", + "5641": "ě", + "5642": "zǔn", + "5643": "fān", + "5644": "chī", + "5645": "huī", + "5646": "zǎn", + "5647": "chuáng", + "5648": "cù", + "5649": "dàn", + "564A": "yù", + "564B": "tūn", + "564C": "cēng", + "564D": "jiào", + "564E": "yē", + "564F": "xī", + "5650": "qì", + "5651": "háo", + "5652": "lián", + "5653": "xū", + "5654": "dēng", + "5655": "huī", + "5656": "yín", + "5657": "pū", + "5658": "juē", + "5659": "qín", + "565A": "xún", + "565B": "niè", + "565C": "lū", + "565D": "sī", + "565E": "yǎn", + "565F": "yìng", + "5660": "dā", + "5661": "zhān", + "5662": "ō", + "5663": "zhòu", + "5664": "jìn", + "5665": "nóng", + "5666": "huì", + "5667": "xiè", + "5668": "qì", + "5669": "è", + "566A": "zào", + "566B": "yī", + "566C": "shì", + "566D": "jiào", + "566E": "yuàn", + "566F": "āi", + "5670": "yōng", + "5671": "jué", + "5672": "kuài", + "5673": "yǔ", + "5674": "pēn", + "5675": "dào", + "5676": "gá", + "5677": "hm", + "5678": "dūn", + "5679": "dāng", + "567A": "xin", + "567B": "sāi", + "567C": "pī", + "567D": "pǐ", + "567E": "yīn", + "567F": "zuǐ", + "5680": "níng", + "5681": "dí", + "5682": "làn", + "5683": "tā", + "5684": "huō", + "5685": "rú", + "5686": "hāo", + "5687": "xià", + "5688": "yè", + "5689": "duō", + "568A": "pì", + "568B": "chóu", + "568C": "jì", + "568D": "jìn", + "568E": "háo", + "568F": "tì", + "5690": "cháng", + "5691": "xun", + "5692": "me", + "5693": "cā", + "5694": "tì", + "5695": "lǔ", + "5696": "huì", + "5697": "bó", + "5698": "yōu", + "5699": "niè", + "569A": "yín", + "569B": "hù", + "569C": "me", + "569D": "hōng", + "569E": "zhé", + "569F": "lí", + "56A0": "liú", + "56A1": "hai", + "56A2": "náng", + "56A3": "xiāo", + "56A4": "mó", + "56A5": "yàn", + "56A6": "lì", + "56A7": "lú", + "56A8": "lóng", + "56A9": "mó", + "56AA": "dàn", + "56AB": "chèn", + "56AC": "pín", + "56AD": "pǐ", + "56AE": "xiàng", + "56AF": "huò", + "56B0": "mó", + "56B1": "xì", + "56B2": "duǒ", + "56B3": "kù", + "56B4": "yán", + "56B5": "chán", + "56B6": "yīng", + "56B7": "rǎng", + "56B8": "diǎn", + "56B9": "la", + "56BA": "tà", + "56BB": "xiāo", + "56BC": "jué", + "56BD": "chuò", + "56BE": "huān", + "56BF": "huò", + "56C0": "zhuàn", + "56C1": "niè", + "56C2": "xiāo", + "56C3": "cà", + "56C4": "lí", + "56C5": "chǎn", + "56C6": "chài", + "56C7": "lì", + "56C8": "yì", + "56C9": "luō", + "56CA": "náng", + "56CB": "zá", + "56CC": "sū", + "56CD": "xǐ", + "56CE": "zen", + "56CF": "jiān", + "56D0": "zá", + "56D1": "zhǔ", + "56D2": "lán", + "56D3": "niè", + "56D4": "nāng", + "56D5": "lǎn", + "56D6": "lo", + "56D7": "wéi", + "56D8": "huí", + "56D9": "yīn", + "56DA": "qiú", + "56DB": "sì", + "56DC": "nín", + "56DD": "jiǎn", + "56DE": "huí", + "56DF": "xìn", + "56E0": "yīn", + "56E1": "nān", + "56E2": "tuán", + "56E3": "tuán", + "56E4": "dùn", + "56E5": "kàng", + "56E6": "yuān", + "56E7": "jiǒng", + "56E8": "piān", + "56E9": "yún", + "56EA": "cōng", + "56EB": "hú", + "56EC": "huí", + "56ED": "yuán", + "56EE": "é", + "56EF": "guó", + "56F0": "kùn", + "56F1": "cōng", + "56F2": "tōng", + "56F3": "tú", + "56F4": "wéi", + "56F5": "lún", + "56F6": "guó", + "56F7": "qūn", + "56F8": "rì", + "56F9": "líng", + "56FA": "gù", + "56FB": "guó", + "56FC": "tāi", + "56FD": "guó", + "56FE": "tú", + "56FF": "yòu", + "5700": "guó", + "5701": "yín", + "5702": "hùn", + "5703": "pǔ", + "5704": "yǔ", + "5705": "hán", + "5706": "yuán", + "5707": "lún", + "5708": "quān", + "5709": "yǔ", + "570A": "qīng", + "570B": "guó", + "570C": "chuán", + "570D": "wéi", + "570E": "yuán", + "570F": "quān", + "5710": "kū", + "5711": "fù", + "5712": "yuán", + "5713": "yuán", + "5714": "yà", + "5715": "tú", + "5716": "tú", + "5717": "tú", + "5718": "tuán", + "5719": "lüè", + "571A": "huì", + "571B": "yì", + "571C": "huán", + "571D": "luán", + "571E": "luán", + "571F": "tǔ", + "5720": "yà", + "5721": "tǔ", + "5722": "tǐng", + "5723": "shèng", + "5724": "pǔ", + "5725": "lù", + "5726": "kuai", + "5727": "yā", + "5728": "zài", + "5729": "wéi", + "572A": "gē", + "572B": "yù", + "572C": "wū", + "572D": "guī", + "572E": "pǐ", + "572F": "yí", + "5730": "de", + "5731": "qiān", + "5732": "qiān", + "5733": "zhèn", + "5734": "zhuó", + "5735": "dàng", + "5736": "qià", + "5737": "xia", + "5738": "shan", + "5739": "kuàng", + "573A": "chǎng", + "573B": "qí", + "573C": "niè", + "573D": "mò", + "573E": "jī", + "573F": "jiá", + "5740": "zhǐ", + "5741": "zhǐ", + "5742": "bǎn", + "5743": "xūn", + "5744": "yì", + "5745": "qǐn", + "5746": "méi", + "5747": "jūn", + "5748": "rǒng", + "5749": "tún", + "574A": "fang", + "574B": "bèn", + "574C": "bèn", + "574D": "tān", + "574E": "kǎn", + "574F": "huài", + "5750": "zuò", + "5751": "kēng", + "5752": "bì", + "5753": "jǐng", + "5754": "dì", + "5755": "jīng", + "5756": "jì", + "5757": "kuài", + "5758": "dǐ", + "5759": "jīng", + "575A": "jiān", + "575B": "tán", + "575C": "lì", + "575D": "bà", + "575E": "wù", + "575F": "fén", + "5760": "zhuì", + "5761": "pō", + "5762": "bàn", + "5763": "tāng", + "5764": "kūn", + "5765": "qū", + "5766": "tǎn", + "5767": "zhī", + "5768": "tuó", + "5769": "gān", + "576A": "píng", + "576B": "diàn", + "576C": "guà", + "576D": "ní", + "576E": "tái", + "576F": "pī", + "5770": "jiōng", + "5771": "yǎng", + "5772": "fó", + "5773": "ào", + "5774": "lù", + "5775": "qiū", + "5776": "mǔ", + "5777": "kě", + "5778": "gòu", + "5779": "xuè", + "577A": "bá", + "577B": "chí", + "577C": "chè", + "577D": "líng", + "577E": "zhù", + "577F": "fù", + "5780": "hū", + "5781": "zhì", + "5782": "chuí", + "5783": "lā", + "5784": "lǒng", + "5785": "lǒng", + "5786": "lú", + "5787": "ào", + "5788": "dài", + "5789": "páo", + "578A": "min", + "578B": "xíng", + "578C": "dòng", + "578D": "jì", + "578E": "hè", + "578F": "lǜ", + "5790": "cí", + "5791": "chǐ", + "5792": "lěi", + "5793": "gāi", + "5794": "yīn", + "5795": "hòu", + "5796": "duī", + "5797": "zhào", + "5798": "fú", + "5799": "guāng", + "579A": "yáo", + "579B": "duǒ", + "579C": "duǒ", + "579D": "guǐ", + "579E": "chá", + "579F": "yáng", + "57A0": "yín", + "57A1": "fá", + "57A2": "gòu", + "57A3": "yuán", + "57A4": "dié", + "57A5": "xié", + "57A6": "kěn", + "57A7": "shǎng", + "57A8": "shǒu", + "57A9": "è", + "57AA": "bing", + "57AB": "diàn", + "57AC": "hóng", + "57AD": "yā", + "57AE": "kuǎ", + "57AF": "da", + "57B0": "ka", + "57B1": "dàng", + "57B2": "kǎi", + "57B3": "hang", + "57B4": "nǎo", + "57B5": "ǎn", + "57B6": "xīng", + "57B7": "xiàn", + "57B8": "yuàn", + "57B9": "bāng", + "57BA": "fū", + "57BB": "bà", + "57BC": "yì", + "57BD": "yìn", + "57BE": "hàn", + "57BF": "xù", + "57C0": "chuí", + "57C1": "qín", + "57C2": "gěng", + "57C3": "āi", + "57C4": "běng", + "57C5": "fáng", + "57C6": "què", + "57C7": "yǒng", + "57C8": "jùn", + "57C9": "jiā", + "57CA": "dì", + "57CB": "mái", + "57CC": "làng", + "57CD": "juǎn", + "57CE": "chéng", + "57CF": "shān", + "57D0": "jīn", + "57D1": "zhé", + "57D2": "liè", + "57D3": "liè", + "57D4": "bù", + "57D5": "chéng", + "57D6": "hua", + "57D7": "bù", + "57D8": "shí", + "57D9": "xūn", + "57DA": "guō", + "57DB": "jiōng", + "57DC": "yě", + "57DD": "niàn", + "57DE": "dǐ", + "57DF": "yù", + "57E0": "bù", + "57E1": "yā", + "57E2": "quán", + "57E3": "suì", + "57E4": "pí", + "57E5": "qīng", + "57E6": "wǎn", + "57E7": "jù", + "57E8": "lǔn", + "57E9": "zhēng", + "57EA": "kōng", + "57EB": "chǒng", + "57EC": "dōng", + "57ED": "dài", + "57EE": "tàn", + "57EF": "ǎn", + "57F0": "cài", + "57F1": "chù", + "57F2": "běng", + "57F3": "kǎn", + "57F4": "zhí", + "57F5": "duǒ", + "57F6": "yì", + "57F7": "zhí", + "57F8": "yì", + "57F9": "péi", + "57FA": "jī", + "57FB": "zhǔn", + "57FC": "qí", + "57FD": "sào", + "57FE": "jù", + "57FF": "ní", + "5800": "kū", + "5801": "kè", + "5802": "táng", + "5803": "kūn", + "5804": "nì", + "5805": "jiān", + "5806": "duī", + "5807": "jǐn", + "5808": "gāng", + "5809": "yù", + "580A": "è", + "580B": "péng", + "580C": "gù", + "580D": "tù", + "580E": "lèng", + "580F": "fang", + "5810": "yá", + "5811": "qiàn", + "5812": "kun", + "5813": "àn", + "5814": "shen", + "5815": "duò", + "5816": "nǎo", + "5817": "tū", + "5818": "chéng", + "5819": "yīn", + "581A": "hún", + "581B": "bì", + "581C": "liàn", + "581D": "guō", + "581E": "dié", + "581F": "zhuàn", + "5820": "hòu", + "5821": "bǎo", + "5822": "bǎo", + "5823": "yú", + "5824": "dī", + "5825": "máo", + "5826": "jiē", + "5827": "ruán", + "5828": "yè", + "5829": "gèng", + "582A": "kān", + "582B": "zōng", + "582C": "yú", + "582D": "huáng", + "582E": "è", + "582F": "yáo", + "5830": "yàn", + "5831": "bào", + "5832": "cí", + "5833": "méi", + "5834": "chǎng", + "5835": "dǔ", + "5836": "tuó", + "5837": "yìn", + "5838": "féng", + "5839": "zhòng", + "583A": "jiè", + "583B": "jīn", + "583C": "hèng", + "583D": "gāng", + "583E": "chūn", + "583F": "jiǎn", + "5840": "ping", + "5841": "lei", + "5842": "xiàng", + "5843": "huāng", + "5844": "léng", + "5845": "duàn", + "5846": "wān", + "5847": "xuān", + "5848": "jì", + "5849": "jí", + "584A": "kuài", + "584B": "yíng", + "584C": "tā", + "584D": "chéng", + "584E": "yǒng", + "584F": "kǎi", + "5850": "sù", + "5851": "sù", + "5852": "shí", + "5853": "mì", + "5854": "tǎ", + "5855": "wěng", + "5856": "chéng", + "5857": "tú", + "5858": "táng", + "5859": "què", + "585A": "zhǒng", + "585B": "lì", + "585C": "zhǒng", + "585D": "bàng", + "585E": "sāi", + "585F": "zàng", + "5860": "duī", + "5861": "tián", + "5862": "wù", + "5863": "zhèng", + "5864": "xūn", + "5865": "gé", + "5866": "zhèn", + "5867": "ài", + "5868": "gōng", + "5869": "yán", + "586A": "kǎn", + "586B": "tián", + "586C": "yuán", + "586D": "wēn", + "586E": "xiè", + "586F": "liù", + "5870": "hai", + "5871": "lǎng", + "5872": "cháng", + "5873": "péng", + "5874": "bèng", + "5875": "chén", + "5876": "lù", + "5877": "lǔ", + "5878": "ōu", + "5879": "qiàn", + "587A": "méi", + "587B": "mò", + "587C": "zhuān", + "587D": "shuǎng", + "587E": "shú", + "587F": "lǒu", + "5880": "chí", + "5881": "màn", + "5882": "biāo", + "5883": "jìng", + "5884": "cè", + "5885": "shù", + "5886": "zhì", + "5887": "zhāng", + "5888": "kàn", + "5889": "yōng", + "588A": "diàn", + "588B": "chěn", + "588C": "zhí", + "588D": "xì", + "588E": "guō", + "588F": "qiǎng", + "5890": "jìn", + "5891": "dì", + "5892": "shāng", + "5893": "mù", + "5894": "cuī", + "5895": "yàn", + "5896": "tǎ", + "5897": "zēng", + "5898": "qián", + "5899": "qiáng", + "589A": "liáng", + "589B": "wei", + "589C": "zhuì", + "589D": "qiāo", + "589E": "zēng", + "589F": "xū", + "58A0": "shàn", + "58A1": "shàn", + "58A2": "bá", + "58A3": "pú", + "58A4": "kuài", + "58A5": "dǒng", + "58A6": "fán", + "58A7": "què", + "58A8": "mò", + "58A9": "dūn", + "58AA": "dūn", + "58AB": "zūn", + "58AC": "dì", + "58AD": "shèng", + "58AE": "duò", + "58AF": "duò", + "58B0": "tán", + "58B1": "dèng", + "58B2": "mú", + "58B3": "fén", + "58B4": "huáng", + "58B5": "tán", + "58B6": "da", + "58B7": "yè", + "58B8": "zhu", + "58B9": "jian", + "58BA": "ào", + "58BB": "qiáng", + "58BC": "jī", + "58BD": "qiāo", + "58BE": "kěn", + "58BF": "yì", + "58C0": "pí", + "58C1": "bì", + "58C2": "diàn", + "58C3": "jiāng", + "58C4": "yě", + "58C5": "yōng", + "58C6": "xué", + "58C7": "tán", + "58C8": "lǎn", + "58C9": "jù", + "58CA": "huài", + "58CB": "dàng", + "58CC": "rǎng", + "58CD": "qiàn", + "58CE": "xūn", + "58CF": "xiàn", + "58D0": "xǐ", + "58D1": "hè", + "58D2": "ài", + "58D3": "yā", + "58D4": "dǎo", + "58D5": "háo", + "58D6": "ruán", + "58D7": "jin", + "58D8": "lěi", + "58D9": "kuàng", + "58DA": "lú", + "58DB": "yán", + "58DC": "tán", + "58DD": "wěi", + "58DE": "huài", + "58DF": "lǒng", + "58E0": "lǒng", + "58E1": "ruì", + "58E2": "lì", + "58E3": "lín", + "58E4": "rǎng", + "58E5": "chan", + "58E6": "xūn", + "58E7": "yán", + "58E8": "léi", + "58E9": "bà", + "58EA": "wān", + "58EB": "shì", + "58EC": "rén", + "58ED": "san", + "58EE": "zhuàng", + "58EF": "zhuàng", + "58F0": "shēng", + "58F1": "yī", + "58F2": "mài", + "58F3": "ké", + "58F4": "zhù", + "58F5": "zhuàng", + "58F6": "hú", + "58F7": "hú", + "58F8": "kǔn", + "58F9": "yī", + "58FA": "hú", + "58FB": "xù", + "58FC": "kǔn", + "58FD": "shòu", + "58FE": "mǎng", + "58FF": "zūn", + "5900": "shòu", + "5901": "yī", + "5902": "zhǐ", + "5903": "gǔ", + "5904": "chù", + "5905": "jiàng", + "5906": "féng", + "5907": "bèi", + "5908": "zhai", + "5909": "biàn", + "590A": "suī", + "590B": "qūn", + "590C": "líng", + "590D": "fù", + "590E": "cuò", + "590F": "xià", + "5910": "xiòng", + "5911": "xie", + "5912": "náo", + "5913": "xià", + "5914": "kuí", + "5915": "xī", + "5916": "wài", + "5917": "yuàn", + "5918": "mǎo", + "5919": "sù", + "591A": "duō", + "591B": "duō", + "591C": "yè", + "591D": "qíng", + "591E": "wài", + "591F": "gòu", + "5920": "gòu", + "5921": "qì", + "5922": "mèng", + "5923": "mèng", + "5924": "yín", + "5925": "huǒ", + "5926": "chěn", + "5927": "dà", + "5928": "zè", + "5929": "tiān", + "592A": "tài", + "592B": "fu", + "592C": "guài", + "592D": "yāo", + "592E": "yāng", + "592F": "hāng", + "5930": "gǎo", + "5931": "shī", + "5932": "tāo", + "5933": "tài", + "5934": "tóu", + "5935": "yǎn", + "5936": "bǐ", + "5937": "yí", + "5938": "kuā", + "5939": "jiā", + "593A": "duó", + "593B": "huà", + "593C": "kuǎng", + "593D": "yǔn", + "593E": "jiā", + "593F": "bā", + "5940": "ēn", + "5941": "lián", + "5942": "huàn", + "5943": "dī", + "5944": "yǎn", + "5945": "pào", + "5946": "juàn", + "5947": "qí", + "5948": "nài", + "5949": "fèng", + "594A": "xié", + "594B": "fèn", + "594C": "diǎn", + "594D": "yang", + "594E": "kuí", + "594F": "zòu", + "5950": "huàn", + "5951": "qì", + "5952": "kāi", + "5953": "zhā", + "5954": "bēn", + "5955": "yì", + "5956": "jiǎng", + "5957": "tào", + "5958": "zàng", + "5959": "běn", + "595A": "xī", + "595B": "huǎng", + "595C": "fěi", + "595D": "diāo", + "595E": "xùn", + "595F": "bēng", + "5960": "diàn", + "5961": "ào", + "5962": "shē", + "5963": "wěng", + "5964": "hǎ", + "5965": "ào", + "5966": "wù", + "5967": "ào", + "5968": "jiǎng", + "5969": "lián", + "596A": "duó", + "596B": "yūn", + "596C": "jiǎng", + "596D": "shì", + "596E": "fèn", + "596F": "huò", + "5970": "bì", + "5971": "luán", + "5972": "duǒ", + "5973": "nǚ", + "5974": "nú", + "5975": "dǐng", + "5976": "nǎi", + "5977": "qiān", + "5978": "jiān", + "5979": "tā", + "597A": "jiǔ", + "597B": "nuán", + "597C": "chà", + "597D": "hǎo", + "597E": "xiān", + "597F": "fàn", + "5980": "jǐ", + "5981": "shuò", + "5982": "rú", + "5983": "fēi", + "5984": "wàng", + "5985": "hóng", + "5986": "zhuāng", + "5987": "fù", + "5988": "mā", + "5989": "dān", + "598A": "rèn", + "598B": "fū", + "598C": "jìng", + "598D": "yán", + "598E": "hài", + "598F": "wèn", + "5990": "zhōng", + "5991": "pā", + "5992": "dù", + "5993": "jì", + "5994": "kēng", + "5995": "zhòng", + "5996": "yāo", + "5997": "jìn", + "5998": "yún", + "5999": "miào", + "599A": "fǒu", + "599B": "chi", + "599C": "yuè", + "599D": "zhuāng", + "599E": "niū", + "599F": "yàn", + "59A0": "nà", + "59A1": "xīn", + "59A2": "fén", + "59A3": "bǐ", + "59A4": "yú", + "59A5": "tuǒ", + "59A6": "fēng", + "59A7": "wàn", + "59A8": "fáng", + "59A9": "wǔ", + "59AA": "yù", + "59AB": "guī", + "59AC": "dù", + "59AD": "bá", + "59AE": "nī", + "59AF": "zhóu", + "59B0": "zhuó", + "59B1": "zhāo", + "59B2": "dá", + "59B3": "nǎi", + "59B4": "yuàn", + "59B5": "tǒu", + "59B6": "xián", + "59B7": "zhí", + "59B8": "ē", + "59B9": "mèi", + "59BA": "mò", + "59BB": "qī", + "59BC": "bì", + "59BD": "shēn", + "59BE": "qiè", + "59BF": "ē", + "59C0": "hé", + "59C1": "xǔ", + "59C2": "fá", + "59C3": "zhēng", + "59C4": "mín", + "59C5": "bàn", + "59C6": "mǔ", + "59C7": "fū", + "59C8": "líng", + "59C9": "zǐ", + "59CA": "zǐ", + "59CB": "shǐ", + "59CC": "rǎn", + "59CD": "shān", + "59CE": "yāng", + "59CF": "mán", + "59D0": "jie", + "59D1": "gū", + "59D2": "sì", + "59D3": "xìng", + "59D4": "wěi", + "59D5": "zī", + "59D6": "jù", + "59D7": "shān", + "59D8": "pīn", + "59D9": "rèn", + "59DA": "yáo", + "59DB": "dòng", + "59DC": "jiāng", + "59DD": "shū", + "59DE": "jí", + "59DF": "gāi", + "59E0": "xiàng", + "59E1": "huá", + "59E2": "juān", + "59E3": "jiāo", + "59E4": "gòu", + "59E5": "lǎo", + "59E6": "jiān", + "59E7": "jiān", + "59E8": "yí", + "59E9": "niàn", + "59EA": "zhí", + "59EB": "jī", + "59EC": "jī", + "59ED": "xiàn", + "59EE": "héng", + "59EF": "guāng", + "59F0": "jūn", + "59F1": "kuā", + "59F2": "yàn", + "59F3": "mǐng", + "59F4": "liè", + "59F5": "pèi", + "59F6": "è", + "59F7": "yòu", + "59F8": "yán", + "59F9": "chà", + "59FA": "shēn", + "59FB": "yīn", + "59FC": "shí", + "59FD": "guǐ", + "59FE": "quán", + "59FF": "zī", + "5A00": "sōng", + "5A01": "wēi", + "5A02": "hóng", + "5A03": "wá", + "5A04": "lóu", + "5A05": "yà", + "5A06": "ráo", + "5A07": "jiāo", + "5A08": "luán", + "5A09": "pīng", + "5A0A": "xiàn", + "5A0B": "shào", + "5A0C": "lǐ", + "5A0D": "chéng", + "5A0E": "xiè", + "5A0F": "máng", + "5A10": "fū", + "5A11": "suō", + "5A12": "méi", + "5A13": "wěi", + "5A14": "kè", + "5A15": "chuò", + "5A16": "chuò", + "5A17": "tǐng", + "5A18": "niang", + "5A19": "xíng", + "5A1A": "nán", + "5A1B": "yú", + "5A1C": "nà", + "5A1D": "pōu", + "5A1E": "něi", + "5A1F": "juān", + "5A20": "shēn", + "5A21": "zhì", + "5A22": "hán", + "5A23": "dì", + "5A24": "zhuāng", + "5A25": "é", + "5A26": "pín", + "5A27": "tuì", + "5A28": "xiàn", + "5A29": "miǎn", + "5A2A": "wú", + "5A2B": "yán", + "5A2C": "wǔ", + "5A2D": "āi", + "5A2E": "yán", + "5A2F": "yú", + "5A30": "sì", + "5A31": "yú", + "5A32": "wā", + "5A33": "li", + "5A34": "xián", + "5A35": "jū", + "5A36": "qǔ", + "5A37": "zhuì", + "5A38": "qī", + "5A39": "xián", + "5A3A": "zhuó", + "5A3B": "dōng", + "5A3C": "chāng", + "5A3D": "lù", + "5A3E": "ǎi", + "5A3F": "ē", + "5A40": "ē", + "5A41": "lóu", + "5A42": "mián", + "5A43": "cóng", + "5A44": "pǒu", + "5A45": "jú", + "5A46": "pó", + "5A47": "cāi", + "5A48": "líng", + "5A49": "wǎn", + "5A4A": "biǎo", + "5A4B": "xiāo", + "5A4C": "shú", + "5A4D": "qǐ", + "5A4E": "huī", + "5A4F": "fàn", + "5A50": "wǒ", + "5A51": "ruí", + "5A52": "tán", + "5A53": "fēi", + "5A54": "fei", + "5A55": "jié", + "5A56": "tiān", + "5A57": "ní", + "5A58": "quán", + "5A59": "jìng", + "5A5A": "hūn", + "5A5B": "jīng", + "5A5C": "qiān", + "5A5D": "diàn", + "5A5E": "xìng", + "5A5F": "hù", + "5A60": "wān", + "5A61": "lái", + "5A62": "bì", + "5A63": "yīn", + "5A64": "chōu", + "5A65": "nào", + "5A66": "fù", + "5A67": "jìng", + "5A68": "lún", + "5A69": "àn", + "5A6A": "lán", + "5A6B": "kūn", + "5A6C": "yín", + "5A6D": "yà", + "5A6E": "jū", + "5A6F": "lì", + "5A70": "diǎn", + "5A71": "xián", + "5A72": "hua", + "5A73": "huà", + "5A74": "yīng", + "5A75": "chán", + "5A76": "shěn", + "5A77": "tíng", + "5A78": "dàng", + "5A79": "yǎo", + "5A7A": "wù", + "5A7B": "nàn", + "5A7C": "chuò", + "5A7D": "jiǎ", + "5A7E": "tōu", + "5A7F": "xù", + "5A80": "yù", + "5A81": "wéi", + "5A82": "dì", + "5A83": "róu", + "5A84": "měi", + "5A85": "dān", + "5A86": "ruǎn", + "5A87": "qīn", + "5A88": "huī", + "5A89": "wò", + "5A8A": "qián", + "5A8B": "chūn", + "5A8C": "miáo", + "5A8D": "fù", + "5A8E": "jiě", + "5A8F": "duān", + "5A90": "yí", + "5A91": "zhòng", + "5A92": "méi", + "5A93": "huáng", + "5A94": "mián", + "5A95": "ān", + "5A96": "yīng", + "5A97": "xuān", + "5A98": "jiē", + "5A99": "wēi", + "5A9A": "mèi", + "5A9B": "yuàn", + "5A9C": "zhēng", + "5A9D": "qiū", + "5A9E": "shì", + "5A9F": "xiè", + "5AA0": "tuǒ", + "5AA1": "liàn", + "5AA2": "mào", + "5AA3": "rǎn", + "5AA4": "sī", + "5AA5": "piān", + "5AA6": "wèi", + "5AA7": "wā", + "5AA8": "jiù", + "5AA9": "hú", + "5AAA": "ǎo", + "5AAB": "qie", + "5AAC": "bǎo", + "5AAD": "xū", + "5AAE": "tōu", + "5AAF": "guī", + "5AB0": "chú", + "5AB1": "yáo", + "5AB2": "pì", + "5AB3": "xí", + "5AB4": "yuán", + "5AB5": "yìng", + "5AB6": "róng", + "5AB7": "rù", + "5AB8": "chī", + "5AB9": "liú", + "5ABA": "měi", + "5ABB": "pán", + "5ABC": "ǎo", + "5ABD": "mā", + "5ABE": "gòu", + "5ABF": "kuì", + "5AC0": "qín", + "5AC1": "jià", + "5AC2": "sǎo", + "5AC3": "zhēn", + "5AC4": "yuán", + "5AC5": "jiē", + "5AC6": "róng", + "5AC7": "míng", + "5AC8": "yīng", + "5AC9": "jí", + "5ACA": "sù", + "5ACB": "niǎo", + "5ACC": "xián", + "5ACD": "tāo", + "5ACE": "páng", + "5ACF": "láng", + "5AD0": "nǎo", + "5AD1": "báo", + "5AD2": "ài", + "5AD3": "pì", + "5AD4": "pín", + "5AD5": "yì", + "5AD6": "piáo", + "5AD7": "yù", + "5AD8": "léi", + "5AD9": "xuán", + "5ADA": "mān", + "5ADB": "yī", + "5ADC": "zhāng", + "5ADD": "kāng", + "5ADE": "yōng", + "5ADF": "nì", + "5AE0": "lí", + "5AE1": "dí", + "5AE2": "guī", + "5AE3": "yān", + "5AE4": "jǐn", + "5AE5": "zhuān", + "5AE6": "cháng", + "5AE7": "zé", + "5AE8": "hān", + "5AE9": "nèn", + "5AEA": "lào", + "5AEB": "mó", + "5AEC": "zhē", + "5AED": "hù", + "5AEE": "hù", + "5AEF": "ào", + "5AF0": "nèn", + "5AF1": "qiáng", + "5AF2": "ma", + "5AF3": "piè", + "5AF4": "gū", + "5AF5": "wǔ", + "5AF6": "qiáo", + "5AF7": "tuǒ", + "5AF8": "zhǎn", + "5AF9": "máo", + "5AFA": "xián", + "5AFB": "xián", + "5AFC": "mò", + "5AFD": "liáo", + "5AFE": "lián", + "5AFF": "huà", + "5B00": "guī", + "5B01": "dēng", + "5B02": "zhí", + "5B03": "xū", + "5B04": "yī", + "5B05": "huà", + "5B06": "xī", + "5B07": "kuì", + "5B08": "ráo", + "5B09": "xī", + "5B0A": "yàn", + "5B0B": "chán", + "5B0C": "jiāo", + "5B0D": "měi", + "5B0E": "fàn", + "5B0F": "fān", + "5B10": "xiān", + "5B11": "yì", + "5B12": "huì", + "5B13": "jiào", + "5B14": "fù", + "5B15": "shì", + "5B16": "bì", + "5B17": "shàn", + "5B18": "suì", + "5B19": "qiáng", + "5B1A": "liǎn", + "5B1B": "huán", + "5B1C": "xīn", + "5B1D": "niǎo", + "5B1E": "dǒng", + "5B1F": "yì", + "5B20": "cān", + "5B21": "ài", + "5B22": "niáng", + "5B23": "níng", + "5B24": "mā", + "5B25": "tiǎo", + "5B26": "chóu", + "5B27": "jìn", + "5B28": "cí", + "5B29": "yú", + "5B2A": "pín", + "5B2B": "róng", + "5B2C": "rú", + "5B2D": "nǎi", + "5B2E": "yān", + "5B2F": "tái", + "5B30": "yīng", + "5B31": "cán", + "5B32": "niǎo", + "5B33": "yuè", + "5B34": "yíng", + "5B35": "mián", + "5B36": "bi", + "5B37": "mā", + "5B38": "shěn", + "5B39": "xìng", + "5B3A": "nì", + "5B3B": "dú", + "5B3C": "liǔ", + "5B3D": "yuān", + "5B3E": "lǎn", + "5B3F": "yàn", + "5B40": "shuāng", + "5B41": "líng", + "5B42": "jiǎo", + "5B43": "niáng", + "5B44": "lǎn", + "5B45": "qiān", + "5B46": "yīng", + "5B47": "shuāng", + "5B48": "huì", + "5B49": "quán", + "5B4A": "mǐ", + "5B4B": "lí", + "5B4C": "luán", + "5B4D": "yán", + "5B4E": "zhú", + "5B4F": "lǎn", + "5B50": "zi", + "5B51": "jié", + "5B52": "jué", + "5B53": "jué", + "5B54": "kǒng", + "5B55": "yùn", + "5B56": "mā", + "5B57": "zì", + "5B58": "cún", + "5B59": "sūn", + "5B5A": "fú", + "5B5B": "bèi", + "5B5C": "zī", + "5B5D": "xiào", + "5B5E": "xìn", + "5B5F": "mèng", + "5B60": "sì", + "5B61": "tāi", + "5B62": "bāo", + "5B63": "jì", + "5B64": "gū", + "5B65": "nú", + "5B66": "xué", + "5B67": "you", + "5B68": "zhuǎn", + "5B69": "hái", + "5B6A": "luán", + "5B6B": "sūn", + "5B6C": "nāo", + "5B6D": "miē", + "5B6E": "cóng", + "5B6F": "qiān", + "5B70": "shú", + "5B71": "càn", + "5B72": "yā", + "5B73": "zī", + "5B74": "nǐ", + "5B75": "fū", + "5B76": "zī", + "5B77": "lí", + "5B78": "xué", + "5B79": "bò", + "5B7A": "rú", + "5B7B": "nái", + "5B7C": "niè", + "5B7D": "niè", + "5B7E": "yīng", + "5B7F": "luán", + "5B80": "mián", + "5B81": "níng", + "5B82": "rǒng", + "5B83": "tā", + "5B84": "guǐ", + "5B85": "zhái", + "5B86": "qióng", + "5B87": "yǔ", + "5B88": "shǒu", + "5B89": "ān", + "5B8A": "tū", + "5B8B": "sòng", + "5B8C": "wán", + "5B8D": "ròu", + "5B8E": "yǎo", + "5B8F": "hóng", + "5B90": "yí", + "5B91": "jǐng", + "5B92": "zhūn", + "5B93": "mì", + "5B94": "zhǔ", + "5B95": "dàng", + "5B96": "hóng", + "5B97": "zōng", + "5B98": "guān", + "5B99": "zhòu", + "5B9A": "dìng", + "5B9B": "wǎn", + "5B9C": "yi", + "5B9D": "bǎo", + "5B9E": "shí", + "5B9F": "shí", + "5BA0": "chǒng", + "5BA1": "shěn", + "5BA2": "kè", + "5BA3": "xuān", + "5BA4": "shì", + "5BA5": "yòu", + "5BA6": "huàn", + "5BA7": "yí", + "5BA8": "tiǎo", + "5BA9": "shǐ", + "5BAA": "xiàn", + "5BAB": "gōng", + "5BAC": "chéng", + "5BAD": "qún", + "5BAE": "gōng", + "5BAF": "xiāo", + "5BB0": "zǎi", + "5BB1": "zhà", + "5BB2": "bǎo", + "5BB3": "hài", + "5BB4": "yàn", + "5BB5": "xiāo", + "5BB6": "jiā", + "5BB7": "shěn", + "5BB8": "chén", + "5BB9": "róng", + "5BBA": "huǎng", + "5BBB": "mì", + "5BBC": "kòu", + "5BBD": "kuān", + "5BBE": "bīn", + "5BBF": "sù", + "5BC0": "cǎi", + "5BC1": "zǎn", + "5BC2": "jì", + "5BC3": "yuān", + "5BC4": "jì", + "5BC5": "yín", + "5BC6": "mì", + "5BC7": "kòu", + "5BC8": "qīng", + "5BC9": "què", + "5BCA": "zhēn", + "5BCB": "jiàn", + "5BCC": "fù", + "5BCD": "níng", + "5BCE": "bìng", + "5BCF": "huán", + "5BD0": "mèi", + "5BD1": "qǐn", + "5BD2": "hán", + "5BD3": "yù", + "5BD4": "shí", + "5BD5": "níng", + "5BD6": "jìn", + "5BD7": "níng", + "5BD8": "zhì", + "5BD9": "yǔ", + "5BDA": "bǎo", + "5BDB": "kuān", + "5BDC": "níng", + "5BDD": "qǐn", + "5BDE": "mò", + "5BDF": "chá", + "5BE0": "jù", + "5BE1": "guǎ", + "5BE2": "qǐn", + "5BE3": "hū", + "5BE4": "wù", + "5BE5": "liáo", + "5BE6": "shí", + "5BE7": "níng", + "5BE8": "zhài", + "5BE9": "shěn", + "5BEA": "wěi", + "5BEB": "xiě", + "5BEC": "kuān", + "5BED": "huì", + "5BEE": "liáo", + "5BEF": "jùn", + "5BF0": "huán", + "5BF1": "yì", + "5BF2": "yí", + "5BF3": "bǎo", + "5BF4": "qīn", + "5BF5": "chǒng", + "5BF6": "bǎo", + "5BF7": "fēng", + "5BF8": "cùn", + "5BF9": "duì", + "5BFA": "sì", + "5BFB": "xún", + "5BFC": "dǎo", + "5BFD": "lǜ", + "5BFE": "duì", + "5BFF": "shòu", + "5C00": "pǒ", + "5C01": "fēng", + "5C02": "zhuān", + "5C03": "fū", + "5C04": "shè", + "5C05": "kè", + "5C06": "jiāng", + "5C07": "jiāng", + "5C08": "zhuān", + "5C09": "wèi", + "5C0A": "zūn", + "5C0B": "xún", + "5C0C": "shù", + "5C0D": "duì", + "5C0E": "dǎo", + "5C0F": "xiǎo", + "5C10": "jié", + "5C11": "shǎo", + "5C12": "ěr", + "5C13": "ěr", + "5C14": "ěr", + "5C15": "gǎ", + "5C16": "jiān", + "5C17": "shū", + "5C18": "chén", + "5C19": "shàng", + "5C1A": "shàng", + "5C1B": "mo", + "5C1C": "gá", + "5C1D": "cháng", + "5C1E": "liào", + "5C1F": "xiǎn", + "5C20": "xiǎn", + "5C21": "kun", + "5C22": "yóu", + "5C23": "wāng", + "5C24": "yóu", + "5C25": "liào", + "5C26": "liào", + "5C27": "yáo", + "5C28": "máng", + "5C29": "wāng", + "5C2A": "wāng", + "5C2B": "wāng", + "5C2C": "gà", + "5C2D": "yáo", + "5C2E": "duò", + "5C2F": "kuì", + "5C30": "zhǒng", + "5C31": "jiù", + "5C32": "gān", + "5C33": "gǔ", + "5C34": "gān", + "5C35": "tuí", + "5C36": "gān", + "5C37": "gān", + "5C38": "shī", + "5C39": "yǐn", + "5C3A": "chǐ", + "5C3B": "kāo", + "5C3C": "ní", + "5C3D": "jǐn", + "5C3E": "wěi", + "5C3F": "niào", + "5C40": "jú", + "5C41": "pì", + "5C42": "céng", + "5C43": "xì", + "5C44": "bī", + "5C45": "jū", + "5C46": "jiè", + "5C47": "tián", + "5C48": "qū", + "5C49": "ti", + "5C4A": "jiè", + "5C4B": "wū", + "5C4C": "diǎo", + "5C4D": "shī", + "5C4E": "shǐ", + "5C4F": "píng", + "5C50": "jī", + "5C51": "xiè", + "5C52": "zhěn", + "5C53": "xiè", + "5C54": "ní", + "5C55": "zhǎn", + "5C56": "xī", + "5C57": "wěi", + "5C58": "mǎn", + "5C59": "ē", + "5C5A": "lòu", + "5C5B": "píng", + "5C5C": "ti", + "5C5D": "fèi", + "5C5E": "shǔ", + "5C5F": "xiè", + "5C60": "tú", + "5C61": "lǚ", + "5C62": "lǚ", + "5C63": "xǐ", + "5C64": "céng", + "5C65": "lǚ", + "5C66": "jù", + "5C67": "xiè", + "5C68": "jù", + "5C69": "juē", + "5C6A": "liáo", + "5C6B": "jué", + "5C6C": "shǔ", + "5C6D": "xì", + "5C6E": "chè", + "5C6F": "tún", + "5C70": "nì", + "5C71": "shān", + "5C72": "wa", + "5C73": "xiān", + "5C74": "lì", + "5C75": "è", + "5C76": "dao", + "5C77": "hui", + "5C78": "lóng", + "5C79": "yì", + "5C7A": "qǐ", + "5C7B": "rèn", + "5C7C": "wù", + "5C7D": "hàn", + "5C7E": "shēn", + "5C7F": "yǔ", + "5C80": "chū", + "5C81": "suì", + "5C82": "qǐ", + "5C83": "rèn", + "5C84": "yuè", + "5C85": "bǎn", + "5C86": "yǎo", + "5C87": "áng", + "5C88": "yá", + "5C89": "wù", + "5C8A": "jié", + "5C8B": "è", + "5C8C": "jí", + "5C8D": "qiān", + "5C8E": "fén", + "5C8F": "wán", + "5C90": "qí", + "5C91": "cén", + "5C92": "qián", + "5C93": "qí", + "5C94": "chà", + "5C95": "jiè", + "5C96": "qū", + "5C97": "gǎng", + "5C98": "xiàn", + "5C99": "ào", + "5C9A": "lán", + "5C9B": "dǎo", + "5C9C": "bā", + "5C9D": "zuò", + "5C9E": "zuò", + "5C9F": "yǎng", + "5CA0": "jù", + "5CA1": "gāng", + "5CA2": "kě", + "5CA3": "gǒu", + "5CA4": "xué", + "5CA5": "pō", + "5CA6": "lì", + "5CA7": "tiáo", + "5CA8": "qū", + "5CA9": "yán", + "5CAA": "fú", + "5CAB": "xiù", + "5CAC": "jiǎ", + "5CAD": "lǐng", + "5CAE": "tuó", + "5CAF": "pí", + "5CB0": "ào", + "5CB1": "dài", + "5CB2": "kuàng", + "5CB3": "yuè", + "5CB4": "qū", + "5CB5": "hù", + "5CB6": "pò", + "5CB7": "mín", + "5CB8": "àn", + "5CB9": "tiáo", + "5CBA": "líng", + "5CBB": "chí", + "5CBC": "ping", + "5CBD": "dōng", + "5CBE": "hàn", + "5CBF": "kuī", + "5CC0": "xiù", + "5CC1": "mǎo", + "5CC2": "tóng", + "5CC3": "xué", + "5CC4": "yì", + "5CC5": "bian", + "5CC6": "hé", + "5CC7": "bā", + "5CC8": "luò", + "5CC9": "è", + "5CCA": "fù", + "5CCB": "xún", + "5CCC": "dié", + "5CCD": "lù", + "5CCE": "ěn", + "5CCF": "ér", + "5CD0": "gāi", + "5CD1": "quān", + "5CD2": "dòng", + "5CD3": "yí", + "5CD4": "mǔ", + "5CD5": "shí", + "5CD6": "ān", + "5CD7": "wéi", + "5CD8": "huán", + "5CD9": "zhì", + "5CDA": "mì", + "5CDB": "lǐ", + "5CDC": "jì", + "5CDD": "tóng", + "5CDE": "wéi", + "5CDF": "yòu", + "5CE0": "gu", + "5CE1": "xiá", + "5CE2": "lǐ", + "5CE3": "yáo", + "5CE4": "jiào", + "5CE5": "zhēng", + "5CE6": "luán", + "5CE7": "jiāo", + "5CE8": "é", + "5CE9": "é", + "5CEA": "yù", + "5CEB": "xié", + "5CEC": "bū", + "5CED": "qiào", + "5CEE": "qūn", + "5CEF": "fēng", + "5CF0": "fēng", + "5CF1": "náo", + "5CF2": "lǐ", + "5CF3": "yóu", + "5CF4": "xiàn", + "5CF5": "hóng", + "5CF6": "dǎo", + "5CF7": "shēn", + "5CF8": "chéng", + "5CF9": "tú", + "5CFA": "gěng", + "5CFB": "jùn", + "5CFC": "hào", + "5CFD": "xiá", + "5CFE": "yín", + "5CFF": "yǔ", + "5D00": "làng", + "5D01": "kàn", + "5D02": "láo", + "5D03": "lái", + "5D04": "xiǎn", + "5D05": "què", + "5D06": "kōng", + "5D07": "chóng", + "5D08": "chóng", + "5D09": "tà", + "5D0A": "lín", + "5D0B": "huà", + "5D0C": "jū", + "5D0D": "lái", + "5D0E": "qí", + "5D0F": "mín", + "5D10": "kūn", + "5D11": "kūn", + "5D12": "zú", + "5D13": "gù", + "5D14": "cuī", + "5D15": "yá", + "5D16": "yá", + "5D17": "gǎng", + "5D18": "lún", + "5D19": "lún", + "5D1A": "léng", + "5D1B": "jué", + "5D1C": "duō", + "5D1D": "zhēng", + "5D1E": "guō", + "5D1F": "yín", + "5D20": "dōng", + "5D21": "hán", + "5D22": "zhēng", + "5D23": "wěi", + "5D24": "xiáo", + "5D25": "pí", + "5D26": "yān", + "5D27": "sōng", + "5D28": "jié", + "5D29": "bēng", + "5D2A": "zú", + "5D2B": "kū", + "5D2C": "dōng", + "5D2D": "zhǎn", + "5D2E": "gù", + "5D2F": "yín", + "5D30": "zi", + "5D31": "zè", + "5D32": "huáng", + "5D33": "yú", + "5D34": "wǎi", + "5D35": "yáng", + "5D36": "fēng", + "5D37": "qiú", + "5D38": "yáng", + "5D39": "tí", + "5D3A": "yǐ", + "5D3B": "zhì", + "5D3C": "shì", + "5D3D": "zǎi", + "5D3E": "yǎo", + "5D3F": "è", + "5D40": "zhù", + "5D41": "kān", + "5D42": "lǜ", + "5D43": "yǎn", + "5D44": "měi", + "5D45": "hán", + "5D46": "jī", + "5D47": "jī", + "5D48": "huàn", + "5D49": "tíng", + "5D4A": "shèng", + "5D4B": "méi", + "5D4C": "qiàn", + "5D4D": "wù", + "5D4E": "yú", + "5D4F": "zōng", + "5D50": "lán", + "5D51": "kě", + "5D52": "yán", + "5D53": "yán", + "5D54": "wěi", + "5D55": "zōng", + "5D56": "chá", + "5D57": "suì", + "5D58": "róng", + "5D59": "ke", + "5D5A": "qīn", + "5D5B": "yú", + "5D5C": "ti", + "5D5D": "lǒu", + "5D5E": "tú", + "5D5F": "duī", + "5D60": "xī", + "5D61": "wěng", + "5D62": "cāng", + "5D63": "dàng", + "5D64": "róng", + "5D65": "jié", + "5D66": "kǎi", + "5D67": "liú", + "5D68": "wù", + "5D69": "sōng", + "5D6A": "qiāo", + "5D6B": "zī", + "5D6C": "wéi", + "5D6D": "bēng", + "5D6E": "diān", + "5D6F": "cuó", + "5D70": "qiǎn", + "5D71": "yǒng", + "5D72": "niè", + "5D73": "cuó", + "5D74": "jǐ", + "5D75": "shi", + "5D76": "ruo", + "5D77": "sǒng", + "5D78": "zōng", + "5D79": "jiàng", + "5D7A": "liáo", + "5D7B": "kāng", + "5D7C": "chǎn", + "5D7D": "dié", + "5D7E": "cēn", + "5D7F": "dǐng", + "5D80": "tū", + "5D81": "lǒu", + "5D82": "zhàng", + "5D83": "zhǎn", + "5D84": "zhǎn", + "5D85": "áo", + "5D86": "cáo", + "5D87": "qū", + "5D88": "qiāng", + "5D89": "cuī", + "5D8A": "zuǐ", + "5D8B": "dǎo", + "5D8C": "dǎo", + "5D8D": "xí", + "5D8E": "yù", + "5D8F": "pèi", + "5D90": "lóng", + "5D91": "xiàng", + "5D92": "céng", + "5D93": "bō", + "5D94": "qīn", + "5D95": "jiāo", + "5D96": "yǎn", + "5D97": "láo", + "5D98": "zhàn", + "5D99": "lín", + "5D9A": "liáo", + "5D9B": "liáo", + "5D9C": "jīn", + "5D9D": "dèng", + "5D9E": "duò", + "5D9F": "zūn", + "5DA0": "jiào", + "5DA1": "guì", + "5DA2": "yáo", + "5DA3": "jiāo", + "5DA4": "yáo", + "5DA5": "jué", + "5DA6": "zhān", + "5DA7": "yì", + "5DA8": "xué", + "5DA9": "náo", + "5DAA": "yè", + "5DAB": "yè", + "5DAC": "yí", + "5DAD": "niè", + "5DAE": "xiǎn", + "5DAF": "jí", + "5DB0": "xiè", + "5DB1": "kě", + "5DB2": "xī", + "5DB3": "dì", + "5DB4": "ào", + "5DB5": "zuǐ", + "5DB6": "wei", + "5DB7": "yí", + "5DB8": "róng", + "5DB9": "dǎo", + "5DBA": "lǐng", + "5DBB": "zá", + "5DBC": "yǔ", + "5DBD": "yuè", + "5DBE": "yǐn", + "5DBF": "ru", + "5DC0": "jié", + "5DC1": "lì", + "5DC2": "guī", + "5DC3": "lóng", + "5DC4": "lóng", + "5DC5": "diān", + "5DC6": "róng", + "5DC7": "xī", + "5DC8": "jú", + "5DC9": "chán", + "5DCA": "yǐng", + "5DCB": "kuī", + "5DCC": "yán", + "5DCD": "wēi", + "5DCE": "náo", + "5DCF": "quán", + "5DD0": "chǎo", + "5DD1": "cuán", + "5DD2": "luán", + "5DD3": "diān", + "5DD4": "diān", + "5DD5": "nie", + "5DD6": "yán", + "5DD7": "yán", + "5DD8": "yǎn", + "5DD9": "kuí", + "5DDA": "yǎn", + "5DDB": "chuān", + "5DDC": "kuài", + "5DDD": "chuān", + "5DDE": "zhōu", + "5DDF": "huāng", + "5DE0": "jīng", + "5DE1": "xún", + "5DE2": "cháo", + "5DE3": "cháo", + "5DE4": "liè", + "5DE5": "gōng", + "5DE6": "zuǒ", + "5DE7": "qiǎo", + "5DE8": "jù", + "5DE9": "gǒng", + "5DEA": "jù", + "5DEB": "wū", + "5DEC": "pu", + "5DED": "pu", + "5DEE": "chà", + "5DEF": "qiú", + "5DF0": "qiú", + "5DF1": "jǐ", + "5DF2": "yǐ", + "5DF3": "sì", + "5DF4": "ba", + "5DF5": "zhī", + "5DF6": "zhāo", + "5DF7": "xiàng", + "5DF8": "yí", + "5DF9": "jǐn", + "5DFA": "xùn", + "5DFB": "juàn", + "5DFC": "bā", + "5DFD": "xùn", + "5DFE": "jīn", + "5DFF": "fú", + "5E00": "zā", + "5E01": "bì", + "5E02": "shì", + "5E03": "bù", + "5E04": "dīng", + "5E05": "shuài", + "5E06": "fān", + "5E07": "niè", + "5E08": "shī", + "5E09": "fēn", + "5E0A": "pà", + "5E0B": "zhǐ", + "5E0C": "xī", + "5E0D": "hù", + "5E0E": "dàn", + "5E0F": "wéi", + "5E10": "zhàng", + "5E11": "tǎng", + "5E12": "dài", + "5E13": "mò", + "5E14": "pèi", + "5E15": "pà", + "5E16": "tiē", + "5E17": "bō", + "5E18": "lián", + "5E19": "zhì", + "5E1A": "zhou", + "5E1B": "bó", + "5E1C": "zhì", + "5E1D": "dì", + "5E1E": "mò", + "5E1F": "yì", + "5E20": "yì", + "5E21": "píng", + "5E22": "qià", + "5E23": "juǎn", + "5E24": "rú", + "5E25": "shuài", + "5E26": "dài", + "5E27": "zhèng", + "5E28": "shuì", + "5E29": "qiào", + "5E2A": "zhēn", + "5E2B": "shī", + "5E2C": "qún", + "5E2D": "xí", + "5E2E": "bāng", + "5E2F": "dài", + "5E30": "guī", + "5E31": "chóu", + "5E32": "píng", + "5E33": "zhàng", + "5E34": "sàn", + "5E35": "wān", + "5E36": "dài", + "5E37": "wéi", + "5E38": "cháng", + "5E39": "shà", + "5E3A": "qí", + "5E3B": "zé", + "5E3C": "guó", + "5E3D": "mào", + "5E3E": "dǔ", + "5E3F": "hóu", + "5E40": "zhèng", + "5E41": "xū", + "5E42": "mì", + "5E43": "wéi", + "5E44": "wò", + "5E45": "fú", + "5E46": "yì", + "5E47": "bāng", + "5E48": "píng", + "5E49": "die", + "5E4A": "gōng", + "5E4B": "pán", + "5E4C": "huǎng", + "5E4D": "tāo", + "5E4E": "mì", + "5E4F": "jià", + "5E50": "téng", + "5E51": "huī", + "5E52": "zhōng", + "5E53": "shān", + "5E54": "màn", + "5E55": "mù", + "5E56": "biāo", + "5E57": "guó", + "5E58": "zé", + "5E59": "mù", + "5E5A": "bāng", + "5E5B": "zhàng", + "5E5C": "jǐng", + "5E5D": "chǎn", + "5E5E": "fú", + "5E5F": "zhì", + "5E60": "hū", + "5E61": "fān", + "5E62": "chuáng", + "5E63": "bì", + "5E64": "bi", + "5E65": "zhang", + "5E66": "mì", + "5E67": "qiāo", + "5E68": "chān", + "5E69": "fén", + "5E6A": "méng", + "5E6B": "bāng", + "5E6C": "chóu", + "5E6D": "miè", + "5E6E": "chú", + "5E6F": "jié", + "5E70": "xiǎn", + "5E71": "lán", + "5E72": "gàn", + "5E73": "píng", + "5E74": "nián", + "5E75": "jiān", + "5E76": "bìng", + "5E77": "bìng", + "5E78": "xìng", + "5E79": "gàn", + "5E7A": "yāo", + "5E7B": "huàn", + "5E7C": "yòu", + "5E7D": "yōu", + "5E7E": "jǐ", + "5E7F": "guǎng", + "5E80": "pǐ", + "5E81": "tīng", + "5E82": "zè", + "5E83": "guǎng", + "5E84": "zhuāng", + "5E85": "mo", + "5E86": "qìng", + "5E87": "bì", + "5E88": "qín", + "5E89": "dùn", + "5E8A": "chuáng", + "5E8B": "guǐ", + "5E8C": "yǎ", + "5E8D": "bài", + "5E8E": "jiè", + "5E8F": "xù", + "5E90": "lú", + "5E91": "wǔ", + "5E92": "zhuang", + "5E93": "kù", + "5E94": "yīng", + "5E95": "dǐ", + "5E96": "páo", + "5E97": "diàn", + "5E98": "yā", + "5E99": "miào", + "5E9A": "gēng", + "5E9B": "cì", + "5E9C": "fǔ", + "5E9D": "tóng", + "5E9E": "páng", + "5E9F": "fèi", + "5EA0": "xiáng", + "5EA1": "yǐ", + "5EA2": "zhì", + "5EA3": "tiāo", + "5EA4": "zhì", + "5EA5": "xiū", + "5EA6": "dù", + "5EA7": "zuò", + "5EA8": "xiāo", + "5EA9": "tú", + "5EAA": "guǐ", + "5EAB": "kù", + "5EAC": "máng", + "5EAD": "tíng", + "5EAE": "yǒu", + "5EAF": "bū", + "5EB0": "bìng", + "5EB1": "chěng", + "5EB2": "lái", + "5EB3": "bì", + "5EB4": "jí", + "5EB5": "ān", + "5EB6": "shù", + "5EB7": "kāng", + "5EB8": "yōng", + "5EB9": "tuǒ", + "5EBA": "sōng", + "5EBB": "shù", + "5EBC": "qǐng", + "5EBD": "yù", + "5EBE": "yǔ", + "5EBF": "miào", + "5EC0": "sōu", + "5EC1": "cè", + "5EC2": "xiāng", + "5EC3": "fèi", + "5EC4": "jiù", + "5EC5": "è", + "5EC6": "guī", + "5EC7": "liù", + "5EC8": "shà", + "5EC9": "lián", + "5ECA": "láng", + "5ECB": "sōu", + "5ECC": "zhì", + "5ECD": "pǒu", + "5ECE": "qǐng", + "5ECF": "jiù", + "5ED0": "jiù", + "5ED1": "jǐn", + "5ED2": "áo", + "5ED3": "kuò", + "5ED4": "lóu", + "5ED5": "yìn", + "5ED6": "liào", + "5ED7": "dài", + "5ED8": "lù", + "5ED9": "yì", + "5EDA": "chú", + "5EDB": "chán", + "5EDC": "tú", + "5EDD": "sī", + "5EDE": "xīn", + "5EDF": "miào", + "5EE0": "chǎng", + "5EE1": "wǔ", + "5EE2": "fèi", + "5EE3": "guǎng", + "5EE4": "kù", + "5EE5": "kuài", + "5EE6": "bì", + "5EE7": "qiáng", + "5EE8": "xiè", + "5EE9": "lǐn", + "5EEA": "lǐn", + "5EEB": "liáo", + "5EEC": "lú", + "5EED": "ji", + "5EEE": "yǐng", + "5EEF": "xiān", + "5EF0": "tīng", + "5EF1": "yōng", + "5EF2": "lí", + "5EF3": "tīng", + "5EF4": "yǐn", + "5EF5": "xún", + "5EF6": "yán", + "5EF7": "tíng", + "5EF8": "dí", + "5EF9": "pǎi", + "5EFA": "jiàn", + "5EFB": "huí", + "5EFC": "nǎi", + "5EFD": "huí", + "5EFE": "gǒng", + "5EFF": "niàn", + "5F00": "kāi", + "5F01": "biàn", + "5F02": "yì", + "5F03": "qì", + "5F04": "nòng", + "5F05": "fèn", + "5F06": "jǔ", + "5F07": "yǎn", + "5F08": "yì", + "5F09": "zàng", + "5F0A": "bì", + "5F0B": "yì", + "5F0C": "yī", + "5F0D": "èr", + "5F0E": "sān", + "5F0F": "shì", + "5F10": "èr", + "5F11": "shì", + "5F12": "shì", + "5F13": "gōng", + "5F14": "diào", + "5F15": "yǐn", + "5F16": "hù", + "5F17": "fú", + "5F18": "hóng", + "5F19": "wū", + "5F1A": "tuí", + "5F1B": "chí", + "5F1C": "jiàng", + "5F1D": "bà", + "5F1E": "shěn", + "5F1F": "dì", + "5F20": "zhāng", + "5F21": "jué", + "5F22": "tāo", + "5F23": "fǔ", + "5F24": "dǐ", + "5F25": "mí", + "5F26": "xián", + "5F27": "hú", + "5F28": "chāo", + "5F29": "nǔ", + "5F2A": "jìng", + "5F2B": "zhěn", + "5F2C": "yí", + "5F2D": "mǐ", + "5F2E": "quān", + "5F2F": "wān", + "5F30": "shāo", + "5F31": "ruò", + "5F32": "xuān", + "5F33": "jìng", + "5F34": "diāo", + "5F35": "zhāng", + "5F36": "jiàng", + "5F37": "qiáng", + "5F38": "péng", + "5F39": "dàn", + "5F3A": "qiáng", + "5F3B": "bì", + "5F3C": "bì", + "5F3D": "shè", + "5F3E": "dàn", + "5F3F": "jiǎn", + "5F40": "gòu", + "5F41": "ge", + "5F42": "fā", + "5F43": "bì", + "5F44": "kōu", + "5F45": "jian", + "5F46": "biè", + "5F47": "xiāo", + "5F48": "dàn", + "5F49": "guō", + "5F4A": "jiàng", + "5F4B": "hóng", + "5F4C": "mí", + "5F4D": "guō", + "5F4E": "wān", + "5F4F": "jué", + "5F50": "jì", + "5F51": "jì", + "5F52": "guī", + "5F53": "dāng", + "5F54": "lù", + "5F55": "lù", + "5F56": "tuàn", + "5F57": "huì", + "5F58": "zhì", + "5F59": "huì", + "5F5A": "huì", + "5F5B": "yí", + "5F5C": "yí", + "5F5D": "yí", + "5F5E": "yí", + "5F5F": "yuē", + "5F60": "yuē", + "5F61": "shān", + "5F62": "xíng", + "5F63": "wén", + "5F64": "tóng", + "5F65": "yàn", + "5F66": "yàn", + "5F67": "yù", + "5F68": "chī", + "5F69": "cǎi", + "5F6A": "biāo", + "5F6B": "diāo", + "5F6C": "bīn", + "5F6D": "péng", + "5F6E": "yǒng", + "5F6F": "piǎo", + "5F70": "zhāng", + "5F71": "yǐng", + "5F72": "chī", + "5F73": "chì", + "5F74": "zhuó", + "5F75": "tuǒ", + "5F76": "jí", + "5F77": "fǎng", + "5F78": "zhōng", + "5F79": "yì", + "5F7A": "wáng", + "5F7B": "chè", + "5F7C": "bǐ", + "5F7D": "dī", + "5F7E": "líng", + "5F7F": "fú", + "5F80": "wǎng", + "5F81": "zhēng", + "5F82": "cú", + "5F83": "wǎng", + "5F84": "jìng", + "5F85": "dài", + "5F86": "xī", + "5F87": "xùn", + "5F88": "hěn", + "5F89": "yáng", + "5F8A": "huái", + "5F8B": "lǜ", + "5F8C": "hòu", + "5F8D": "wǎng", + "5F8E": "chěng", + "5F8F": "zhì", + "5F90": "xú", + "5F91": "jìng", + "5F92": "tú", + "5F93": "cóng", + "5F94": "zhi", + "5F95": "lái", + "5F96": "cóng", + "5F97": "de", + "5F98": "pái", + "5F99": "xǐ", + "5F9A": "dōng", + "5F9B": "jì", + "5F9C": "cháng", + "5F9D": "zhì", + "5F9E": "cóng", + "5F9F": "zhōu", + "5FA0": "lái", + "5FA1": "yù", + "5FA2": "xiè", + "5FA3": "jiè", + "5FA4": "jiàn", + "5FA5": "shì", + "5FA6": "jiǎ", + "5FA7": "biàn", + "5FA8": "huáng", + "5FA9": "fù", + "5FAA": "xún", + "5FAB": "wěi", + "5FAC": "páng", + "5FAD": "yáo", + "5FAE": "wēi", + "5FAF": "xī", + "5FB0": "zhēng", + "5FB1": "piào", + "5FB2": "tí", + "5FB3": "dé", + "5FB4": "zhēng", + "5FB5": "zhēng", + "5FB6": "bié", + "5FB7": "dé", + "5FB8": "chōng", + "5FB9": "chè", + "5FBA": "jiǎo", + "5FBB": "huì", + "5FBC": "jiǎo", + "5FBD": "huī", + "5FBE": "méi", + "5FBF": "lòng", + "5FC0": "xiāng", + "5FC1": "bào", + "5FC2": "qú", + "5FC3": "xīn", + "5FC4": "xin", + "5FC5": "bì", + "5FC6": "yì", + "5FC7": "lè", + "5FC8": "rén", + "5FC9": "dāo", + "5FCA": "dìng", + "5FCB": "gǎi", + "5FCC": "jì", + "5FCD": "rěn", + "5FCE": "rén", + "5FCF": "chàn", + "5FD0": "tǎn", + "5FD1": "tè", + "5FD2": "tè", + "5FD3": "gān", + "5FD4": "qì", + "5FD5": "shì", + "5FD6": "cǔn", + "5FD7": "zhì", + "5FD8": "wàng", + "5FD9": "máng", + "5FDA": "xī", + "5FDB": "fán", + "5FDC": "yīng", + "5FDD": "tiǎn", + "5FDE": "mín", + "5FDF": "wěn", + "5FE0": "zhōng", + "5FE1": "chōng", + "5FE2": "wù", + "5FE3": "jí", + "5FE4": "wǔ", + "5FE5": "xì", + "5FE6": "jiá", + "5FE7": "yōu", + "5FE8": "wàn", + "5FE9": "cōng", + "5FEA": "sōng", + "5FEB": "kuài", + "5FEC": "yù", + "5FED": "biàn", + "5FEE": "zhì", + "5FEF": "qí", + "5FF0": "cuì", + "5FF1": "chén", + "5FF2": "tài", + "5FF3": "tún", + "5FF4": "qián", + "5FF5": "niàn", + "5FF6": "hún", + "5FF7": "xiōng", + "5FF8": "niǔ", + "5FF9": "kuáng", + "5FFA": "xiān", + "5FFB": "xīn", + "5FFC": "kāng", + "5FFD": "hū", + "5FFE": "kài", + "5FFF": "fèn", + "6000": "huái", + "6001": "tài", + "6002": "sǒng", + "6003": "wǔ", + "6004": "òu", + "6005": "chàng", + "6006": "chuàng", + "6007": "jù", + "6008": "yì", + "6009": "bǎo", + "600A": "chāo", + "600B": "mín", + "600C": "pēi", + "600D": "zuò", + "600E": "zěn", + "600F": "yàng", + "6010": "jù", + "6011": "bàn", + "6012": "nù", + "6013": "náo", + "6014": "zhēng", + "6015": "pà", + "6016": "bù", + "6017": "tiē", + "6018": "hù", + "6019": "hù", + "601A": "jù", + "601B": "dá", + "601C": "lián", + "601D": "sī", + "601E": "chóu", + "601F": "dì", + "6020": "dài", + "6021": "yí", + "6022": "tū", + "6023": "yóu", + "6024": "fū", + "6025": "jí", + "6026": "pēng", + "6027": "xìng", + "6028": "yuàn", + "6029": "ní", + "602A": "guài", + "602B": "fú", + "602C": "xì", + "602D": "bì", + "602E": "yōu", + "602F": "qiè", + "6030": "xuàn", + "6031": "cōng", + "6032": "bǐng", + "6033": "huǎng", + "6034": "xù", + "6035": "chù", + "6036": "bì", + "6037": "shù", + "6038": "xī", + "6039": "tān", + "603A": "yong", + "603B": "zǒng", + "603C": "duì", + "603D": "mo", + "603E": "zhǐ", + "603F": "yì", + "6040": "shì", + "6041": "nèn", + "6042": "xún", + "6043": "shì", + "6044": "xì", + "6045": "lǎo", + "6046": "héng", + "6047": "kuāng", + "6048": "móu", + "6049": "zhǐ", + "604A": "xié", + "604B": "liàn", + "604C": "tiāo", + "604D": "huǎng", + "604E": "dié", + "604F": "hào", + "6050": "kǒng", + "6051": "guǐ", + "6052": "héng", + "6053": "xī", + "6054": "jiǎo", + "6055": "shù", + "6056": "si", + "6057": "hū", + "6058": "qiū", + "6059": "yàng", + "605A": "huì", + "605B": "huí", + "605C": "chì", + "605D": "jiá", + "605E": "yí", + "605F": "xiōng", + "6060": "guài", + "6061": "lìn", + "6062": "huī", + "6063": "zì", + "6064": "xù", + "6065": "chǐ", + "6066": "shàng", + "6067": "nǜ", + "6068": "hèn", + "6069": "ēn", + "606A": "kè", + "606B": "dòng", + "606C": "tián", + "606D": "gōng", + "606E": "quān", + "606F": "xi", + "6070": "qià", + "6071": "yuè", + "6072": "pēng", + "6073": "kěn", + "6074": "dé", + "6075": "huì", + "6076": "è", + "6077": "xiao", + "6078": "tòng", + "6079": "yān", + "607A": "kǎi", + "607B": "cè", + "607C": "nǎo", + "607D": "yùn", + "607E": "máng", + "607F": "yǒng", + "6080": "yǒng", + "6081": "yuān", + "6082": "pī", + "6083": "kǔn", + "6084": "qiāo", + "6085": "yuè", + "6086": "yù", + "6087": "tú", + "6088": "jiè", + "6089": "xī", + "608A": "zhé", + "608B": "lìn", + "608C": "tì", + "608D": "hàn", + "608E": "hào", + "608F": "qiè", + "6090": "tì", + "6091": "bù", + "6092": "yì", + "6093": "qiàn", + "6094": "huǐ", + "6095": "xī", + "6096": "bèi", + "6097": "mán", + "6098": "yī", + "6099": "hēng", + "609A": "sǒng", + "609B": "quān", + "609C": "chěng", + "609D": "kuī", + "609E": "wù", + "609F": "wù", + "60A0": "yōu", + "60A1": "lí", + "60A2": "liàng", + "60A3": "huàn", + "60A4": "cōng", + "60A5": "yì", + "60A6": "yuè", + "60A7": "lì", + "60A8": "nín", + "60A9": "nǎo", + "60AA": "è", + "60AB": "què", + "60AC": "xuán", + "60AD": "qiān", + "60AE": "wù", + "60AF": "mǐn", + "60B0": "cóng", + "60B1": "fěi", + "60B2": "bēi", + "60B3": "duó", + "60B4": "cuì", + "60B5": "chàng", + "60B6": "mèn", + "60B7": "sàn", + "60B8": "jì", + "60B9": "guàn", + "60BA": "guàn", + "60BB": "xìng", + "60BC": "dào", + "60BD": "qī", + "60BE": "kōng", + "60BF": "tiǎn", + "60C0": "lún", + "60C1": "xī", + "60C2": "kǎn", + "60C3": "gǔn", + "60C4": "nì", + "60C5": "qíng", + "60C6": "chóu", + "60C7": "dūn", + "60C8": "guǒ", + "60C9": "zhān", + "60CA": "jīng", + "60CB": "wǎn", + "60CC": "yuān", + "60CD": "jīn", + "60CE": "jì", + "60CF": "lán", + "60D0": "yù", + "60D1": "huò", + "60D2": "hé", + "60D3": "quán", + "60D4": "tán", + "60D5": "tì", + "60D6": "tì", + "60D7": "niè", + "60D8": "wǎng", + "60D9": "chuò", + "60DA": "hū", + "60DB": "hūn", + "60DC": "xī", + "60DD": "chǎng", + "60DE": "xīn", + "60DF": "wéi", + "60E0": "huì", + "60E1": "è", + "60E2": "suǒ", + "60E3": "zǒng", + "60E4": "jiān", + "60E5": "yǒng", + "60E6": "diàn", + "60E7": "jù", + "60E8": "cǎn", + "60E9": "chéng", + "60EA": "dé", + "60EB": "bèi", + "60EC": "qiè", + "60ED": "cán", + "60EE": "dàn", + "60EF": "guàn", + "60F0": "duò", + "60F1": "nǎo", + "60F2": "yùn", + "60F3": "xiǎng", + "60F4": "zhuì", + "60F5": "dié", + "60F6": "huáng", + "60F7": "chǔn", + "60F8": "qióng", + "60F9": "rě", + "60FA": "xīng", + "60FB": "cè", + "60FC": "biǎn", + "60FD": "mǐn", + "60FE": "zōng", + "60FF": "tí", + "6100": "qiǎo", + "6101": "chóu", + "6102": "bèi", + "6103": "xuān", + "6104": "wēi", + "6105": "gé", + "6106": "qiān", + "6107": "wěi", + "6108": "yù", + "6109": "yú", + "610A": "bì", + "610B": "xuān", + "610C": "huàn", + "610D": "mǐn", + "610E": "bì", + "610F": "yì", + "6110": "miǎn", + "6111": "yǒng", + "6112": "kài", + "6113": "dàng", + "6114": "yīn", + "6115": "è", + "6116": "chén", + "6117": "mào", + "6118": "qià", + "6119": "kè", + "611A": "yú", + "611B": "ài", + "611C": "qiè", + "611D": "yǎn", + "611E": "nuò", + "611F": "gǎn", + "6120": "yùn", + "6121": "zǒng", + "6122": "sāi", + "6123": "lèng", + "6124": "fèn", + "6125": "ying", + "6126": "kuì", + "6127": "kuì", + "6128": "què", + "6129": "gōng", + "612A": "yún", + "612B": "sù", + "612C": "sù", + "612D": "qí", + "612E": "yáo", + "612F": "sǒng", + "6130": "huàng", + "6131": "jí", + "6132": "gǔ", + "6133": "jù", + "6134": "chuàng", + "6135": "nì", + "6136": "xié", + "6137": "kǎi", + "6138": "zhěng", + "6139": "yǒng", + "613A": "cǎo", + "613B": "xùn", + "613C": "shèn", + "613D": "bó", + "613E": "kài", + "613F": "yuàn", + "6140": "xì", + "6141": "hùn", + "6142": "yǒng", + "6143": "yǎng", + "6144": "lì", + "6145": "sāo", + "6146": "tāo", + "6147": "yīn", + "6148": "cí", + "6149": "xù", + "614A": "qiàn", + "614B": "tài", + "614C": "huāng", + "614D": "yùn", + "614E": "shèn", + "614F": "mǐng", + "6150": "gong", + "6151": "shè", + "6152": "cóng", + "6153": "piāo", + "6154": "mù", + "6155": "mù", + "6156": "guó", + "6157": "chì", + "6158": "cǎn", + "6159": "cán", + "615A": "cán", + "615B": "cuī", + "615C": "mǐn", + "615D": "tè", + "615E": "zhāng", + "615F": "tòng", + "6160": "ào", + "6161": "shuǎng", + "6162": "màn", + "6163": "guàn", + "6164": "què", + "6165": "zào", + "6166": "jiù", + "6167": "huì", + "6168": "kǎi", + "6169": "lián", + "616A": "òu", + "616B": "sǒng", + "616C": "qín", + "616D": "yìn", + "616E": "lǜ", + "616F": "shāng", + "6170": "wèi", + "6171": "tuán", + "6172": "mán", + "6173": "qiān", + "6174": "shè", + "6175": "yōng", + "6176": "qìng", + "6177": "kāng", + "6178": "dì", + "6179": "zhí", + "617A": "lóu", + "617B": "juàn", + "617C": "qī", + "617D": "qī", + "617E": "yù", + "617F": "píng", + "6180": "liáo", + "6181": "còng", + "6182": "yōu", + "6183": "chōng", + "6184": "zhì", + "6185": "tòng", + "6186": "chēng", + "6187": "qì", + "6188": "qū", + "6189": "péng", + "618A": "bèi", + "618B": "biē", + "618C": "qióng", + "618D": "jiāo", + "618E": "zēng", + "618F": "chì", + "6190": "lián", + "6191": "píng", + "6192": "kuì", + "6193": "huì", + "6194": "qiáo", + "6195": "chéng", + "6196": "yìn", + "6197": "yìn", + "6198": "xǐ", + "6199": "xī", + "619A": "dàn", + "619B": "tán", + "619C": "duǒ", + "619D": "duì", + "619E": "duì", + "619F": "sù", + "61A0": "jué", + "61A1": "cè", + "61A2": "xiāo", + "61A3": "fān", + "61A4": "fèn", + "61A5": "láo", + "61A6": "lào", + "61A7": "chōng", + "61A8": "hān", + "61A9": "qì", + "61AA": "xián", + "61AB": "mǐn", + "61AC": "jǐng", + "61AD": "liǎo", + "61AE": "wǔ", + "61AF": "cǎn", + "61B0": "jué", + "61B1": "cù", + "61B2": "xiàn", + "61B3": "tǎn", + "61B4": "shéng", + "61B5": "pī", + "61B6": "yì", + "61B7": "chù", + "61B8": "xiān", + "61B9": "náo", + "61BA": "dàn", + "61BB": "tǎn", + "61BC": "jǐng", + "61BD": "sōng", + "61BE": "hàn", + "61BF": "jiǎo", + "61C0": "wèi", + "61C1": "xuān", + "61C2": "dǒng", + "61C3": "qín", + "61C4": "qín", + "61C5": "jù", + "61C6": "cǎo", + "61C7": "kěn", + "61C8": "xiè", + "61C9": "yīng", + "61CA": "ào", + "61CB": "mào", + "61CC": "yì", + "61CD": "lǐn", + "61CE": "sè", + "61CF": "jùn", + "61D0": "huái", + "61D1": "mèn", + "61D2": "lǎn", + "61D3": "ài", + "61D4": "lǐn", + "61D5": "yān", + "61D6": "kuò", + "61D7": "xià", + "61D8": "chì", + "61D9": "yǔ", + "61DA": "yìn", + "61DB": "dāi", + "61DC": "měng", + "61DD": "ài", + "61DE": "méng", + "61DF": "duì", + "61E0": "qí", + "61E1": "mǒ", + "61E2": "lán", + "61E3": "mèn", + "61E4": "chóu", + "61E5": "zhì", + "61E6": "nuò", + "61E7": "nuò", + "61E8": "yān", + "61E9": "yǎng", + "61EA": "bó", + "61EB": "zhì", + "61EC": "kuàng", + "61ED": "kuǎng", + "61EE": "yǒu", + "61EF": "fū", + "61F0": "liú", + "61F1": "miè", + "61F2": "chéng", + "61F3": "hui", + "61F4": "chàn", + "61F5": "měng", + "61F6": "lǎn", + "61F7": "huái", + "61F8": "xuán", + "61F9": "ràng", + "61FA": "chàn", + "61FB": "jì", + "61FC": "jù", + "61FD": "huān", + "61FE": "shè", + "61FF": "yì", + "6200": "liàn", + "6201": "nǎn", + "6202": "mí", + "6203": "tǎng", + "6204": "jué", + "6205": "gàng", + "6206": "gàng", + "6207": "zhuàng", + "6208": "gē", + "6209": "yuè", + "620A": "wù", + "620B": "jiān", + "620C": "xū", + "620D": "shù", + "620E": "róng", + "620F": "xì", + "6210": "chéng", + "6211": "wǒ", + "6212": "jiè", + "6213": "gē", + "6214": "jiān", + "6215": "qiāng", + "6216": "huò", + "6217": "qiāng", + "6218": "zhàn", + "6219": "dòng", + "621A": "qi", + "621B": "jiá", + "621C": "dié", + "621D": "zéi", + "621E": "jiá", + "621F": "jǐ", + "6220": "zhī", + "6221": "kān", + "6222": "jí", + "6223": "kuí", + "6224": "gài", + "6225": "děng", + "6226": "zhàn", + "6227": "qiāng", + "6228": "gē", + "6229": "jiǎn", + "622A": "jié", + "622B": "yù", + "622C": "jiǎn", + "622D": "yǎn", + "622E": "lù", + "622F": "hū", + "6230": "zhàn", + "6231": "xì", + "6232": "xì", + "6233": "chuō", + "6234": "dài", + "6235": "qú", + "6236": "hù", + "6237": "hù", + "6238": "hù", + "6239": "è", + "623A": "shì", + "623B": "tì", + "623C": "mǎo", + "623D": "hù", + "623E": "lì", + "623F": "fáng", + "6240": "suǒ", + "6241": "biǎn", + "6242": "diàn", + "6243": "jiōng", + "6244": "shǎng", + "6245": "yí", + "6246": "yǐ", + "6247": "shàn", + "6248": "hù", + "6249": "fēi", + "624A": "yǎn", + "624B": "shǒu", + "624C": "shou", + "624D": "cái", + "624E": "zhā", + "624F": "qiú", + "6250": "lè", + "6251": "pū", + "6252": "bā", + "6253": "dǎ", + "6254": "rēng", + "6255": "fǎn", + "6256": "ru", + "6257": "zài", + "6258": "tuō", + "6259": "zhàng", + "625A": "diǎo", + "625B": "káng", + "625C": "yū", + "625D": "kū", + "625E": "gǎn", + "625F": "shēn", + "6260": "chā", + "6261": "tuō", + "6262": "gǔ", + "6263": "kòu", + "6264": "wù", + "6265": "dèn", + "6266": "qiān", + "6267": "zhí", + "6268": "rèn", + "6269": "kuò", + "626A": "mén", + "626B": "sǎo", + "626C": "yáng", + "626D": "niǔ", + "626E": "ban", + "626F": "chě", + "6270": "rǎo", + "6271": "xī", + "6272": "qián", + "6273": "bān", + "6274": "jiá", + "6275": "yú", + "6276": "fú", + "6277": "ào", + "6278": "xī", + "6279": "pī", + "627A": "zhǐ", + "627B": "zhì", + "627C": "è", + "627D": "dèn", + "627E": "zhǎo", + "627F": "chéng", + "6280": "jì", + "6281": "yǎn", + "6282": "kuáng", + "6283": "biàn", + "6284": "chāo", + "6285": "jū", + "6286": "wěn", + "6287": "hú", + "6288": "yuè", + "6289": "jué", + "628A": "bǎ", + "628B": "qìn", + "628C": "dǎn", + "628D": "zhěng", + "628E": "yǔn", + "628F": "wán", + "6290": "nè", + "6291": "yì", + "6292": "shū", + "6293": "zhuā", + "6294": "póu", + "6295": "tóu", + "6296": "dǒu", + "6297": "kàng", + "6298": "zhé", + "6299": "póu", + "629A": "fǔ", + "629B": "pāo", + "629C": "bá", + "629D": "ǎo", + "629E": "zé", + "629F": "tuán", + "62A0": "kōu", + "62A1": "lūn", + "62A2": "qiǎng", + "62A3": "yun", + "62A4": "hù", + "62A5": "bào", + "62A6": "bǐng", + "62A7": "zhǐ", + "62A8": "pēng", + "62A9": "tān", + "62AA": "bù", + "62AB": "pī", + "62AC": "tái", + "62AD": "yǎo", + "62AE": "zhěn", + "62AF": "zhā", + "62B0": "yāng", + "62B1": "bào", + "62B2": "hē", + "62B3": "nǐ", + "62B4": "yè", + "62B5": "dǐ", + "62B6": "chì", + "62B7": "pī", + "62B8": "jiā", + "62B9": "mǒ", + "62BA": "mèi", + "62BB": "chēn", + "62BC": "yā", + "62BD": "chōu", + "62BE": "qū", + "62BF": "mǐn", + "62C0": "chù", + "62C1": "jiā", + "62C2": "fú", + "62C3": "zhǎ", + "62C4": "zhǔ", + "62C5": "dān", + "62C6": "chāi", + "62C7": "mu", + "62C8": "niān", + "62C9": "lā", + "62CA": "fǔ", + "62CB": "pāo", + "62CC": "bàn", + "62CD": "pāi", + "62CE": "līn", + "62CF": "ná", + "62D0": "guǎi", + "62D1": "qián", + "62D2": "jù", + "62D3": "tà", + "62D4": "bá", + "62D5": "tuō", + "62D6": "tuō", + "62D7": "ǎo", + "62D8": "jū", + "62D9": "zhuō", + "62DA": "pàn", + "62DB": "zhāo", + "62DC": "bài", + "62DD": "bài", + "62DE": "dǐ", + "62DF": "nǐ", + "62E0": "jù", + "62E1": "kuò", + "62E2": "lǒng", + "62E3": "jiǎn", + "62E4": "qiá", + "62E5": "yōng", + "62E6": "lán", + "62E7": "níng", + "62E8": "bō", + "62E9": "zé", + "62EA": "qiān", + "62EB": "hén", + "62EC": "kuò", + "62ED": "shì", + "62EE": "jié", + "62EF": "zhěng", + "62F0": "nǐn", + "62F1": "gǒng", + "62F2": "gǒng", + "62F3": "quán", + "62F4": "shuān", + "62F5": "cún", + "62F6": "zā", + "62F7": "kǎo", + "62F8": "yí", + "62F9": "xié", + "62FA": "cè", + "62FB": "huī", + "62FC": "pīn", + "62FD": "zhuāi", + "62FE": "shi", + "62FF": "ná", + "6300": "bāi", + "6301": "chí", + "6302": "guà", + "6303": "zhì", + "6304": "kuò", + "6305": "duǒ", + "6306": "duǒ", + "6307": "zhǐ", + "6308": "qiè", + "6309": "àn", + "630A": "nòng", + "630B": "zhèn", + "630C": "gé", + "630D": "jiào", + "630E": "kuà", + "630F": "dòng", + "6310": "ná", + "6311": "tiāo", + "6312": "liè", + "6313": "zhā", + "6314": "lǚ", + "6315": "dié", + "6316": "wā", + "6317": "jué", + "6318": "lie", + "6319": "jǔ", + "631A": "zhì", + "631B": "luán", + "631C": "yà", + "631D": "wō", + "631E": "tà", + "631F": "xié", + "6320": "náo", + "6321": "dǎng", + "6322": "jiǎo", + "6323": "zhēng", + "6324": "jǐ", + "6325": "huī", + "6326": "xián", + "6327": "yu", + "6328": "āi", + "6329": "tuō", + "632A": "nuó", + "632B": "cuò", + "632C": "bó", + "632D": "gěng", + "632E": "tǐ", + "632F": "zhèn", + "6330": "chéng", + "6331": "sā", + "6332": "sā", + "6333": "kēng", + "6334": "měi", + "6335": "lòng", + "6336": "jū", + "6337": "péng", + "6338": "jiǎn", + "6339": "yì", + "633A": "tǐng", + "633B": "shān", + "633C": "ruá", + "633D": "wǎn", + "633E": "xié", + "633F": "chā", + "6340": "féng", + "6341": "jiǎo", + "6342": "wǔ", + "6343": "jùn", + "6344": "jiù", + "6345": "tǒng", + "6346": "kǔn", + "6347": "huò", + "6348": "tú", + "6349": "zhuō", + "634A": "póu", + "634B": "lǚ", + "634C": "bā", + "634D": "hàn", + "634E": "shāo", + "634F": "niē", + "6350": "juān", + "6351": "zè", + "6352": "shù", + "6353": "yé", + "6354": "jué", + "6355": "bǔ", + "6356": "wán", + "6357": "bù", + "6358": "zùn", + "6359": "yì", + "635A": "zhāi", + "635B": "lǚ", + "635C": "sōu", + "635D": "tuō", + "635E": "lāo", + "635F": "sǔn", + "6360": "bāng", + "6361": "jiǎn", + "6362": "huàn", + "6363": "dǎo", + "6364": "wei", + "6365": "wàn", + "6366": "qín", + "6367": "pěng", + "6368": "shě", + "6369": "liè", + "636A": "mín", + "636B": "mén", + "636C": "fǔ", + "636D": "bǎi", + "636E": "jù", + "636F": "dáo", + "6370": "wǒ", + "6371": "ái", + "6372": "juǎn", + "6373": "yuè", + "6374": "zǒng", + "6375": "chēn", + "6376": "chuí", + "6377": "jié", + "6378": "tū", + "6379": "bèn", + "637A": "nà", + "637B": "niǎn", + "637C": "ruó", + "637D": "zuó", + "637E": "wò", + "637F": "xī", + "6380": "xiān", + "6381": "chéng", + "6382": "diān", + "6383": "sǎo", + "6384": "lūn", + "6385": "qìng", + "6386": "gāng", + "6387": "duō", + "6388": "shòu", + "6389": "diào", + "638A": "póu", + "638B": "dǐ", + "638C": "zhǎng", + "638D": "hùn", + "638E": "jǐ", + "638F": "tāo", + "6390": "qiā", + "6391": "qí", + "6392": "pái", + "6393": "shū", + "6394": "qiān", + "6395": "líng", + "6396": "yē", + "6397": "yà", + "6398": "jué", + "6399": "zhēng", + "639A": "liǎng", + "639B": "guà", + "639C": "yì", + "639D": "huò", + "639E": "shàn", + "639F": "zhěng", + "63A0": "è", + "63A1": "cǎi", + "63A2": "tàn", + "63A3": "chè", + "63A4": "bīng", + "63A5": "jiē", + "63A6": "tì", + "63A7": "kòng", + "63A8": "tuī", + "63A9": "yǎn", + "63AA": "cuò", + "63AB": "zhōu", + "63AC": "jū", + "63AD": "tiàn", + "63AE": "qián", + "63AF": "kèn", + "63B0": "bāi", + "63B1": "pá", + "63B2": "jiē", + "63B3": "lǔ", + "63B4": "guāi", + "63B5": "ming", + "63B6": "geng", + "63B7": "zhì", + "63B8": "dǎn", + "63B9": "meng", + "63BA": "càn", + "63BB": "sāo", + "63BC": "guàn", + "63BD": "pèng", + "63BE": "yuàn", + "63BF": "nuò", + "63C0": "jiǎn", + "63C1": "zhēng", + "63C2": "jiū", + "63C3": "jiǎn", + "63C4": "yú", + "63C5": "yán", + "63C6": "kuí", + "63C7": "nǎn", + "63C8": "hōng", + "63C9": "róu", + "63CA": "pì", + "63CB": "wēi", + "63CC": "sāi", + "63CD": "zòu", + "63CE": "xuān", + "63CF": "miáo", + "63D0": "tí", + "63D1": "niē", + "63D2": "chā", + "63D3": "shì", + "63D4": "zǒng", + "63D5": "zhèn", + "63D6": "yī", + "63D7": "xún", + "63D8": "yóng", + "63D9": "biān", + "63DA": "yáng", + "63DB": "huàn", + "63DC": "yǎn", + "63DD": "zǎn", + "63DE": "ǎn", + "63DF": "xū", + "63E0": "yà", + "63E1": "wò", + "63E2": "ké", + "63E3": "chuāi", + "63E4": "jí", + "63E5": "tì", + "63E6": "lá", + "63E7": "là", + "63E8": "chén", + "63E9": "kāi", + "63EA": "jiū", + "63EB": "jiū", + "63EC": "tú", + "63ED": "jiē", + "63EE": "huī", + "63EF": "gèn", + "63F0": "chòng", + "63F1": "xiāo", + "63F2": "dié", + "63F3": "xiē", + "63F4": "yuán", + "63F5": "qián", + "63F6": "yé", + "63F7": "chā", + "63F8": "zhā", + "63F9": "bēi", + "63FA": "yáo", + "63FB": "wēi", + "63FC": "beng", + "63FD": "lǎn", + "63FE": "wèn", + "63FF": "qìn", + "6400": "chān", + "6401": "gē", + "6402": "lǒu", + "6403": "zǒng", + "6404": "gēng", + "6405": "jiǎo", + "6406": "gòu", + "6407": "qìn", + "6408": "róng", + "6409": "què", + "640A": "chōu", + "640B": "chuāi", + "640C": "zhǎn", + "640D": "sǔn", + "640E": "sūn", + "640F": "bó", + "6410": "chù", + "6411": "róng", + "6412": "bàng", + "6413": "cuō", + "6414": "sāo", + "6415": "kē", + "6416": "yáo", + "6417": "dǎo", + "6418": "zhī", + "6419": "nù", + "641A": "lā", + "641B": "jiān", + "641C": "sōu", + "641D": "qiǔ", + "641E": "gǎo", + "641F": "xiǎn", + "6420": "shuò", + "6421": "sǎng", + "6422": "jìn", + "6423": "miè", + "6424": "è", + "6425": "chuí", + "6426": "nuò", + "6427": "shān", + "6428": "tà", + "6429": "zhǎ", + "642A": "táng", + "642B": "pán", + "642C": "bān", + "642D": "dā", + "642E": "lì", + "642F": "tāo", + "6430": "hú", + "6431": "zhì", + "6432": "wā", + "6433": "huá", + "6434": "qiān", + "6435": "wèn", + "6436": "qiǎng", + "6437": "tián", + "6438": "zhēn", + "6439": "è", + "643A": "xié", + "643B": "nuò", + "643C": "quán", + "643D": "chá", + "643E": "zhà", + "643F": "gé", + "6440": "wǔ", + "6441": "èn", + "6442": "shè", + "6443": "káng", + "6444": "shè", + "6445": "shū", + "6446": "bǎi", + "6447": "yáo", + "6448": "bìn", + "6449": "sōu", + "644A": "tān", + "644B": "sà", + "644C": "chǎn", + "644D": "suō", + "644E": "jiū", + "644F": "chōng", + "6450": "chuāng", + "6451": "guāi", + "6452": "bǐng", + "6453": "féng", + "6454": "shuāi", + "6455": "dì", + "6456": "qì", + "6457": "sōu", + "6458": "zhāi", + "6459": "liǎn", + "645A": "chēng", + "645B": "chī", + "645C": "guàn", + "645D": "lù", + "645E": "luò", + "645F": "lǒu", + "6460": "zǒng", + "6461": "gài", + "6462": "hù", + "6463": "zhā", + "6464": "chuǎng", + "6465": "tàng", + "6466": "huà", + "6467": "cuī", + "6468": "nái", + "6469": "mó", + "646A": "jiāng", + "646B": "guī", + "646C": "yǐng", + "646D": "zhí", + "646E": "áo", + "646F": "zhì", + "6470": "niè", + "6471": "màn", + "6472": "chàn", + "6473": "kōu", + "6474": "chū", + "6475": "shè", + "6476": "tuán", + "6477": "jiǎo", + "6478": "mō", + "6479": "mó", + "647A": "zhé", + "647B": "càn", + "647C": "kēng", + "647D": "biāo", + "647E": "jiàng", + "647F": "yīn", + "6480": "gòu", + "6481": "qiān", + "6482": "liào", + "6483": "jí", + "6484": "yīng", + "6485": "juē", + "6486": "piē", + "6487": "piē", + "6488": "lāo", + "6489": "dūn", + "648A": "xiàn", + "648B": "ruán", + "648C": "guì", + "648D": "zǎn", + "648E": "yì", + "648F": "xián", + "6490": "chēng", + "6491": "chēng", + "6492": "sā", + "6493": "náo", + "6494": "hòng", + "6495": "sī", + "6496": "hàn", + "6497": "guàng", + "6498": "dā", + "6499": "zǔn", + "649A": "niǎn", + "649B": "lǐn", + "649C": "zhěng", + "649D": "huī", + "649E": "zhuàng", + "649F": "jiǎo", + "64A0": "jǐ", + "64A1": "cāo", + "64A2": "dǎn", + "64A3": "dǎn", + "64A4": "chè", + "64A5": "bō", + "64A6": "chě", + "64A7": "juē", + "64A8": "fǔ", + "64A9": "liāo", + "64AA": "bèn", + "64AB": "fǔ", + "64AC": "qiào", + "64AD": "bō", + "64AE": "cuō", + "64AF": "zhuó", + "64B0": "zhuàn", + "64B1": "wěi", + "64B2": "pū", + "64B3": "qìn", + "64B4": "dūn", + "64B5": "niǎn", + "64B6": "huá", + "64B7": "xié", + "64B8": "lū", + "64B9": "jiǎo", + "64BA": "cuān", + "64BB": "tà", + "64BC": "hàn", + "64BD": "qiào", + "64BE": "wō", + "64BF": "jiǎn", + "64C0": "gǎn", + "64C1": "yōng", + "64C2": "léi", + "64C3": "nǎng", + "64C4": "lǔ", + "64C5": "shàn", + "64C6": "zhuó", + "64C7": "zé", + "64C8": "pū", + "64C9": "chuò", + "64CA": "jī", + "64CB": "dǎng", + "64CC": "sè", + "64CD": "cāo", + "64CE": "qíng", + "64CF": "qíng", + "64D0": "huàn", + "64D1": "jiē", + "64D2": "qín", + "64D3": "kuǎi", + "64D4": "dān", + "64D5": "xié", + "64D6": "kā", + "64D7": "pǐ", + "64D8": "bāi", + "64D9": "ào", + "64DA": "jù", + "64DB": "yè", + "64DC": "e", + "64DD": "meng", + "64DE": "sǒu", + "64DF": "mí", + "64E0": "jǐ", + "64E1": "tái", + "64E2": "zhuó", + "64E3": "dǎo", + "64E4": "xǐng", + "64E5": "lǎn", + "64E6": "cā", + "64E7": "jǔ", + "64E8": "yé", + "64E9": "rǔ", + "64EA": "yè", + "64EB": "yè", + "64EC": "nǐ", + "64ED": "wò", + "64EE": "jí", + "64EF": "bìn", + "64F0": "níng", + "64F1": "gē", + "64F2": "zhì", + "64F3": "zhì", + "64F4": "kuò", + "64F5": "mó", + "64F6": "jiàn", + "64F7": "xié", + "64F8": "liè", + "64F9": "tān", + "64FA": "bǎi", + "64FB": "sǒu", + "64FC": "lǔ", + "64FD": "lüè", + "64FE": "rǎo", + "64FF": "tī", + "6500": "pān", + "6501": "yǎng", + "6502": "lèi", + "6503": "cā", + "6504": "shū", + "6505": "zǎn", + "6506": "niǎn", + "6507": "xiǎn", + "6508": "jùn", + "6509": "huō", + "650A": "lì", + "650B": "là", + "650C": "huǎn", + "650D": "yíng", + "650E": "lú", + "650F": "lǒng", + "6510": "qiān", + "6511": "qiān", + "6512": "zǎn", + "6513": "qiān", + "6514": "lán", + "6515": "xiān", + "6516": "yīng", + "6517": "méi", + "6518": "rǎng", + "6519": "chān", + "651A": "ying", + "651B": "cuān", + "651C": "xié", + "651D": "shè", + "651E": "luó", + "651F": "jùn", + "6520": "mí", + "6521": "lí", + "6522": "zǎn", + "6523": "luán", + "6524": "tān", + "6525": "zuàn", + "6526": "lì", + "6527": "diān", + "6528": "wā", + "6529": "dǎng", + "652A": "jiǎo", + "652B": "jué", + "652C": "lǎn", + "652D": "lì", + "652E": "nǎng", + "652F": "zhī", + "6530": "guì", + "6531": "guǐ", + "6532": "qī", + "6533": "xún", + "6534": "pū", + "6535": "suī", + "6536": "shōu", + "6537": "kǎo", + "6538": "yōu", + "6539": "gǎi", + "653A": "yǐ", + "653B": "gōng", + "653C": "gān", + "653D": "bān", + "653E": "fàng", + "653F": "zhèng", + "6540": "pò", + "6541": "diān", + "6542": "kòu", + "6543": "mǐn", + "6544": "wù", + "6545": "gù", + "6546": "hé", + "6547": "cè", + "6548": "xiào", + "6549": "mǐ", + "654A": "chù", + "654B": "gé", + "654C": "dí", + "654D": "xù", + "654E": "jiào", + "654F": "mǐn", + "6550": "chén", + "6551": "jiù", + "6552": "shēn", + "6553": "duó", + "6554": "yǔ", + "6555": "chì", + "6556": "áo", + "6557": "bài", + "6558": "xù", + "6559": "jiào", + "655A": "duó", + "655B": "liǎn", + "655C": "niè", + "655D": "bì", + "655E": "chang", + "655F": "diǎn", + "6560": "duō", + "6561": "yì", + "6562": "gǎn", + "6563": "sàn", + "6564": "kě", + "6565": "yàn", + "6566": "dūn", + "6567": "jī", + "6568": "tǒu", + "6569": "xiào", + "656A": "duó", + "656B": "jiǎo", + "656C": "jìng", + "656D": "yáng", + "656E": "xiá", + "656F": "mín", + "6570": "shù", + "6571": "ái", + "6572": "qiāo", + "6573": "ái", + "6574": "zhěng", + "6575": "dí", + "6576": "zhèn", + "6577": "fū", + "6578": "shù", + "6579": "liáo", + "657A": "qū", + "657B": "xiòng", + "657C": "yǐ", + "657D": "jiǎo", + "657E": "shan", + "657F": "jiǎo", + "6580": "zhuó", + "6581": "yì", + "6582": "liǎn", + "6583": "bì", + "6584": "lí", + "6585": "xiào", + "6586": "xiào", + "6587": "wén", + "6588": "xué", + "6589": "qí", + "658A": "qí", + "658B": "zhāi", + "658C": "bīn", + "658D": "jué", + "658E": "zhāi", + "658F": "láng", + "6590": "fěi", + "6591": "bān", + "6592": "bān", + "6593": "lán", + "6594": "yǔ", + "6595": "lán", + "6596": "wěi", + "6597": "dòu", + "6598": "shēng", + "6599": "liào", + "659A": "jiǎ", + "659B": "hú", + "659C": "xié", + "659D": "jiǎ", + "659E": "yǔ", + "659F": "zhēn", + "65A0": "jiào", + "65A1": "wò", + "65A2": "tiǎo", + "65A3": "dòu", + "65A4": "jīn", + "65A5": "chì", + "65A6": "yín", + "65A7": "fǔ", + "65A8": "qiāng", + "65A9": "zhǎn", + "65AA": "qú", + "65AB": "zhuó", + "65AC": "zhǎn", + "65AD": "duàn", + "65AE": "cuò", + "65AF": "sī", + "65B0": "xīn", + "65B1": "zhuó", + "65B2": "zhuó", + "65B3": "qín", + "65B4": "lín", + "65B5": "zhuó", + "65B6": "chù", + "65B7": "duàn", + "65B8": "zhǔ", + "65B9": "fāng", + "65BA": "chǎn", + "65BB": "háng", + "65BC": "yú", + "65BD": "shī", + "65BE": "pèi", + "65BF": "yóu", + "65C0": "mèi", + "65C1": "páng", + "65C2": "qí", + "65C3": "zhān", + "65C4": "máo", + "65C5": "lǚ", + "65C6": "pèi", + "65C7": "pī", + "65C8": "liú", + "65C9": "fū", + "65CA": "fǎng", + "65CB": "xuán", + "65CC": "jīng", + "65CD": "jīng", + "65CE": "nǐ", + "65CF": "zú", + "65D0": "zhào", + "65D1": "yǐ", + "65D2": "liú", + "65D3": "shāo", + "65D4": "jiàn", + "65D5": "yú", + "65D6": "yǐ", + "65D7": "qí", + "65D8": "zhì", + "65D9": "fān", + "65DA": "piāo", + "65DB": "fān", + "65DC": "zhān", + "65DD": "kuài", + "65DE": "suì", + "65DF": "yú", + "65E0": "wú", + "65E1": "jì", + "65E2": "jì", + "65E3": "jì", + "65E4": "huò", + "65E5": "rì", + "65E6": "dàn", + "65E7": "jiù", + "65E8": "zhǐ", + "65E9": "zǎo", + "65EA": "xié", + "65EB": "tiāo", + "65EC": "xún", + "65ED": "xù", + "65EE": "gā", + "65EF": "lá", + "65F0": "gàn", + "65F1": "hàn", + "65F2": "tái", + "65F3": "dì", + "65F4": "xū", + "65F5": "chǎn", + "65F6": "shí", + "65F7": "kuàng", + "65F8": "yáng", + "65F9": "shí", + "65FA": "wàng", + "65FB": "mín", + "65FC": "mín", + "65FD": "tùn", + "65FE": "chūn", + "65FF": "wǔ", + "6600": "yún", + "6601": "bèi", + "6602": "áng", + "6603": "zè", + "6604": "bǎn", + "6605": "jié", + "6606": "kūn", + "6607": "shēng", + "6608": "hù", + "6609": "fǎng", + "660A": "hào", + "660B": "guì", + "660C": "chāng", + "660D": "xuān", + "660E": "míng", + "660F": "hūn", + "6610": "fēn", + "6611": "qǐn", + "6612": "hū", + "6613": "yì", + "6614": "xī", + "6615": "xīn", + "6616": "yán", + "6617": "zè", + "6618": "fǎng", + "6619": "tán", + "661A": "shèn", + "661B": "jù", + "661C": "yáng", + "661D": "zǎn", + "661E": "bǐng", + "661F": "xīng", + "6620": "yìng", + "6621": "xuàn", + "6622": "pò", + "6623": "zhěn", + "6624": "líng", + "6625": "chūn", + "6626": "hào", + "6627": "mèi", + "6628": "zuó", + "6629": "mò", + "662A": "biàn", + "662B": "xù", + "662C": "hūn", + "662D": "zhāo", + "662E": "zòng", + "662F": "shì", + "6630": "shì", + "6631": "yù", + "6632": "fèi", + "6633": "dié", + "6634": "mǎo", + "6635": "nì", + "6636": "chǎng", + "6637": "wēn", + "6638": "dōng", + "6639": "ǎi", + "663A": "bǐng", + "663B": "áng", + "663C": "zhòu", + "663D": "lóng", + "663E": "xiǎn", + "663F": "kuàng", + "6640": "tiǎo", + "6641": "cháo", + "6642": "shí", + "6643": "huang", + "6644": "huǎng", + "6645": "xuǎn", + "6646": "kuí", + "6647": "xū", + "6648": "jiǎo", + "6649": "jìn", + "664A": "zhì", + "664B": "jìn", + "664C": "shǎng", + "664D": "tóng", + "664E": "hǒng", + "664F": "yàn", + "6650": "gāi", + "6651": "xiǎng", + "6652": "shài", + "6653": "xiǎo", + "6654": "yè", + "6655": "yūn", + "6656": "huī", + "6657": "hán", + "6658": "hàn", + "6659": "jùn", + "665A": "wǎn", + "665B": "xiàn", + "665C": "kūn", + "665D": "zhòu", + "665E": "xī", + "665F": "chéng", + "6660": "shèng", + "6661": "bū", + "6662": "zhé", + "6663": "zhé", + "6664": "wù", + "6665": "hàn", + "6666": "huì", + "6667": "hào", + "6668": "chen", + "6669": "wǎn", + "666A": "tiǎn", + "666B": "zhuó", + "666C": "zuì", + "666D": "zhǒu", + "666E": "pǔ", + "666F": "jǐng", + "6670": "xī", + "6671": "shǎn", + "6672": "nǐ", + "6673": "xī", + "6674": "qíng", + "6675": "qǐ", + "6676": "jīng", + "6677": "guǐ", + "6678": "zhěng", + "6679": "yì", + "667A": "zhì", + "667B": "àn", + "667C": "wǎn", + "667D": "lín", + "667E": "liàng", + "667F": "chāng", + "6680": "wǎng", + "6681": "xiǎo", + "6682": "zàn", + "6683": "fei", + "6684": "xuān", + "6685": "gèng", + "6686": "yí", + "6687": "xiá", + "6688": "yūn", + "6689": "huī", + "668A": "xǔ", + "668B": "mǐn", + "668C": "kuí", + "668D": "yē", + "668E": "yìng", + "668F": "shǔ", + "6690": "wěi", + "6691": "shǔ", + "6692": "qíng", + "6693": "mào", + "6694": "nán", + "6695": "jiǎn", + "6696": "nuǎn", + "6697": "àn", + "6698": "yáng", + "6699": "chūn", + "669A": "yáo", + "669B": "suǒ", + "669C": "jìn", + "669D": "míng", + "669E": "jiǎo", + "669F": "kǎi", + "66A0": "gǎo", + "66A1": "wěng", + "66A2": "chàng", + "66A3": "qì", + "66A4": "hào", + "66A5": "yàn", + "66A6": "lì", + "66A7": "ài", + "66A8": "jì", + "66A9": "jì", + "66AA": "mèn", + "66AB": "zàn", + "66AC": "xiè", + "66AD": "hào", + "66AE": "mù", + "66AF": "mò", + "66B0": "cōng", + "66B1": "nì", + "66B2": "zhāng", + "66B3": "huì", + "66B4": "bào", + "66B5": "hàn", + "66B6": "xuán", + "66B7": "chuán", + "66B8": "liáo", + "66B9": "xiān", + "66BA": "dàn", + "66BB": "jǐng", + "66BC": "piē", + "66BD": "lín", + "66BE": "tūn", + "66BF": "xǐ", + "66C0": "yì", + "66C1": "jì", + "66C2": "huàng", + "66C3": "dài", + "66C4": "yè", + "66C5": "yè", + "66C6": "lì", + "66C7": "tán", + "66C8": "tóng", + "66C9": "xiǎo", + "66CA": "fèi", + "66CB": "shěn", + "66CC": "zhào", + "66CD": "hào", + "66CE": "yì", + "66CF": "xiǎng", + "66D0": "xīng", + "66D1": "shēn", + "66D2": "jiǎo", + "66D3": "bào", + "66D4": "jìng", + "66D5": "yàn", + "66D6": "ài", + "66D7": "yè", + "66D8": "rú", + "66D9": "shǔ", + "66DA": "méng", + "66DB": "xūn", + "66DC": "yào", + "66DD": "pù", + "66DE": "lì", + "66DF": "chén", + "66E0": "kuàng", + "66E1": "dié", + "66E2": "liǎo", + "66E3": "yàn", + "66E4": "huò", + "66E5": "lú", + "66E6": "xī", + "66E7": "róng", + "66E8": "lóng", + "66E9": "nǎng", + "66EA": "luǒ", + "66EB": "luán", + "66EC": "shài", + "66ED": "tǎng", + "66EE": "yǎn", + "66EF": "zhú", + "66F0": "yuē", + "66F1": "yuē", + "66F2": "qū", + "66F3": "yè", + "66F4": "gèng", + "66F5": "yè", + "66F6": "hū", + "66F7": "hé", + "66F8": "shū", + "66F9": "cáo", + "66FA": "cáo", + "66FB": "sheng", + "66FC": "màn", + "66FD": "cēng", + "66FE": "céng", + "66FF": "tì", + "6700": "zuì", + "6701": "cǎn", + "6702": "xù", + "6703": "huì", + "6704": "yǐn", + "6705": "qiè", + "6706": "fēn", + "6707": "pí", + "6708": "yuè", + "6709": "yǒu", + "670A": "ruǎn", + "670B": "péng", + "670C": "fén", + "670D": "fú", + "670E": "líng", + "670F": "fěi", + "6710": "qú", + "6711": "tì", + "6712": "nǜ", + "6713": "tiǎo", + "6714": "shuò", + "6715": "zhèn", + "6716": "lǎng", + "6717": "lǎng", + "6718": "zuī", + "6719": "míng", + "671A": "huāng", + "671B": "wàng", + "671C": "tūn", + "671D": "cháo", + "671E": "jī", + "671F": "qī", + "6720": "yīng", + "6721": "zōng", + "6722": "wàng", + "6723": "tóng", + "6724": "lǎng", + "6725": "lao", + "6726": "méng", + "6727": "lóng", + "6728": "mù", + "6729": "děng", + "672A": "wèi", + "672B": "mò", + "672C": "běn", + "672D": "zhá", + "672E": "shù", + "672F": "shù", + "6730": "mù", + "6731": "zhū", + "6732": "rén", + "6733": "bā", + "6734": "pǔ", + "6735": "duo", + "6736": "duǒ", + "6737": "dāo", + "6738": "lì", + "6739": "guǐ", + "673A": "jī", + "673B": "jiū", + "673C": "bǐ", + "673D": "xiǔ", + "673E": "chéng", + "673F": "cì", + "6740": "shā", + "6741": "ru", + "6742": "zá", + "6743": "quán", + "6744": "qiān", + "6745": "yú", + "6746": "gān", + "6747": "wū", + "6748": "chā", + "6749": "shān", + "674A": "xún", + "674B": "fán", + "674C": "wù", + "674D": "zǐ", + "674E": "li", + "674F": "xìng", + "6750": "cái", + "6751": "cūn", + "6752": "rèn", + "6753": "biāo", + "6754": "tuō", + "6755": "dì", + "6756": "zhàng", + "6757": "máng", + "6758": "chì", + "6759": "yì", + "675A": "gài", + "675B": "gōng", + "675C": "dù", + "675D": "lí", + "675E": "qǐ", + "675F": "shù", + "6760": "gāng", + "6761": "tiáo", + "6762": "jiang", + "6763": "shan", + "6764": "wan", + "6765": "lái", + "6766": "jiu", + "6767": "máng", + "6768": "yáng", + "6769": "mà", + "676A": "miǎo", + "676B": "sì", + "676C": "yuán", + "676D": "háng", + "676E": "fèi", + "676F": "bēi", + "6770": "jié", + "6771": "dōng", + "6772": "gǎo", + "6773": "yǎo", + "6774": "xiān", + "6775": "chǔ", + "6776": "chūn", + "6777": "pá", + "6778": "shū", + "6779": "huà", + "677A": "xīn", + "677B": "chǒu", + "677C": "zhù", + "677D": "chǒu", + "677E": "sōng", + "677F": "bǎn", + "6780": "sōng", + "6781": "jí", + "6782": "wò", + "6783": "jìn", + "6784": "gòu", + "6785": "jī", + "6786": "máo", + "6787": "pí", + "6788": "bì", + "6789": "wang", + "678A": "àng", + "678B": "fāng", + "678C": "fén", + "678D": "yì", + "678E": "fú", + "678F": "nán", + "6790": "xī", + "6791": "hù", + "6792": "yā", + "6793": "dǒu", + "6794": "xín", + "6795": "zhěn", + "6796": "yāo", + "6797": "lín", + "6798": "ruì", + "6799": "ě", + "679A": "méi", + "679B": "zhào", + "679C": "guǒ", + "679D": "zhī", + "679E": "cōng", + "679F": "yùn", + "67A0": "zui", + "67A1": "dǒu", + "67A2": "shū", + "67A3": "zǎo", + "67A4": "duo", + "67A5": "lì", + "67A6": "lu", + "67A7": "jiǎn", + "67A8": "chéng", + "67A9": "song", + "67AA": "qiāng", + "67AB": "fēng", + "67AC": "nán", + "67AD": "xiāo", + "67AE": "xiān", + "67AF": "kū", + "67B0": "píng", + "67B1": "tái", + "67B2": "xǐ", + "67B3": "zhǐ", + "67B4": "guǎi", + "67B5": "xiāo", + "67B6": "jià", + "67B7": "jiā", + "67B8": "gǒu", + "67B9": "bāo", + "67BA": "mò", + "67BB": "yì", + "67BC": "yè", + "67BD": "yè", + "67BE": "shì", + "67BF": "niè", + "67C0": "bǐ", + "67C1": "duò", + "67C2": "yí", + "67C3": "líng", + "67C4": "bǐng", + "67C5": "nǐ", + "67C6": "lā", + "67C7": "hé", + "67C8": "bàn", + "67C9": "fán", + "67CA": "zhōng", + "67CB": "dài", + "67CC": "cí", + "67CD": "yǎng", + "67CE": "fū", + "67CF": "bǎi", + "67D0": "mǒu", + "67D1": "gān", + "67D2": "qī", + "67D3": "rǎn", + "67D4": "róu", + "67D5": "mào", + "67D6": "sháo", + "67D7": "sōng", + "67D8": "zhè", + "67D9": "xiá", + "67DA": "yòu", + "67DB": "shēn", + "67DC": "guì", + "67DD": "tuò", + "67DE": "zhà", + "67DF": "nán", + "67E0": "níng", + "67E1": "yǒng", + "67E2": "dǐ", + "67E3": "zhì", + "67E4": "zhā", + "67E5": "chá", + "67E6": "dàn", + "67E7": "gū", + "67E8": "bù", + "67E9": "jiù", + "67EA": "āo", + "67EB": "fú", + "67EC": "jiǎn", + "67ED": "bā", + "67EE": "duò", + "67EF": "kē", + "67F0": "nài", + "67F1": "zhù", + "67F2": "bì", + "67F3": "liǔ", + "67F4": "chái", + "67F5": "shān", + "67F6": "sì", + "67F7": "chù", + "67F8": "pēi", + "67F9": "shì", + "67FA": "guǎi", + "67FB": "zhā", + "67FC": "yǎo", + "67FD": "chēng", + "67FE": "jiù", + "67FF": "shì", + "6800": "zhī", + "6801": "liǔ", + "6802": "méi", + "6803": "li", + "6804": "róng", + "6805": "zhà", + "6806": "zao", + "6807": "biāo", + "6808": "zhàn", + "6809": "zhì", + "680A": "lóng", + "680B": "dòng", + "680C": "lú", + "680D": "shēng", + "680E": "lì", + "680F": "lán", + "6810": "yǒng", + "6811": "shù", + "6812": "xún", + "6813": "shuān", + "6814": "qì", + "6815": "zhēn", + "6816": "qī", + "6817": "lì", + "6818": "yí", + "6819": "xiáng", + "681A": "zhèn", + "681B": "lì", + "681C": "sè", + "681D": "guā", + "681E": "kān", + "681F": "bēn", + "6820": "rěn", + "6821": "xiào", + "6822": "bǎi", + "6823": "rěn", + "6824": "bìng", + "6825": "zī", + "6826": "chóu", + "6827": "yì", + "6828": "cì", + "6829": "xǔ", + "682A": "zhū", + "682B": "jiàn", + "682C": "zuì", + "682D": "ér", + "682E": "ěr", + "682F": "yǒu", + "6830": "fá", + "6831": "gǒng", + "6832": "kǎo", + "6833": "lǎo", + "6834": "zhān", + "6835": "liè", + "6836": "yīn", + "6837": "yàng", + "6838": "hé", + "6839": "gēn", + "683A": "yì", + "683B": "shì", + "683C": "gé", + "683D": "zāi", + "683E": "luán", + "683F": "fú", + "6840": "jié", + "6841": "héng", + "6842": "guì", + "6843": "táo", + "6844": "guāng", + "6845": "wéi", + "6846": "kuāng", + "6847": "rú", + "6848": "àn", + "6849": "ān", + "684A": "juàn", + "684B": "yí", + "684C": "zhuō", + "684D": "kū", + "684E": "zhì", + "684F": "qióng", + "6850": "tóng", + "6851": "sāng", + "6852": "sāng", + "6853": "huán", + "6854": "jú", + "6855": "jiù", + "6856": "xuè", + "6857": "duò", + "6858": "zhuì", + "6859": "yú", + "685A": "zǎn", + "685C": "yīng", + "685D": "jie", + "685E": "liu", + "685F": "zhàn", + "6860": "yā", + "6861": "ráo", + "6862": "zhēn", + "6863": "dàng", + "6864": "qī", + "6865": "qiáo", + "6866": "huà", + "6867": "guì", + "6868": "jiǎng", + "6869": "zhuāng", + "686A": "xún", + "686B": "suō", + "686C": "shā", + "686D": "zhēn", + "686E": "bēi", + "686F": "tīng", + "6870": "kuò", + "6871": "jìng", + "6872": "po", + "6873": "bèn", + "6874": "fú", + "6875": "ruí", + "6876": "tǒng", + "6877": "jué", + "6878": "xī", + "6879": "láng", + "687A": "liǔ", + "687B": "fēng", + "687C": "qī", + "687D": "wěn", + "687E": "jūn", + "687F": "gǎn", + "6880": "sù", + "6881": "liáng", + "6882": "qiú", + "6883": "tǐng", + "6884": "yǒu", + "6885": "méi", + "6886": "bāng", + "6887": "lòng", + "6888": "pēng", + "6889": "zhuāng", + "688A": "dì", + "688B": "xuān", + "688C": "tú", + "688D": "zào", + "688E": "āo", + "688F": "gù", + "6890": "bì", + "6891": "dí", + "6892": "hán", + "6893": "zǐ", + "6894": "zhī", + "6895": "rèn", + "6896": "bèi", + "6897": "gěng", + "6898": "jiǎn", + "6899": "huàn", + "689A": "wǎn", + "689B": "nuó", + "689C": "jiā", + "689D": "tiáo", + "689E": "jì", + "689F": "xiāo", + "68A0": "lǚ", + "68A1": "hún", + "68A2": "shāo", + "68A3": "cén", + "68A4": "fén", + "68A5": "sōng", + "68A6": "mèng", + "68A7": "wú", + "68A8": "lí", + "68A9": "lí", + "68AA": "dòu", + "68AB": "qǐn", + "68AC": "yǐng", + "68AD": "suō", + "68AE": "jū", + "68AF": "tī", + "68B0": "xiè", + "68B1": "kǔn", + "68B2": "zhuó", + "68B3": "shū", + "68B4": "chān", + "68B5": "fàn", + "68B6": "wěi", + "68B7": "jìng", + "68B8": "lí", + "68B9": "bīn", + "68BA": "xia", + "68BB": "fo", + "68BC": "táo", + "68BD": "zhì", + "68BE": "lái", + "68BF": "lián", + "68C0": "jiǎn", + "68C1": "zhuō", + "68C2": "líng", + "68C3": "lí", + "68C4": "qì", + "68C5": "bìng", + "68C6": "lún", + "68C7": "cōng", + "68C8": "qiàn", + "68C9": "mián", + "68CA": "qí", + "68CB": "qí", + "68CC": "cài", + "68CD": "gùn", + "68CE": "chán", + "68CF": "dé", + "68D0": "fěi", + "68D1": "pái", + "68D2": "bàng", + "68D3": "bàng", + "68D4": "hūn", + "68D5": "zōng", + "68D6": "chéng", + "68D7": "zǎo", + "68D8": "jí", + "68D9": "lì", + "68DA": "péng", + "68DB": "yù", + "68DC": "yù", + "68DD": "gù", + "68DE": "jùn", + "68DF": "dòng", + "68E0": "táng", + "68E1": "gāng", + "68E2": "wǎng", + "68E3": "dì", + "68E4": "cuò", + "68E5": "fán", + "68E6": "chēng", + "68E7": "zhàn", + "68E8": "qǐ", + "68E9": "yuān", + "68EA": "yǎn", + "68EB": "yù", + "68EC": "quān", + "68ED": "yì", + "68EE": "sēn", + "68EF": "rěn", + "68F0": "chuí", + "68F1": "léng", + "68F2": "qī", + "68F3": "zhuō", + "68F4": "fú", + "68F5": "kē", + "68F6": "lái", + "68F7": "zōu", + "68F8": "zōu", + "68F9": "zhào", + "68FA": "guān", + "68FB": "fēn", + "68FC": "fén", + "68FD": "shēn", + "68FE": "qíng", + "68FF": "ní", + "6900": "wǎn", + "6901": "guǒ", + "6902": "lù", + "6903": "háo", + "6904": "jiē", + "6905": "yǐ", + "6906": "chóu", + "6907": "jǔ", + "6908": "jú", + "6909": "chéng", + "690A": "zuó", + "690B": "liáng", + "690C": "qiāng", + "690D": "zhí", + "690E": "chuí", + "690F": "yā", + "6910": "jū", + "6911": "bēi", + "6912": "jiāo", + "6913": "zhuó", + "6914": "zī", + "6915": "bīn", + "6916": "péng", + "6917": "dìng", + "6918": "chǔ", + "6919": "chang", + "691A": "men", + "691B": "hua", + "691C": "jiǎn", + "691D": "guī", + "691E": "xì", + "691F": "dú", + "6920": "qiàn", + "6921": "dao", + "6922": "gui", + "6923": "dian", + "6924": "luó", + "6925": "zhī", + "6926": "quan", + "6927": "mìng", + "6928": "fu", + "6929": "geng", + "692A": "pèng", + "692B": "zhǎn", + "692C": "yi", + "692D": "tuǒ", + "692E": "sēn", + "692F": "duǒ", + "6930": "yē", + "6931": "fù", + "6932": "wěi", + "6933": "wēi", + "6934": "duàn", + "6935": "jiǎ", + "6936": "zōng", + "6937": "jiān", + "6938": "yí", + "6939": "shèn", + "693A": "xí", + "693B": "yàn", + "693C": "yǎn", + "693D": "chuán", + "693E": "jiān", + "693F": "chūn", + "6940": "yǔ", + "6941": "hé", + "6942": "zhā", + "6943": "wò", + "6944": "pián", + "6945": "bī", + "6946": "yāo", + "6947": "huò", + "6948": "xū", + "6949": "ruò", + "694A": "yáng", + "694B": "là", + "694C": "yán", + "694D": "běn", + "694E": "huī", + "694F": "kuí", + "6950": "jiè", + "6951": "kuí", + "6952": "sī", + "6953": "fēng", + "6954": "xiē", + "6955": "tuǒ", + "6956": "zhì", + "6957": "jiàn", + "6958": "mù", + "6959": "mào", + "695A": "chu", + "695B": "hù", + "695C": "hú", + "695D": "liàn", + "695E": "léng", + "695F": "tíng", + "6960": "nán", + "6961": "yú", + "6962": "yóu", + "6963": "méi", + "6964": "sǒng", + "6965": "xuàn", + "6966": "xuàn", + "6967": "yǎng", + "6968": "zhēn", + "6969": "pián", + "696A": "yè", + "696B": "jí", + "696C": "jié", + "696D": "yè", + "696E": "chǔ", + "696F": "dùn", + "6970": "yú", + "6971": "zòu", + "6972": "wēi", + "6973": "méi", + "6974": "tì", + "6975": "jí", + "6976": "jié", + "6977": "kǎi", + "6978": "qiū", + "6979": "yíng", + "697A": "rǒu", + "697B": "huáng", + "697C": "lóu", + "697D": "lè", + "697E": "quan", + "697F": "xiang", + "6980": "pǐn", + "6981": "shi", + "6982": "gài", + "6983": "tán", + "6984": "lǎn", + "6985": "wēn", + "6986": "yú", + "6987": "chèn", + "6988": "lǘ", + "6989": "jǔ", + "698A": "shen", + "698B": "chu", + "698C": "pi", + "698D": "xiè", + "698E": "jiǎ", + "698F": "yì", + "6990": "zhǎn", + "6991": "fú", + "6992": "nuò", + "6993": "mì", + "6994": "láng", + "6995": "róng", + "6996": "gǔ", + "6997": "jiàn", + "6998": "jǔ", + "6999": "tā", + "699A": "yǎo", + "699B": "zhēn", + "699C": "bǎng", + "699D": "shā", + "699E": "yuán", + "699F": "zǐ", + "69A0": "míng", + "69A1": "sù", + "69A2": "jià", + "69A3": "yáo", + "69A4": "jié", + "69A5": "huàng", + "69A6": "gàn", + "69A7": "fěi", + "69A8": "zhà", + "69A9": "qián", + "69AA": "mà", + "69AB": "sǔn", + "69AC": "yuán", + "69AD": "xiè", + "69AE": "róng", + "69AF": "shí", + "69B0": "zhī", + "69B1": "cuī", + "69B2": "yún", + "69B3": "tíng", + "69B4": "liú", + "69B5": "róng", + "69B6": "táng", + "69B7": "què", + "69B8": "zhāi", + "69B9": "sī", + "69BA": "shèng", + "69BB": "tà", + "69BC": "kē", + "69BD": "xī", + "69BE": "gǔ", + "69BF": "qī", + "69C0": "gǎo", + "69C1": "gǎo", + "69C2": "sūn", + "69C3": "pán", + "69C4": "tāo", + "69C5": "gé", + "69C6": "xún", + "69C7": "diān", + "69C8": "nòu", + "69C9": "jí", + "69CA": "shuò", + "69CB": "gòu", + "69CC": "chuí", + "69CD": "qiāng", + "69CE": "chá", + "69CF": "qiǎn", + "69D0": "huái", + "69D1": "méi", + "69D2": "xù", + "69D3": "gàng", + "69D4": "gāo", + "69D5": "zhuó", + "69D6": "tuó", + "69D7": "qiao", + "69D8": "yàng", + "69D9": "diān", + "69DA": "jiǎ", + "69DB": "kǎn", + "69DC": "zuì", + "69DD": "dao", + "69DE": "long", + "69DF": "bīn", + "69E0": "zhū", + "69E1": "sang", + "69E2": "xí", + "69E3": "jī", + "69E4": "lián", + "69E5": "huì", + "69E6": "yōng", + "69E7": "qiàn", + "69E8": "guǒ", + "69E9": "gài", + "69EA": "gài", + "69EB": "tuán", + "69EC": "huà", + "69ED": "qī", + "69EE": "sēn", + "69EF": "cuī", + "69F0": "péng", + "69F1": "yǒu", + "69F2": "hú", + "69F3": "jiǎng", + "69F4": "hù", + "69F5": "huàn", + "69F6": "guì", + "69F7": "niè", + "69F8": "yì", + "69F9": "gāo", + "69FA": "kāng", + "69FB": "guī", + "69FC": "guī", + "69FD": "cáo", + "69FE": "màn", + "69FF": "jǐn", + "6A00": "dī", + "6A01": "zhuāng", + "6A02": "lè", + "6A03": "lǎng", + "6A04": "chén", + "6A05": "cōng", + "6A06": "lí", + "6A07": "xiū", + "6A08": "qíng", + "6A09": "shuǎng", + "6A0A": "fán", + "6A0B": "tǒng", + "6A0C": "guàn", + "6A0D": "zé", + "6A0E": "sù", + "6A0F": "lěi", + "6A10": "lǔ", + "6A11": "liáng", + "6A12": "mì", + "6A13": "lóu", + "6A14": "cháo", + "6A15": "sù", + "6A16": "kē", + "6A17": "chū", + "6A18": "táng", + "6A19": "biāo", + "6A1A": "lù", + "6A1B": "jiū", + "6A1C": "zhè", + "6A1D": "zhā", + "6A1E": "shū", + "6A1F": "zhāng", + "6A20": "mán", + "6A21": "mó", + "6A22": "niǎo", + "6A23": "yàng", + "6A24": "tiáo", + "6A25": "péng", + "6A26": "zhù", + "6A27": "shā", + "6A28": "xī", + "6A29": "quán", + "6A2A": "héng", + "6A2B": "jiān", + "6A2C": "cōng", + "6A2D": "ji", + "6A2E": "yan", + "6A2F": "qiáng", + "6A30": "xue", + "6A31": "yīng", + "6A32": "èr", + "6A33": "xún", + "6A34": "zhí", + "6A35": "qiáo", + "6A36": "zuī", + "6A37": "cóng", + "6A38": "pǔ", + "6A39": "shù", + "6A3A": "huà", + "6A3B": "kuì", + "6A3C": "zhēn", + "6A3D": "zūn", + "6A3E": "yuè", + "6A3F": "shàn", + "6A40": "xī", + "6A41": "chūn", + "6A42": "diàn", + "6A43": "fá", + "6A44": "gǎn", + "6A45": "mó", + "6A46": "wǔ", + "6A47": "qiāo", + "6A48": "ráo", + "6A49": "lìn", + "6A4A": "liú", + "6A4B": "qiáo", + "6A4C": "xiàn", + "6A4D": "rùn", + "6A4E": "fán", + "6A4F": "zhǎn", + "6A50": "tuó", + "6A51": "lǎo", + "6A52": "yún", + "6A53": "shùn", + "6A54": "dūn", + "6A55": "chēng", + "6A56": "táng", + "6A57": "méng", + "6A58": "jú", + "6A59": "chéng", + "6A5A": "sù", + "6A5B": "jué", + "6A5C": "jué", + "6A5D": "diàn", + "6A5E": "huì", + "6A5F": "jī", + "6A60": "nuǒ", + "6A61": "xiàng", + "6A62": "tuǒ", + "6A63": "nǐng", + "6A64": "ruǐ", + "6A65": "zhū", + "6A66": "tóng", + "6A67": "zēng", + "6A68": "fén", + "6A69": "qióng", + "6A6A": "rǎn", + "6A6B": "héng", + "6A6C": "qián", + "6A6D": "gū", + "6A6E": "liǔ", + "6A6F": "lào", + "6A70": "gāo", + "6A71": "chú", + "6A72": "xi", + "6A73": "sheng", + "6A74": "zi", + "6A75": "san", + "6A76": "jí", + "6A77": "dōu", + "6A78": "jing", + "6A79": "lǔ", + "6A7A": "jian", + "6A7B": "chu", + "6A7C": "yuán", + "6A7D": "tà", + "6A7E": "shū", + "6A7F": "jiāng", + "6A80": "tán", + "6A81": "lǐn", + "6A82": "nóng", + "6A83": "yǐn", + "6A84": "xí", + "6A85": "suì", + "6A86": "shān", + "6A87": "zuì", + "6A88": "xuán", + "6A89": "chēng", + "6A8A": "gàn", + "6A8B": "jú", + "6A8C": "zuì", + "6A8D": "yì", + "6A8E": "qín", + "6A8F": "pǔ", + "6A90": "yán", + "6A91": "léi", + "6A92": "fēng", + "6A93": "huǐ", + "6A94": "dàng", + "6A95": "jì", + "6A96": "suì", + "6A97": "bò", + "6A98": "píng", + "6A99": "chéng", + "6A9A": "chǔ", + "6A9B": "zhuā", + "6A9C": "guì", + "6A9D": "jí", + "6A9E": "jiě", + "6A9F": "jiǎ", + "6AA0": "qíng", + "6AA1": "zhái", + "6AA2": "jiǎn", + "6AA3": "qiáng", + "6AA4": "dào", + "6AA5": "yǐ", + "6AA6": "biǎo", + "6AA7": "sōng", + "6AA8": "shē", + "6AA9": "lǐn", + "6AAA": "li", + "6AAB": "chá", + "6AAC": "méng", + "6AAD": "yín", + "6AAE": "táo", + "6AAF": "tái", + "6AB0": "mián", + "6AB1": "qí", + "6AB2": "tuán", + "6AB3": "bīn", + "6AB4": "huò", + "6AB5": "jì", + "6AB6": "qiān", + "6AB7": "nǐ", + "6AB8": "níng", + "6AB9": "yī", + "6ABA": "gǎo", + "6ABB": "kǎn", + "6ABC": "yìn", + "6ABD": "nòu", + "6ABE": "qǐng", + "6ABF": "yǎn", + "6AC0": "qí", + "6AC1": "mì", + "6AC2": "zhào", + "6AC3": "guì", + "6AC4": "chūn", + "6AC5": "jī", + "6AC6": "kuí", + "6AC7": "pó", + "6AC8": "dèng", + "6AC9": "chú", + "6ACA": "ge", + "6ACB": "mián", + "6ACC": "yōu", + "6ACD": "zhì", + "6ACE": "huǎng", + "6ACF": "qiān", + "6AD0": "lěi", + "6AD1": "léi", + "6AD2": "sà", + "6AD3": "lǔ", + "6AD4": "lì", + "6AD5": "cuán", + "6AD6": "lǜ", + "6AD7": "miè", + "6AD8": "huì", + "6AD9": "ōu", + "6ADA": "lú", + "6ADB": "zhì", + "6ADC": "gāo", + "6ADD": "dú", + "6ADE": "yuán", + "6ADF": "lì", + "6AE0": "fèi", + "6AE1": "zhuó", + "6AE2": "sǒu", + "6AE3": "lián", + "6AE4": "jiang", + "6AE5": "chú", + "6AE6": "qing", + "6AE7": "zhū", + "6AE8": "lú", + "6AE9": "yán", + "6AEA": "lì", + "6AEB": "zhū", + "6AEC": "chèn", + "6AED": "jié", + "6AEE": "è", + "6AEF": "sū", + "6AF0": "huái", + "6AF1": "niè", + "6AF2": "yù", + "6AF3": "lóng", + "6AF4": "lài", + "6AF5": "jiao", + "6AF6": "xiǎn", + "6AF7": "guī", + "6AF8": "jǔ", + "6AF9": "xiāo", + "6AFA": "líng", + "6AFB": "yīng", + "6AFC": "jiān", + "6AFD": "yǐn", + "6AFE": "yóu", + "6AFF": "yíng", + "6B00": "xiāng", + "6B01": "nóng", + "6B02": "bó", + "6B03": "chán", + "6B04": "lán", + "6B05": "jǔ", + "6B06": "shuāng", + "6B07": "shè", + "6B08": "wéi", + "6B09": "cóng", + "6B0A": "quán", + "6B0B": "qú", + "6B0C": "cang", + "6B0D": "jiu", + "6B0E": "yù", + "6B0F": "luó", + "6B10": "lì", + "6B11": "cuán", + "6B12": "luán", + "6B13": "dǎng", + "6B14": "jué", + "6B15": "yan", + "6B16": "lǎn", + "6B17": "lán", + "6B18": "zhú", + "6B19": "léi", + "6B1A": "lǐ", + "6B1B": "bà", + "6B1C": "náng", + "6B1D": "yù", + "6B1E": "líng", + "6B1F": "guang", + "6B20": "qiàn", + "6B21": "cì", + "6B22": "huan", + "6B23": "xīn", + "6B24": "yú", + "6B25": "yì", + "6B26": "qiān", + "6B27": "ōu", + "6B28": "xū", + "6B29": "chāo", + "6B2A": "chù", + "6B2B": "qì", + "6B2C": "kài", + "6B2D": "yì", + "6B2E": "jué", + "6B2F": "xì", + "6B30": "xù", + "6B31": "hē", + "6B32": "yù", + "6B33": "kuì", + "6B34": "láng", + "6B35": "kuǎn", + "6B36": "shuò", + "6B37": "xī", + "6B38": "āi", + "6B39": "yī", + "6B3A": "qī", + "6B3B": "chuā", + "6B3C": "chǐ", + "6B3D": "qīn", + "6B3E": "kuǎn", + "6B3F": "kǎn", + "6B40": "kuǎn", + "6B41": "kǎn", + "6B42": "chuǎn", + "6B43": "shà", + "6B44": "guā", + "6B45": "yīn", + "6B46": "xīn", + "6B47": "xiē", + "6B48": "yú", + "6B49": "qiàn", + "6B4A": "xiāo", + "6B4B": "yè", + "6B4C": "gē", + "6B4D": "wū", + "6B4E": "tàn", + "6B4F": "jìn", + "6B50": "ōu", + "6B51": "hū", + "6B52": "tì", + "6B53": "huān", + "6B54": "xū", + "6B55": "pēn", + "6B56": "xǐ", + "6B57": "xiào", + "6B58": "chuā", + "6B59": "shè", + "6B5A": "shàn", + "6B5B": "hān", + "6B5C": "chù", + "6B5D": "yì", + "6B5E": "è", + "6B5F": "yú", + "6B60": "chuò", + "6B61": "huan", + "6B62": "zhǐ", + "6B63": "zhèng", + "6B64": "cǐ", + "6B65": "bù", + "6B66": "wǔ", + "6B67": "qí", + "6B68": "bù", + "6B69": "bù", + "6B6A": "wāi", + "6B6B": "jù", + "6B6C": "qián", + "6B6D": "chí", + "6B6E": "sè", + "6B6F": "chǐ", + "6B70": "sè", + "6B71": "zhǒng", + "6B72": "suì", + "6B73": "suì", + "6B74": "lì", + "6B75": "cuò", + "6B76": "yú", + "6B77": "lì", + "6B78": "guī", + "6B79": "dǎi", + "6B7A": "è", + "6B7B": "sǐ", + "6B7C": "jiān", + "6B7D": "zhé", + "6B7E": "mò", + "6B7F": "mò", + "6B80": "yāo", + "6B81": "mò", + "6B82": "cú", + "6B83": "yāng", + "6B84": "tiǎn", + "6B85": "shēng", + "6B86": "dài", + "6B87": "shāng", + "6B88": "xù", + "6B89": "xùn", + "6B8A": "shū", + "6B8B": "cán", + "6B8C": "jué", + "6B8D": "piǎo", + "6B8E": "qià", + "6B8F": "qiú", + "6B90": "sù", + "6B91": "qíng", + "6B92": "yǔn", + "6B93": "liàn", + "6B94": "yì", + "6B95": "fǒu", + "6B96": "zhí", + "6B97": "yè", + "6B98": "cán", + "6B99": "hūn", + "6B9A": "dān", + "6B9B": "jí", + "6B9C": "dié", + "6B9D": "zhēn", + "6B9E": "yǔn", + "6B9F": "wēn", + "6BA0": "chòu", + "6BA1": "bìn", + "6BA2": "tì", + "6BA3": "jìn", + "6BA4": "shāng", + "6BA5": "yín", + "6BA6": "diāo", + "6BA7": "jiù", + "6BA8": "huì", + "6BA9": "cuàn", + "6BAA": "yì", + "6BAB": "dān", + "6BAC": "dù", + "6BAD": "jiāng", + "6BAE": "liàn", + "6BAF": "bìn", + "6BB0": "dú", + "6BB1": "jian", + "6BB2": "jiān", + "6BB3": "shū", + "6BB4": "ōu", + "6BB5": "duàn", + "6BB6": "zhù", + "6BB7": "yīn", + "6BB8": "qìng", + "6BB9": "yì", + "6BBA": "shā", + "6BBB": "qiào", + "6BBC": "ké", + "6BBD": "xiáo", + "6BBE": "xùn", + "6BBF": "diàn", + "6BC0": "huǐ", + "6BC1": "huǐ", + "6BC2": "gǔ", + "6BC3": "qiāo", + "6BC4": "jī", + "6BC5": "yì", + "6BC6": "ōu", + "6BC7": "huǐ", + "6BC8": "duàn", + "6BC9": "yī", + "6BCA": "xiāo", + "6BCB": "wú", + "6BCC": "guàn", + "6BCD": "mǔ", + "6BCE": "měi", + "6BCF": "měi", + "6BD0": "ǎi", + "6BD1": "jiě", + "6BD2": "dú", + "6BD3": "yù", + "6BD4": "bǐ", + "6BD5": "bì", + "6BD6": "bì", + "6BD7": "pí", + "6BD8": "pí", + "6BD9": "bì", + "6BDA": "chán", + "6BDB": "máo", + "6BDC": "háo", + "6BDD": "cǎi", + "6BDE": "pí", + "6BDF": "lie", + "6BE0": "jiā", + "6BE1": "zhān", + "6BE2": "sāi", + "6BE3": "mù", + "6BE4": "tuò", + "6BE5": "xún", + "6BE6": "ěr", + "6BE7": "róng", + "6BE8": "xiǎn", + "6BE9": "jú", + "6BEA": "mú", + "6BEB": "háo", + "6BEC": "qiú", + "6BED": "dòu", + "6BEE": "shā", + "6BEF": "tǎn", + "6BF0": "péi", + "6BF1": "jú", + "6BF2": "duō", + "6BF3": "cuì", + "6BF4": "bī", + "6BF5": "sān", + "6BF6": "san", + "6BF7": "mào", + "6BF8": "sāi", + "6BF9": "shū", + "6BFA": "yū", + "6BFB": "tuò", + "6BFC": "hé", + "6BFD": "jiàn", + "6BFE": "tà", + "6BFF": "sān", + "6C00": "lǘ", + "6C01": "mú", + "6C02": "máo", + "6C03": "tóng", + "6C04": "rǒng", + "6C05": "chǎng", + "6C06": "pǔ", + "6C07": "lu", + "6C08": "zhān", + "6C09": "sào", + "6C0A": "zhān", + "6C0B": "méng", + "6C0C": "lǔ", + "6C0D": "qú", + "6C0E": "dié", + "6C0F": "shì", + "6C10": "dī", + "6C11": "mín", + "6C12": "jué", + "6C13": "máng", + "6C14": "qì", + "6C15": "piē", + "6C16": "nǎi", + "6C17": "qì", + "6C18": "dāo", + "6C19": "xiān", + "6C1A": "chuān", + "6C1B": "fēn", + "6C1C": "yáng", + "6C1D": "nèi", + "6C1E": "bin", + "6C1F": "fú", + "6C20": "shēn", + "6C21": "dōng", + "6C22": "qīng", + "6C23": "qì", + "6C24": "yīn", + "6C25": "xī", + "6C26": "hài", + "6C27": "yǎng", + "6C28": "ān", + "6C29": "yà", + "6C2A": "kè", + "6C2B": "qīng", + "6C2C": "yà", + "6C2D": "dōng", + "6C2E": "dàn", + "6C2F": "lǜ", + "6C30": "qíng", + "6C31": "yǎng", + "6C32": "yūn", + "6C33": "yūn", + "6C34": "shuǐ", + "6C35": "shui", + "6C36": "zhěng", + "6C37": "bīng", + "6C38": "yǒng", + "6C39": "dàng", + "6C3A": "shui", + "6C3B": "lè", + "6C3C": "nì", + "6C3D": "tǔn", + "6C3E": "fàn", + "6C3F": "guǐ", + "6C40": "tīng", + "6C41": "zhī", + "6C42": "qiú", + "6C43": "bīn", + "6C44": "zè", + "6C45": "miǎn", + "6C46": "cuān", + "6C47": "huì", + "6C48": "diāo", + "6C49": "hàn", + "6C4A": "chà", + "6C4B": "zhuó", + "6C4C": "chuàn", + "6C4D": "wán", + "6C4E": "fàn", + "6C4F": "dà", + "6C50": "xī", + "6C51": "tuō", + "6C52": "máng", + "6C53": "qiú", + "6C54": "qì", + "6C55": "shàn", + "6C56": "pìn", + "6C57": "hàn", + "6C58": "qiān", + "6C59": "wū", + "6C5A": "wū", + "6C5B": "xùn", + "6C5C": "sì", + "6C5D": "rǔ", + "6C5E": "gǒng", + "6C5F": "jiāng", + "6C60": "chí", + "6C61": "wū", + "6C62": "tu", + "6C63": "jiu", + "6C64": "tāng", + "6C65": "zhī", + "6C66": "zhǐ", + "6C67": "qiān", + "6C68": "mì", + "6C69": "gǔ", + "6C6A": "wāng", + "6C6B": "jǐng", + "6C6C": "jǐng", + "6C6D": "ruì", + "6C6E": "jūn", + "6C6F": "hóng", + "6C70": "tài", + "6C71": "quǎn", + "6C72": "jí", + "6C73": "biàn", + "6C74": "biàn", + "6C75": "gàn", + "6C76": "wèn", + "6C77": "zhōng", + "6C78": "fāng", + "6C79": "xiōng", + "6C7A": "jué", + "6C7B": "hǔ", + "6C7C": "niú", + "6C7D": "qì", + "6C7E": "fén", + "6C7F": "xù", + "6C80": "xù", + "6C81": "qìn", + "6C82": "yí", + "6C83": "wò", + "6C84": "yún", + "6C85": "yuán", + "6C86": "hàng", + "6C87": "yǎn", + "6C88": "chén", + "6C89": "chén", + "6C8A": "dàn", + "6C8B": "yóu", + "6C8C": "dùn", + "6C8D": "hù", + "6C8E": "huò", + "6C8F": "qī", + "6C90": "mù", + "6C91": "nǜ", + "6C92": "méi", + "6C93": "dá", + "6C94": "miǎn", + "6C95": "mì", + "6C96": "chōng", + "6C97": "pāng", + "6C98": "bǐ", + "6C99": "shā", + "6C9A": "zhǐ", + "6C9B": "pèi", + "6C9C": "pàn", + "6C9D": "zhuǐ", + "6C9E": "zā", + "6C9F": "gōu", + "6CA0": "liú", + "6CA1": "méi", + "6CA2": "zé", + "6CA3": "fēng", + "6CA4": "ōu", + "6CA5": "lì", + "6CA6": "lún", + "6CA7": "cāng", + "6CA8": "fēng", + "6CA9": "wéi", + "6CAA": "hù", + "6CAB": "mò", + "6CAC": "mèi", + "6CAD": "shù", + "6CAE": "jǔ", + "6CAF": "zǎn", + "6CB0": "tuō", + "6CB1": "tuó", + "6CB2": "tuó", + "6CB3": "hé", + "6CB4": "lì", + "6CB5": "mǐ", + "6CB6": "yí", + "6CB7": "fā", + "6CB8": "fèi", + "6CB9": "yóu", + "6CBA": "tián", + "6CBB": "zhì", + "6CBC": "zhǎo", + "6CBD": "gū", + "6CBE": "zhān", + "6CBF": "yán", + "6CC0": "sī", + "6CC1": "kuàng", + "6CC2": "jiǒng", + "6CC3": "jū", + "6CC4": "xiè", + "6CC5": "qiú", + "6CC6": "yì", + "6CC7": "jiā", + "6CC8": "zhōng", + "6CC9": "quán", + "6CCA": "pō", + "6CCB": "huì", + "6CCC": "mì", + "6CCD": "bēn", + "6CCE": "zé", + "6CCF": "zhú", + "6CD0": "lè", + "6CD1": "yōu", + "6CD2": "gū", + "6CD3": "hóng", + "6CD4": "gān", + "6CD5": "fǎ", + "6CD6": "mǎo", + "6CD7": "sì", + "6CD8": "hū", + "6CD9": "píng", + "6CDA": "cǐ", + "6CDB": "fàn", + "6CDC": "zhī", + "6CDD": "sù", + "6CDE": "nìng", + "6CDF": "chēng", + "6CE0": "líng", + "6CE1": "pào", + "6CE2": "bō", + "6CE3": "qì", + "6CE4": "sì", + "6CE5": "ní", + "6CE6": "jú", + "6CE7": "sà", + "6CE8": "zhù", + "6CE9": "shēng", + "6CEA": "lèi", + "6CEB": "xuàn", + "6CEC": "jué", + "6CED": "fú", + "6CEE": "pàn", + "6CEF": "mǐn", + "6CF0": "tài", + "6CF1": "yāng", + "6CF2": "jǐ", + "6CF3": "yǒng", + "6CF4": "guàn", + "6CF5": "bèng", + "6CF6": "xué", + "6CF7": "lóng", + "6CF8": "lú", + "6CF9": "dan", + "6CFA": "luò", + "6CFB": "xiè", + "6CFC": "po", + "6CFD": "zé", + "6CFE": "jīng", + "6CFF": "yín", + "6D00": "pán", + "6D01": "jié", + "6D02": "yì", + "6D03": "huī", + "6D04": "huí", + "6D05": "zài", + "6D06": "chéng", + "6D07": "yīn", + "6D08": "wéi", + "6D09": "hòu", + "6D0A": "jiàn", + "6D0B": "yáng", + "6D0C": "liè", + "6D0D": "sì", + "6D0E": "jì", + "6D0F": "ér", + "6D10": "xíng", + "6D11": "fú", + "6D12": "sǎ", + "6D13": "sè", + "6D14": "zhǐ", + "6D15": "yìn", + "6D16": "wú", + "6D17": "xǐ", + "6D18": "kǎo", + "6D19": "zhū", + "6D1A": "jiàng", + "6D1B": "luò", + "6D1C": "luò", + "6D1D": "àn", + "6D1E": "dòng", + "6D1F": "tì", + "6D20": "móu", + "6D21": "lèi", + "6D22": "yī", + "6D23": "mǐ", + "6D24": "quán", + "6D25": "jīn", + "6D26": "pò", + "6D27": "wěi", + "6D28": "xiáo", + "6D29": "xiè", + "6D2A": "hóng", + "6D2B": "xù", + "6D2C": "sù", + "6D2D": "kuāng", + "6D2E": "táo", + "6D2F": "qiè", + "6D30": "jù", + "6D31": "ěr", + "6D32": "zhōu", + "6D33": "rù", + "6D34": "píng", + "6D35": "xún", + "6D36": "xiōng", + "6D37": "zhì", + "6D38": "guāng", + "6D39": "huán", + "6D3A": "míng", + "6D3B": "huó", + "6D3C": "wā", + "6D3D": "qià", + "6D3E": "pài", + "6D3F": "wū", + "6D40": "qū", + "6D41": "liú", + "6D42": "yì", + "6D43": "jiā", + "6D44": "jìng", + "6D45": "qiǎn", + "6D46": "jiāng", + "6D47": "jiāo", + "6D48": "zhēn", + "6D49": "shī", + "6D4A": "zhuó", + "6D4B": "cè", + "6D4C": "fá", + "6D4D": "huì", + "6D4E": "jì", + "6D4F": "liú", + "6D50": "chǎn", + "6D51": "hún", + "6D52": "hǔ", + "6D53": "nóng", + "6D54": "xún", + "6D55": "jìn", + "6D56": "liè", + "6D57": "qiú", + "6D58": "wěi", + "6D59": "zhè", + "6D5A": "jùn", + "6D5B": "hán", + "6D5C": "bāng", + "6D5D": "máng", + "6D5E": "zhuó", + "6D5F": "yóu", + "6D60": "xī", + "6D61": "bó", + "6D62": "dòu", + "6D63": "huàn", + "6D64": "hóng", + "6D65": "yì", + "6D66": "pǔ", + "6D67": "yǐng", + "6D68": "lǎn", + "6D69": "hào", + "6D6A": "làng", + "6D6B": "hǎn", + "6D6C": "lǐ", + "6D6D": "gēng", + "6D6E": "fú", + "6D6F": "wú", + "6D70": "liàn", + "6D71": "chún", + "6D72": "féng", + "6D73": "yì", + "6D74": "yù", + "6D75": "tóng", + "6D76": "láo", + "6D77": "hǎi", + "6D78": "jìn", + "6D79": "jiā", + "6D7A": "chōng", + "6D7B": "jiǒng", + "6D7C": "měi", + "6D7D": "suī", + "6D7E": "chēng", + "6D7F": "pèi", + "6D80": "xiàn", + "6D81": "shèn", + "6D82": "tu", + "6D83": "kùn", + "6D84": "pīng", + "6D85": "niè", + "6D86": "hàn", + "6D87": "jīng", + "6D88": "xiāo", + "6D89": "shè", + "6D8A": "niǎn", + "6D8B": "tū", + "6D8C": "yǒng", + "6D8D": "xiào", + "6D8E": "xián", + "6D8F": "tǐng", + "6D90": "é", + "6D91": "sù", + "6D92": "tūn", + "6D93": "juān", + "6D94": "cén", + "6D95": "tì", + "6D96": "lì", + "6D97": "shuì", + "6D98": "sì", + "6D99": "lèi", + "6D9A": "shuì", + "6D9B": "tāo", + "6D9C": "dú", + "6D9D": "lào", + "6D9E": "lái", + "6D9F": "lián", + "6DA0": "wéi", + "6DA1": "wō", + "6DA2": "yún", + "6DA3": "huàn", + "6DA4": "dí", + "6DA5": "heng", + "6DA6": "rùn", + "6DA7": "jiàn", + "6DA8": "zhǎng", + "6DA9": "sè", + "6DAA": "fú", + "6DAB": "guàn", + "6DAC": "xìng", + "6DAD": "shòu", + "6DAE": "shuàn", + "6DAF": "yá", + "6DB0": "chuò", + "6DB1": "zhàng", + "6DB2": "yè", + "6DB3": "kōng", + "6DB4": "wò", + "6DB5": "hán", + "6DB6": "tuō", + "6DB7": "dōng", + "6DB8": "hé", + "6DB9": "wō", + "6DBA": "jū", + "6DBB": "shè", + "6DBC": "liáng", + "6DBD": "hūn", + "6DBE": "tà", + "6DBF": "zhuō", + "6DC0": "diàn", + "6DC1": "qiè", + "6DC2": "dé", + "6DC3": "juàn", + "6DC4": "zī", + "6DC5": "xī", + "6DC6": "xiáo", + "6DC7": "qí", + "6DC8": "gǔ", + "6DC9": "guǒ", + "6DCA": "yān", + "6DCB": "lín", + "6DCC": "tǎng", + "6DCD": "zhōu", + "6DCE": "pěng", + "6DCF": "hào", + "6DD0": "chāng", + "6DD1": "shū", + "6DD2": "qī", + "6DD3": "fāng", + "6DD4": "zhí", + "6DD5": "lù", + "6DD6": "nào", + "6DD7": "jú", + "6DD8": "táo", + "6DD9": "cóng", + "6DDA": "lèi", + "6DDB": "zhè", + "6DDC": "píng", + "6DDD": "féi", + "6DDE": "sōng", + "6DDF": "tiǎn", + "6DE0": "pì", + "6DE1": "dàn", + "6DE2": "yù", + "6DE3": "ní", + "6DE4": "yū", + "6DE5": "lù", + "6DE6": "gàn", + "6DE7": "mì", + "6DE8": "jìng", + "6DE9": "líng", + "6DEA": "lún", + "6DEB": "yín", + "6DEC": "cuì", + "6DED": "qú", + "6DEE": "huái", + "6DEF": "yù", + "6DF0": "niǎn", + "6DF1": "shēn", + "6DF2": "biāo", + "6DF3": "chún", + "6DF4": "hū", + "6DF5": "yuān", + "6DF6": "lái", + "6DF7": "hùn", + "6DF8": "qīng", + "6DF9": "yān", + "6DFA": "qiǎn", + "6DFB": "tiān", + "6DFC": "miǎo", + "6DFD": "zhǐ", + "6DFE": "yǐn", + "6DFF": "mì", + "6E00": "bèn", + "6E01": "yuān", + "6E02": "wèn", + "6E03": "ruò", + "6E04": "fēi", + "6E05": "qīng", + "6E06": "yuān", + "6E07": "kě", + "6E08": "jì", + "6E09": "shè", + "6E0A": "yuān", + "6E0B": "se", + "6E0C": "lù", + "6E0D": "zì", + "6E0E": "dú", + "6E0F": "qi", + "6E10": "jiàn", + "6E11": "miǎn", + "6E12": "pì", + "6E13": "xi", + "6E14": "yú", + "6E15": "yuān", + "6E16": "shěn", + "6E17": "shèn", + "6E18": "róu", + "6E19": "huàn", + "6E1A": "zhǔ", + "6E1B": "jiǎn", + "6E1C": "nuǎn", + "6E1D": "yú", + "6E1E": "qiú", + "6E1F": "tíng", + "6E20": "qú", + "6E21": "dù", + "6E22": "fán", + "6E23": "zhā", + "6E24": "bó", + "6E25": "wò", + "6E26": "wō", + "6E27": "dì", + "6E28": "wēi", + "6E29": "wēn", + "6E2A": "rú", + "6E2B": "xiè", + "6E2C": "cè", + "6E2D": "wèi", + "6E2E": "hé", + "6E2F": "gǎng", + "6E30": "yǎn", + "6E31": "hóng", + "6E32": "xuàn", + "6E33": "mǐ", + "6E34": "kě", + "6E35": "máo", + "6E36": "yīng", + "6E37": "yǎn", + "6E38": "yóu", + "6E39": "hōng", + "6E3A": "miǎo", + "6E3B": "shěng", + "6E3C": "měi", + "6E3D": "zāi", + "6E3E": "hún", + "6E3F": "nài", + "6E40": "guǐ", + "6E41": "chì", + "6E42": "è", + "6E43": "pài", + "6E44": "méi", + "6E45": "liàn", + "6E46": "qì", + "6E47": "qì", + "6E48": "méi", + "6E49": "tián", + "6E4A": "còu", + "6E4B": "wéi", + "6E4C": "cān", + "6E4D": "tuān", + "6E4E": "miǎn", + "6E4F": "huì", + "6E50": "mò", + "6E51": "xū", + "6E52": "jí", + "6E53": "pén", + "6E54": "jiān", + "6E55": "jiǎn", + "6E56": "hú", + "6E57": "fèng", + "6E58": "xiāng", + "6E59": "yì", + "6E5A": "yìn", + "6E5B": "zhàn", + "6E5C": "shí", + "6E5D": "jiē", + "6E5E": "chēng", + "6E5F": "huáng", + "6E60": "tàn", + "6E61": "yú", + "6E62": "bì", + "6E63": "mǐn", + "6E64": "shī", + "6E65": "tū", + "6E66": "shēng", + "6E67": "yǒng", + "6E68": "jú", + "6E69": "dòng", + "6E6A": "tuàn", + "6E6B": "jiǎo", + "6E6C": "jiǎo", + "6E6D": "qiú", + "6E6E": "yān", + "6E6F": "tāng", + "6E70": "lóng", + "6E71": "huò", + "6E72": "yuán", + "6E73": "nǎn", + "6E74": "bàn", + "6E75": "yǒu", + "6E76": "quán", + "6E77": "zhuāng", + "6E78": "liàng", + "6E79": "chán", + "6E7A": "yán", + "6E7B": "chún", + "6E7C": "niè", + "6E7D": "zī", + "6E7E": "wān", + "6E7F": "shī", + "6E80": "mǎn", + "6E81": "yíng", + "6E82": "la", + "6E83": "kuì", + "6E84": "feng", + "6E85": "jiàn", + "6E86": "xù", + "6E87": "lóu", + "6E88": "wéi", + "6E89": "gài", + "6E8A": "xia", + "6E8B": "yíng", + "6E8C": "pō", + "6E8D": "jìn", + "6E8E": "yàn", + "6E8F": "táng", + "6E90": "yuán", + "6E91": "suǒ", + "6E92": "yuán", + "6E93": "lián", + "6E94": "yǎo", + "6E95": "mèng", + "6E96": "zhǔn", + "6E97": "chéng", + "6E98": "kè", + "6E99": "tài", + "6E9A": "tǎ", + "6E9B": "wā", + "6E9C": "liū", + "6E9D": "gōu", + "6E9E": "sāo", + "6E9F": "míng", + "6EA0": "zhà", + "6EA1": "shí", + "6EA2": "yì", + "6EA3": "lùn", + "6EA4": "mǎ", + "6EA5": "pǔ", + "6EA6": "wēi", + "6EA7": "lì", + "6EA8": "cái", + "6EA9": "wù", + "6EAA": "xī", + "6EAB": "wēn", + "6EAC": "qiāng", + "6EAD": "zé", + "6EAE": "shī", + "6EAF": "sù", + "6EB0": "ái", + "6EB1": "qín", + "6EB2": "sōu", + "6EB3": "yún", + "6EB4": "xiù", + "6EB5": "yīn", + "6EB6": "róng", + "6EB7": "hùn", + "6EB8": "sù", + "6EB9": "suò", + "6EBA": "nì", + "6EBB": "tā", + "6EBC": "shī", + "6EBD": "rù", + "6EBE": "āi", + "6EBF": "pàn", + "6EC0": "chù", + "6EC1": "chú", + "6EC2": "pāng", + "6EC3": "wēng", + "6EC4": "cāng", + "6EC5": "miè", + "6EC6": "gé", + "6EC7": "diān", + "6EC8": "hào", + "6EC9": "huàng", + "6ECA": "xì", + "6ECB": "zī", + "6ECC": "dí", + "6ECD": "zhì", + "6ECE": "xíng", + "6ECF": "fǔ", + "6ED0": "jié", + "6ED1": "huá", + "6ED2": "gē", + "6ED3": "zǐ", + "6ED4": "tāo", + "6ED5": "téng", + "6ED6": "suī", + "6ED7": "bì", + "6ED8": "jiào", + "6ED9": "huì", + "6EDA": "gǔn", + "6EDB": "yín", + "6EDC": "gāo", + "6EDD": "lóng", + "6EDE": "zhì", + "6EDF": "yàn", + "6EE0": "shè", + "6EE1": "mǎn", + "6EE2": "yíng", + "6EE3": "chún", + "6EE4": "lǜ", + "6EE5": "làn", + "6EE6": "luán", + "6EE7": "xiao", + "6EE8": "bīn", + "6EE9": "tān", + "6EEA": "yù", + "6EEB": "xiǔ", + "6EEC": "hù", + "6EED": "bì", + "6EEE": "biāo", + "6EEF": "zhì", + "6EF0": "jiàng", + "6EF1": "kòu", + "6EF2": "shèn", + "6EF3": "shāng", + "6EF4": "dī", + "6EF5": "mì", + "6EF6": "áo", + "6EF7": "lǔ", + "6EF8": "hǔ", + "6EF9": "hū", + "6EFA": "yōu", + "6EFB": "chǎn", + "6EFC": "fàn", + "6EFD": "yōng", + "6EFE": "gǔn", + "6EFF": "mǎn", + "6F00": "qǐng", + "6F01": "yú", + "6F02": "piào", + "6F03": "jì", + "6F04": "yá", + "6F05": "cháo", + "6F06": "qī", + "6F07": "xǐ", + "6F08": "jì", + "6F09": "lù", + "6F0A": "lóu", + "6F0B": "lóng", + "6F0C": "jǐn", + "6F0D": "guó", + "6F0E": "cóng", + "6F0F": "lòu", + "6F10": "zhí", + "6F11": "gài", + "6F12": "qiáng", + "6F13": "lí", + "6F14": "yǎn", + "6F15": "cáo", + "6F16": "jiào", + "6F17": "cōng", + "6F18": "chún", + "6F19": "tuán", + "6F1A": "ōu", + "6F1B": "téng", + "6F1C": "yě", + "6F1D": "xí", + "6F1E": "mì", + "6F1F": "táng", + "6F20": "mò", + "6F21": "shāng", + "6F22": "hàn", + "6F23": "lián", + "6F24": "lǎn", + "6F25": "wā", + "6F26": "chí", + "6F27": "gān", + "6F28": "féng", + "6F29": "xuán", + "6F2A": "yī", + "6F2B": "màn", + "6F2C": "zì", + "6F2D": "mǎng", + "6F2E": "kāng", + "6F2F": "luò", + "6F30": "pēng", + "6F31": "shù", + "6F32": "zhǎng", + "6F33": "zhāng", + "6F34": "zhuàng", + "6F35": "xù", + "6F36": "huàn", + "6F37": "huǒ", + "6F38": "jiàn", + "6F39": "yān", + "6F3A": "shuǎng", + "6F3B": "liáo", + "6F3C": "cuǐ", + "6F3D": "tí", + "6F3E": "yàng", + "6F3F": "jiāng", + "6F40": "cóng", + "6F41": "yǐng", + "6F42": "hóng", + "6F43": "xún", + "6F44": "shù", + "6F45": "guàn", + "6F46": "yíng", + "6F47": "xiāo", + "6F48": "zong", + "6F49": "kun", + "6F4A": "xù", + "6F4B": "liàn", + "6F4C": "zhì", + "6F4D": "wéi", + "6F4E": "pì", + "6F4F": "yù", + "6F50": "jiào", + "6F51": "po", + "6F52": "dàng", + "6F53": "huì", + "6F54": "jié", + "6F55": "wǔ", + "6F56": "pá", + "6F57": "jí", + "6F58": "pān", + "6F59": "wéi", + "6F5A": "sù", + "6F5B": "qián", + "6F5C": "qián", + "6F5D": "xī", + "6F5E": "lù", + "6F5F": "xì", + "6F60": "xùn", + "6F61": "dùn", + "6F62": "huáng", + "6F63": "mǐn", + "6F64": "rùn", + "6F65": "sù", + "6F66": "lǎo", + "6F67": "zhēn", + "6F68": "cóng", + "6F69": "yì", + "6F6A": "zhè", + "6F6B": "wān", + "6F6C": "shàn", + "6F6D": "tán", + "6F6E": "cháo", + "6F6F": "xún", + "6F70": "kuì", + "6F71": "yē", + "6F72": "shào", + "6F73": "tú", + "6F74": "zhū", + "6F75": "sǎ", + "6F76": "hēi", + "6F77": "bì", + "6F78": "shān", + "6F79": "chán", + "6F7A": "chán", + "6F7B": "shǔ", + "6F7C": "tóng", + "6F7D": "pū", + "6F7E": "lín", + "6F7F": "wéi", + "6F80": "sè", + "6F81": "sè", + "6F82": "chéng", + "6F83": "jiǒng", + "6F84": "chéng", + "6F85": "huà", + "6F86": "jiāo", + "6F87": "lào", + "6F88": "chè", + "6F89": "gǎn", + "6F8A": "cūn", + "6F8B": "hòng", + "6F8C": "sī", + "6F8D": "shù", + "6F8E": "pēng", + "6F8F": "hán", + "6F90": "yún", + "6F91": "liù", + "6F92": "hòng", + "6F93": "fú", + "6F94": "hào", + "6F95": "hé", + "6F96": "xián", + "6F97": "jiàn", + "6F98": "shān", + "6F99": "xì", + "6F9A": "yu", + "6F9B": "lu", + "6F9C": "lán", + "6F9D": "ning", + "6F9E": "yú", + "6F9F": "lǐn", + "6FA0": "miǎn", + "6FA1": "zǎo", + "6FA2": "dāng", + "6FA3": "huàn", + "6FA4": "zé", + "6FA5": "xiè", + "6FA6": "yù", + "6FA7": "lǐ", + "6FA8": "shì", + "6FA9": "xué", + "6FAA": "líng", + "6FAB": "wàn", + "6FAC": "zī", + "6FAD": "yōng", + "6FAE": "huì", + "6FAF": "càn", + "6FB0": "liàn", + "6FB1": "diàn", + "6FB2": "yè", + "6FB3": "ào", + "6FB4": "huán", + "6FB5": "zhēn", + "6FB6": "chán", + "6FB7": "màn", + "6FB8": "dǎn", + "6FB9": "dàn", + "6FBA": "yì", + "6FBB": "suì", + "6FBC": "pì", + "6FBD": "jù", + "6FBE": "tà", + "6FBF": "qín", + "6FC0": "jī", + "6FC1": "zhuó", + "6FC2": "lián", + "6FC3": "nóng", + "6FC4": "guō", + "6FC5": "jìn", + "6FC6": "fén", + "6FC7": "sè", + "6FC8": "jí", + "6FC9": "suī", + "6FCA": "huì", + "6FCB": "chǔ", + "6FCC": "tà", + "6FCD": "sōng", + "6FCE": "dǐng", + "6FCF": "se", + "6FD0": "zhǔ", + "6FD1": "lài", + "6FD2": "bīn", + "6FD3": "lián", + "6FD4": "mǐ", + "6FD5": "shī", + "6FD6": "shù", + "6FD7": "mì", + "6FD8": "nìng", + "6FD9": "yíng", + "6FDA": "yíng", + "6FDB": "méng", + "6FDC": "jìn", + "6FDD": "qí", + "6FDE": "bì", + "6FDF": "jì", + "6FE0": "háo", + "6FE1": "rú", + "6FE2": "cuì", + "6FE3": "wò", + "6FE4": "tāo", + "6FE5": "yǐn", + "6FE6": "yǐn", + "6FE7": "duì", + "6FE8": "cí", + "6FE9": "huò", + "6FEA": "jìng", + "6FEB": "làn", + "6FEC": "jùn", + "6FED": "ǎi", + "6FEE": "pú", + "6FEF": "zhuó", + "6FF0": "wéi", + "6FF1": "bīn", + "6FF2": "gǔ", + "6FF3": "qián", + "6FF4": "yíng", + "6FF5": "bin", + "6FF6": "kuò", + "6FF7": "fèi", + "6FF8": "cang", + "6FF9": "me", + "6FFA": "jiàn", + "6FFB": "wěi", + "6FFC": "luò", + "6FFD": "zàn", + "6FFE": "lǜ", + "6FFF": "lì", + "7000": "yōu", + "7001": "yàng", + "7002": "lǔ", + "7003": "sì", + "7004": "zhì", + "7005": "yíng", + "7006": "dú", + "7007": "wǎng", + "7008": "huī", + "7009": "xiè", + "700A": "pán", + "700B": "shěn", + "700C": "biāo", + "700D": "chán", + "700E": "mò", + "700F": "liú", + "7010": "jiān", + "7011": "pù", + "7012": "sè", + "7013": "chéng", + "7014": "gǔ", + "7015": "bīn", + "7016": "huò", + "7017": "xiàn", + "7018": "lú", + "7019": "qìn", + "701A": "hàn", + "701B": "yíng", + "701C": "róng", + "701D": "lì", + "701E": "jìng", + "701F": "xiāo", + "7020": "yíng", + "7021": "suǐ", + "7022": "wěi", + "7023": "xiè", + "7024": "huái", + "7025": "xuè", + "7026": "zhū", + "7027": "lóng", + "7028": "lài", + "7029": "duì", + "702A": "fán", + "702B": "hú", + "702C": "lài", + "702D": "shu", + "702E": "ling", + "702F": "yíng", + "7030": "mí", + "7031": "jì", + "7032": "liàn", + "7033": "jiàn", + "7034": "yíng", + "7035": "fèn", + "7036": "lín", + "7037": "yì", + "7038": "jiān", + "7039": "yuè", + "703A": "chán", + "703B": "dài", + "703C": "ráng", + "703D": "jiǎn", + "703E": "lán", + "703F": "fán", + "7040": "shuàng", + "7041": "yuān", + "7042": "zhuó", + "7043": "fēng", + "7044": "shè", + "7045": "lěi", + "7046": "lán", + "7047": "cóng", + "7048": "qú", + "7049": "yōng", + "704A": "qián", + "704B": "fǎ", + "704C": "guàn", + "704D": "què", + "704E": "yàn", + "704F": "hào", + "7050": "ying", + "7051": "sǎ", + "7052": "zàn", + "7053": "luán", + "7054": "yàn", + "7055": "lí", + "7056": "mǐ", + "7057": "shàn", + "7058": "tān", + "7059": "dǎng", + "705A": "jiǎo", + "705B": "chǎn", + "705C": "ying", + "705D": "hào", + "705E": "bà", + "705F": "zhú", + "7060": "lǎn", + "7061": "lán", + "7062": "nǎng", + "7063": "wān", + "7064": "luán", + "7065": "xún", + "7066": "xiǎn", + "7067": "yàn", + "7068": "gàn", + "7069": "yàn", + "706A": "yù", + "706B": "huǒ", + "706C": "biāo", + "706D": "miè", + "706E": "guāng", + "706F": "dēng", + "7070": "huī", + "7071": "xiāo", + "7072": "xiāo", + "7073": "hui", + "7074": "hōng", + "7075": "líng", + "7076": "zào", + "7077": "zhuàn", + "7078": "jiǔ", + "7079": "zhà", + "707A": "xiè", + "707B": "chì", + "707C": "zhuó", + "707D": "zāi", + "707E": "zāi", + "707F": "càn", + "7080": "yáng", + "7081": "qì", + "7082": "zhōng", + "7083": "fén", + "7084": "niǔ", + "7085": "jiǒng", + "7086": "wén", + "7087": "pò", + "7088": "yì", + "7089": "lú", + "708A": "chuī", + "708B": "pī", + "708C": "kài", + "708D": "pàn", + "708E": "yán", + "708F": "kài", + "7090": "pàng", + "7091": "mù", + "7092": "chǎo", + "7093": "liào", + "7094": "guì", + "7095": "kàng", + "7096": "dùn", + "7097": "guāng", + "7098": "xīn", + "7099": "zhì", + "709A": "guang", + "709B": "guāng", + "709C": "wěi", + "709D": "qiàng", + "709E": "bian", + "709F": "dá", + "70A0": "xiá", + "70A1": "zhēng", + "70A2": "zhú", + "70A3": "kě", + "70A4": "zhào", + "70A5": "fú", + "70A6": "bá", + "70A7": "xiè", + "70A8": "duò", + "70A9": "lìng", + "70AA": "zhuō", + "70AB": "xuàn", + "70AC": "jù", + "70AD": "tàn", + "70AE": "pào", + "70AF": "jiǒng", + "70B0": "páo", + "70B1": "tái", + "70B2": "tái", + "70B3": "bǐng", + "70B4": "yǎng", + "70B5": "tōng", + "70B6": "hān", + "70B7": "zhù", + "70B8": "zhà", + "70B9": "diǎn", + "70BA": "wèi", + "70BB": "shí", + "70BC": "liàn", + "70BD": "chì", + "70BE": "huǎng", + "70BF": "zhou", + "70C0": "hū", + "70C1": "shuò", + "70C2": "làn", + "70C3": "tīng", + "70C4": "jiǎo", + "70C5": "xù", + "70C6": "héng", + "70C7": "quǎn", + "70C8": "liè", + "70C9": "huàn", + "70CA": "yáng", + "70CB": "xiū", + "70CC": "xiū", + "70CD": "xiǎn", + "70CE": "yín", + "70CF": "wū", + "70D0": "zhōu", + "70D1": "yáo", + "70D2": "shì", + "70D3": "wēi", + "70D4": "tóng", + "70D5": "miè", + "70D6": "zāi", + "70D7": "kài", + "70D8": "hōng", + "70D9": "lào", + "70DA": "xiá", + "70DB": "zhú", + "70DC": "xuǎn", + "70DD": "zhēng", + "70DE": "pò", + "70DF": "yān", + "70E0": "huí", + "70E1": "guāng", + "70E2": "chè", + "70E3": "huī", + "70E4": "kǎo", + "70E5": "chen", + "70E6": "fán", + "70E7": "shāo", + "70E8": "yè", + "70E9": "huì", + "70EB": "tàng", + "70EC": "jìn", + "70ED": "rè", + "70EE": "lie", + "70EF": "xī", + "70F0": "fú", + "70F1": "jiǒng", + "70F2": "xiè", + "70F3": "pǔ", + "70F4": "tīng", + "70F5": "zhuó", + "70F6": "tǐng", + "70F7": "wán", + "70F8": "hǎi", + "70F9": "pēng", + "70FA": "lǎng", + "70FB": "yàn", + "70FC": "xù", + "70FD": "fēng", + "70FE": "chì", + "70FF": "róng", + "7100": "hú", + "7101": "xī", + "7102": "shū", + "7103": "hè", + "7104": "xūn", + "7105": "kù", + "7106": "juān", + "7107": "xiāo", + "7108": "xī", + "7109": "yān", + "710A": "hàn", + "710B": "zhuàng", + "710C": "jùn", + "710D": "dì", + "710E": "xiè", + "710F": "jí", + "7110": "wù", + "7111": "yān", + "7112": "lü", + "7113": "hán", + "7114": "yàn", + "7115": "huàn", + "7116": "mèn", + "7117": "jú", + "7118": "dào", + "7119": "bèi", + "711A": "fén", + "711B": "lìn", + "711C": "kūn", + "711D": "hùn", + "711E": "tūn", + "711F": "xī", + "7120": "cuì", + "7121": "wú", + "7122": "hōng", + "7123": "chǎo", + "7124": "fǔ", + "7125": "wò", + "7126": "jiāo", + "7127": "cōng", + "7128": "fèng", + "7129": "píng", + "712A": "qióng", + "712B": "ruò", + "712C": "xī", + "712D": "qióng", + "712E": "xìn", + "712F": "chāo", + "7130": "yàn", + "7131": "yàn", + "7132": "yì", + "7133": "jué", + "7134": "yù", + "7135": "gàng", + "7136": "rán", + "7137": "pí", + "7138": "xiòng", + "7139": "wang", + "713A": "shēng", + "713B": "chàng", + "713C": "shāo", + "713D": "xiǒng", + "713E": "niǎn", + "713F": "gēng", + "7140": "wei", + "7141": "chén", + "7142": "hè", + "7143": "kuǐ", + "7144": "zhǒng", + "7145": "duàn", + "7146": "xiā", + "7147": "huī", + "7148": "fèng", + "7149": "liàn", + "714A": "xuān", + "714B": "xīng", + "714C": "huáng", + "714D": "jiǎo", + "714E": "jiān", + "714F": "bì", + "7150": "yīng", + "7151": "zhǔ", + "7152": "wěi", + "7153": "tuān", + "7154": "shǎn", + "7155": "xī", + "7156": "nuǎn", + "7157": "nuǎn", + "7158": "chán", + "7159": "yān", + "715A": "jiǒng", + "715B": "jiǒng", + "715C": "yù", + "715D": "mèi", + "715E": "shā", + "715F": "wèi", + "7160": "zhá", + "7161": "xìn", + "7162": "qióng", + "7163": "róu", + "7164": "méi", + "7165": "huàn", + "7166": "xù", + "7167": "zhào", + "7168": "wēi", + "7169": "fán", + "716A": "qiú", + "716B": "suì", + "716C": "yáng", + "716D": "liè", + "716E": "zhǔ", + "716F": "jiē", + "7170": "gào", + "7171": "guā", + "7172": "bāo", + "7173": "hú", + "7174": "yūn", + "7175": "xiā", + "7176": "shi", + "7177": "liang", + "7178": "biān", + "7179": "gòu", + "717A": "tuì", + "717B": "táng", + "717C": "chǎo", + "717D": "shān", + "717E": "ēn", + "717F": "bó", + "7180": "huǎng", + "7181": "xié", + "7182": "xì", + "7183": "wù", + "7184": "xī", + "7185": "yùn", + "7186": "hé", + "7187": "hè", + "7188": "xī", + "7189": "yún", + "718A": "xióng", + "718B": "nái", + "718C": "shǎn", + "718D": "qiong", + "718E": "yào", + "718F": "xūn", + "7190": "mì", + "7191": "lián", + "7192": "yíng", + "7193": "wǔ", + "7194": "róng", + "7195": "gōng", + "7196": "yan", + "7197": "qiàng", + "7198": "liū", + "7199": "xī", + "719A": "bì", + "719B": "biāo", + "719C": "cōng", + "719D": "lù", + "719E": "jiān", + "719F": "shú", + "71A0": "yì", + "71A1": "lóu", + "71A2": "péng", + "71A3": "suī", + "71A4": "yì", + "71A5": "tēng", + "71A6": "jué", + "71A7": "zōng", + "71A8": "yùn", + "71A9": "hù", + "71AA": "yí", + "71AB": "zhì", + "71AC": "áo", + "71AD": "wèi", + "71AE": "liǔ", + "71AF": "hàn", + "71B0": "ōu", + "71B1": "rè", + "71B2": "jiǒng", + "71B3": "màn", + "71B4": "kun", + "71B5": "shāng", + "71B6": "cuàn", + "71B7": "zēng", + "71B8": "jiān", + "71B9": "xī", + "71BA": "xī", + "71BB": "xī", + "71BC": "yì", + "71BD": "xiào", + "71BE": "chì", + "71BF": "huáng", + "71C0": "chǎn", + "71C1": "yè", + "71C2": "tán", + "71C3": "rán", + "71C4": "yàn", + "71C5": "xián", + "71C6": "qiāo", + "71C7": "jùn", + "71C8": "dēng", + "71C9": "dùn", + "71CA": "shēn", + "71CB": "jiāo", + "71CC": "fén", + "71CD": "sī", + "71CE": "liáo", + "71CF": "yù", + "71D0": "lín", + "71D1": "tóng", + "71D2": "shāo", + "71D3": "fēn", + "71D4": "fán", + "71D5": "yàn", + "71D6": "xún", + "71D7": "làn", + "71D8": "měi", + "71D9": "tàng", + "71DA": "yì", + "71DB": "jǐng", + "71DC": "mèn", + "71DD": "jing", + "71DE": "jiǎo", + "71DF": "yíng", + "71E0": "yù", + "71E1": "yì", + "71E2": "xué", + "71E3": "lán", + "71E4": "tài", + "71E5": "zào", + "71E6": "càn", + "71E7": "suì", + "71E8": "xī", + "71E9": "què", + "71EA": "cōng", + "71EB": "lián", + "71EC": "huǐ", + "71ED": "zhú", + "71EE": "xiè", + "71EF": "líng", + "71F0": "wēi", + "71F1": "yì", + "71F2": "xié", + "71F3": "zhào", + "71F4": "huì", + "71F5": "da", + "71F6": "nóng", + "71F7": "lán", + "71F8": "rú", + "71F9": "xiǎn", + "71FA": "kǎo", + "71FB": "xūn", + "71FC": "jìn", + "71FD": "chóu", + "71FE": "dào", + "71FF": "yào", + "7200": "hè", + "7201": "làn", + "7202": "biāo", + "7203": "róng", + "7204": "lì", + "7205": "mò", + "7206": "bào", + "7207": "ruò", + "7208": "lǜ", + "7209": "là", + "720A": "āo", + "720B": "xùn", + "720C": "kuàng", + "720D": "shuò", + "720E": "liao", + "720F": "lì", + "7210": "lú", + "7211": "jué", + "7212": "liǎo", + "7213": "yàn", + "7214": "xī", + "7215": "xiè", + "7216": "lóng", + "7217": "yè", + "7218": "can", + "7219": "rǎng", + "721A": "yuè", + "721B": "làn", + "721C": "cóng", + "721D": "jué", + "721E": "chóng", + "721F": "guàn", + "7220": "ju", + "7221": "chè", + "7222": "mí", + "7223": "tǎng", + "7224": "làn", + "7225": "zhú", + "7226": "lan", + "7227": "líng", + "7228": "cuàn", + "7229": "yù", + "722A": "zhǎo", + "722B": "zhao", + "722C": "pá", + "722D": "zhēng", + "722E": "páo", + "722F": "chēng", + "7230": "yuán", + "7231": "ài", + "7232": "wèi", + "7233": "han", + "7234": "jué", + "7235": "jué", + "7236": "fù", + "7237": "ye", + "7238": "bà", + "7239": "diē", + "723A": "ye", + "723B": "yáo", + "723C": "zǔ", + "723D": "shuǎng", + "723E": "ěr", + "723F": "pán", + "7240": "chuáng", + "7241": "kē", + "7242": "zāng", + "7243": "dié", + "7244": "qiāng", + "7245": "yóng", + "7246": "qiáng", + "7247": "piàn", + "7248": "bǎn", + "7249": "pàn", + "724A": "cháo", + "724B": "jiān", + "724C": "pái", + "724D": "dú", + "724E": "chuāng", + "724F": "yú", + "7250": "zhá", + "7251": "biān", + "7252": "dié", + "7253": "bǎng", + "7254": "bó", + "7255": "chuāng", + "7256": "yǒu", + "7257": "you", + "7258": "dú", + "7259": "yá", + "725A": "chēng", + "725B": "niú", + "725C": "niu", + "725D": "pìn", + "725E": "jiū", + "725F": "móu", + "7260": "tā", + "7261": "mǔ", + "7262": "láo", + "7263": "rèn", + "7264": "māng", + "7265": "fāng", + "7266": "máo", + "7267": "mù", + "7268": "gāng", + "7269": "wù", + "726A": "yàn", + "726B": "gē", + "726C": "bèi", + "726D": "sì", + "726E": "jiàn", + "726F": "gǔ", + "7270": "yòu", + "7271": "gē", + "7272": "shēng", + "7273": "mǔ", + "7274": "dǐ", + "7275": "qiān", + "7276": "quàn", + "7277": "quán", + "7278": "zì", + "7279": "tè", + "727A": "xī", + "727B": "máng", + "727C": "kēng", + "727D": "qiān", + "727E": "wǔ", + "727F": "gù", + "7280": "xī", + "7281": "lí", + "7282": "lí", + "7283": "pǒu", + "7284": "jī", + "7285": "gāng", + "7286": "zhí", + "7287": "bēn", + "7288": "quán", + "7289": "chún", + "728A": "dú", + "728B": "jù", + "728C": "jiā", + "728D": "jiān", + "728E": "fēng", + "728F": "piān", + "7290": "kē", + "7291": "jú", + "7292": "kào", + "7293": "chú", + "7294": "xì", + "7295": "bèi", + "7296": "luò", + "7297": "jiè", + "7298": "má", + "7299": "sān", + "729A": "wèi", + "729B": "máo", + "729C": "dūn", + "729D": "tóng", + "729E": "qiao", + "729F": "jiàng", + "72A0": "xi", + "72A1": "lì", + "72A2": "dú", + "72A3": "liè", + "72A4": "pái", + "72A5": "piāo", + "72A6": "bó", + "72A7": "xī", + "72A8": "chōu", + "72A9": "wéi", + "72AA": "kuí", + "72AB": "chōu", + "72AC": "quǎn", + "72AD": "quan", + "72AE": "bá", + "72AF": "fàn", + "72B0": "qiú", + "72B1": "jǐ", + "72B2": "cái", + "72B3": "zhuó", + "72B4": "àn", + "72B5": "gē", + "72B6": "zhuàng", + "72B7": "guǎng", + "72B8": "mà", + "72B9": "yóu", + "72BA": "kàng", + "72BB": "bó", + "72BC": "hǒu", + "72BD": "yà", + "72BE": "yín", + "72BF": "huān", + "72C0": "zhuàng", + "72C1": "yǔn", + "72C2": "kuáng", + "72C3": "niǔ", + "72C4": "dí", + "72C5": "qīng", + "72C6": "zhòng", + "72C7": "mù", + "72C8": "bèi", + "72C9": "pī", + "72CA": "jú", + "72CB": "yí", + "72CC": "shēng", + "72CD": "páo", + "72CE": "xiá", + "72CF": "tuó", + "72D0": "hú", + "72D1": "líng", + "72D2": "fèi", + "72D3": "pí", + "72D4": "nǐ", + "72D5": "yǎo", + "72D6": "yòu", + "72D7": "gǒu", + "72D8": "xuè", + "72D9": "jū", + "72DA": "dàn", + "72DB": "bó", + "72DC": "kǔ", + "72DD": "xiǎn", + "72DE": "níng", + "72DF": "huán", + "72E0": "hěn", + "72E1": "jiǎo", + "72E2": "hé", + "72E3": "zhào", + "72E4": "jí", + "72E5": "xùn", + "72E6": "shān", + "72E7": "tà", + "72E8": "róng", + "72E9": "shòu", + "72EA": "tóng", + "72EB": "lǎo", + "72EC": "dú", + "72ED": "xiá", + "72EE": "shī", + "72EF": "kuài", + "72F0": "zhēng", + "72F1": "yù", + "72F2": "sūn", + "72F3": "yú", + "72F4": "bì", + "72F5": "máng", + "72F6": "xī", + "72F7": "juàn", + "72F8": "li", + "72F9": "xiá", + "72FA": "yín", + "72FB": "suān", + "72FC": "láng", + "72FD": "bèi", + "72FE": "zhì", + "72FF": "yán", + "7300": "shā", + "7301": "lì", + "7302": "hàn", + "7303": "xiǎn", + "7304": "jīng", + "7305": "pái", + "7306": "fēi", + "7307": "xiāo", + "7308": "bài", + "7309": "qí", + "730A": "ní", + "730B": "biāo", + "730C": "yìn", + "730D": "lái", + "730E": "liè", + "730F": "jiān", + "7310": "qiāng", + "7311": "kūn", + "7312": "yān", + "7313": "guǒ", + "7314": "zòng", + "7315": "mí", + "7316": "chāng", + "7317": "yī", + "7318": "zhì", + "7319": "zhēng", + "731A": "yá", + "731B": "měng", + "731C": "cāi", + "731D": "cù", + "731E": "shē", + "731F": "lie", + "7320": "diǎn", + "7321": "luó", + "7322": "hú", + "7323": "zōng", + "7324": "guì", + "7325": "wěi", + "7326": "fēng", + "7327": "wō", + "7328": "yuán", + "7329": "xīng", + "732A": "zhū", + "732B": "māo", + "732C": "wei", + "732D": "chuān", + "732E": "xiàn", + "732F": "tuān", + "7330": "yà", + "7331": "náo", + "7332": "xiē", + "7333": "jiā", + "7334": "hóu", + "7335": "biān", + "7336": "yóu", + "7337": "yóu", + "7338": "méi", + "7339": "chá", + "733A": "yáo", + "733B": "sūn", + "733C": "bó", + "733D": "míng", + "733E": "huá", + "733F": "yuán", + "7340": "sōu", + "7341": "mà", + "7342": "yuán", + "7343": "dāi", + "7344": "yù", + "7345": "shī", + "7346": "háo", + "7347": "qiang", + "7348": "yì", + "7349": "zhēn", + "734A": "cāng", + "734B": "háo", + "734C": "màn", + "734D": "jìng", + "734E": "jiǎng", + "734F": "mò", + "7350": "zhāng", + "7351": "chán", + "7352": "áo", + "7353": "áo", + "7354": "háo", + "7355": "cuī", + "7356": "bèn", + "7357": "jué", + "7358": "bì", + "7359": "bì", + "735A": "huáng", + "735B": "pú", + "735C": "lín", + "735D": "xù", + "735E": "tóng", + "735F": "yào", + "7360": "liáo", + "7361": "shuò", + "7362": "xiāo", + "7363": "shou", + "7364": "dūn", + "7365": "jiào", + "7366": "gé", + "7367": "juàn", + "7368": "dú", + "7369": "huì", + "736A": "kuài", + "736B": "xiǎn", + "736C": "xiè", + "736D": "tǎ", + "736E": "xiǎn", + "736F": "xūn", + "7370": "níng", + "7371": "pín", + "7372": "huò", + "7373": "nòu", + "7374": "měng", + "7375": "liè", + "7376": "nǎo", + "7377": "guǎng", + "7378": "shòu", + "7379": "lú", + "737A": "tǎ", + "737B": "xiàn", + "737C": "mí", + "737D": "ráng", + "737E": "huān", + "737F": "nǎo", + "7380": "luó", + "7381": "xiǎn", + "7382": "qí", + "7383": "jué", + "7384": "xuán", + "7385": "miào", + "7386": "zī", + "7387": "lǜ", + "7388": "lú", + "7389": "yù", + "738A": "sù", + "738B": "wáng", + "738C": "qiú", + "738D": "gǎ", + "738E": "dīng", + "738F": "lè", + "7390": "bā", + "7391": "jī", + "7392": "hóng", + "7393": "dì", + "7394": "chuàn", + "7395": "gān", + "7396": "jiǔ", + "7397": "yú", + "7398": "qǐ", + "7399": "yú", + "739A": "chàng", + "739B": "mǎ", + "739C": "gōng", + "739D": "wǔ", + "739E": "fū", + "739F": "wén", + "73A0": "jiè", + "73A1": "yá", + "73A2": "bīn", + "73A3": "biàn", + "73A4": "bàng", + "73A5": "yuè", + "73A6": "jué", + "73A7": "mén", + "73A8": "jué", + "73A9": "wán", + "73AA": "jiān", + "73AB": "méi", + "73AC": "dǎn", + "73AD": "pín", + "73AE": "wěi", + "73AF": "huán", + "73B0": "xiàn", + "73B1": "qiāng", + "73B2": "líng", + "73B3": "dài", + "73B4": "yì", + "73B5": "án", + "73B6": "píng", + "73B7": "diàn", + "73B8": "fú", + "73B9": "xuán", + "73BA": "xǐ", + "73BB": "bō", + "73BC": "cǐ", + "73BD": "gǒu", + "73BE": "jiǎ", + "73BF": "sháo", + "73C0": "pò", + "73C1": "cí", + "73C2": "kē", + "73C3": "rǎn", + "73C4": "shēng", + "73C5": "shēn", + "73C6": "yí", + "73C7": "zǔ", + "73C8": "jiā", + "73C9": "mín", + "73CA": "shān", + "73CB": "liǔ", + "73CC": "bì", + "73CD": "zhēn", + "73CE": "zhēn", + "73CF": "jué", + "73D0": "fà", + "73D1": "lóng", + "73D2": "jīn", + "73D3": "jiào", + "73D4": "jiàn", + "73D5": "lì", + "73D6": "guàng", + "73D7": "xiān", + "73D8": "zhōu", + "73D9": "gǒng", + "73DA": "yān", + "73DB": "xiù", + "73DC": "yáng", + "73DD": "xǔ", + "73DE": "luò", + "73DF": "sù", + "73E0": "zhū", + "73E1": "qín", + "73E2": "yín", + "73E3": "xún", + "73E4": "bǎo", + "73E5": "ěr", + "73E6": "xiàng", + "73E7": "yáo", + "73E8": "xiá", + "73E9": "háng", + "73EA": "guī", + "73EB": "chōng", + "73EC": "xù", + "73ED": "bān", + "73EE": "pèi", + "73EF": "lao", + "73F0": "dāng", + "73F1": "ying", + "73F2": "huī", + "73F3": "wén", + "73F4": "é", + "73F5": "chéng", + "73F6": "dì", + "73F7": "wǔ", + "73F8": "wú", + "73F9": "chéng", + "73FA": "jùn", + "73FB": "méi", + "73FC": "bèi", + "73FD": "tǐng", + "73FE": "xiàn", + "73FF": "chù", + "7400": "hán", + "7401": "xuán", + "7402": "yán", + "7403": "qiú", + "7404": "xuàn", + "7405": "láng", + "7406": "lǐ", + "7407": "xiù", + "7408": "fú", + "7409": "liú", + "740A": "yá", + "740B": "xī", + "740C": "líng", + "740D": "lí", + "740E": "jìn", + "740F": "liǎn", + "7410": "suǒ", + "7411": "suo", + "7412": "feng", + "7413": "wán", + "7414": "diàn", + "7415": "pín", + "7416": "zhǎn", + "7417": "sè", + "7418": "mín", + "7419": "yù", + "741A": "jū", + "741B": "chēn", + "741C": "lái", + "741D": "wén", + "741E": "shèng", + "741F": "wéi", + "7420": "tiǎn", + "7421": "chù", + "7422": "zuó", + "7423": "běng", + "7424": "chēng", + "7425": "hǔ", + "7426": "qí", + "7427": "è", + "7428": "kūn", + "7429": "chāng", + "742A": "qí", + "742B": "běng", + "742C": "wǎn", + "742D": "lù", + "742E": "cóng", + "742F": "guǎn", + "7430": "yǎn", + "7431": "diāo", + "7432": "bèi", + "7433": "lín", + "7434": "qín", + "7435": "pí", + "7436": "pá", + "7437": "què", + "7438": "zhuó", + "7439": "qín", + "743A": "fà", + "743B": "jin", + "743C": "qióng", + "743D": "dǔ", + "743E": "jiè", + "743F": "hún", + "7440": "yǔ", + "7441": "mào", + "7442": "méi", + "7443": "chūn", + "7444": "xuān", + "7445": "tí", + "7446": "xīng", + "7447": "dài", + "7448": "róu", + "7449": "mín", + "744A": "jiān", + "744B": "wěi", + "744C": "ruǎn", + "744D": "huàn", + "744E": "xié", + "744F": "chuān", + "7450": "jiǎn", + "7451": "zhuàn", + "7452": "chàng", + "7453": "liàn", + "7454": "quán", + "7455": "xiá", + "7456": "duàn", + "7457": "yuàn", + "7458": "yá", + "7459": "nǎo", + "745A": "hú", + "745B": "yīng", + "745C": "yú", + "745D": "huáng", + "745E": "ruì", + "745F": "sè", + "7460": "liú", + "7461": "shī", + "7462": "róng", + "7463": "suǒ", + "7464": "yáo", + "7465": "wēn", + "7466": "wǔ", + "7467": "zhēn", + "7468": "jìn", + "7469": "yíng", + "746A": "mǎ", + "746B": "tāo", + "746C": "liú", + "746D": "táng", + "746E": "lì", + "746F": "láng", + "7470": "guī", + "7471": "zhèn", + "7472": "qiāng", + "7473": "cuō", + "7474": "jué", + "7475": "zhǎo", + "7476": "yáo", + "7477": "ài", + "7478": "bīn", + "7479": "shū", + "747A": "cháng", + "747B": "kūn", + "747C": "zhuān", + "747D": "cōng", + "747E": "jǐn", + "747F": "yī", + "7480": "cuǐ", + "7481": "cōng", + "7482": "qí", + "7483": "lí", + "7484": "yǐng", + "7485": "suǒ", + "7486": "qiú", + "7487": "xuán", + "7488": "áo", + "7489": "liǎn", + "748A": "mén", + "748B": "zhāng", + "748C": "yín", + "748D": "hua", + "748E": "yīng", + "748F": "wèi", + "7490": "lù", + "7491": "wú", + "7492": "dēng", + "7493": "xiù", + "7494": "zēng", + "7495": "xún", + "7496": "qú", + "7497": "dàng", + "7498": "lín", + "7499": "liáo", + "749A": "qióng", + "749B": "sù", + "749C": "huáng", + "749D": "guī", + "749E": "pú", + "749F": "jǐng", + "74A0": "fán", + "74A1": "jìn", + "74A2": "liú", + "74A3": "jī", + "74A4": "hui", + "74A5": "jǐng", + "74A6": "ài", + "74A7": "bì", + "74A8": "càn", + "74A9": "qú", + "74AA": "zǎo", + "74AB": "dāng", + "74AC": "jiǎo", + "74AD": "gùn", + "74AE": "tǎn", + "74AF": "huì", + "74B0": "huán", + "74B1": "sè", + "74B2": "suì", + "74B3": "tián", + "74B4": "chu", + "74B5": "yú", + "74B6": "jìn", + "74B7": "lú", + "74B8": "bīn", + "74B9": "shú", + "74BA": "wèn", + "74BB": "zuǐ", + "74BC": "lán", + "74BD": "xǐ", + "74BE": "zī", + "74BF": "xuán", + "74C0": "ruǎn", + "74C1": "wò", + "74C2": "gài", + "74C3": "léi", + "74C4": "dú", + "74C5": "lì", + "74C6": "zhì", + "74C7": "róu", + "74C8": "lí", + "74C9": "zàn", + "74CA": "qióng", + "74CB": "tì", + "74CC": "guī", + "74CD": "suí", + "74CE": "là", + "74CF": "lóng", + "74D0": "lú", + "74D1": "lì", + "74D2": "zàn", + "74D3": "làn", + "74D4": "yīng", + "74D5": "mí", + "74D6": "xiāng", + "74D7": "qióng", + "74D8": "guàn", + "74D9": "dào", + "74DA": "zàn", + "74DB": "huán", + "74DC": "guā", + "74DD": "bó", + "74DE": "dié", + "74DF": "bó", + "74E0": "hù", + "74E1": "zhí", + "74E2": "piáo", + "74E3": "bàn", + "74E4": "ráng", + "74E5": "lì", + "74E6": "wǎ", + "74E8": "xiáng", + "74E9": "qiān", + "74EA": "bǎn", + "74EB": "pén", + "74EC": "fǎng", + "74ED": "dǎn", + "74EE": "wèng", + "74EF": "ōu", + "74F2": "wa", + "74F3": "hú", + "74F4": "líng", + "74F5": "yí", + "74F6": "píng", + "74F7": "cí", + "74F8": "bǎi", + "74F9": "juān", + "74FA": "cháng", + "74FB": "chī", + "74FD": "dàng", + "74FE": "měng", + "74FF": "bù", + "7500": "zhuì", + "7501": "píng", + "7502": "biān", + "7503": "zhòu", + "7504": "zhēn", + "7506": "cí", + "7507": "yīng", + "7508": "qì", + "7509": "xián", + "750A": "lǒu", + "750B": "dì", + "750C": "ōu", + "750D": "méng", + "750E": "zhuān", + "750F": "bèng", + "7510": "lìn", + "7511": "zèng", + "7512": "wǔ", + "7513": "pì", + "7514": "dān", + "7515": "wèng", + "7516": "yīng", + "7517": "yǎn", + "7518": "gān", + "7519": "dài", + "751A": "shén", + "751B": "tián", + "751C": "tián", + "751D": "hán", + "751E": "cháng", + "751F": "shēng", + "7520": "qíng", + "7521": "shēn", + "7522": "chǎn", + "7523": "chǎn", + "7524": "ruí", + "7525": "shēng", + "7526": "sū", + "7527": "shēn", + "7528": "yòng", + "7529": "shuǎi", + "752A": "lù", + "752B": "fu", + "752C": "yǒng", + "752D": "béng", + "752E": "fèng", + "752F": "níng", + "7530": "tián", + "7531": "yóu", + "7532": "jiǎ", + "7533": "shēn", + "7534": "zhá", + "7535": "diàn", + "7536": "fú", + "7537": "nán", + "7538": "diān", + "7539": "pīng", + "753A": "tīng", + "753B": "huà", + "753C": "tǐng", + "753D": "zhèn", + "753E": "zāi", + "753F": "méng", + "7540": "bì", + "7541": "qí", + "7542": "liù", + "7543": "xún", + "7544": "liú", + "7545": "chàng", + "7546": "mǔ", + "7547": "yún", + "7548": "fàn", + "7549": "fú", + "754A": "gēng", + "754B": "tián", + "754C": "jiè", + "754D": "jiè", + "754E": "quǎn", + "754F": "wèi", + "7550": "fú", + "7551": "tián", + "7552": "mǔ", + "7553": "duō", + "7554": "pàn", + "7555": "jiāng", + "7556": "wā", + "7557": "dá", + "7558": "nán", + "7559": "liú", + "755A": "běn", + "755B": "zhěn", + "755C": "chù", + "755D": "mǔ", + "755E": "mǔ", + "755F": "cè", + "7560": "tián", + "7561": "gāi", + "7562": "bì", + "7563": "dá", + "7564": "zhì", + "7565": "è", + "7566": "qí", + "7567": "lüè", + "7568": "pān", + "7569": "yi", + "756A": "fān", + "756B": "huà", + "756C": "shē", + "756D": "yú", + "756E": "mǔ", + "756F": "jùn", + "7570": "yì", + "7571": "liú", + "7572": "shē", + "7573": "dié", + "7574": "chóu", + "7575": "huà", + "7576": "dāng", + "7577": "zhuì", + "7578": "jī", + "7579": "wǎn", + "757A": "jiāng", + "757B": "chéng", + "757C": "chàng", + "757D": "tǔn", + "757E": "léi", + "757F": "jī", + "7580": "chā", + "7581": "liú", + "7582": "die", + "7583": "tuǎn", + "7584": "lìn", + "7585": "jiāng", + "7586": "jiāng", + "7587": "chóu", + "7588": "pì", + "7589": "dié", + "758A": "dié", + "758B": "pǐ", + "758C": "jié", + "758D": "dàn", + "758E": "shū", + "758F": "shū", + "7590": "zhì", + "7591": "yí", + "7592": "nè", + "7593": "nǎi", + "7594": "dīng", + "7595": "bǐ", + "7596": "jiē", + "7597": "liáo", + "7598": "gāng", + "7599": "gē", + "759A": "jiù", + "759B": "zhǒu", + "759C": "xià", + "759D": "shàn", + "759E": "xū", + "759F": "nüè", + "75A0": "lì", + "75A1": "yáng", + "75A2": "chèn", + "75A3": "yóu", + "75A4": "bā", + "75A5": "jiè", + "75A6": "jué", + "75A7": "qí", + "75A8": "xiā", + "75A9": "cuì", + "75AA": "bì", + "75AB": "yì", + "75AC": "lì", + "75AD": "zòng", + "75AE": "chuāng", + "75AF": "fēng", + "75B0": "zhù", + "75B1": "pào", + "75B2": "pí", + "75B3": "gān", + "75B4": "kē", + "75B5": "cī", + "75B6": "xuē", + "75B7": "zhī", + "75B8": "dǎn", + "75B9": "zhěn", + "75BA": "fá", + "75BB": "zhǐ", + "75BC": "téng", + "75BD": "jū", + "75BE": "jí", + "75BF": "fèi", + "75C0": "jū", + "75C1": "shān", + "75C2": "jiā", + "75C3": "xuán", + "75C4": "zhà", + "75C5": "bìng", + "75C6": "niè", + "75C7": "zhèng", + "75C8": "yōng", + "75C9": "jìng", + "75CA": "quán", + "75CB": "téng", + "75CC": "tōng", + "75CD": "yí", + "75CE": "jiē", + "75CF": "wěi", + "75D0": "huí", + "75D1": "tān", + "75D2": "yǎng", + "75D3": "chì", + "75D4": "zhì", + "75D5": "hén", + "75D6": "yǎ", + "75D7": "mèi", + "75D8": "dòu", + "75D9": "jìng", + "75DA": "xiāo", + "75DB": "tòng", + "75DC": "tū", + "75DD": "máng", + "75DE": "pǐ", + "75DF": "xiāo", + "75E0": "suān", + "75E1": "fū", + "75E2": "lì", + "75E3": "zhì", + "75E4": "cuó", + "75E5": "duó", + "75E6": "wù", + "75E7": "shā", + "75E8": "láo", + "75E9": "shòu", + "75EA": "huàn", + "75EB": "xián", + "75EC": "yì", + "75ED": "bēng", + "75EE": "zhàng", + "75EF": "guǎn", + "75F0": "tán", + "75F1": "fèi", + "75F2": "má", + "75F3": "lín", + "75F4": "chī", + "75F5": "jì", + "75F6": "tiǎn", + "75F7": "ān", + "75F8": "chì", + "75F9": "bì", + "75FA": "bì", + "75FB": "mín", + "75FC": "gù", + "75FD": "duī", + "75FE": "ē", + "75FF": "wěi", + "7600": "yū", + "7601": "cuì", + "7602": "yǎ", + "7603": "zhú", + "7604": "cù", + "7605": "dān", + "7606": "shèn", + "7607": "zhǒng", + "7608": "chì", + "7609": "yù", + "760A": "hóu", + "760B": "fēng", + "760C": "là", + "760D": "yáng", + "760E": "chén", + "760F": "tú", + "7610": "yǔ", + "7611": "guō", + "7612": "wén", + "7613": "huàn", + "7614": "kù", + "7615": "jiǎ", + "7616": "yīn", + "7617": "yì", + "7618": "lòu", + "7619": "sào", + "761A": "jué", + "761B": "chì", + "761C": "xī", + "761D": "guān", + "761E": "yì", + "761F": "wēn", + "7620": "jí", + "7621": "chuāng", + "7622": "bān", + "7623": "huì", + "7624": "liú", + "7625": "chài", + "7626": "shòu", + "7627": "nüè", + "7628": "diān", + "7629": "da", + "762A": "biě", + "762B": "tān", + "762C": "zhàng", + "762D": "biāo", + "762E": "shèn", + "762F": "cù", + "7630": "luǒ", + "7631": "yì", + "7632": "zòng", + "7633": "chōu", + "7634": "zhàng", + "7635": "zhài", + "7636": "sòu", + "7637": "sè", + "7638": "qué", + "7639": "diào", + "763A": "lòu", + "763B": "lòu", + "763C": "mò", + "763D": "qín", + "763E": "yǐn", + "763F": "yǐng", + "7640": "huáng", + "7641": "fú", + "7642": "liáo", + "7643": "lóng", + "7644": "qiáo", + "7645": "liú", + "7646": "láo", + "7647": "xián", + "7648": "fèi", + "7649": "dān", + "764A": "yìn", + "764B": "hè", + "764C": "ái", + "764D": "bān", + "764E": "xián", + "764F": "guān", + "7650": "guì", + "7651": "nòng", + "7652": "yù", + "7653": "wéi", + "7654": "yì", + "7655": "yōng", + "7656": "pǐ", + "7657": "lěi", + "7658": "lì", + "7659": "shǔ", + "765A": "dàn", + "765B": "lǐn", + "765C": "diàn", + "765D": "lǐn", + "765E": "lài", + "765F": "biě", + "7660": "jì", + "7661": "chī", + "7662": "yǎng", + "7663": "xuǎn", + "7664": "jiē", + "7665": "zhēng", + "7666": "me", + "7667": "lì", + "7668": "huò", + "7669": "lài", + "766A": "ji", + "766B": "diān", + "766C": "xuǎn", + "766D": "yǐng", + "766E": "yǐn", + "766F": "qú", + "7670": "yōng", + "7671": "tān", + "7672": "diān", + "7673": "luǒ", + "7674": "luán", + "7675": "luán", + "7676": "bō", + "7677": "bō", + "7678": "guǐ", + "7679": "bá", + "767A": "fā", + "767B": "dēng", + "767C": "fā", + "767D": "bái", + "767E": "bǎi", + "767F": "qié", + "7680": "jí", + "7681": "zào", + "7682": "zào", + "7683": "mào", + "7684": "de", + "7685": "pā", + "7686": "jiē", + "7687": "huáng", + "7688": "guī", + "7689": "cǐ", + "768A": "líng", + "768B": "gāo", + "768C": "mò", + "768D": "jí", + "768E": "jiǎo", + "768F": "pěng", + "7690": "gāo", + "7691": "ái", + "7692": "é", + "7693": "hào", + "7694": "hàn", + "7695": "bì", + "7696": "wǎn", + "7697": "chóu", + "7698": "qiàn", + "7699": "xī", + "769A": "ái", + "769B": "xiǎo", + "769C": "hào", + "769D": "huàng", + "769E": "hào", + "769F": "zé", + "76A0": "cuǐ", + "76A1": "hào", + "76A2": "xiǎo", + "76A3": "yè", + "76A4": "pó", + "76A5": "hào", + "76A6": "jiǎo", + "76A7": "ài", + "76A8": "xīng", + "76A9": "huàng", + "76AA": "lì", + "76AB": "piǎo", + "76AC": "hé", + "76AD": "jiào", + "76AE": "pí", + "76AF": "gǎn", + "76B0": "pào", + "76B1": "zhòu", + "76B2": "jūn", + "76B3": "qiú", + "76B4": "cūn", + "76B5": "què", + "76B6": "zhā", + "76B7": "gǔ", + "76B8": "jūn", + "76B9": "jūn", + "76BA": "zhòu", + "76BB": "zhā", + "76BC": "gǔ", + "76BD": "zhāo", + "76BE": "dú", + "76BF": "mǐn", + "76C0": "qǐ", + "76C1": "yíng", + "76C2": "yú", + "76C3": "bēi", + "76C4": "zhāo", + "76C5": "zhōng", + "76C6": "pén", + "76C7": "hé", + "76C8": "yíng", + "76C9": "hé", + "76CA": "yì", + "76CB": "bō", + "76CC": "wǎn", + "76CD": "hé", + "76CE": "àng", + "76CF": "zhǎn", + "76D0": "yán", + "76D1": "jiān", + "76D2": "hé", + "76D3": "yū", + "76D4": "kuī", + "76D5": "fàn", + "76D6": "gài", + "76D7": "dào", + "76D8": "pán", + "76D9": "fǔ", + "76DA": "qiú", + "76DB": "shèng", + "76DC": "dào", + "76DD": "lù", + "76DE": "zhǎn", + "76DF": "méng", + "76E0": "lí", + "76E1": "jǐn", + "76E2": "xù", + "76E3": "jiān", + "76E4": "pán", + "76E5": "guàn", + "76E6": "ān", + "76E7": "lú", + "76E8": "xǔ", + "76E9": "zhōu", + "76EA": "dàng", + "76EB": "ān", + "76EC": "gǔ", + "76ED": "lì", + "76EE": "mù", + "76EF": "dīng", + "76F0": "gàn", + "76F1": "xū", + "76F2": "máng", + "76F3": "wàng", + "76F4": "zhí", + "76F5": "qì", + "76F6": "yuǎn", + "76F7": "tián", + "76F8": "xiāng", + "76F9": "dǔn", + "76FA": "xīn", + "76FB": "xì", + "76FC": "pàn", + "76FD": "fēng", + "76FE": "dùn", + "76FF": "mín", + "7700": "míng", + "7701": "shěng", + "7702": "shì", + "7703": "yún", + "7704": "miǎn", + "7705": "pān", + "7706": "fǎng", + "7707": "miǎo", + "7708": "dān", + "7709": "méi", + "770A": "mào", + "770B": "kàn", + "770C": "xiàn", + "770D": "kōu", + "770E": "shì", + "770F": "yāng", + "7710": "zhēng", + "7711": "yǎo", + "7712": "shēn", + "7713": "huò", + "7714": "dà", + "7715": "zhěn", + "7716": "kuàng", + "7717": "jū", + "7718": "shèn", + "7719": "yí", + "771A": "shěng", + "771B": "mèi", + "771C": "mò", + "771D": "zhù", + "771E": "zhēn", + "771F": "zhēn", + "7720": "mián", + "7721": "shì", + "7722": "yuān", + "7723": "dié", + "7724": "nì", + "7725": "zì", + "7726": "zì", + "7727": "chǎo", + "7728": "zhǎ", + "7729": "xuàn", + "772A": "bǐng", + "772B": "mǐ", + "772C": "lóng", + "772D": "suī", + "772E": "tóng", + "772F": "mī", + "7730": "diè", + "7731": "dì", + "7732": "nè", + "7733": "míng", + "7734": "xuàn", + "7735": "chī", + "7736": "kuàng", + "7737": "juàn", + "7738": "móu", + "7739": "zhèn", + "773A": "tiào", + "773B": "yáng", + "773C": "yǎn", + "773D": "mò", + "773E": "zhòng", + "773F": "mò", + "7740": "zhe", + "7741": "zhēng", + "7742": "méi", + "7743": "suō", + "7744": "shào", + "7745": "hàn", + "7746": "huàn", + "7747": "dì", + "7748": "chěng", + "7749": "cuó", + "774A": "juàn", + "774B": "é", + "774C": "mǎn", + "774D": "xiàn", + "774E": "xī", + "774F": "kùn", + "7750": "lài", + "7751": "jiǎn", + "7752": "shǎn", + "7753": "tiǎn", + "7754": "gùn", + "7755": "wǎn", + "7756": "lèng", + "7757": "shì", + "7758": "qióng", + "7759": "liè", + "775A": "yá", + "775B": "jing", + "775C": "zhēng", + "775D": "lí", + "775E": "lài", + "775F": "suì", + "7760": "juàn", + "7761": "shuì", + "7762": "suī", + "7763": "dū", + "7764": "bì", + "7765": "pì", + "7766": "mù", + "7767": "hūn", + "7768": "nì", + "7769": "lù", + "776A": "yì", + "776B": "jié", + "776C": "cǎi", + "776D": "zhǒu", + "776E": "yú", + "776F": "hūn", + "7770": "mà", + "7771": "xià", + "7772": "xǐng", + "7773": "huī", + "7774": "gùn", + "7775": "zāi", + "7776": "chǔn", + "7777": "jiān", + "7778": "mèi", + "7779": "dǔ", + "777A": "hóu", + "777B": "xuān", + "777C": "tiàn", + "777D": "kuí", + "777E": "gāo", + "777F": "ruì", + "7780": "mào", + "7781": "xù", + "7782": "fá", + "7783": "wò", + "7784": "miáo", + "7785": "chǒu", + "7786": "kuì", + "7787": "mī", + "7788": "wěng", + "7789": "kòu", + "778A": "dàng", + "778B": "chēn", + "778C": "kē", + "778D": "sǒu", + "778E": "xiā", + "778F": "qióng", + "7790": "mò", + "7791": "míng", + "7792": "mán", + "7793": "shuì", + "7794": "zé", + "7795": "zhàng", + "7796": "yì", + "7797": "diāo", + "7798": "kōu", + "7799": "mò", + "779A": "shùn", + "779B": "cōng", + "779C": "lōu", + "779D": "chī", + "779E": "mán", + "779F": "piǎo", + "77A0": "chēng", + "77A1": "guǐ", + "77A2": "méng", + "77A3": "huan", + "77A4": "rún", + "77A5": "piē", + "77A6": "xī", + "77A7": "qiáo", + "77A8": "pú", + "77A9": "zhǔ", + "77AA": "dèng", + "77AB": "shěn", + "77AC": "shùn", + "77AD": "liǎo", + "77AE": "chè", + "77AF": "xián", + "77B0": "kàn", + "77B1": "yè", + "77B2": "xù", + "77B3": "tóng", + "77B4": "móu", + "77B5": "lín", + "77B6": "guì", + "77B7": "jiàn", + "77B8": "yè", + "77B9": "ài", + "77BA": "huì", + "77BB": "zhān", + "77BC": "jiǎn", + "77BD": "gǔ", + "77BE": "zhào", + "77BF": "qú", + "77C0": "méi", + "77C1": "chǒu", + "77C2": "sào", + "77C3": "nǐng", + "77C4": "xūn", + "77C5": "yào", + "77C6": "huò", + "77C7": "méng", + "77C8": "mián", + "77C9": "pín", + "77CA": "mián", + "77CB": "lì", + "77CC": "kuàng", + "77CD": "jué", + "77CE": "xuān", + "77CF": "mián", + "77D0": "huò", + "77D1": "lú", + "77D2": "méng", + "77D3": "lóng", + "77D4": "guàn", + "77D5": "mǎn", + "77D6": "xǐ", + "77D7": "chù", + "77D8": "tǎng", + "77D9": "kàn", + "77DA": "zhǔ", + "77DB": "máo", + "77DC": "jīn", + "77DD": "lín", + "77DE": "yù", + "77DF": "shuò", + "77E0": "zé", + "77E1": "jué", + "77E2": "shǐ", + "77E3": "yǐ", + "77E4": "shěn", + "77E5": "zhī", + "77E6": "hóu", + "77E7": "shěn", + "77E8": "yǐng", + "77E9": "ju", + "77EA": "zhōu", + "77EB": "jiǎo", + "77EC": "cuó", + "77ED": "duǎn", + "77EE": "ǎi", + "77EF": "jiǎo", + "77F0": "zēng", + "77F1": "yuē", + "77F2": "bà", + "77F3": "shí", + "77F4": "dìng", + "77F5": "qì", + "77F6": "jī", + "77F7": "zǐ", + "77F8": "gān", + "77F9": "wù", + "77FA": "zhé", + "77FB": "kū", + "77FC": "gāng", + "77FD": "xì", + "77FE": "fán", + "77FF": "kuàng", + "7800": "dàng", + "7801": "mǎ", + "7802": "shā", + "7803": "dān", + "7804": "jué", + "7805": "lì", + "7806": "fū", + "7807": "mín", + "7808": "ě", + "7809": "huò", + "780A": "kāng", + "780B": "zhǐ", + "780C": "qì", + "780D": "kǎn", + "780E": "jiè", + "780F": "bīn", + "7810": "è", + "7811": "yà", + "7812": "pī", + "7813": "zhé", + "7814": "yán", + "7815": "suì", + "7816": "zhuān", + "7817": "chē", + "7818": "dùn", + "7819": "pān", + "781A": "yàn", + "781B": "jin", + "781C": "fēng", + "781D": "fá", + "781E": "mò", + "781F": "zhǎ", + "7820": "jū", + "7821": "yù", + "7822": "kē", + "7823": "tuó", + "7824": "tuó", + "7825": "dǐ", + "7826": "zhài", + "7827": "zhēn", + "7828": "è", + "7829": "fú", + "782A": "mǔ", + "782B": "zhù", + "782C": "lá", + "782D": "biān", + "782E": "nǔ", + "782F": "pīng", + "7830": "pēng", + "7831": "líng", + "7832": "pào", + "7833": "lè", + "7834": "pò", + "7835": "bō", + "7836": "pò", + "7837": "shēn", + "7838": "zá", + "7839": "ài", + "783A": "lì", + "783B": "lóng", + "783C": "tóng", + "783D": "yong", + "783E": "lì", + "783F": "kuang", + "7840": "chǔ", + "7841": "kēng", + "7842": "quán", + "7843": "zhū", + "7844": "kuāng", + "7845": "guī", + "7846": "è", + "7847": "náo", + "7848": "qià", + "7849": "lù", + "784A": "wěi", + "784B": "ài", + "784C": "gè", + "784D": "xiàn", + "784E": "xíng", + "784F": "yán", + "7850": "dòng", + "7851": "pēng", + "7852": "xī", + "7853": "lao", + "7854": "hóng", + "7855": "shuò", + "7856": "xiá", + "7857": "qiāo", + "7858": "qing", + "7859": "wéi", + "785A": "qiáo", + "785B": "yì", + "785C": "kēng", + "785D": "xiāo", + "785E": "què", + "785F": "chàn", + "7860": "láng", + "7861": "hōng", + "7862": "yú", + "7863": "xiāo", + "7864": "xiá", + "7865": "mǎng", + "7866": "luò", + "7867": "yǒng", + "7868": "chē", + "7869": "chè", + "786A": "wò", + "786B": "liú", + "786C": "yìng", + "786D": "máng", + "786E": "què", + "786F": "yàn", + "7870": "shā", + "7871": "kǔn", + "7872": "yù", + "7873": "chì", + "7874": "hua", + "7875": "lǔ", + "7876": "chěn", + "7877": "jiǎn", + "7878": "nüè", + "7879": "sōng", + "787A": "zhuó", + "787B": "kēng", + "787C": "péng", + "787D": "yān", + "787E": "zhuì", + "787F": "kōng", + "7880": "chéng", + "7881": "qí", + "7882": "zòng", + "7883": "qìng", + "7884": "lín", + "7885": "jūn", + "7886": "bō", + "7887": "dìng", + "7888": "mín", + "7889": "diāo", + "788A": "jiān", + "788B": "hè", + "788C": "lù", + "788D": "ài", + "788E": "suì", + "788F": "què", + "7890": "léng", + "7891": "bēi", + "7892": "yín", + "7893": "duì", + "7894": "wǔ", + "7895": "qí", + "7896": "lǔn", + "7897": "wǎn", + "7898": "diǎn", + "7899": "náo", + "789A": "bèi", + "789B": "qì", + "789C": "chěn", + "789D": "ruǎn", + "789E": "yán", + "789F": "dié", + "78A0": "dìng", + "78A1": "dú", + "78A2": "tuó", + "78A3": "jié", + "78A4": "yīng", + "78A5": "biǎn", + "78A6": "kè", + "78A7": "bì", + "78A8": "wèi", + "78A9": "shuò", + "78AA": "zhēn", + "78AB": "duàn", + "78AC": "xiá", + "78AD": "dàng", + "78AE": "tí", + "78AF": "nǎo", + "78B0": "pèng", + "78B1": "jiǎn", + "78B2": "dì", + "78B3": "tàn", + "78B4": "chá", + "78B5": "tian", + "78B6": "qì", + "78B7": "dun", + "78B8": "fēng", + "78B9": "xuàn", + "78BA": "què", + "78BB": "què", + "78BC": "mǎ", + "78BD": "gōng", + "78BE": "niǎn", + "78BF": "sù", + "78C0": "é", + "78C1": "cí", + "78C2": "liú", + "78C3": "sī", + "78C4": "táng", + "78C5": "bàng", + "78C6": "huá", + "78C7": "pī", + "78C8": "wěi", + "78C9": "sǎng", + "78CA": "lěi", + "78CB": "cuō", + "78CC": "tián", + "78CD": "xiá", + "78CE": "xī", + "78CF": "lián", + "78D0": "pán", + "78D1": "wéi", + "78D2": "yǔn", + "78D3": "duī", + "78D4": "zhé", + "78D5": "kē", + "78D6": "lá", + "78D7": "zhuān", + "78D8": "qìng", + "78D9": "gǔn", + "78DA": "zhuān", + "78DB": "chán", + "78DC": "qì", + "78DD": "áo", + "78DE": "pēng", + "78DF": "liù", + "78E0": "lǔ", + "78E1": "kàn", + "78E2": "chuǎng", + "78E3": "chěn", + "78E4": "yǐn", + "78E5": "lěi", + "78E6": "biāo", + "78E7": "qì", + "78E8": "mó", + "78E9": "qì", + "78EA": "cuī", + "78EB": "zōng", + "78EC": "qìng", + "78ED": "chuò", + "78EE": "lun", + "78EF": "jī", + "78F0": "shàn", + "78F1": "láo", + "78F2": "qú", + "78F3": "zēng", + "78F4": "dèng", + "78F5": "jiàn", + "78F6": "xì", + "78F7": "lín", + "78F8": "dìng", + "78F9": "tán", + "78FA": "huáng", + "78FB": "pán", + "78FC": "zá", + "78FD": "qiāo", + "78FE": "dī", + "78FF": "lì", + "7900": "jian", + "7901": "jiāo", + "7902": "xi", + "7903": "zhǎng", + "7904": "qiáo", + "7905": "dūn", + "7906": "jiǎn", + "7907": "yù", + "7908": "zhuì", + "7909": "hé", + "790A": "kè", + "790B": "zé", + "790C": "léi", + "790D": "kě", + "790E": "chǔ", + "790F": "yè", + "7910": "què", + "7911": "dàng", + "7912": "yǐ", + "7913": "jiāng", + "7914": "pī", + "7915": "pī", + "7916": "yù", + "7917": "pīn", + "7918": "è", + "7919": "ài", + "791A": "kē", + "791B": "jiān", + "791C": "yù", + "791D": "ruǎn", + "791E": "méng", + "791F": "pào", + "7920": "cí", + "7921": "bó", + "7922": "yang", + "7923": "mà", + "7924": "cǎ", + "7925": "xián", + "7926": "kuàng", + "7927": "léi", + "7928": "lěi", + "7929": "zhì", + "792A": "lì", + "792B": "lì", + "792C": "fán", + "792D": "què", + "792E": "pào", + "792F": "yīng", + "7930": "lì", + "7931": "lóng", + "7932": "lóng", + "7933": "mò", + "7934": "bó", + "7935": "shuāng", + "7936": "guàn", + "7937": "lán", + "7938": "zǎn", + "7939": "yán", + "793A": "shì", + "793B": "shì", + "793C": "lǐ", + "793D": "réng", + "793E": "shè", + "793F": "yuè", + "7940": "sì", + "7941": "qí", + "7942": "tā", + "7943": "mà", + "7944": "xiè", + "7945": "yāo", + "7946": "xiān", + "7947": "qí", + "7948": "qí", + "7949": "zhǐ", + "794A": "bēng", + "794B": "duì", + "794C": "zhòng", + "794D": "rèn", + "794E": "yī", + "794F": "shí", + "7950": "yòu", + "7951": "zhì", + "7952": "tiáo", + "7953": "fú", + "7954": "fù", + "7955": "mì", + "7956": "zǔ", + "7957": "zhī", + "7958": "suàn", + "7959": "mèi", + "795A": "zuò", + "795B": "qū", + "795C": "hù", + "795D": "zhù", + "795E": "shén", + "795F": "suì", + "7960": "cí", + "7961": "chái", + "7962": "mí", + "7963": "lǚ", + "7964": "yǔ", + "7965": "xiáng", + "7966": "wú", + "7967": "tiāo", + "7968": "piào", + "7969": "zhù", + "796A": "guǐ", + "796B": "xiá", + "796C": "zhī", + "796D": "jì", + "796E": "gào", + "796F": "zhēn", + "7970": "gào", + "7971": "shuì", + "7972": "jìn", + "7973": "shèn", + "7974": "gāi", + "7975": "kǔn", + "7976": "dì", + "7977": "dǎo", + "7978": "huò", + "7979": "táo", + "797A": "qí", + "797B": "gù", + "797C": "guàn", + "797D": "zuì", + "797E": "líng", + "797F": "lù", + "7980": "bǐng", + "7981": "jìn", + "7982": "dǎo", + "7983": "zhí", + "7984": "lù", + "7985": "chán", + "7986": "bēi", + "7987": "zhě", + "7988": "huī", + "7989": "yǒu", + "798A": "xì", + "798B": "yīn", + "798C": "zī", + "798D": "huò", + "798E": "zhēn", + "798F": "fú", + "7990": "yuàn", + "7991": "wú", + "7992": "xiǎn", + "7993": "yáng", + "7994": "zhī", + "7995": "yī", + "7996": "méi", + "7997": "sī", + "7998": "dì", + "7999": "bei", + "799A": "zhuó", + "799B": "zhēn", + "799C": "yǒng", + "799D": "jí", + "799E": "gào", + "799F": "táng", + "79A0": "sī", + "79A1": "mà", + "79A2": "tà", + "79A3": "fu", + "79A4": "xuān", + "79A5": "qí", + "79A6": "yù", + "79A7": "xǐ", + "79A8": "jī", + "79A9": "sì", + "79AA": "chán", + "79AB": "dàn", + "79AC": "guì", + "79AD": "suì", + "79AE": "lǐ", + "79AF": "nóng", + "79B0": "mí", + "79B1": "dǎo", + "79B2": "lì", + "79B3": "ráng", + "79B4": "yuè", + "79B5": "tí", + "79B6": "zàn", + "79B7": "lèi", + "79B8": "róu", + "79B9": "yǔ", + "79BA": "yú", + "79BB": "lí", + "79BC": "xiè", + "79BD": "qín", + "79BE": "hé", + "79BF": "tū", + "79C0": "xiù", + "79C1": "sī", + "79C2": "rén", + "79C3": "tū", + "79C4": "zǐ", + "79C5": "chá", + "79C6": "gǎn", + "79C7": "yì", + "79C8": "xiān", + "79C9": "bǐng", + "79CA": "nián", + "79CB": "qiū", + "79CC": "qiū", + "79CD": "zhǒng", + "79CE": "fèn", + "79CF": "hào", + "79D0": "yún", + "79D1": "kē", + "79D2": "miǎo", + "79D3": "zhī", + "79D4": "jīng", + "79D5": "bǐ", + "79D6": "zhī", + "79D7": "yù", + "79D8": "mì", + "79D9": "kù", + "79DA": "bàn", + "79DB": "pī", + "79DC": "ní", + "79DD": "lì", + "79DE": "yóu", + "79DF": "zū", + "79E0": "pī", + "79E1": "bó", + "79E2": "líng", + "79E3": "mò", + "79E4": "chèng", + "79E5": "nián", + "79E6": "qín", + "79E7": "yāng", + "79E8": "zuó", + "79E9": "zhì", + "79EA": "zhī", + "79EB": "shú", + "79EC": "jù", + "79ED": "zǐ", + "79EE": "huó", + "79EF": "jī", + "79F0": "chēng", + "79F1": "tóng", + "79F2": "zhì", + "79F3": "huó", + "79F4": "hé", + "79F5": "yīn", + "79F6": "zī", + "79F7": "zhì", + "79F8": "jiē", + "79F9": "rěn", + "79FA": "dù", + "79FB": "yí", + "79FC": "zhū", + "79FD": "huì", + "79FE": "nóng", + "79FF": "fù", + "7A00": "xī", + "7A01": "kǎo", + "7A02": "láng", + "7A03": "fū", + "7A04": "xùn", + "7A05": "shuì", + "7A06": "lǚ", + "7A07": "kǔn", + "7A08": "gǎn", + "7A09": "jīng", + "7A0A": "tí", + "7A0B": "chéng", + "7A0C": "tú", + "7A0D": "shāo", + "7A0E": "shuì", + "7A0F": "yà", + "7A10": "lǔn", + "7A11": "lù", + "7A12": "gù", + "7A13": "zuó", + "7A14": "rěn", + "7A15": "zhùn", + "7A16": "bàng", + "7A17": "bài", + "7A18": "jī", + "7A19": "zhī", + "7A1A": "zhì", + "7A1B": "kǔn", + "7A1C": "léng", + "7A1D": "péng", + "7A1E": "kē", + "7A1F": "bǐng", + "7A20": "chóu", + "7A21": "zuì", + "7A22": "yù", + "7A23": "sū", + "7A24": "lüè", + "7A25": "xiāng", + "7A26": "yī", + "7A27": "xì", + "7A28": "biǎn", + "7A29": "jì", + "7A2A": "fú", + "7A2B": "pì", + "7A2C": "nuò", + "7A2D": "jiē", + "7A2E": "zhǒng", + "7A2F": "zōng", + "7A30": "xǔ", + "7A31": "chēng", + "7A32": "dào", + "7A33": "wěn", + "7A34": "xián", + "7A35": "zī", + "7A36": "yù", + "7A37": "jì", + "7A38": "xù", + "7A39": "zhěn", + "7A3A": "zhì", + "7A3B": "dào", + "7A3C": "jia", + "7A3D": "jī", + "7A3E": "gǎo", + "7A3F": "gǎo", + "7A40": "gǔ", + "7A41": "róng", + "7A42": "suì", + "7A43": "rong", + "7A44": "jì", + "7A45": "kāng", + "7A46": "mù", + "7A47": "cǎn", + "7A48": "méi", + "7A49": "zhì", + "7A4A": "jì", + "7A4B": "lù", + "7A4C": "sū", + "7A4D": "jī", + "7A4E": "yǐng", + "7A4F": "wěn", + "7A50": "qiū", + "7A51": "sè", + "7A52": "hè", + "7A53": "yì", + "7A54": "huáng", + "7A55": "qiè", + "7A56": "jǐ", + "7A57": "suì", + "7A58": "xiāo", + "7A59": "pú", + "7A5A": "jiāo", + "7A5B": "zhuō", + "7A5C": "zhǒng", + "7A5D": "zui", + "7A5E": "lǚ", + "7A5F": "suì", + "7A60": "nóng", + "7A61": "sè", + "7A62": "huì", + "7A63": "ráng", + "7A64": "nuò", + "7A65": "yù", + "7A66": "pīn", + "7A67": "jì", + "7A68": "tuí", + "7A69": "wěn", + "7A6A": "chēng", + "7A6B": "huò", + "7A6C": "kuàng", + "7A6D": "lǚ", + "7A6E": "biāo", + "7A6F": "se", + "7A70": "ráng", + "7A71": "zhuō", + "7A72": "lí", + "7A73": "cuán", + "7A74": "xué", + "7A75": "wā", + "7A76": "jiū", + "7A77": "qióng", + "7A78": "xī", + "7A79": "qióng", + "7A7A": "kōng", + "7A7B": "yū", + "7A7C": "shēn", + "7A7D": "jǐng", + "7A7E": "yào", + "7A7F": "chuān", + "7A80": "zhūn", + "7A81": "tū", + "7A82": "láo", + "7A83": "qiè", + "7A84": "zhǎi", + "7A85": "yǎo", + "7A86": "biǎn", + "7A87": "báo", + "7A88": "yǎo", + "7A89": "bǐng", + "7A8A": "wā", + "7A8B": "zhú", + "7A8C": "jiào", + "7A8D": "qiào", + "7A8E": "diào", + "7A8F": "wū", + "7A90": "guī", + "7A91": "yáo", + "7A92": "zhì", + "7A93": "chuāng", + "7A94": "yào", + "7A95": "tiǎo", + "7A96": "jiào", + "7A97": "chuāng", + "7A98": "jiǒng", + "7A99": "xiāo", + "7A9A": "chéng", + "7A9B": "kòu", + "7A9C": "cuàn", + "7A9D": "wō", + "7A9E": "dàn", + "7A9F": "kū", + "7AA0": "kē", + "7AA1": "zhuó", + "7AA2": "xū", + "7AA3": "sū", + "7AA4": "guān", + "7AA5": "kuī", + "7AA6": "dòu", + "7AA7": "zhuo", + "7AA8": "xūn", + "7AA9": "wō", + "7AAA": "wā", + "7AAB": "yà", + "7AAC": "yú", + "7AAD": "jù", + "7AAE": "qióng", + "7AAF": "yáo", + "7AB0": "yáo", + "7AB1": "tiǎo", + "7AB2": "cháo", + "7AB3": "yǔ", + "7AB4": "tián", + "7AB5": "diào", + "7AB6": "jù", + "7AB7": "liào", + "7AB8": "xī", + "7AB9": "wù", + "7ABA": "kuī", + "7ABB": "chuāng", + "7ABC": "zhāo", + "7ABD": "kuan", + "7ABE": "kuǎn", + "7ABF": "long", + "7AC0": "chēng", + "7AC1": "cuì", + "7AC2": "piáo", + "7AC3": "zào", + "7AC4": "cuàn", + "7AC5": "qiào", + "7AC6": "qióng", + "7AC7": "dòu", + "7AC8": "zào", + "7AC9": "lǒng", + "7ACA": "qiè", + "7ACB": "lì", + "7ACC": "chù", + "7ACD": "shi", + "7ACE": "fù", + "7ACF": "qian", + "7AD0": "chù", + "7AD1": "hóng", + "7AD2": "qí", + "7AD3": "hao", + "7AD4": "sheng", + "7AD5": "fen", + "7AD6": "shù", + "7AD7": "miào", + "7AD8": "qǔ", + "7AD9": "zhàn", + "7ADA": "zhù", + "7ADB": "líng", + "7ADC": "lóng", + "7ADD": "bìng", + "7ADE": "jìng", + "7ADF": "jìng", + "7AE0": "zhāng", + "7AE1": "bai", + "7AE2": "sì", + "7AE3": "jùn", + "7AE4": "hóng", + "7AE5": "tóng", + "7AE6": "sǒng", + "7AE7": "jìng", + "7AE8": "diào", + "7AE9": "yì", + "7AEA": "shù", + "7AEB": "jìng", + "7AEC": "qǔ", + "7AED": "jié", + "7AEE": "pīng", + "7AEF": "duān", + "7AF0": "sháo", + "7AF1": "zhuǎn", + "7AF2": "céng", + "7AF3": "dēng", + "7AF4": "cūn", + "7AF5": "wāi", + "7AF6": "jìng", + "7AF7": "kǎn", + "7AF8": "jìng", + "7AF9": "zhú", + "7AFA": "zhú", + "7AFB": "lè", + "7AFC": "péng", + "7AFD": "yú", + "7AFE": "chí", + "7AFF": "gān", + "7B00": "máng", + "7B01": "zhú", + "7B02": "wan", + "7B03": "dǔ", + "7B04": "jī", + "7B05": "xiáo", + "7B06": "ba", + "7B07": "suàn", + "7B08": "jí", + "7B09": "qǐn", + "7B0A": "zhào", + "7B0B": "sǔn", + "7B0C": "yá", + "7B0D": "zhuì", + "7B0E": "yuán", + "7B0F": "hù", + "7B10": "háng", + "7B11": "xiào", + "7B12": "cén", + "7B13": "bì", + "7B14": "bǐ", + "7B15": "jiǎn", + "7B16": "yǐ", + "7B17": "dōng", + "7B18": "shān", + "7B19": "shēng", + "7B1A": "dā", + "7B1B": "dí", + "7B1C": "zhú", + "7B1D": "nà", + "7B1E": "chī", + "7B1F": "gū", + "7B20": "lì", + "7B21": "qiè", + "7B22": "mǐn", + "7B23": "bāo", + "7B24": "tiáo", + "7B25": "sì", + "7B26": "fú", + "7B27": "cè", + "7B28": "bèn", + "7B29": "pèi", + "7B2A": "dá", + "7B2B": "zǐ", + "7B2C": "dì", + "7B2D": "líng", + "7B2E": "zé", + "7B2F": "nú", + "7B30": "fú", + "7B31": "gǒu", + "7B32": "fán", + "7B33": "jiā", + "7B34": "gǎn", + "7B35": "fàn", + "7B36": "shǐ", + "7B37": "mǎo", + "7B38": "pǒ", + "7B39": "ti", + "7B3A": "jiān", + "7B3B": "qióng", + "7B3C": "lóng", + "7B3D": "min", + "7B3E": "biān", + "7B3F": "luò", + "7B40": "guì", + "7B41": "qū", + "7B42": "chí", + "7B43": "yīn", + "7B44": "yào", + "7B45": "xiǎn", + "7B46": "bǐ", + "7B47": "qióng", + "7B48": "kuò", + "7B49": "děng", + "7B4A": "xiáo", + "7B4B": "jīn", + "7B4C": "quán", + "7B4D": "sǔn", + "7B4E": "rú", + "7B4F": "fá", + "7B50": "kuāng", + "7B51": "zhù", + "7B52": "tǒng", + "7B53": "jī", + "7B54": "dá", + "7B55": "háng", + "7B56": "cè", + "7B57": "zhòng", + "7B58": "kòu", + "7B59": "lái", + "7B5A": "bì", + "7B5B": "shāi", + "7B5C": "dāng", + "7B5D": "zhēng", + "7B5E": "cè", + "7B5F": "fū", + "7B60": "yún", + "7B61": "tú", + "7B62": "pá", + "7B63": "lí", + "7B64": "láng", + "7B65": "jǔ", + "7B66": "guǎn", + "7B67": "jiǎn", + "7B68": "hán", + "7B69": "tóng", + "7B6A": "xiá", + "7B6B": "zhì", + "7B6C": "chéng", + "7B6D": "suàn", + "7B6E": "shì", + "7B6F": "zhù", + "7B70": "zuó", + "7B71": "xiǎo", + "7B72": "shāo", + "7B73": "tíng", + "7B74": "cè", + "7B75": "yán", + "7B76": "gào", + "7B77": "kuài", + "7B78": "gān", + "7B79": "chóu", + "7B7A": "kuang", + "7B7B": "gàng", + "7B7C": "yún", + "7B7D": "o", + "7B7E": "qiān", + "7B7F": "xiǎo", + "7B80": "jiǎn", + "7B81": "póu", + "7B82": "lái", + "7B83": "zōu", + "7B84": "bǐ", + "7B85": "bì", + "7B86": "bì", + "7B87": "gè", + "7B88": "tái", + "7B89": "guǎi", + "7B8A": "yū", + "7B8B": "jiān", + "7B8C": "dào", + "7B8D": "gū", + "7B8E": "chí", + "7B8F": "zhēng", + "7B90": "qìng", + "7B91": "shà", + "7B92": "zhǒu", + "7B93": "lù", + "7B94": "bó", + "7B95": "jī", + "7B96": "lín", + "7B97": "suàn", + "7B98": "jùn", + "7B99": "fú", + "7B9A": "zhá", + "7B9B": "gū", + "7B9C": "kōng", + "7B9D": "qián", + "7B9E": "qiān", + "7B9F": "jùn", + "7BA0": "chuí", + "7BA1": "guǎn", + "7BA2": "yuān", + "7BA3": "cè", + "7BA4": "zú", + "7BA5": "bǒ", + "7BA6": "zé", + "7BA7": "qiè", + "7BA8": "tuò", + "7BA9": "luó", + "7BAA": "dān", + "7BAB": "xiāo", + "7BAC": "ruò", + "7BAD": "jiàn", + "7BAE": "xuān", + "7BAF": "biān", + "7BB0": "sǔn", + "7BB1": "xiāng", + "7BB2": "xiǎn", + "7BB3": "píng", + "7BB4": "zhēn", + "7BB5": "xīng", + "7BB6": "hú", + "7BB7": "yí", + "7BB8": "zhù", + "7BB9": "yuē", + "7BBA": "chūn", + "7BBB": "lǜ", + "7BBC": "wū", + "7BBD": "dǒng", + "7BBE": "shuò", + "7BBF": "jí", + "7BC0": "jié", + "7BC1": "huáng", + "7BC2": "xīng", + "7BC3": "mèi", + "7BC4": "fàn", + "7BC5": "chuán", + "7BC6": "zhuàn", + "7BC7": "piān", + "7BC8": "fēng", + "7BC9": "zhú", + "7BCA": "huáng", + "7BCB": "qiè", + "7BCC": "hóu", + "7BCD": "qiū", + "7BCE": "miǎo", + "7BCF": "qiàn", + "7BD0": "gu", + "7BD1": "kuì", + "7BD2": "shi", + "7BD3": "lǒu", + "7BD4": "yún", + "7BD5": "hé", + "7BD6": "táng", + "7BD7": "yuè", + "7BD8": "chōu", + "7BD9": "gāo", + "7BDA": "fěi", + "7BDB": "ruò", + "7BDC": "zhēng", + "7BDD": "gōu", + "7BDE": "niè", + "7BDF": "qiàn", + "7BE0": "xiǎo", + "7BE1": "cuàn", + "7BE2": "lǒng", + "7BE3": "péng", + "7BE4": "dǔ", + "7BE5": "lì", + "7BE6": "bì", + "7BE7": "zhuó", + "7BE8": "chú", + "7BE9": "shāi", + "7BEA": "chí", + "7BEB": "zhù", + "7BEC": "qiāng", + "7BED": "lóng", + "7BEE": "lán", + "7BEF": "jiān", + "7BF0": "bù", + "7BF1": "lí", + "7BF2": "huì", + "7BF3": "bì", + "7BF4": "dí", + "7BF5": "cōng", + "7BF6": "yān", + "7BF7": "peng", + "7BF8": "cǎn", + "7BF9": "zhuàn", + "7BFA": "pí", + "7BFB": "piǎo", + "7BFC": "dōu", + "7BFD": "yù", + "7BFE": "miè", + "7BFF": "tuán", + "7C00": "zé", + "7C01": "shāi", + "7C02": "guì", + "7C03": "yí", + "7C04": "hù", + "7C05": "chǎn", + "7C06": "kòu", + "7C07": "cù", + "7C08": "píng", + "7C09": "zào", + "7C0A": "jī", + "7C0B": "guǐ", + "7C0C": "sù", + "7C0D": "lǒu", + "7C0E": "cè", + "7C0F": "lù", + "7C10": "niǎn", + "7C11": "suō", + "7C12": "cuàn", + "7C13": "diao", + "7C14": "suō", + "7C15": "lè", + "7C16": "duàn", + "7C17": "liang", + "7C18": "xiāo", + "7C19": "bó", + "7C1A": "mì", + "7C1B": "shāi", + "7C1C": "dàng", + "7C1D": "liáo", + "7C1E": "dān", + "7C1F": "diàn", + "7C20": "fǔ", + "7C21": "jiǎn", + "7C22": "mǐn", + "7C23": "kuì", + "7C24": "dài", + "7C25": "jiāo", + "7C26": "dēng", + "7C27": "huáng", + "7C28": "sǔn", + "7C29": "láo", + "7C2A": "zān", + "7C2B": "xiāo", + "7C2C": "lù", + "7C2D": "shì", + "7C2E": "zān", + "7C2F": "qi", + "7C30": "pái", + "7C31": "qi", + "7C32": "pái", + "7C33": "gǎn", + "7C34": "jù", + "7C35": "dù", + "7C36": "lù", + "7C37": "yán", + "7C38": "bǒ", + "7C39": "dāng", + "7C3A": "sài", + "7C3B": "zhuā", + "7C3C": "lóng", + "7C3D": "qiān", + "7C3E": "lián", + "7C3F": "bù", + "7C40": "zhòu", + "7C41": "lài", + "7C42": "shi", + "7C43": "lán", + "7C44": "kuì", + "7C45": "yú", + "7C46": "yuè", + "7C47": "háo", + "7C48": "zhēn", + "7C49": "tái", + "7C4A": "tì", + "7C4B": "niè", + "7C4C": "chóu", + "7C4D": "jí", + "7C4E": "yi", + "7C4F": "qi", + "7C50": "téng", + "7C51": "zhuàn", + "7C52": "zhòu", + "7C53": "fān", + "7C54": "sǒu", + "7C55": "zhòu", + "7C56": "qian", + "7C57": "zhuó", + "7C58": "téng", + "7C59": "lù", + "7C5A": "lú", + "7C5B": "jiǎn", + "7C5C": "tuò", + "7C5D": "yíng", + "7C5E": "yù", + "7C5F": "lài", + "7C60": "lóng", + "7C61": "qie", + "7C62": "lián", + "7C63": "lán", + "7C64": "qiān", + "7C65": "yuè", + "7C66": "zhōng", + "7C67": "qú", + "7C68": "lián", + "7C69": "biān", + "7C6A": "duàn", + "7C6B": "zuǎn", + "7C6C": "lí", + "7C6D": "sī", + "7C6E": "luó", + "7C6F": "yíng", + "7C70": "yuè", + "7C71": "zhuó", + "7C72": "yù", + "7C73": "mǐ", + "7C74": "dí", + "7C75": "fán", + "7C76": "shēn", + "7C77": "zhé", + "7C78": "shēn", + "7C79": "nǚ", + "7C7A": "hé", + "7C7B": "lèi", + "7C7C": "xiān", + "7C7D": "zǐ", + "7C7E": "ní", + "7C7F": "cùn", + "7C80": "zhang", + "7C81": "qiān", + "7C82": "zhai", + "7C83": "bǐ", + "7C84": "bǎn", + "7C85": "wù", + "7C86": "shā", + "7C87": "kāng", + "7C88": "rǒu", + "7C89": "fěn", + "7C8A": "bì", + "7C8B": "cuì", + "7C8C": "yin", + "7C8D": "zhé", + "7C8E": "chǐ", + "7C8F": "tai", + "7C90": "hu", + "7C91": "bā", + "7C92": "lì", + "7C93": "gān", + "7C94": "jù", + "7C95": "pò", + "7C96": "mò", + "7C97": "cū", + "7C98": "zhān", + "7C99": "zhòu", + "7C9A": "lí", + "7C9B": "sù", + "7C9C": "tiào", + "7C9D": "lì", + "7C9E": "xī", + "7C9F": "sù", + "7CA0": "hóng", + "7CA1": "tóng", + "7CA2": "zī", + "7CA3": "cè", + "7CA4": "yuè", + "7CA5": "zhōu", + "7CA6": "lín", + "7CA7": "zhuāng", + "7CA8": "bǎi", + "7CA9": "lao", + "7CAA": "fèn", + "7CAB": "ér", + "7CAC": "qū", + "7CAD": "he", + "7CAE": "liáng", + "7CAF": "xiàn", + "7CB0": "fú", + "7CB1": "liáng", + "7CB2": "càn", + "7CB3": "jīng", + "7CB4": "lǐ", + "7CB5": "yuè", + "7CB6": "lù", + "7CB7": "jú", + "7CB8": "qí", + "7CB9": "cuì", + "7CBA": "bài", + "7CBB": "zhāng", + "7CBC": "lín", + "7CBD": "zòng", + "7CBE": "jīng", + "7CBF": "guǒ", + "7CC0": "hua", + "7CC1": "sǎn", + "7CC2": "sǎn", + "7CC3": "táng", + "7CC4": "biǎn", + "7CC5": "róu", + "7CC6": "miàn", + "7CC7": "hóu", + "7CC8": "xǔ", + "7CC9": "zòng", + "7CCA": "hu", + "7CCB": "jiàn", + "7CCC": "zān", + "7CCD": "cí", + "7CCE": "lí", + "7CCF": "xiè", + "7CD0": "fū", + "7CD1": "nuò", + "7CD2": "bèi", + "7CD3": "gǔ", + "7CD4": "xiǔ", + "7CD5": "gāo", + "7CD6": "táng", + "7CD7": "qiǔ", + "7CD8": "jia", + "7CD9": "cāo", + "7CDA": "zhuāng", + "7CDB": "táng", + "7CDC": "mí", + "7CDD": "sǎn", + "7CDE": "fèn", + "7CDF": "zāo", + "7CE0": "kāng", + "7CE1": "jiàng", + "7CE2": "mó", + "7CE3": "sǎn", + "7CE4": "sǎn", + "7CE5": "nuò", + "7CE6": "xī", + "7CE7": "liáng", + "7CE8": "jiàng", + "7CE9": "kuài", + "7CEA": "bò", + "7CEB": "huán", + "7CEC": "shu", + "7CED": "zòng", + "7CEE": "xiàn", + "7CEF": "nuò", + "7CF0": "tuán", + "7CF1": "niè", + "7CF2": "lì", + "7CF3": "zuò", + "7CF4": "dí", + "7CF5": "niè", + "7CF6": "tiào", + "7CF7": "làn", + "7CF8": "mì", + "7CF9": "sī", + "7CFA": "jiū", + "7CFB": "xì", + "7CFC": "gōng", + "7CFD": "zhěng", + "7CFE": "jiū", + "7CFF": "yòu", + "7D00": "jì", + "7D01": "chà", + "7D02": "zhòu", + "7D03": "xún", + "7D04": "yuē", + "7D05": "hóng", + "7D06": "yū", + "7D07": "hé", + "7D08": "wán", + "7D09": "rèn", + "7D0A": "wěn", + "7D0B": "wén", + "7D0C": "qiú", + "7D0D": "nà", + "7D0E": "zī", + "7D0F": "tǒu", + "7D10": "niǔ", + "7D11": "fóu", + "7D12": "jì", + "7D13": "shū", + "7D14": "chún", + "7D15": "pī", + "7D16": "zhèn", + "7D17": "shā", + "7D18": "hóng", + "7D19": "zhǐ", + "7D1A": "jí", + "7D1B": "fēn", + "7D1C": "yún", + "7D1D": "rèn", + "7D1E": "dǎn", + "7D1F": "jīn", + "7D20": "sù", + "7D21": "fǎng", + "7D22": "suǒ", + "7D23": "cuì", + "7D24": "jiǔ", + "7D25": "zā", + "7D26": "ba", + "7D27": "jǐn", + "7D28": "fū", + "7D29": "zhì", + "7D2A": "cǐ", + "7D2B": "zǐ", + "7D2C": "chóu", + "7D2D": "hóng", + "7D2E": "zā", + "7D2F": "lèi", + "7D30": "xì", + "7D31": "fú", + "7D32": "xiè", + "7D33": "shēn", + "7D34": "bō", + "7D35": "zhù", + "7D36": "qū", + "7D37": "líng", + "7D38": "zhù", + "7D39": "shào", + "7D3A": "gàn", + "7D3B": "yǎng", + "7D3C": "fú", + "7D3D": "tuó", + "7D3E": "zhěn", + "7D3F": "dài", + "7D40": "chù", + "7D41": "shī", + "7D42": "zhōng", + "7D43": "xián", + "7D44": "zǔ", + "7D45": "jiōng", + "7D46": "bàn", + "7D47": "qú", + "7D48": "mò", + "7D49": "shù", + "7D4A": "zuì", + "7D4B": "kuang", + "7D4C": "jīng", + "7D4D": "rèn", + "7D4E": "háng", + "7D4F": "xiè", + "7D50": "jié", + "7D51": "zhū", + "7D52": "chóu", + "7D53": "guà", + "7D54": "bǎi", + "7D55": "jué", + "7D56": "kuàng", + "7D57": "hú", + "7D58": "cì", + "7D59": "huán", + "7D5A": "gēng", + "7D5B": "tāo", + "7D5C": "jié", + "7D5D": "kù", + "7D5E": "jiǎo", + "7D5F": "quán", + "7D60": "gǎi", + "7D61": "luò", + "7D62": "xuàn", + "7D63": "bēng", + "7D64": "xiàn", + "7D65": "fú", + "7D66": "gěi", + "7D67": "dòng", + "7D68": "róng", + "7D69": "tiào", + "7D6A": "yīn", + "7D6B": "lěi", + "7D6C": "xiè", + "7D6D": "juàn", + "7D6E": "xù", + "7D6F": "gāi", + "7D70": "dié", + "7D71": "tǒng", + "7D72": "sī", + "7D73": "jiàng", + "7D74": "xiáng", + "7D75": "huì", + "7D76": "jué", + "7D77": "zhí", + "7D78": "jiǎn", + "7D79": "juàn", + "7D7A": "chī", + "7D7B": "miǎn", + "7D7C": "zhěn", + "7D7D": "lǚ", + "7D7E": "chéng", + "7D7F": "qiú", + "7D80": "shū", + "7D81": "bǎng", + "7D82": "tǒng", + "7D83": "xiāo", + "7D84": "huán", + "7D85": "qīn", + "7D86": "gěng", + "7D87": "xiǔ", + "7D88": "tí", + "7D89": "tòu", + "7D8A": "xié", + "7D8B": "hóng", + "7D8C": "xì", + "7D8D": "fú", + "7D8E": "tīng", + "7D8F": "suī", + "7D90": "duì", + "7D91": "kǔn", + "7D92": "fū", + "7D93": "jīng", + "7D94": "hù", + "7D95": "zhī", + "7D96": "yán", + "7D97": "jiǒng", + "7D98": "féng", + "7D99": "jì", + "7D9A": "xu", + "7D9B": "ren", + "7D9C": "zōng", + "7D9D": "chēn", + "7D9E": "duǒ", + "7D9F": "lì", + "7DA0": "lǜ", + "7DA1": "liáng", + "7DA2": "chóu", + "7DA3": "quǎn", + "7DA4": "shào", + "7DA5": "qí", + "7DA6": "qí", + "7DA7": "zhǔn", + "7DA8": "qí", + "7DA9": "wǎn", + "7DAA": "qiàn", + "7DAB": "xiàn", + "7DAC": "shòu", + "7DAD": "wéi", + "7DAE": "qǐ", + "7DAF": "táo", + "7DB0": "wǎn", + "7DB1": "gāng", + "7DB2": "wǎng", + "7DB3": "bēng", + "7DB4": "zhui", + "7DB5": "cǎi", + "7DB6": "guǒ", + "7DB7": "cuì", + "7DB8": "lún", + "7DB9": "liǔ", + "7DBA": "qǐ", + "7DBB": "zhàn", + "7DBC": "bì", + "7DBD": "chuò", + "7DBE": "líng", + "7DBF": "mián", + "7DC0": "qī", + "7DC1": "qiè", + "7DC2": "tián", + "7DC3": "zōng", + "7DC4": "gǔn", + "7DC5": "zōu", + "7DC6": "xī", + "7DC7": "zī", + "7DC8": "xìng", + "7DC9": "liǎng", + "7DCA": "jǐn", + "7DCB": "fēi", + "7DCC": "ruí", + "7DCD": "mín", + "7DCE": "yù", + "7DCF": "zǒng", + "7DD0": "fán", + "7DD1": "lǜ", + "7DD2": "xù", + "7DD3": "yīng", + "7DD4": "shàng", + "7DD5": "qi", + "7DD6": "xù", + "7DD7": "xiāng", + "7DD8": "jiān", + "7DD9": "kè", + "7DDA": "xiàn", + "7DDB": "ruǎn", + "7DDC": "mián", + "7DDD": "jī", + "7DDE": "duàn", + "7DDF": "chóng", + "7DE0": "dì", + "7DE1": "mín", + "7DE2": "miáo", + "7DE3": "yuán", + "7DE4": "xiè", + "7DE5": "bǎo", + "7DE6": "sī", + "7DE7": "qiū", + "7DE8": "biān", + "7DE9": "huǎn", + "7DEA": "gēng", + "7DEB": "cōng", + "7DEC": "miǎn", + "7DED": "wèi", + "7DEE": "fù", + "7DEF": "wěi", + "7DF0": "tóu", + "7DF1": "gōu", + "7DF2": "miǎo", + "7DF3": "xié", + "7DF4": "liàn", + "7DF5": "zōng", + "7DF6": "biàn", + "7DF7": "yùn", + "7DF8": "yīn", + "7DF9": "tí", + "7DFA": "guā", + "7DFB": "zhì", + "7DFC": "yùn", + "7DFD": "chēng", + "7DFE": "chán", + "7DFF": "dài", + "7E00": "xiá", + "7E01": "yuán", + "7E02": "zǒng", + "7E03": "xū", + "7E04": "ying", + "7E05": "wei", + "7E06": "gēng", + "7E07": "xuān", + "7E08": "yíng", + "7E09": "jìn", + "7E0A": "yì", + "7E0B": "zhuì", + "7E0C": "nì", + "7E0D": "bāng", + "7E0E": "gǔ", + "7E0F": "pán", + "7E10": "zhòu", + "7E11": "jiān", + "7E12": "cī", + "7E13": "quán", + "7E14": "shuǎng", + "7E15": "yūn", + "7E16": "xiá", + "7E17": "cuī", + "7E18": "xī", + "7E19": "róng", + "7E1A": "tāo", + "7E1B": "fù", + "7E1C": "yún", + "7E1D": "chēn", + "7E1E": "gǎo", + "7E1F": "rù", + "7E20": "hú", + "7E21": "zài", + "7E22": "téng", + "7E23": "xiàn", + "7E24": "sù", + "7E25": "zhěn", + "7E26": "zòng", + "7E27": "tāo", + "7E28": "huang", + "7E29": "cài", + "7E2A": "bì", + "7E2B": "fèng", + "7E2C": "cù", + "7E2D": "lí", + "7E2E": "suō", + "7E2F": "yǎn", + "7E30": "xǐ", + "7E31": "zòng", + "7E32": "léi", + "7E33": "juàn", + "7E34": "qiàn", + "7E35": "màn", + "7E36": "zhí", + "7E37": "lǚ", + "7E38": "mù", + "7E39": "piǎo", + "7E3A": "lián", + "7E3B": "mí", + "7E3C": "xuàn", + "7E3D": "zǒng", + "7E3E": "jī", + "7E3F": "shān", + "7E40": "suì", + "7E41": "fán", + "7E42": "lǜ", + "7E43": "běng", + "7E44": "yī", + "7E45": "sāo", + "7E46": "móu", + "7E47": "yáo", + "7E48": "qiǎng", + "7E49": "hún", + "7E4A": "xian", + "7E4B": "jì", + "7E4C": "sha", + "7E4D": "xiù", + "7E4E": "rán", + "7E4F": "xuàn", + "7E50": "suì", + "7E51": "qiāo", + "7E52": "zēng", + "7E53": "zuǒ", + "7E54": "zhī", + "7E55": "shàn", + "7E56": "sǎn", + "7E57": "lín", + "7E58": "yù", + "7E59": "fān", + "7E5A": "liáo", + "7E5B": "chuò", + "7E5C": "zūn", + "7E5D": "jiàn", + "7E5E": "rào", + "7E5F": "chǎn", + "7E60": "ruǐ", + "7E61": "xiù", + "7E62": "huì", + "7E63": "huà", + "7E64": "zuǎn", + "7E65": "xī", + "7E66": "qiǎng", + "7E67": "yun", + "7E68": "da", + "7E69": "shéng", + "7E6A": "huì", + "7E6B": "xì", + "7E6C": "sè", + "7E6D": "jiǎn", + "7E6E": "jiāng", + "7E6F": "huán", + "7E70": "zǎo", + "7E71": "cōng", + "7E72": "xiè", + "7E73": "jiǎo", + "7E74": "bì", + "7E75": "dàn", + "7E76": "yì", + "7E77": "nǒng", + "7E78": "suì", + "7E79": "yì", + "7E7A": "shǎi", + "7E7B": "xū", + "7E7C": "jì", + "7E7D": "bīn", + "7E7E": "qiǎn", + "7E7F": "lán", + "7E80": "pú", + "7E81": "xūn", + "7E82": "zuǎn", + "7E83": "qí", + "7E84": "péng", + "7E85": "yào", + "7E86": "mò", + "7E87": "lèi", + "7E88": "xié", + "7E89": "zuǎn", + "7E8A": "kuàng", + "7E8B": "yōu", + "7E8C": "xù", + "7E8D": "léi", + "7E8E": "xiān", + "7E8F": "chán", + "7E90": "jiao", + "7E91": "lú", + "7E92": "chán", + "7E93": "yīng", + "7E94": "cái", + "7E95": "rǎng", + "7E96": "xiān", + "7E97": "zuī", + "7E98": "zuǎn", + "7E99": "luò", + "7E9A": "lí", + "7E9B": "dào", + "7E9C": "lǎn", + "7E9D": "léi", + "7E9E": "liàn", + "7E9F": "sī", + "7EA0": "jiū", + "7EA1": "yū", + "7EA2": "hóng", + "7EA3": "zhòu", + "7EA4": "xiān", + "7EA5": "gē", + "7EA6": "yuē", + "7EA7": "jí", + "7EA8": "wán", + "7EA9": "kuàng", + "7EAA": "jì", + "7EAB": "rèn", + "7EAC": "wěi", + "7EAD": "yún", + "7EAE": "hóng", + "7EAF": "chún", + "7EB0": "pī", + "7EB1": "shā", + "7EB2": "gāng", + "7EB3": "nà", + "7EB4": "rèn", + "7EB5": "zòng", + "7EB6": "lún", + "7EB7": "fēn", + "7EB8": "zhǐ", + "7EB9": "wén", + "7EBA": "fǎng", + "7EBB": "zhù", + "7EBC": "zhèn", + "7EBD": "niǔ", + "7EBE": "shū", + "7EBF": "xiàn", + "7EC0": "gàn", + "7EC1": "xiè", + "7EC2": "fú", + "7EC3": "liàn", + "7EC4": "zǔ", + "7EC5": "shēn", + "7EC6": "xì", + "7EC7": "zhī", + "7EC8": "zhōng", + "7EC9": "zhòu", + "7ECA": "bàn", + "7ECB": "fú", + "7ECC": "chù", + "7ECD": "shào", + "7ECE": "yì", + "7ECF": "jīng", + "7ED0": "dài", + "7ED1": "bǎng", + "7ED2": "róng", + "7ED3": "jié", + "7ED4": "kù", + "7ED5": "rào", + "7ED6": "dié", + "7ED7": "háng", + "7ED8": "huì", + "7ED9": "gěi", + "7EDA": "xuàn", + "7EDB": "jiàng", + "7EDC": "luò", + "7EDD": "jué", + "7EDE": "jiǎo", + "7EDF": "tǒng", + "7EE0": "gěng", + "7EE1": "xiāo", + "7EE2": "juàn", + "7EE3": "xiù", + "7EE4": "xì", + "7EE5": "suí", + "7EE6": "tāo", + "7EE7": "jì", + "7EE8": "tí", + "7EE9": "jī", + "7EEA": "xù", + "7EEB": "líng", + "7EEC": "yīng", + "7EED": "xù", + "7EEE": "qǐ", + "7EEF": "fēi", + "7EF0": "chuò", + "7EF1": "shàng", + "7EF2": "gǔn", + "7EF3": "shéng", + "7EF4": "wéi", + "7EF5": "mián", + "7EF6": "shòu", + "7EF7": "běng", + "7EF8": "chóu", + "7EF9": "táo", + "7EFA": "liǔ", + "7EFB": "quǎn", + "7EFC": "zōng", + "7EFD": "zhàn", + "7EFE": "wǎn", + "7EFF": "lǜ", + "7F00": "zhui", + "7F01": "zī", + "7F02": "kè", + "7F03": "xiāng", + "7F04": "jiān", + "7F05": "miǎn", + "7F06": "lǎn", + "7F07": "tí", + "7F08": "miǎo", + "7F09": "jī", + "7F0A": "yūn", + "7F0B": "huì", + "7F0C": "sī", + "7F0D": "duǒ", + "7F0E": "duàn", + "7F0F": "biàn", + "7F10": "xiàn", + "7F11": "gōu", + "7F12": "zhuì", + "7F13": "huǎn", + "7F14": "dì", + "7F15": "lǚ", + "7F16": "biān", + "7F17": "mín", + "7F18": "yuán", + "7F19": "jìn", + "7F1A": "fù", + "7F1B": "rù", + "7F1C": "zhěn", + "7F1D": "fèng", + "7F1E": "cuī", + "7F1F": "gǎo", + "7F20": "chán", + "7F21": "lí", + "7F22": "yì", + "7F23": "jiān", + "7F24": "bīn", + "7F25": "piāo", + "7F26": "màn", + "7F27": "léi", + "7F28": "yīng", + "7F29": "suō", + "7F2A": "móu", + "7F2B": "sāo", + "7F2C": "xié", + "7F2D": "liáo", + "7F2E": "shàn", + "7F2F": "zēng", + "7F30": "jiāng", + "7F31": "qiǎn", + "7F32": "qiāo", + "7F33": "huán", + "7F34": "jiǎo", + "7F35": "zuǎn", + "7F36": "fǒu", + "7F37": "xiè", + "7F38": "gāng", + "7F39": "fǒu", + "7F3A": "quē", + "7F3B": "fǒu", + "7F3C": "qi", + "7F3D": "bō", + "7F3E": "píng", + "7F3F": "xiàng", + "7F40": "zhao", + "7F41": "gāng", + "7F42": "yīng", + "7F43": "yīng", + "7F44": "qìng", + "7F45": "xià", + "7F46": "guàn", + "7F47": "zūn", + "7F48": "tán", + "7F49": "cang", + "7F4A": "qì", + "7F4B": "wèng", + "7F4C": "yīng", + "7F4D": "léi", + "7F4E": "tán", + "7F4F": "lú", + "7F50": "guàn", + "7F51": "wǎng", + "7F52": "wǎng", + "7F53": "gāng", + "7F54": "wǎng", + "7F55": "hǎn", + "7F56": "luó", + "7F57": "luō", + "7F58": "fú", + "7F59": "mí", + "7F5A": "fá", + "7F5B": "gū", + "7F5C": "zhǔ", + "7F5D": "jū", + "7F5E": "máo", + "7F5F": "gǔ", + "7F60": "mín", + "7F61": "gāng", + "7F62": "ba", + "7F63": "guà", + "7F64": "tí", + "7F65": "juàn", + "7F66": "fú", + "7F67": "shèn", + "7F68": "yǎn", + "7F69": "zhào", + "7F6A": "zuì", + "7F6B": "guà", + "7F6C": "zhuó", + "7F6D": "yù", + "7F6E": "zhì", + "7F6F": "ǎn", + "7F70": "fá", + "7F71": "lǎn", + "7F72": "shǔ", + "7F73": "sī", + "7F74": "pí", + "7F75": "mà", + "7F76": "liǔ", + "7F77": "ba", + "7F78": "fá", + "7F79": "lí", + "7F7A": "cháo", + "7F7B": "wèi", + "7F7C": "bì", + "7F7D": "jì", + "7F7E": "zēng", + "7F7F": "chōng", + "7F80": "liǔ", + "7F81": "jī", + "7F82": "juàn", + "7F83": "mì", + "7F84": "zhào", + "7F85": "luó", + "7F86": "pí", + "7F87": "jī", + "7F88": "jī", + "7F89": "luán", + "7F8A": "yáng", + "7F8B": "mǐ", + "7F8C": "qiāng", + "7F8D": "dá", + "7F8E": "měi", + "7F8F": "yáng", + "7F90": "yǒu", + "7F91": "yǒu", + "7F92": "fén", + "7F93": "bā", + "7F94": "gāo", + "7F95": "yàng", + "7F96": "gǔ", + "7F97": "qiāng", + "7F98": "zāng", + "7F99": "gāo", + "7F9A": "líng", + "7F9B": "yì", + "7F9C": "zhù", + "7F9D": "dī", + "7F9E": "xiū", + "7F9F": "qiǎng", + "7FA0": "yí", + "7FA1": "xiàn", + "7FA2": "róng", + "7FA3": "qún", + "7FA4": "qún", + "7FA5": "qiǎng", + "7FA6": "huán", + "7FA7": "suō", + "7FA8": "xiàn", + "7FA9": "yì", + "7FAA": "yang", + "7FAB": "qiāng", + "7FAC": "qián", + "7FAD": "yú", + "7FAE": "gēng", + "7FAF": "jié", + "7FB0": "tāng", + "7FB1": "yuán", + "7FB2": "xī", + "7FB3": "fán", + "7FB4": "shān", + "7FB5": "fén", + "7FB6": "shān", + "7FB7": "liǎn", + "7FB8": "léi", + "7FB9": "gēng", + "7FBA": "nóu", + "7FBB": "qiàng", + "7FBC": "chàn", + "7FBD": "yǔ", + "7FBE": "gòng", + "7FBF": "yì", + "7FC0": "chōng", + "7FC1": "wēng", + "7FC2": "fēn", + "7FC3": "hóng", + "7FC4": "chì", + "7FC5": "chì", + "7FC6": "cuì", + "7FC7": "fú", + "7FC8": "xiá", + "7FC9": "běn", + "7FCA": "yì", + "7FCB": "lā", + "7FCC": "yì", + "7FCD": "pī", + "7FCE": "líng", + "7FCF": "liù", + "7FD0": "zhì", + "7FD1": "qú", + "7FD2": "xí", + "7FD3": "xié", + "7FD4": "xiáng", + "7FD5": "xī", + "7FD6": "xì", + "7FD7": "ké", + "7FD8": "qiào", + "7FD9": "huì", + "7FDA": "huī", + "7FDB": "xiāo", + "7FDC": "shà", + "7FDD": "hóng", + "7FDE": "jiāng", + "7FDF": "dí", + "7FE0": "cuì", + "7FE1": "fěi", + "7FE2": "dào", + "7FE3": "shà", + "7FE4": "chì", + "7FE5": "zhù", + "7FE6": "jiǎn", + "7FE7": "xuān", + "7FE8": "chì", + "7FE9": "piān", + "7FEA": "zōng", + "7FEB": "wán", + "7FEC": "huī", + "7FED": "hóu", + "7FEE": "hé", + "7FEF": "hè", + "7FF0": "hàn", + "7FF1": "áo", + "7FF2": "piāo", + "7FF3": "yì", + "7FF4": "lián", + "7FF5": "hóu", + "7FF6": "ao", + "7FF7": "lín", + "7FF8": "pěn", + "7FF9": "qiào", + "7FFA": "áo", + "7FFB": "fān", + "7FFC": "yì", + "7FFD": "huì", + "7FFE": "xuān", + "7FFF": "dào", + "8000": "yào", + "8001": "lǎo", + "8002": "lǎo", + "8003": "kǎo", + "8004": "mào", + "8005": "zhě", + "8006": "qí", + "8007": "gǒu", + "8008": "gǒu", + "8009": "gǒu", + "800A": "diè", + "800B": "dié", + "800C": "ér", + "800D": "shuǎ", + "800E": "ruǎn", + "800F": "nài", + "8010": "nài", + "8011": "duān", + "8012": "lěi", + "8013": "tīng", + "8014": "zǐ", + "8015": "gēng", + "8016": "chào", + "8017": "hào", + "8018": "yún", + "8019": "bà", + "801A": "pī", + "801B": "yí", + "801C": "sì", + "801D": "qù", + "801E": "jiā", + "801F": "jù", + "8020": "huō", + "8021": "chú", + "8022": "lào", + "8023": "lǔn", + "8024": "jí", + "8025": "tāng", + "8026": "ǒu", + "8027": "lóu", + "8028": "nòu", + "8029": "jiǎng", + "802A": "pǎng", + "802B": "zhá", + "802C": "lóu", + "802D": "jī", + "802E": "lào", + "802F": "huò", + "8030": "yōu", + "8031": "mò", + "8032": "huái", + "8033": "ěr", + "8034": "yì", + "8035": "dīng", + "8036": "yé", + "8037": "dā", + "8038": "sǒng", + "8039": "qín", + "803A": "yún", + "803B": "chǐ", + "803C": "dān", + "803D": "dān", + "803E": "hóng", + "803F": "gěng", + "8040": "zhí", + "8041": "pàn", + "8042": "niè", + "8043": "dān", + "8044": "zhěn", + "8045": "chè", + "8046": "líng", + "8047": "zhēng", + "8048": "yǒu", + "8049": "wà", + "804A": "liáo", + "804B": "lóng", + "804C": "zhí", + "804D": "níng", + "804E": "tiāo", + "804F": "ér", + "8050": "yà", + "8051": "tiē", + "8052": "guā", + "8053": "xu", + "8054": "lián", + "8055": "hào", + "8056": "shèng", + "8057": "liè", + "8058": "pìn", + "8059": "jīng", + "805A": "jù", + "805B": "bǐ", + "805C": "dǐ", + "805D": "guó", + "805E": "wén", + "805F": "xù", + "8060": "pīng", + "8061": "cōng", + "8062": "ding", + "8063": "ní", + "8064": "tíng", + "8065": "jǔ", + "8066": "cōng", + "8067": "kuī", + "8068": "lian", + "8069": "kuì", + "806A": "cōng", + "806B": "lián", + "806C": "wěng", + "806D": "kuì", + "806E": "lián", + "806F": "lián", + "8070": "cōng", + "8071": "áo", + "8072": "shēng", + "8073": "sǒng", + "8074": "tīng", + "8075": "kuì", + "8076": "niè", + "8077": "zhí", + "8078": "dān", + "8079": "níng", + "807A": "qié", + "807B": "nǐ", + "807C": "tīng", + "807D": "tīng", + "807E": "lóng", + "807F": "yù", + "8080": "yù", + "8081": "zhào", + "8082": "sì", + "8083": "sù", + "8084": "yì", + "8085": "sù", + "8086": "sì", + "8087": "zhào", + "8088": "zhào", + "8089": "ròu", + "808A": "yì", + "808B": "lē", + "808C": "jī", + "808D": "qiú", + "808E": "kěn", + "808F": "cào", + "8090": "gē", + "8091": "bó", + "8092": "huàn", + "8093": "huāng", + "8094": "yǐ", + "8095": "rèn", + "8096": "xiào", + "8097": "rǔ", + "8098": "zhǒu", + "8099": "yuàn", + "809A": "dù", + "809B": "gāng", + "809C": "róng", + "809D": "gān", + "809E": "chā", + "809F": "wò", + "80A0": "cháng", + "80A1": "gǔ", + "80A2": "zhī", + "80A3": "hán", + "80A4": "fū", + "80A5": "féi", + "80A6": "fén", + "80A7": "pēi", + "80A8": "pàng", + "80A9": "jiān", + "80AA": "fáng", + "80AB": "zhūn", + "80AC": "yóu", + "80AD": "nà", + "80AE": "āng", + "80AF": "kěn", + "80B0": "rán", + "80B1": "gōng", + "80B2": "yù", + "80B3": "wěn", + "80B4": "yáo", + "80B5": "qí", + "80B6": "pí", + "80B7": "qiǎn", + "80B8": "xī", + "80B9": "xī", + "80BA": "fèi", + "80BB": "kěn", + "80BC": "jǐng", + "80BD": "tài", + "80BE": "shèn", + "80BF": "zhǒng", + "80C0": "zhàng", + "80C1": "xié", + "80C2": "shèn", + "80C3": "wèi", + "80C4": "zhòu", + "80C5": "dié", + "80C6": "dǎn", + "80C7": "fèi", + "80C8": "bá", + "80C9": "bó", + "80CA": "qú", + "80CB": "tián", + "80CC": "bèi", + "80CD": "guā", + "80CE": "tāi", + "80CF": "zǐ", + "80D0": "kū", + "80D1": "zhī", + "80D2": "nì", + "80D3": "píng", + "80D4": "zì", + "80D5": "fǔ", + "80D6": "pàng", + "80D7": "zhēn", + "80D8": "xián", + "80D9": "zuò", + "80DA": "pēi", + "80DB": "jiǎ", + "80DC": "shèng", + "80DD": "zhī", + "80DE": "bāo", + "80DF": "mǔ", + "80E0": "qū", + "80E1": "hú", + "80E2": "kē", + "80E3": "chǐ", + "80E4": "yìn", + "80E5": "xū", + "80E6": "yāng", + "80E7": "lóng", + "80E8": "dòng", + "80E9": "kǎ", + "80EA": "lú", + "80EB": "jìng", + "80EC": "nǔ", + "80ED": "yān", + "80EE": "pāng", + "80EF": "kuà", + "80F0": "yí", + "80F1": "guāng", + "80F2": "hǎi", + "80F3": "gē", + "80F4": "dòng", + "80F5": "chī", + "80F6": "jiāo", + "80F7": "xiōng", + "80F8": "xiōng", + "80F9": "ér", + "80FA": "àn", + "80FB": "héng", + "80FC": "pián", + "80FD": "néng", + "80FE": "zì", + "80FF": "guī", + "8100": "chéng", + "8101": "tiǎo", + "8102": "zhī", + "8103": "cuì", + "8104": "méi", + "8105": "xié", + "8106": "cuì", + "8107": "xié", + "8108": "mài", + "8109": "mài", + "810A": "jí", + "810B": "xie", + "810C": "nin", + "810D": "kuài", + "810E": "sà", + "810F": "zàng", + "8110": "qí", + "8111": "nǎo", + "8112": "mǐ", + "8113": "nóng", + "8114": "luán", + "8115": "wàn", + "8116": "bó", + "8117": "wěn", + "8118": "wǎn", + "8119": "xiū", + "811A": "jiǎo", + "811B": "jìng", + "811C": "yǒu", + "811D": "hēng", + "811E": "cuǒ", + "811F": "liè", + "8120": "shān", + "8121": "tǐng", + "8122": "méi", + "8123": "chún", + "8124": "shèn", + "8125": "qiǎn", + "8126": "de", + "8127": "juān", + "8128": "cù", + "8129": "xiū", + "812A": "xìn", + "812B": "tuō", + "812C": "pāo", + "812D": "chéng", + "812E": "něi", + "812F": "pú", + "8130": "dòu", + "8131": "tuō", + "8132": "niào", + "8133": "nao", + "8134": "pǐ", + "8135": "gǔ", + "8136": "luó", + "8137": "lì", + "8138": "liǎn", + "8139": "zhàng", + "813A": "cuì", + "813B": "jiē", + "813C": "liǎng", + "813D": "shuí", + "813E": "pí", + "813F": "biāo", + "8140": "lún", + "8141": "pián", + "8142": "lěi", + "8143": "kuì", + "8144": "chuí", + "8145": "dàn", + "8146": "tiǎn", + "8147": "něi", + "8148": "jīng", + "8149": "nái", + "814A": "là", + "814B": "yè", + "814C": "yān", + "814D": "rèn", + "814E": "shèn", + "814F": "chuò", + "8150": "fǔ", + "8151": "fǔ", + "8152": "jū", + "8153": "féi", + "8154": "qiāng", + "8155": "wàn", + "8156": "dòng", + "8157": "pí", + "8158": "guó", + "8159": "zōng", + "815A": "dìng", + "815B": "wò", + "815C": "měi", + "815D": "ní", + "815E": "zhuàn", + "815F": "chì", + "8160": "còu", + "8161": "luó", + "8162": "ǒu", + "8163": "dì", + "8164": "ān", + "8165": "xīng", + "8166": "nǎo", + "8167": "shù", + "8168": "shuàn", + "8169": "nǎn", + "816A": "yùn", + "816B": "zhǒng", + "816C": "ròu", + "816D": "è", + "816E": "sāi", + "816F": "tú", + "8170": "yāo", + "8171": "jiàn", + "8172": "wěi", + "8173": "jiǎo", + "8174": "yú", + "8175": "jiā", + "8176": "duàn", + "8177": "bì", + "8178": "cháng", + "8179": "fù", + "817A": "xiàn", + "817B": "nì", + "817C": "miǎn", + "817D": "wà", + "817E": "téng", + "817F": "tuǐ", + "8180": "bǎng", + "8181": "qiǎn", + "8182": "lǚ", + "8183": "wà", + "8184": "sòu", + "8185": "táng", + "8186": "sù", + "8187": "zhuì", + "8188": "gé", + "8189": "yì", + "818A": "bo", + "818B": "liáo", + "818C": "jí", + "818D": "pí", + "818E": "xié", + "818F": "gāo", + "8190": "lǚ", + "8191": "bìn", + "8192": "óu", + "8193": "cháng", + "8194": "lù", + "8195": "guó", + "8196": "pāng", + "8197": "chuái", + "8198": "biāo", + "8199": "jiǎng", + "819A": "fū", + "819B": "táng", + "819C": "mó", + "819D": "xī", + "819E": "zhuān", + "819F": "lǜ", + "81A0": "jiāo", + "81A1": "yìng", + "81A2": "lǘ", + "81A3": "zhì", + "81A4": "xue", + "81A5": "chūn", + "81A6": "lìn", + "81A7": "tóng", + "81A8": "péng", + "81A9": "nì", + "81AA": "chuài", + "81AB": "liáo", + "81AC": "cuì", + "81AD": "guī", + "81AE": "xiāo", + "81AF": "tēng", + "81B0": "fán", + "81B1": "zhí", + "81B2": "jiāo", + "81B3": "shàn", + "81B4": "hū", + "81B5": "cuì", + "81B6": "rùn", + "81B7": "xiāng", + "81B8": "suǐ", + "81B9": "fèn", + "81BA": "yīng", + "81BB": "shān", + "81BC": "zhuā", + "81BD": "dǎn", + "81BE": "kuài", + "81BF": "nóng", + "81C0": "tún", + "81C1": "lián", + "81C2": "bì", + "81C3": "yōng", + "81C4": "jué", + "81C5": "chù", + "81C6": "yì", + "81C7": "juǎn", + "81C8": "là", + "81C9": "liǎn", + "81CA": "sāo", + "81CB": "tún", + "81CC": "gǔ", + "81CD": "qí", + "81CE": "cuì", + "81CF": "bìn", + "81D0": "xūn", + "81D1": "nào", + "81D2": "wò", + "81D3": "zàng", + "81D4": "xiàn", + "81D5": "biāo", + "81D6": "xìng", + "81D7": "kuān", + "81D8": "là", + "81D9": "yān", + "81DA": "lú", + "81DB": "huò", + "81DC": "zā", + "81DD": "luǒ", + "81DE": "qú", + "81DF": "zàng", + "81E0": "luán", + "81E1": "ní", + "81E2": "zā", + "81E3": "chén", + "81E4": "qiān", + "81E5": "wò", + "81E6": "guàng", + "81E7": "zāng", + "81E8": "lín", + "81E9": "guǎng", + "81EA": "zì", + "81EB": "jiǎo", + "81EC": "niè", + "81ED": "chòu", + "81EE": "jì", + "81EF": "gāo", + "81F0": "chòu", + "81F1": "mián", + "81F2": "niè", + "81F3": "zhì", + "81F4": "zhì", + "81F5": "gé", + "81F6": "jiàn", + "81F7": "dié", + "81F8": "zhī", + "81F9": "xiū", + "81FA": "tái", + "81FB": "zhēn", + "81FC": "jiù", + "81FD": "xiàn", + "81FE": "yú", + "81FF": "chā", + "8200": "yǎo", + "8201": "yú", + "8202": "chōng", + "8203": "xì", + "8204": "xì", + "8205": "jiù", + "8206": "yú", + "8207": "yǔ", + "8208": "xìng", + "8209": "jǔ", + "820A": "jiù", + "820B": "xìn", + "820C": "shé", + "820D": "shě", + "820E": "she", + "820F": "jiǔ", + "8210": "shì", + "8211": "tān", + "8212": "shū", + "8213": "shì", + "8214": "tiǎn", + "8215": "tàn", + "8216": "pù", + "8217": "pù", + "8218": "guǎn", + "8219": "huà", + "821A": "tiàn", + "821B": "chuǎn", + "821C": "shùn", + "821D": "xiá", + "821E": "wǔ", + "821F": "zhōu", + "8220": "dāo", + "8221": "chuán", + "8222": "shān", + "8223": "yǐ", + "8224": "fan", + "8225": "pā", + "8226": "tài", + "8227": "fán", + "8228": "bǎn", + "8229": "chuán", + "822A": "háng", + "822B": "fǎng", + "822C": "bān", + "822D": "bǐ", + "822E": "lu", + "822F": "zhōng", + "8230": "jiàn", + "8231": "cāng", + "8232": "líng", + "8233": "zhú", + "8234": "zé", + "8235": "duò", + "8236": "bó", + "8237": "xián", + "8238": "gě", + "8239": "chuán", + "823A": "xiá", + "823B": "lú", + "823C": "qióng", + "823D": "páng", + "823E": "xī", + "823F": "kua", + "8240": "fú", + "8241": "zào", + "8242": "féng", + "8243": "lí", + "8244": "shāo", + "8245": "yú", + "8246": "láng", + "8247": "tǐng", + "8248": "yù", + "8249": "wěi", + "824A": "bó", + "824B": "měng", + "824C": "niàn", + "824D": "jū", + "824E": "huáng", + "824F": "shǒu", + "8250": "kè", + "8251": "biàn", + "8252": "mù", + "8253": "dié", + "8254": "dou", + "8255": "bàng", + "8256": "chā", + "8257": "yì", + "8258": "sōu", + "8259": "cāng", + "825A": "cáo", + "825B": "lóu", + "825C": "dài", + "825D": "xue", + "825E": "yào", + "825F": "chōng", + "8260": "deng", + "8261": "dāng", + "8262": "qiáng", + "8263": "lǔ", + "8264": "yǐ", + "8265": "jí", + "8266": "jiàn", + "8267": "huò", + "8268": "méng", + "8269": "qí", + "826A": "lǔ", + "826B": "lú", + "826C": "chán", + "826D": "shuāng", + "826E": "gěn", + "826F": "liáng", + "8270": "jiān", + "8271": "jiān", + "8272": "sè", + "8273": "yàn", + "8274": "fú", + "8275": "pīng", + "8276": "yàn", + "8277": "yàn", + "8278": "cǎo", + "8279": "cao", + "827A": "yì", + "827B": "lè", + "827C": "tīng", + "827D": "jiāo", + "827E": "ài", + "827F": "nǎi", + "8280": "tiáo", + "8281": "jiāo", + "8282": "jié", + "8283": "péng", + "8284": "wán", + "8285": "yì", + "8286": "chāi", + "8287": "mián", + "8288": "mǐ", + "8289": "gān", + "828A": "qiān", + "828B": "yù", + "828C": "yù", + "828D": "sháo", + "828E": "qiōng", + "828F": "dù", + "8290": "hù", + "8291": "qǐ", + "8292": "máng", + "8293": "zì", + "8294": "huì", + "8295": "suī", + "8296": "zhì", + "8297": "xiāng", + "8298": "pí", + "8299": "fú", + "829A": "tún", + "829B": "wěi", + "829C": "wú", + "829D": "zhī", + "829E": "qì", + "829F": "shān", + "82A0": "wén", + "82A1": "qiàn", + "82A2": "rén", + "82A3": "fú", + "82A4": "kōu", + "82A5": "jiè", + "82A6": "lú", + "82A7": "xù", + "82A8": "jī", + "82A9": "qín", + "82AA": "qí", + "82AB": "yán", + "82AC": "fēn", + "82AD": "bā", + "82AE": "ruì", + "82AF": "xīn", + "82B0": "jì", + "82B1": "huā", + "82B2": "huā", + "82B3": "fāng", + "82B4": "wù", + "82B5": "jué", + "82B6": "gǒu", + "82B7": "zhǐ", + "82B8": "yún", + "82B9": "qín", + "82BA": "ǎo", + "82BB": "chú", + "82BC": "mào", + "82BD": "yá", + "82BE": "fèi", + "82BF": "rèng", + "82C0": "háng", + "82C1": "cōng", + "82C2": "yín", + "82C3": "yǒu", + "82C4": "biàn", + "82C5": "yì", + "82C6": "qie", + "82C7": "wěi", + "82C8": "lì", + "82C9": "pǐ", + "82CA": "è", + "82CB": "xiàn", + "82CC": "cháng", + "82CD": "cāng", + "82CE": "zhù", + "82CF": "sū", + "82D0": "tí", + "82D1": "yuàn", + "82D2": "rǎn", + "82D3": "líng", + "82D4": "tái", + "82D5": "sháo", + "82D6": "dí", + "82D7": "miáo", + "82D8": "qǐng", + "82D9": "lì", + "82DA": "yòng", + "82DB": "kē", + "82DC": "mù", + "82DD": "bèi", + "82DE": "bāo", + "82DF": "gǒu", + "82E0": "mín", + "82E1": "yǐ", + "82E2": "yǐ", + "82E3": "jù", + "82E4": "piě", + "82E5": "ruò", + "82E6": "kǔ", + "82E7": "níng", + "82E8": "nǐ", + "82E9": "bó", + "82EA": "bǐng", + "82EB": "shān", + "82EC": "xiú", + "82ED": "yǎo", + "82EE": "xiān", + "82EF": "běn", + "82F0": "hóng", + "82F1": "yīng", + "82F2": "zhǎ", + "82F3": "dōng", + "82F4": "jū", + "82F5": "dié", + "82F6": "nié", + "82F7": "gān", + "82F8": "hū", + "82F9": "píng", + "82FA": "méi", + "82FB": "fú", + "82FC": "shēng", + "82FD": "gū", + "82FE": "bì", + "82FF": "wèi", + "8300": "fú", + "8301": "zhuó", + "8302": "mào", + "8303": "fàn", + "8304": "jiā", + "8305": "máo", + "8306": "máo", + "8307": "bá", + "8308": "cí", + "8309": "mò", + "830A": "zī", + "830B": "dǐ", + "830C": "chí", + "830D": "jì", + "830E": "jīng", + "830F": "lóng", + "8310": "cong", + "8311": "niǎo", + "8312": "yuán", + "8313": "xué", + "8314": "yíng", + "8315": "qióng", + "8316": "gé", + "8317": "míng", + "8318": "lì", + "8319": "róng", + "831A": "yìn", + "831B": "gèn", + "831C": "qiàn", + "831D": "chǎi", + "831E": "chén", + "831F": "yù", + "8320": "hāo", + "8321": "zì", + "8322": "liè", + "8323": "wú", + "8324": "jì", + "8325": "guī", + "8326": "cì", + "8327": "jiǎn", + "8328": "cí", + "8329": "gòu", + "832A": "guāng", + "832B": "máng", + "832C": "chá", + "832D": "jiāo", + "832E": "jiāo", + "832F": "fú", + "8330": "yú", + "8331": "zhū", + "8332": "zī", + "8333": "jiāng", + "8334": "huí", + "8335": "yīn", + "8336": "chá", + "8337": "fá", + "8338": "rōng", + "8339": "rú", + "833A": "chōng", + "833B": "mǎng", + "833C": "tóng", + "833D": "zhòng", + "833E": "qiān", + "833F": "zhú", + "8340": "xún", + "8341": "huán", + "8342": "fū", + "8343": "quán", + "8344": "gāi", + "8345": "dā", + "8346": "jīng", + "8347": "xìng", + "8348": "chuǎn", + "8349": "cǎo", + "834A": "jīng", + "834B": "ér", + "834C": "àn", + "834D": "qiáo", + "834E": "chí", + "834F": "rěn", + "8350": "jiàn", + "8351": "tí", + "8352": "huāng", + "8353": "píng", + "8354": "lì", + "8355": "jīn", + "8356": "lǎo", + "8357": "shù", + "8358": "zhuāng", + "8359": "dá", + "835A": "jiá", + "835B": "ráo", + "835C": "bì", + "835D": "zé", + "835E": "qiáo", + "835F": "huì", + "8360": "jì", + "8361": "dàng", + "8362": "yu", + "8363": "róng", + "8364": "hūn", + "8365": "xíng", + "8366": "luò", + "8367": "yíng", + "8368": "xún", + "8369": "jìn", + "836A": "sūn", + "836B": "yīn", + "836C": "mǎi", + "836D": "hóng", + "836E": "zhòu", + "836F": "yào", + "8370": "dù", + "8371": "wěi", + "8372": "lí", + "8373": "dòu", + "8374": "fū", + "8375": "rěn", + "8376": "yín", + "8377": "hé", + "8378": "bí", + "8379": "bù", + "837A": "yǔn", + "837B": "dí", + "837C": "tú", + "837D": "suī", + "837E": "suī", + "837F": "chéng", + "8380": "chén", + "8381": "wú", + "8382": "bié", + "8383": "xī", + "8384": "gěng", + "8385": "lì", + "8386": "pú", + "8387": "zhù", + "8388": "mò", + "8389": "lì", + "838A": "zhuāng", + "838B": "zuó", + "838C": "tuō", + "838D": "qiú", + "838E": "shā", + "838F": "suō", + "8390": "chén", + "8391": "péng", + "8392": "jǔ", + "8393": "méi", + "8394": "méng", + "8395": "xìng", + "8396": "jīng", + "8397": "chē", + "8398": "shēn", + "8399": "jūn", + "839A": "yán", + "839B": "tíng", + "839C": "yóu", + "839D": "cuò", + "839E": "guǎn", + "839F": "hàn", + "83A0": "yǒu", + "83A1": "cuò", + "83A2": "jiá", + "83A3": "wáng", + "83A4": "sù", + "83A5": "niǔ", + "83A6": "shāo", + "83A7": "xiàn", + "83A8": "làng", + "83A9": "fú", + "83AA": "é", + "83AB": "mò", + "83AC": "wèn", + "83AD": "jié", + "83AE": "nán", + "83AF": "mù", + "83B0": "kǎn", + "83B1": "lái", + "83B2": "lián", + "83B3": "shí", + "83B4": "wō", + "83B5": "tu", + "83B6": "xiān", + "83B7": "huò", + "83B8": "yóu", + "83B9": "yíng", + "83BA": "yīng", + "83BB": "gòng", + "83BC": "chún", + "83BD": "mǎng", + "83BE": "mǎng", + "83BF": "cì", + "83C0": "wǎn", + "83C1": "jīng", + "83C2": "dì", + "83C3": "qú", + "83C4": "dōng", + "83C5": "jiān", + "83C6": "zōu", + "83C7": "gu", + "83C8": "lā", + "83C9": "lù", + "83CA": "jú", + "83CB": "wèi", + "83CC": "jūn", + "83CD": "niè", + "83CE": "kūn", + "83CF": "hé", + "83D0": "pú", + "83D1": "zāi", + "83D2": "gǎo", + "83D3": "guǒ", + "83D4": "fú", + "83D5": "lún", + "83D6": "chāng", + "83D7": "chóu", + "83D8": "sōng", + "83D9": "chuí", + "83DA": "zhàn", + "83DB": "mén", + "83DC": "cài", + "83DD": "bá", + "83DE": "lí", + "83DF": "tú", + "83E0": "bō", + "83E1": "hàn", + "83E2": "bào", + "83E3": "qìn", + "83E4": "juǎn", + "83E5": "xī", + "83E6": "qín", + "83E7": "dǐ", + "83E8": "jiē", + "83E9": "pú", + "83EA": "dàng", + "83EB": "jǐn", + "83EC": "qiáo", + "83ED": "tái", + "83EE": "gēng", + "83EF": "huá", + "83F0": "gū", + "83F1": "líng", + "83F2": "fēi", + "83F3": "qín", + "83F4": "ān", + "83F5": "wǎng", + "83F6": "běng", + "83F7": "zhǒu", + "83F8": "yān", + "83F9": "jū", + "83FA": "jiān", + "83FB": "lǐn", + "83FC": "tǎn", + "83FD": "shū", + "83FE": "tián", + "83FF": "dào", + "8400": "hǔ", + "8401": "qí", + "8402": "hé", + "8403": "cuì", + "8404": "táo", + "8405": "chūn", + "8406": "bì", + "8407": "cháng", + "8408": "huán", + "8409": "fèi", + "840A": "lái", + "840B": "qī", + "840C": "méng", + "840D": "píng", + "840E": "wēi", + "840F": "dàn", + "8410": "shà", + "8411": "huán", + "8412": "yǎn", + "8413": "yí", + "8414": "tiáo", + "8415": "qí", + "8416": "wǎn", + "8417": "cè", + "8418": "nài", + "8419": "zhen", + "841A": "tuò", + "841B": "jiū", + "841C": "tiē", + "841D": "luó", + "841E": "bi", + "841F": "yi", + "8420": "méng", + "8421": "bo", + "8422": "pao", + "8423": "ding", + "8424": "yíng", + "8425": "yíng", + "8426": "yíng", + "8427": "xiāo", + "8428": "sà", + "8429": "qiū", + "842A": "kē", + "842B": "xiàng", + "842C": "wàn", + "842D": "yǔ", + "842E": "yú", + "842F": "fù", + "8430": "liàn", + "8431": "xuān", + "8432": "xuān", + "8433": "nǎn", + "8434": "cè", + "8435": "wō", + "8436": "chǔn", + "8437": "xiāo", + "8438": "yú", + "8439": "biǎn", + "843A": "mào", + "843B": "ān", + "843C": "è", + "843D": "luò", + "843E": "yíng", + "843F": "kuò", + "8440": "kuò", + "8441": "jiāng", + "8442": "miǎn", + "8443": "zuò", + "8444": "zuò", + "8445": "zū", + "8446": "bǎo", + "8447": "róu", + "8448": "xǐ", + "8449": "yè", + "844A": "ān", + "844B": "qú", + "844C": "jiān", + "844D": "fú", + "844E": "lǜ", + "844F": "jīng", + "8450": "pén", + "8451": "fēng", + "8452": "hóng", + "8453": "hóng", + "8454": "hóu", + "8455": "yàn", + "8456": "tū", + "8457": "zhe", + "8458": "zī", + "8459": "xiāng", + "845A": "rèn", + "845B": "gé", + "845C": "qiā", + "845D": "qíng", + "845E": "mǐ", + "845F": "huáng", + "8460": "shēn", + "8461": "pú", + "8462": "gài", + "8463": "dǒng", + "8464": "zhòu", + "8465": "jiàn", + "8466": "wěi", + "8467": "bó", + "8468": "wēi", + "8469": "pā", + "846A": "jì", + "846B": "hú", + "846C": "zàng", + "846D": "jiā", + "846E": "duàn", + "846F": "yào", + "8470": "suī", + "8471": "cōng", + "8472": "quán", + "8473": "wēi", + "8474": "zhēn", + "8475": "kuí", + "8476": "tíng", + "8477": "hūn", + "8478": "xǐ", + "8479": "shī", + "847A": "qì", + "847B": "lán", + "847C": "zōng", + "847D": "yāo", + "847E": "yuān", + "847F": "méi", + "8480": "yūn", + "8481": "shù", + "8482": "dì", + "8483": "zhuàn", + "8484": "guān", + "8485": "ran", + "8486": "xuē", + "8487": "chǎn", + "8488": "kǎi", + "8489": "kuì", + "848A": "huā", + "848B": "jiǎng", + "848C": "lóu", + "848D": "wěi", + "848E": "pài", + "848F": "you", + "8490": "sōu", + "8491": "yìn", + "8492": "shī", + "8493": "chún", + "8494": "shí", + "8495": "yūn", + "8496": "zhēn", + "8497": "làng", + "8498": "rú", + "8499": "méng", + "849A": "lì", + "849B": "quē", + "849C": "suàn", + "849D": "yuán", + "849E": "lì", + "849F": "jǔ", + "84A0": "xī", + "84A1": "bàng", + "84A2": "chú", + "84A3": "xú", + "84A4": "tú", + "84A5": "liú", + "84A6": "huò", + "84A7": "diǎn", + "84A8": "qiàn", + "84A9": "zū", + "84AA": "pò", + "84AB": "cuó", + "84AC": "yuān", + "84AD": "chú", + "84AE": "yù", + "84AF": "kuǎi", + "84B0": "pán", + "84B1": "pú", + "84B2": "pú", + "84B3": "nà", + "84B4": "shuò", + "84B5": "xí", + "84B6": "fén", + "84B7": "yún", + "84B8": "zhēng", + "84B9": "jiān", + "84BA": "jí", + "84BB": "ruò", + "84BC": "cāng", + "84BD": "ēn", + "84BE": "mí", + "84BF": "hāo", + "84C0": "sūn", + "84C1": "zhēn", + "84C2": "míng", + "84C3": "sōu", + "84C4": "xù", + "84C5": "liú", + "84C6": "xí", + "84C7": "gǔ", + "84C8": "láng", + "84C9": "róng", + "84CA": "wěng", + "84CB": "gài", + "84CC": "cuò", + "84CD": "shī", + "84CE": "táng", + "84CF": "luǒ", + "84D0": "rù", + "84D1": "suō", + "84D2": "xuān", + "84D3": "bèi", + "84D4": "yǎo", + "84D5": "guì", + "84D6": "bì", + "84D7": "zǒng", + "84D8": "gǔn", + "84D9": "zuo", + "84DA": "tiáo", + "84DB": "cè", + "84DC": "pei", + "84DD": "lán", + "84DE": "dàn", + "84DF": "jì", + "84E0": "lí", + "84E1": "shēn", + "84E2": "lǎng", + "84E3": "yù", + "84E4": "ling", + "84E5": "yíng", + "84E6": "mò", + "84E7": "diào", + "84E8": "tiáo", + "84E9": "mǎo", + "84EA": "tōng", + "84EB": "chù", + "84EC": "péng", + "84ED": "ān", + "84EE": "lián", + "84EF": "cōng", + "84F0": "xǐ", + "84F1": "píng", + "84F2": "qiū", + "84F3": "jǐn", + "84F4": "chún", + "84F5": "jié", + "84F6": "wéi", + "84F7": "tuī", + "84F8": "cáo", + "84F9": "yù", + "84FA": "yì", + "84FB": "zí", + "84FC": "liǎo", + "84FD": "bì", + "84FE": "lǔ", + "84FF": "xu", + "8500": "bù", + "8501": "zhāng", + "8502": "léi", + "8503": "qiáng", + "8504": "màn", + "8505": "yán", + "8506": "líng", + "8507": "jì", + "8508": "biāo", + "8509": "gǔn", + "850A": "hǎn", + "850B": "dí", + "850C": "sù", + "850D": "lù", + "850E": "shè", + "850F": "shāng", + "8510": "dí", + "8511": "miè", + "8512": "xūn", + "8513": "màn", + "8514": "bó", + "8515": "dì", + "8516": "cuó", + "8517": "zhe", + "8518": "shēn", + "8519": "xuàn", + "851A": "wèi", + "851B": "hú", + "851C": "áo", + "851D": "mǐ", + "851E": "lóu", + "851F": "cù", + "8520": "zhōng", + "8521": "cài", + "8522": "pó", + "8523": "jiǎng", + "8524": "mì", + "8525": "cōng", + "8526": "niǎo", + "8527": "huì", + "8528": "juàn", + "8529": "yín", + "852A": "jiàn", + "852B": "niān", + "852C": "shū", + "852D": "yīn", + "852E": "guó", + "852F": "chén", + "8530": "hù", + "8531": "shā", + "8532": "kòu", + "8533": "qiàn", + "8534": "má", + "8535": "zāng", + "8536": "ze", + "8537": "qiáng", + "8538": "dōu", + "8539": "liǎn", + "853A": "lìn", + "853B": "kòu", + "853C": "ǎi", + "853D": "bì", + "853E": "lí", + "853F": "wěi", + "8540": "jí", + "8541": "qián", + "8542": "shèng", + "8543": "fān", + "8544": "méng", + "8545": "ǒu", + "8546": "chǎn", + "8547": "diǎn", + "8548": "xùn", + "8549": "jiāo", + "854A": "ruǐ", + "854B": "ruǐ", + "854C": "lěi", + "854D": "yú", + "854E": "qiáo", + "854F": "chú", + "8550": "huá", + "8551": "jiān", + "8552": "mǎi", + "8553": "yún", + "8554": "bāo", + "8555": "yóu", + "8556": "qú", + "8557": "lù", + "8558": "ráo", + "8559": "huì", + "855A": "è", + "855B": "tí", + "855C": "fěi", + "855D": "jué", + "855E": "zuì", + "855F": "fà", + "8560": "rú", + "8561": "fén", + "8562": "kuì", + "8563": "shùn", + "8564": "ruí", + "8565": "yǎ", + "8566": "xū", + "8567": "fù", + "8568": "jué", + "8569": "dàng", + "856A": "wú", + "856B": "dǒng", + "856C": "sī", + "856D": "xiāo", + "856E": "xì", + "856F": "lóng", + "8570": "wēn", + "8571": "shao", + "8572": "qí", + "8573": "jiān", + "8574": "yùn", + "8575": "sūn", + "8576": "líng", + "8577": "yù", + "8578": "xiá", + "8579": "wèng", + "857A": "jí", + "857B": "hóng", + "857C": "sì", + "857D": "nóng", + "857E": "lěi", + "857F": "xuān", + "8580": "yùn", + "8581": "yù", + "8582": "xí", + "8583": "hào", + "8584": "báo", + "8585": "hāo", + "8586": "ài", + "8587": "wēi", + "8588": "huì", + "8589": "huì", + "858A": "jì", + "858B": "cí", + "858C": "xiāng", + "858D": "wàn", + "858E": "miè", + "858F": "yì", + "8590": "léng", + "8591": "jiāng", + "8592": "càn", + "8593": "shēn", + "8594": "qiáng", + "8595": "lián", + "8596": "kē", + "8597": "yuán", + "8598": "dá", + "8599": "tì", + "859A": "tāng", + "859B": "xuē", + "859C": "bì", + "859D": "zhān", + "859E": "sūn", + "859F": "xiān", + "85A0": "fán", + "85A1": "dǐng", + "85A2": "xiè", + "85A3": "gǔ", + "85A4": "xiè", + "85A5": "shǔ", + "85A6": "jiàn", + "85A7": "hāo", + "85A8": "hōng", + "85A9": "sà", + "85AA": "xīn", + "85AB": "xūn", + "85AC": "yào", + "85AD": "bai", + "85AE": "sǒu", + "85AF": "shǔ", + "85B0": "xūn", + "85B1": "duì", + "85B2": "pín", + "85B3": "wěi", + "85B4": "níng", + "85B5": "chóu", + "85B6": "mái", + "85B7": "rú", + "85B8": "piáo", + "85B9": "tái", + "85BA": "jì", + "85BB": "zǎo", + "85BC": "chén", + "85BD": "zhēn", + "85BE": "ěr", + "85BF": "nǐ", + "85C0": "yíng", + "85C1": "gǎo", + "85C2": "cóng", + "85C3": "xiāo", + "85C4": "qí", + "85C5": "fá", + "85C6": "jiǎn", + "85C7": "xù", + "85C8": "kuí", + "85C9": "jí", + "85CA": "biǎn", + "85CB": "diào", + "85CC": "mì", + "85CD": "lán", + "85CE": "jìn", + "85CF": "cáng", + "85D0": "miǎo", + "85D1": "qióng", + "85D2": "qiè", + "85D3": "xiǎn", + "85D4": "liáo", + "85D5": "ǒu", + "85D6": "xián", + "85D7": "sù", + "85D8": "lǘ", + "85D9": "yì", + "85DA": "xù", + "85DB": "xiě", + "85DC": "lí", + "85DD": "yì", + "85DE": "lǎ", + "85DF": "lěi", + "85E0": "jiào", + "85E1": "dí", + "85E2": "zhǐ", + "85E3": "bēi", + "85E4": "téng", + "85E5": "yào", + "85E6": "mò", + "85E7": "huàn", + "85E8": "biāo", + "85E9": "fān", + "85EA": "sǒu", + "85EB": "tán", + "85EC": "tuī", + "85ED": "qióng", + "85EE": "qiáo", + "85EF": "wèi", + "85F0": "liú", + "85F1": "huì", + "85F2": "ou", + "85F3": "gǎo", + "85F4": "yùn", + "85F5": "bao", + "85F6": "lì", + "85F7": "shǔ", + "85F8": "chú", + "85F9": "ǎi", + "85FA": "lìn", + "85FB": "zǎo", + "85FC": "xuān", + "85FD": "qìn", + "85FE": "lài", + "85FF": "huò", + "8600": "tuò", + "8601": "wù", + "8602": "ruǐ", + "8603": "ruǐ", + "8604": "qí", + "8605": "héng", + "8606": "lú", + "8607": "sū", + "8608": "tuí", + "8609": "méng", + "860A": "yùn", + "860B": "píng", + "860C": "yǔ", + "860D": "xūn", + "860E": "jì", + "860F": "jiōng", + "8610": "xuān", + "8611": "mó", + "8612": "qiu", + "8613": "sū", + "8614": "jiōng", + "8615": "feng", + "8616": "niè", + "8617": "bò", + "8618": "ráng", + "8619": "yì", + "861A": "xiǎn", + "861B": "yú", + "861C": "jú", + "861D": "liàn", + "861E": "liǎn", + "861F": "yǐn", + "8620": "qiáng", + "8621": "yīng", + "8622": "lóng", + "8623": "tǒu", + "8624": "wěi", + "8625": "yuè", + "8626": "líng", + "8627": "qú", + "8628": "yáo", + "8629": "fán", + "862A": "méi", + "862B": "hàn", + "862C": "kuī", + "862D": "lán", + "862E": "jì", + "862F": "dàng", + "8630": "man", + "8631": "lèi", + "8632": "léi", + "8633": "huī", + "8634": "fēng", + "8635": "zhī", + "8636": "wèi", + "8637": "kuí", + "8638": "zhàn", + "8639": "huái", + "863A": "lí", + "863B": "jì", + "863C": "mí", + "863D": "lěi", + "863E": "huài", + "863F": "luó", + "8640": "jī", + "8641": "kuí", + "8642": "lù", + "8643": "jiān", + "8644": "sà", + "8645": "teng", + "8646": "léi", + "8647": "quǎn", + "8648": "xiāo", + "8649": "yì", + "864A": "luán", + "864B": "mén", + "864C": "biē", + "864D": "hū", + "864E": "hǔ", + "864F": "lǔ", + "8650": "nüè", + "8651": "lǜ", + "8652": "sī", + "8653": "xiāo", + "8654": "qián", + "8655": "chù", + "8656": "hū", + "8657": "xū", + "8658": "cuó", + "8659": "fú", + "865A": "xū", + "865B": "xū", + "865C": "lǔ", + "865D": "hǔ", + "865E": "yú", + "865F": "hào", + "8660": "jiāo", + "8661": "jù", + "8662": "guó", + "8663": "bào", + "8664": "yán", + "8665": "zhàn", + "8666": "zhàn", + "8667": "kuī", + "8668": "bīn", + "8669": "xì", + "866A": "shù", + "866B": "chóng", + "866C": "qiú", + "866D": "diāo", + "866E": "jǐ", + "866F": "qiú", + "8670": "dīng", + "8671": "shī", + "8672": "xiā", + "8673": "jué", + "8674": "zhé", + "8675": "shé", + "8676": "yū", + "8677": "hán", + "8678": "zǐ", + "8679": "hóng", + "867A": "huī", + "867B": "méng", + "867C": "gè", + "867D": "suī", + "867E": "xiā", + "867F": "chài", + "8680": "shí", + "8681": "yǐ", + "8682": "mǎ", + "8683": "xiàng", + "8684": "fāng", + "8685": "è", + "8686": "bā", + "8687": "chǐ", + "8688": "qiān", + "8689": "wén", + "868A": "wén", + "868B": "ruì", + "868C": "bàng", + "868D": "pí", + "868E": "yuè", + "868F": "yuè", + "8690": "jūn", + "8691": "qí", + "8692": "tóng", + "8693": "yǐn", + "8694": "qí", + "8695": "cán", + "8696": "yuán", + "8697": "jué", + "8698": "huí", + "8699": "qín", + "869A": "qí", + "869B": "zhòng", + "869C": "yá", + "869D": "háo", + "869E": "mù", + "869F": "wáng", + "86A0": "fén", + "86A1": "fén", + "86A2": "háng", + "86A3": "gong", + "86A4": "zǎo", + "86A5": "fù", + "86A6": "rán", + "86A7": "jiè", + "86A8": "fú", + "86A9": "chī", + "86AA": "dǒu", + "86AB": "bào", + "86AC": "xiǎn", + "86AD": "ní", + "86AE": "tè", + "86AF": "qiū", + "86B0": "yóu", + "86B1": "zhà", + "86B2": "píng", + "86B3": "chí", + "86B4": "yòu", + "86B5": "hé", + "86B6": "hān", + "86B7": "jù", + "86B8": "lì", + "86B9": "fù", + "86BA": "rán", + "86BB": "zhá", + "86BC": "gǒu", + "86BD": "pí", + "86BE": "pí", + "86BF": "xián", + "86C0": "zhù", + "86C1": "diāo", + "86C2": "bié", + "86C3": "bǐng", + "86C4": "gū", + "86C5": "zhān", + "86C6": "qū", + "86C7": "shé", + "86C8": "tiě", + "86C9": "líng", + "86CA": "gǔ", + "86CB": "dàn", + "86CC": "gǔ", + "86CD": "yíng", + "86CE": "lì", + "86CF": "chēng", + "86D0": "qū", + "86D1": "móu", + "86D2": "gé", + "86D3": "cì", + "86D4": "huí", + "86D5": "huí", + "86D6": "máng", + "86D7": "fù", + "86D8": "yáng", + "86D9": "wā", + "86DA": "liè", + "86DB": "zhū", + "86DC": "yī", + "86DD": "xián", + "86DE": "kuò", + "86DF": "jiāo", + "86E0": "lì", + "86E1": "yì", + "86E2": "píng", + "86E3": "qī", + "86E4": "há", + "86E5": "shé", + "86E6": "yí", + "86E7": "wǎng", + "86E8": "mò", + "86E9": "qióng", + "86EA": "qiè", + "86EB": "guǐ", + "86EC": "qióng", + "86ED": "zhì", + "86EE": "mán", + "86EF": "lao", + "86F0": "zhé", + "86F1": "jiá", + "86F2": "náo", + "86F3": "sī", + "86F4": "qí", + "86F5": "xīng", + "86F6": "jiè", + "86F7": "qiú", + "86F8": "shāo", + "86F9": "yǒng", + "86FA": "jiá", + "86FB": "tuì", + "86FC": "chē", + "86FD": "bài", + "86FE": "é", + "86FF": "hàn", + "8700": "shǔ", + "8701": "xuán", + "8702": "fēng", + "8703": "shèn", + "8704": "shèn", + "8705": "fǔ", + "8706": "xiàn", + "8707": "zhē", + "8708": "wú", + "8709": "fú", + "870A": "lí", + "870B": "láng", + "870C": "bì", + "870D": "chú", + "870E": "yuān", + "870F": "yǒu", + "8710": "jié", + "8711": "dàn", + "8712": "yán", + "8713": "tíng", + "8714": "diàn", + "8715": "tuì", + "8716": "huí", + "8717": "wō", + "8718": "zhī", + "8719": "sōng", + "871A": "fēi", + "871B": "jū", + "871C": "mì", + "871D": "qí", + "871E": "qí", + "871F": "yù", + "8720": "jùn", + "8721": "là", + "8722": "měng", + "8723": "qiāng", + "8724": "sī", + "8725": "xī", + "8726": "lún", + "8727": "lì", + "8728": "dié", + "8729": "tiáo", + "872A": "táo", + "872B": "kūn", + "872C": "hán", + "872D": "hàn", + "872E": "yù", + "872F": "bàng", + "8730": "féi", + "8731": "pí", + "8732": "wēi", + "8733": "dūn", + "8734": "yì", + "8735": "yuān", + "8736": "suò", + "8737": "quán", + "8738": "qiǎn", + "8739": "ruì", + "873A": "ní", + "873B": "qīng", + "873C": "wèi", + "873D": "liǎng", + "873E": "guǒ", + "873F": "wān", + "8740": "dōng", + "8741": "è", + "8742": "bǎn", + "8743": "dì", + "8744": "wǎng", + "8745": "cán", + "8746": "yǎng", + "8747": "ying", + "8748": "guō", + "8749": "chán", + "874A": "dìng", + "874B": "là", + "874C": "kē", + "874D": "jié", + "874E": "xiē", + "874F": "tíng", + "8750": "mào", + "8751": "xū", + "8752": "mián", + "8753": "yú", + "8754": "jiē", + "8755": "shí", + "8756": "xuān", + "8757": "huáng", + "8758": "yǎn", + "8759": "biān", + "875A": "róu", + "875B": "wēi", + "875C": "fù", + "875D": "yuán", + "875E": "mèi", + "875F": "wei", + "8760": "fú", + "8761": "rú", + "8762": "xié", + "8763": "yóu", + "8764": "qiú", + "8765": "máo", + "8766": "xiā", + "8767": "yīng", + "8768": "shī", + "8769": "chóng", + "876A": "tāng", + "876B": "zhū", + "876C": "zōng", + "876D": "tí", + "876E": "fù", + "876F": "yuán", + "8770": "kuí", + "8771": "méng", + "8772": "là", + "8773": "dú", + "8774": "hú", + "8775": "qiū", + "8776": "dié", + "8777": "lì", + "8778": "wō", + "8779": "yūn", + "877A": "qǔ", + "877B": "nǎn", + "877C": "lóu", + "877D": "chūn", + "877E": "róng", + "877F": "yíng", + "8780": "jiāng", + "8781": "ban", + "8782": "láng", + "8783": "páng", + "8784": "sī", + "8785": "xī", + "8786": "cì", + "8787": "xī", + "8788": "yuán", + "8789": "wēng", + "878A": "lián", + "878B": "sōu", + "878C": "bān", + "878D": "róng", + "878E": "róng", + "878F": "jí", + "8790": "wū", + "8791": "xiù", + "8792": "hàn", + "8793": "qín", + "8794": "yí", + "8795": "bī", + "8796": "huá", + "8797": "táng", + "8798": "yǐ", + "8799": "dù", + "879A": "nài", + "879B": "hé", + "879C": "hú", + "879D": "guī", + "879E": "mǎ", + "879F": "míng", + "87A0": "yì", + "87A1": "wén", + "87A2": "yíng", + "87A3": "tè", + "87A4": "zhōng", + "87A5": "cāng", + "87A6": "sao", + "87A7": "qi", + "87A8": "mǎn", + "87A9": "tiao", + "87AA": "shāng", + "87AB": "shì", + "87AC": "cáo", + "87AD": "chī", + "87AE": "dì", + "87AF": "áo", + "87B0": "lù", + "87B1": "wèi", + "87B2": "zhì", + "87B3": "táng", + "87B4": "chén", + "87B5": "piāo", + "87B6": "qú", + "87B7": "pí", + "87B8": "yú", + "87B9": "jiàn", + "87BA": "luó", + "87BB": "lóu", + "87BC": "qǐn", + "87BD": "zhōng", + "87BE": "yǐn", + "87BF": "jiāng", + "87C0": "shuài", + "87C1": "wén", + "87C2": "xiāo", + "87C3": "wàn", + "87C4": "zhé", + "87C5": "zhè", + "87C6": "ma", + "87C7": "má", + "87C8": "guō", + "87C9": "liú", + "87CA": "máo", + "87CB": "xī", + "87CC": "cōng", + "87CD": "lí", + "87CE": "mǎn", + "87CF": "xiāo", + "87D0": "chang", + "87D1": "zhāng", + "87D2": "mǎng", + "87D3": "xiàng", + "87D4": "mò", + "87D5": "zuī", + "87D6": "sī", + "87D7": "qiū", + "87D8": "tè", + "87D9": "zhí", + "87DA": "péng", + "87DB": "péng", + "87DC": "jiǎo", + "87DD": "qú", + "87DE": "biē", + "87DF": "liáo", + "87E0": "pán", + "87E1": "guǐ", + "87E2": "xǐ", + "87E3": "jǐ", + "87E4": "zhuān", + "87E5": "huáng", + "87E6": "féi", + "87E7": "láo", + "87E8": "jué", + "87E9": "jué", + "87EA": "huì", + "87EB": "yín", + "87EC": "chán", + "87ED": "jiāo", + "87EE": "shàn", + "87EF": "náo", + "87F0": "xiāo", + "87F1": "wú", + "87F2": "chóng", + "87F3": "xún", + "87F4": "sī", + "87F5": "chu", + "87F6": "chēng", + "87F7": "dāng", + "87F8": "lǐ", + "87F9": "xiè", + "87FA": "shàn", + "87FB": "yǐ", + "87FC": "jǐng", + "87FD": "dá", + "87FE": "chán", + "87FF": "qì", + "8800": "cī", + "8801": "xiǎng", + "8802": "shè", + "8803": "luǒ", + "8804": "qín", + "8805": "ying", + "8806": "chài", + "8807": "lì", + "8808": "zéi", + "8809": "xuān", + "880A": "lián", + "880B": "zhú", + "880C": "zé", + "880D": "xiē", + "880E": "mǎng", + "880F": "xiè", + "8810": "qí", + "8811": "róng", + "8812": "jiǎn", + "8813": "měng", + "8814": "háo", + "8815": "rú", + "8816": "huò", + "8817": "zhuó", + "8818": "jié", + "8819": "pín", + "881A": "hē", + "881B": "miè", + "881C": "fán", + "881D": "léi", + "881E": "jié", + "881F": "là", + "8820": "mǐn", + "8821": "lí", + "8822": "chǔn", + "8823": "lì", + "8824": "qiū", + "8825": "niè", + "8826": "lú", + "8827": "dù", + "8828": "xiāo", + "8829": "zhū", + "882A": "lóng", + "882B": "lì", + "882C": "lóng", + "882D": "fēng", + "882E": "yē", + "882F": "bèng", + "8830": "náng", + "8831": "gǔ", + "8832": "juān", + "8833": "yīng", + "8834": "shu", + "8835": "xī", + "8836": "cán", + "8837": "qú", + "8838": "quán", + "8839": "dù", + "883A": "cán", + "883B": "mán", + "883C": "qú", + "883D": "jié", + "883E": "zhú", + "883F": "zhuō", + "8840": "xuè", + "8841": "huāng", + "8842": "niù", + "8843": "pēi", + "8844": "nǜ", + "8845": "xìn", + "8846": "zhòng", + "8847": "mài", + "8848": "èr", + "8849": "kā", + "884A": "miè", + "884B": "xì", + "884C": "xíng", + "884D": "yǎn", + "884E": "kàn", + "884F": "yuàn", + "8850": "qu", + "8851": "líng", + "8852": "xuàn", + "8853": "shù", + "8854": "xián", + "8855": "tòng", + "8856": "xiàng", + "8857": "jiē", + "8858": "xián", + "8859": "yá", + "885A": "hú", + "885B": "wèi", + "885C": "dào", + "885D": "chōng", + "885E": "wèi", + "885F": "dào", + "8860": "zhūn", + "8861": "héng", + "8862": "qú", + "8863": "yī", + "8864": "yi", + "8865": "bǔ", + "8866": "gǎn", + "8867": "yú", + "8868": "biǎo", + "8869": "chǎ", + "886A": "yí", + "886B": "shān", + "886C": "chèn", + "886D": "fū", + "886E": "gǔn", + "886F": "fēn", + "8870": "shuāi", + "8871": "jié", + "8872": "nà", + "8873": "zhōng", + "8874": "dǎn", + "8875": "yì", + "8876": "zhòng", + "8877": "zhōng", + "8878": "jiè", + "8879": "zhǐ", + "887A": "xié", + "887B": "rán", + "887C": "zhī", + "887D": "rèn", + "887E": "qīn", + "887F": "jīn", + "8880": "jūn", + "8881": "yuán", + "8882": "mèi", + "8883": "chài", + "8884": "ǎo", + "8885": "niǎo", + "8886": "huī", + "8887": "rán", + "8888": "jiā", + "8889": "tuó", + "888A": "lǐng", + "888B": "dài", + "888C": "bào", + "888D": "páo", + "888E": "yào", + "888F": "zuò", + "8890": "bì", + "8891": "shào", + "8892": "tǎn", + "8893": "jù", + "8894": "hè", + "8895": "xué", + "8896": "xiù", + "8897": "zhěn", + "8898": "yí", + "8899": "pà", + "889A": "bō", + "889B": "dī", + "889C": "wà", + "889D": "fù", + "889E": "gǔn", + "889F": "zhì", + "88A0": "zhì", + "88A1": "rán", + "88A2": "pàn", + "88A3": "yì", + "88A4": "mào", + "88A5": "tuō", + "88A6": "nà", + "88A7": "gōu", + "88A8": "xuàn", + "88A9": "zhé", + "88AA": "qū", + "88AB": "bèi", + "88AC": "gǔn", + "88AD": "xí", + "88AE": "ni", + "88AF": "bó", + "88B0": "bō", + "88B1": "fu", + "88B2": "chǐ", + "88B3": "chǐ", + "88B4": "kù", + "88B5": "rèn", + "88B6": "jiàng", + "88B7": "jiá", + "88B8": "jiàn", + "88B9": "bó", + "88BA": "jié", + "88BB": "ér", + "88BC": "gē", + "88BD": "rú", + "88BE": "zhū", + "88BF": "guī", + "88C0": "yīn", + "88C1": "cái", + "88C2": "liè", + "88C3": "ka", + "88C4": "xing", + "88C5": "zhuāng", + "88C6": "dāng", + "88C7": "xū", + "88C8": "kūn", + "88C9": "kèn", + "88CA": "niǎo", + "88CB": "shù", + "88CC": "jiá", + "88CD": "kǔn", + "88CE": "chéng", + "88CF": "lǐ", + "88D0": "juān", + "88D1": "shēn", + "88D2": "póu", + "88D3": "gé", + "88D4": "yì", + "88D5": "yù", + "88D6": "zhěn", + "88D7": "liú", + "88D8": "qiú", + "88D9": "qún", + "88DA": "jì", + "88DB": "yì", + "88DC": "bǔ", + "88DD": "zhuāng", + "88DE": "shuì", + "88DF": "shā", + "88E0": "qún", + "88E1": "li", + "88E2": "lián", + "88E3": "liǎn", + "88E4": "kù", + "88E5": "jiǎn", + "88E6": "fóu", + "88E7": "chān", + "88E8": "bì", + "88E9": "kūn", + "88EA": "táo", + "88EB": "yuàn", + "88EC": "líng", + "88ED": "chǐ", + "88EE": "chāng", + "88EF": "chóu", + "88F0": "duō", + "88F1": "biǎo", + "88F2": "liǎng", + "88F3": "shang", + "88F4": "péi", + "88F5": "péi", + "88F6": "fēi", + "88F7": "yuān", + "88F8": "luǒ", + "88F9": "guǒ", + "88FA": "yǎn", + "88FB": "dú", + "88FC": "tì", + "88FD": "zhì", + "88FE": "jū", + "88FF": "yǐ", + "8900": "jì", + "8901": "zhí", + "8902": "guà", + "8903": "kèn", + "8904": "qi", + "8905": "tì", + "8906": "tí", + "8907": "fù", + "8908": "chóng", + "8909": "xiè", + "890A": "biǎn", + "890B": "dié", + "890C": "kūn", + "890D": "duān", + "890E": "xiù", + "890F": "xiù", + "8910": "hè", + "8911": "yuàn", + "8912": "bāo", + "8913": "bǎo", + "8914": "fù", + "8915": "yú", + "8916": "tuàn", + "8917": "yǎn", + "8918": "huī", + "8919": "bèi", + "891A": "chǔ", + "891B": "lǚ", + "891C": "pao", + "891D": "dān", + "891E": "yǔn", + "891F": "tā", + "8920": "gōu", + "8921": "dā", + "8922": "huái", + "8923": "róng", + "8924": "yuàn", + "8925": "rù", + "8926": "nài", + "8927": "jiǒng", + "8928": "suǒ", + "8929": "bān", + "892A": "tuì", + "892B": "chǐ", + "892C": "sǎng", + "892D": "niǎo", + "892E": "yīng", + "892F": "jiè", + "8930": "qiān", + "8931": "huái", + "8932": "kù", + "8933": "lián", + "8934": "lán", + "8935": "lí", + "8936": "zhě", + "8937": "shī", + "8938": "lǚ", + "8939": "yì", + "893A": "diē", + "893B": "xiè", + "893C": "xiān", + "893D": "wèi", + "893E": "biǎo", + "893F": "cáo", + "8940": "jī", + "8941": "qiǎng", + "8942": "sēn", + "8943": "bāo", + "8944": "xiāng", + "8945": "bi", + "8946": "fú", + "8947": "jiǎn", + "8948": "zhuàn", + "8949": "jiǎn", + "894A": "cuì", + "894B": "jí", + "894C": "dān", + "894D": "zá", + "894E": "fán", + "894F": "bó", + "8950": "xiàng", + "8951": "xín", + "8952": "bié", + "8953": "ráo", + "8954": "mǎn", + "8955": "lán", + "8956": "ǎo", + "8957": "zé", + "8958": "guì", + "8959": "cào", + "895A": "suì", + "895B": "nóng", + "895C": "chān", + "895D": "liǎn", + "895E": "bì", + "895F": "jīn", + "8960": "dāng", + "8961": "shǔ", + "8962": "tǎn", + "8963": "bì", + "8964": "lán", + "8965": "pú", + "8966": "rú", + "8967": "zhǐ", + "8968": "dùi", + "8969": "shǔ", + "896A": "wà", + "896B": "shì", + "896C": "bǎi", + "896D": "xié", + "896E": "bó", + "896F": "chèn", + "8970": "lài", + "8971": "lóng", + "8972": "xí", + "8973": "xiān", + "8974": "lán", + "8975": "zhě", + "8976": "dài", + "8977": "ju", + "8978": "zàn", + "8979": "shī", + "897A": "jiǎn", + "897B": "pàn", + "897C": "yì", + "897D": "lan", + "897E": "yà", + "897F": "xi", + "8980": "xī", + "8981": "yào", + "8982": "fěng", + "8983": "tán", + "8984": "fu", + "8985": "fiào", + "8986": "fù", + "8987": "bà", + "8988": "hé", + "8989": "jī", + "898A": "jī", + "898B": "jiàn", + "898C": "guān", + "898D": "biàn", + "898E": "yàn", + "898F": "guī", + "8990": "jué", + "8991": "piǎn", + "8992": "mào", + "8993": "mì", + "8994": "mì", + "8995": "miè", + "8996": "shì", + "8997": "sì", + "8998": "chān", + "8999": "luó", + "899A": "jué", + "899B": "mì", + "899C": "tiào", + "899D": "lián", + "899E": "yào", + "899F": "zhì", + "89A0": "jūn", + "89A1": "xí", + "89A2": "shǎn", + "89A3": "wēi", + "89A4": "xì", + "89A5": "tiǎn", + "89A6": "yú", + "89A7": "lǎn", + "89A8": "è", + "89A9": "dǔ", + "89AA": "qīn", + "89AB": "pǎng", + "89AC": "jì", + "89AD": "míng", + "89AE": "yíng", + "89AF": "gòu", + "89B0": "qū", + "89B1": "zhàn", + "89B2": "jìn", + "89B3": "guān", + "89B4": "dēng", + "89B5": "jiàn", + "89B6": "luó", + "89B7": "qù", + "89B8": "jiān", + "89B9": "wéi", + "89BA": "jué", + "89BB": "qū", + "89BC": "luó", + "89BD": "lǎn", + "89BE": "shěn", + "89BF": "dí", + "89C0": "guān", + "89C1": "jiàn", + "89C2": "guān", + "89C3": "yàn", + "89C4": "guī", + "89C5": "mì", + "89C6": "shì", + "89C7": "chān", + "89C8": "lǎn", + "89C9": "jué", + "89CA": "jì", + "89CB": "xí", + "89CC": "dí", + "89CD": "tiǎn", + "89CE": "yú", + "89CF": "gòu", + "89D0": "jìn", + "89D1": "qù", + "89D2": "jiǎo", + "89D3": "qiú", + "89D4": "jīn", + "89D5": "cū", + "89D6": "jué", + "89D7": "zhì", + "89D8": "chào", + "89D9": "jí", + "89DA": "gū", + "89DB": "dàn", + "89DC": "zī", + "89DD": "dǐ", + "89DE": "shāng", + "89DF": "huà", + "89E0": "quán", + "89E1": "gé", + "89E2": "shì", + "89E3": "jiě", + "89E4": "guǐ", + "89E5": "gōng", + "89E6": "chù", + "89E7": "jiě", + "89E8": "hùn", + "89E9": "qiú", + "89EA": "xīng", + "89EB": "sù", + "89EC": "ní", + "89ED": "jī", + "89EE": "lù", + "89EF": "zhì", + "89F0": "zhā", + "89F1": "bì", + "89F2": "xīng", + "89F3": "hú", + "89F4": "shāng", + "89F5": "gōng", + "89F6": "zhì", + "89F7": "xué", + "89F8": "chù", + "89F9": "xī", + "89FA": "yí", + "89FB": "lì", + "89FC": "jué", + "89FD": "xī", + "89FE": "yàn", + "89FF": "xī", + "8A00": "yán", + "8A01": "yan", + "8A02": "dìng", + "8A03": "fù", + "8A04": "qiú", + "8A05": "qiú", + "8A06": "jiào", + "8A07": "hōng", + "8A08": "jì", + "8A09": "fān", + "8A0A": "xùn", + "8A0B": "diào", + "8A0C": "hòng", + "8A0D": "chài", + "8A0E": "tǎo", + "8A0F": "xū", + "8A10": "jié", + "8A11": "yí", + "8A12": "rèn", + "8A13": "xun", + "8A14": "yín", + "8A15": "shàn", + "8A16": "qì", + "8A17": "tuō", + "8A18": "jì", + "8A19": "xùn", + "8A1A": "yín", + "8A1B": "é", + "8A1C": "fēn", + "8A1D": "yà", + "8A1E": "yāo", + "8A1F": "sòng", + "8A20": "shěn", + "8A21": "yín", + "8A22": "xīn", + "8A23": "jué", + "8A24": "xiáo", + "8A25": "nè", + "8A26": "chén", + "8A27": "yóu", + "8A28": "zhǐ", + "8A29": "xiōng", + "8A2A": "fǎng", + "8A2B": "xìn", + "8A2C": "chāo", + "8A2D": "shè", + "8A2E": "xiān", + "8A2F": "sǎ", + "8A30": "zhùn", + "8A31": "xǔ", + "8A32": "yì", + "8A33": "yì", + "8A34": "su", + "8A35": "chī", + "8A36": "hē", + "8A37": "shēn", + "8A38": "hé", + "8A39": "xù", + "8A3A": "zhěn", + "8A3B": "zhù", + "8A3C": "zhèng", + "8A3D": "gòu", + "8A3E": "zī", + "8A3F": "zǐ", + "8A40": "zhān", + "8A41": "gǔ", + "8A42": "fù", + "8A43": "jiǎn", + "8A44": "dié", + "8A45": "líng", + "8A46": "dǐ", + "8A47": "yàng", + "8A48": "lì", + "8A49": "náo", + "8A4A": "pàn", + "8A4B": "zhòu", + "8A4C": "gàn", + "8A4D": "yì", + "8A4E": "jù", + "8A4F": "yào", + "8A50": "zhà", + "8A51": "yí", + "8A52": "yí", + "8A53": "qǔ", + "8A54": "zhào", + "8A55": "píng", + "8A56": "bì", + "8A57": "xiòng", + "8A58": "qū", + "8A59": "bá", + "8A5A": "dá", + "8A5B": "zǔ", + "8A5C": "tāo", + "8A5D": "zhǔ", + "8A5E": "cí", + "8A5F": "zhé", + "8A60": "yǒng", + "8A61": "xǔ", + "8A62": "xún", + "8A63": "yì", + "8A64": "huǎng", + "8A65": "hé", + "8A66": "shì", + "8A67": "chá", + "8A68": "xiào", + "8A69": "shī", + "8A6A": "hěn", + "8A6B": "chà", + "8A6C": "gòu", + "8A6D": "guǐ", + "8A6E": "quán", + "8A6F": "huì", + "8A70": "jié", + "8A71": "huà", + "8A72": "gāi", + "8A73": "xiáng", + "8A74": "wēi", + "8A75": "shēn", + "8A76": "zhòu", + "8A77": "tóng", + "8A78": "mí", + "8A79": "zhān", + "8A7A": "mìng", + "8A7B": "è", + "8A7C": "huī", + "8A7D": "yán", + "8A7E": "xiōng", + "8A7F": "guà", + "8A80": "èr", + "8A81": "bìng", + "8A82": "tiǎo", + "8A83": "yí", + "8A84": "lěi", + "8A85": "zhū", + "8A86": "kuāng", + "8A87": "kuā", + "8A88": "wú", + "8A89": "yù", + "8A8A": "téng", + "8A8B": "jì", + "8A8C": "zhì", + "8A8D": "rèn", + "8A8E": "cù", + "8A8F": "lǎng", + "8A90": "é", + "8A91": "kuáng", + "8A92": "éi", + "8A93": "shì", + "8A94": "tǐng", + "8A95": "dàn", + "8A96": "bèi", + "8A97": "chán", + "8A98": "yòu", + "8A99": "kēng", + "8A9A": "qiào", + "8A9B": "qīn", + "8A9C": "shuà", + "8A9D": "ān", + "8A9E": "yǔ", + "8A9F": "xiào", + "8AA0": "chéng", + "8AA1": "jiè", + "8AA2": "xiàn", + "8AA3": "wū", + "8AA4": "wù", + "8AA5": "gào", + "8AA6": "sòng", + "8AA7": "bū", + "8AA8": "huì", + "8AA9": "jìng", + "8AAA": "shuō", + "8AAB": "zhèn", + "8AAC": "shuō", + "8AAD": "dú", + "8AAE": "hua", + "8AAF": "chàng", + "8AB0": "shuí", + "8AB1": "jié", + "8AB2": "kè", + "8AB3": "qū", + "8AB4": "cóng", + "8AB5": "xiáo", + "8AB6": "suì", + "8AB7": "wǎng", + "8AB8": "xián", + "8AB9": "fěi", + "8ABA": "chī", + "8ABB": "tà", + "8ABC": "yì", + "8ABD": "nì", + "8ABE": "yín", + "8ABF": "diào", + "8AC0": "pǐ", + "8AC1": "zhuó", + "8AC2": "chǎn", + "8AC3": "chēn", + "8AC4": "zhūn", + "8AC5": "jì", + "8AC6": "qī", + "8AC7": "tán", + "8AC8": "zhuì", + "8AC9": "wěi", + "8ACA": "jú", + "8ACB": "qǐng", + "8ACC": "dǒng", + "8ACD": "zhèng", + "8ACE": "zé", + "8ACF": "zōu", + "8AD0": "qiān", + "8AD1": "zhuó", + "8AD2": "liàng", + "8AD3": "jiàn", + "8AD4": "chù", + "8AD5": "háo", + "8AD6": "lùn", + "8AD7": "shěn", + "8AD8": "biǎo", + "8AD9": "huài", + "8ADA": "pián", + "8ADB": "yú", + "8ADC": "dié", + "8ADD": "xū", + "8ADE": "piǎn", + "8ADF": "shì", + "8AE0": "xuān", + "8AE1": "shì", + "8AE2": "hùn", + "8AE3": "huà", + "8AE4": "è", + "8AE5": "zhòng", + "8AE6": "dì", + "8AE7": "xié", + "8AE8": "fú", + "8AE9": "pǔ", + "8AEA": "tíng", + "8AEB": "jiàn", + "8AEC": "qǐ", + "8AED": "yù", + "8AEE": "zī", + "8AEF": "zhuān", + "8AF0": "xǐ", + "8AF1": "huì", + "8AF2": "yīn", + "8AF3": "ān", + "8AF4": "xián", + "8AF5": "nán", + "8AF6": "chén", + "8AF7": "fěng", + "8AF8": "zhū", + "8AF9": "yáng", + "8AFA": "yàn", + "8AFB": "huáng", + "8AFC": "xuān", + "8AFD": "gé", + "8AFE": "nuò", + "8AFF": "qī", + "8B00": "móu", + "8B01": "yè", + "8B02": "wèi", + "8B03": "xing", + "8B04": "téng", + "8B05": "zhōu", + "8B06": "shàn", + "8B07": "jiǎn", + "8B08": "pó", + "8B09": "kuì", + "8B0A": "huǎng", + "8B0B": "huò", + "8B0C": "gē", + "8B0D": "yíng", + "8B0E": "mí", + "8B0F": "xiǎo", + "8B10": "mì", + "8B11": "xǐ", + "8B12": "qiāng", + "8B13": "chēn", + "8B14": "xuè", + "8B15": "tí", + "8B16": "sù", + "8B17": "bàng", + "8B18": "chí", + "8B19": "qiān", + "8B1A": "shì", + "8B1B": "jiǎng", + "8B1C": "yuán", + "8B1D": "xiè", + "8B1E": "hè", + "8B1F": "tāo", + "8B20": "yáo", + "8B21": "yáo", + "8B22": "zhi", + "8B23": "yú", + "8B24": "biāo", + "8B25": "còng", + "8B26": "qìng", + "8B27": "lí", + "8B28": "mó", + "8B29": "mò", + "8B2A": "shāng", + "8B2B": "zhé", + "8B2C": "miù", + "8B2D": "jiǎn", + "8B2E": "zé", + "8B2F": "jiē", + "8B30": "lián", + "8B31": "lóu", + "8B32": "càn", + "8B33": "ōu", + "8B34": "gùn", + "8B35": "xí", + "8B36": "zhuó", + "8B37": "áo", + "8B38": "áo", + "8B39": "jǐn", + "8B3A": "zhé", + "8B3B": "yí", + "8B3C": "hū", + "8B3D": "jiàng", + "8B3E": "mán", + "8B3F": "cháo", + "8B40": "hàn", + "8B41": "huá", + "8B42": "chǎn", + "8B43": "xū", + "8B44": "zēng", + "8B45": "sè", + "8B46": "xī", + "8B47": "zhā", + "8B48": "duì", + "8B49": "zhèng", + "8B4A": "náo", + "8B4B": "lán", + "8B4C": "é", + "8B4D": "yīng", + "8B4E": "jué", + "8B4F": "jī", + "8B50": "zǔn", + "8B51": "jiǎo", + "8B52": "bò", + "8B53": "huì", + "8B54": "zhuàn", + "8B55": "wú", + "8B56": "zèn", + "8B57": "zhá", + "8B58": "shi", + "8B59": "qiào", + "8B5A": "tán", + "8B5B": "zèn", + "8B5C": "pǔ", + "8B5D": "shéng", + "8B5E": "xuān", + "8B5F": "zào", + "8B60": "tán", + "8B61": "dǎng", + "8B62": "suì", + "8B63": "xiǎn", + "8B64": "jī", + "8B65": "jiào", + "8B66": "jǐng", + "8B67": "zhàn", + "8B68": "náng", + "8B69": "yī", + "8B6A": "ài", + "8B6B": "zhān", + "8B6C": "pì", + "8B6D": "huǐ", + "8B6E": "huà", + "8B6F": "yì", + "8B70": "yì", + "8B71": "shàn", + "8B72": "ràng", + "8B73": "nòu", + "8B74": "qiǎn", + "8B75": "zhuì", + "8B76": "tà", + "8B77": "hù", + "8B78": "zhōu", + "8B79": "háo", + "8B7A": "ài", + "8B7B": "yīng", + "8B7C": "jiàn", + "8B7D": "yù", + "8B7E": "jiǎn", + "8B7F": "huì", + "8B80": "dú", + "8B81": "zhé", + "8B82": "xuàn", + "8B83": "zàn", + "8B84": "lěi", + "8B85": "shěn", + "8B86": "wèi", + "8B87": "chǎn", + "8B88": "lì", + "8B89": "yí", + "8B8A": "biàn", + "8B8B": "zhé", + "8B8C": "yàn", + "8B8D": "è", + "8B8E": "chóu", + "8B8F": "wèi", + "8B90": "chóu", + "8B91": "yào", + "8B92": "chán", + "8B93": "ràng", + "8B94": "yǐn", + "8B95": "lán", + "8B96": "chèn", + "8B97": "xié", + "8B98": "niè", + "8B99": "huān", + "8B9A": "zàn", + "8B9B": "yì", + "8B9C": "dǎng", + "8B9D": "zhán", + "8B9E": "yàn", + "8B9F": "dú", + "8BA0": "yán", + "8BA1": "jì", + "8BA2": "dìng", + "8BA3": "fù", + "8BA4": "rèn", + "8BA5": "jī", + "8BA6": "jié", + "8BA7": "hòng", + "8BA8": "tǎo", + "8BA9": "ràng", + "8BAA": "shàn", + "8BAB": "qì", + "8BAC": "tuō", + "8BAD": "xun", + "8BAE": "yì", + "8BAF": "xùn", + "8BB0": "jì", + "8BB1": "rèn", + "8BB2": "jiǎng", + "8BB3": "huì", + "8BB4": "ōu", + "8BB5": "jù", + "8BB6": "yà", + "8BB7": "nè", + "8BB8": "xǔ", + "8BB9": "é", + "8BBA": "lùn", + "8BBB": "xiōng", + "8BBC": "sòng", + "8BBD": "fěng", + "8BBE": "shè", + "8BBF": "fǎng", + "8BC0": "jué", + "8BC1": "zhèng", + "8BC2": "gǔ", + "8BC3": "hē", + "8BC4": "píng", + "8BC5": "zǔ", + "8BC6": "shi", + "8BC7": "xiòng", + "8BC8": "zhà", + "8BC9": "su", + "8BCA": "zhěn", + "8BCB": "dǐ", + "8BCC": "zhōu", + "8BCD": "cí", + "8BCE": "qū", + "8BCF": "zhào", + "8BD0": "bì", + "8BD1": "yì", + "8BD2": "yí", + "8BD3": "kuāng", + "8BD4": "lěi", + "8BD5": "shì", + "8BD6": "guà", + "8BD7": "shī", + "8BD8": "jí", + "8BD9": "huī", + "8BDA": "chéng", + "8BDB": "zhū", + "8BDC": "shēn", + "8BDD": "huà", + "8BDE": "dàn", + "8BDF": "gòu", + "8BE0": "quán", + "8BE1": "guǐ", + "8BE2": "xún", + "8BE3": "yì", + "8BE4": "zhēng", + "8BE5": "gāi", + "8BE6": "xiáng", + "8BE7": "chà", + "8BE8": "hùn", + "8BE9": "xǔ", + "8BEA": "zhōu", + "8BEB": "jiè", + "8BEC": "wū", + "8BED": "yǔ", + "8BEE": "qiào", + "8BEF": "wù", + "8BF0": "gào", + "8BF1": "yòu", + "8BF2": "huì", + "8BF3": "kuáng", + "8BF4": "shuō", + "8BF5": "sòng", + "8BF6": "éi", + "8BF7": "qǐng", + "8BF8": "zhū", + "8BF9": "zōu", + "8BFA": "nuò", + "8BFB": "dú", + "8BFC": "zhuó", + "8BFD": "fěi", + "8BFE": "kè", + "8BFF": "wěi", + "8C00": "yú", + "8C01": "shuí", + "8C02": "shěn", + "8C03": "diào", + "8C04": "chǎn", + "8C05": "liàng", + "8C06": "zhūn", + "8C07": "suì", + "8C08": "tán", + "8C09": "shěn", + "8C0A": "yì", + "8C0B": "móu", + "8C0C": "chén", + "8C0D": "dié", + "8C0E": "huǎng", + "8C0F": "jiàn", + "8C10": "xié", + "8C11": "xuè", + "8C12": "yè", + "8C13": "wèi", + "8C14": "è", + "8C15": "yù", + "8C16": "xuān", + "8C17": "chán", + "8C18": "zī", + "8C19": "ān", + "8C1A": "yàn", + "8C1B": "dì", + "8C1C": "mí", + "8C1D": "piǎn", + "8C1E": "xū", + "8C1F": "mó", + "8C20": "dǎng", + "8C21": "sù", + "8C22": "xiè", + "8C23": "yáo", + "8C24": "bàng", + "8C25": "shì", + "8C26": "qiān", + "8C27": "mì", + "8C28": "jǐn", + "8C29": "mán", + "8C2A": "zhé", + "8C2B": "jiǎn", + "8C2C": "miù", + "8C2D": "tán", + "8C2E": "zèn", + "8C2F": "qiáo", + "8C30": "lán", + "8C31": "pǔ", + "8C32": "jué", + "8C33": "yàn", + "8C34": "qiǎn", + "8C35": "zhān", + "8C36": "chèn", + "8C37": "gǔ", + "8C38": "qiān", + "8C39": "hóng", + "8C3A": "xiā", + "8C3B": "jí", + "8C3C": "hóng", + "8C3D": "hān", + "8C3E": "hōng", + "8C3F": "xī", + "8C40": "xī", + "8C41": "huō", + "8C42": "liáo", + "8C43": "hǎn", + "8C44": "dú", + "8C45": "lóng", + "8C46": "dòu", + "8C47": "jiāng", + "8C48": "qǐ", + "8C49": "shì", + "8C4A": "lǐ", + "8C4B": "dēng", + "8C4C": "wān", + "8C4D": "bī", + "8C4E": "shù", + "8C4F": "xiàn", + "8C50": "fēng", + "8C51": "zhì", + "8C52": "zhì", + "8C53": "yàn", + "8C54": "yàn", + "8C55": "shǐ", + "8C56": "chù", + "8C57": "huī", + "8C58": "tún", + "8C59": "yì", + "8C5A": "tún", + "8C5B": "yì", + "8C5C": "jiān", + "8C5D": "bā", + "8C5E": "hòu", + "8C5F": "è", + "8C60": "chú", + "8C61": "xiàng", + "8C62": "huàn", + "8C63": "jiān", + "8C64": "kěn", + "8C65": "gāi", + "8C66": "jù", + "8C67": "fū", + "8C68": "xī", + "8C69": "bīn", + "8C6A": "háo", + "8C6B": "yù", + "8C6C": "zhū", + "8C6D": "jiā", + "8C6E": "fén", + "8C6F": "xī", + "8C70": "bó", + "8C71": "wēn", + "8C72": "huán", + "8C73": "bīn", + "8C74": "dí", + "8C75": "zōng", + "8C76": "fén", + "8C77": "yì", + "8C78": "zhì", + "8C79": "bào", + "8C7A": "chái", + "8C7B": "àn", + "8C7C": "pí", + "8C7D": "nà", + "8C7E": "pī", + "8C7F": "gǒu", + "8C80": "nà", + "8C81": "yòu", + "8C82": "diāo", + "8C83": "mò", + "8C84": "sì", + "8C85": "xiū", + "8C86": "huán", + "8C87": "kūn", + "8C88": "hé", + "8C89": "háo", + "8C8A": "mò", + "8C8B": "hàn", + "8C8C": "mào", + "8C8D": "lí", + "8C8E": "ní", + "8C8F": "bǐ", + "8C90": "yǔ", + "8C91": "jiā", + "8C92": "tuān", + "8C93": "māo", + "8C94": "pí", + "8C95": "xī", + "8C96": "è", + "8C97": "jù", + "8C98": "mò", + "8C99": "chū", + "8C9A": "tán", + "8C9B": "huān", + "8C9C": "jué", + "8C9D": "bèi", + "8C9E": "zhēn", + "8C9F": "yuán", + "8CA0": "fù", + "8CA1": "cái", + "8CA2": "gòng", + "8CA3": "tè", + "8CA4": "yí", + "8CA5": "háng", + "8CA6": "wàn", + "8CA7": "pín", + "8CA8": "huò", + "8CA9": "fàn", + "8CAA": "tān", + "8CAB": "guàn", + "8CAC": "zé", + "8CAD": "zhí", + "8CAE": "èr", + "8CAF": "zhù", + "8CB0": "shì", + "8CB1": "bì", + "8CB2": "zī", + "8CB3": "èr", + "8CB4": "guì", + "8CB5": "piǎn", + "8CB6": "biǎn", + "8CB7": "mǎi", + "8CB8": "dài", + "8CB9": "shèng", + "8CBA": "kuàng", + "8CBB": "fèi", + "8CBC": "tiē", + "8CBD": "yí", + "8CBE": "chí", + "8CBF": "mào", + "8CC0": "hè", + "8CC1": "bì", + "8CC2": "lù", + "8CC3": "lìn", + "8CC4": "huì", + "8CC5": "gāi", + "8CC6": "pián", + "8CC7": "zī", + "8CC8": "jiǎ", + "8CC9": "xù", + "8CCA": "zéi", + "8CCB": "jiǎo", + "8CCC": "gài", + "8CCD": "zāng", + "8CCE": "jiàn", + "8CCF": "yīng", + "8CD0": "xùn", + "8CD1": "zhèn", + "8CD2": "shē", + "8CD3": "bīn", + "8CD4": "bīn", + "8CD5": "qiú", + "8CD6": "shē", + "8CD7": "chuàn", + "8CD8": "zāng", + "8CD9": "zhōu", + "8CDA": "lài", + "8CDB": "zàn", + "8CDC": "cì", + "8CDD": "chēn", + "8CDE": "shǎng", + "8CDF": "tiǎn", + "8CE0": "péi", + "8CE1": "gēng", + "8CE2": "xián", + "8CE3": "mài", + "8CE4": "jiàn", + "8CE5": "suì", + "8CE6": "fù", + "8CE7": "tàn", + "8CE8": "cóng", + "8CE9": "cóng", + "8CEA": "zhì", + "8CEB": "jī", + "8CEC": "zhàng", + "8CED": "dǔ", + "8CEE": "jìn", + "8CEF": "xiōng", + "8CF0": "chǔn", + "8CF1": "yǔn", + "8CF2": "bǎo", + "8CF3": "zāi", + "8CF4": "lài", + "8CF5": "fèng", + "8CF6": "càng", + "8CF7": "jī", + "8CF8": "shèng", + "8CF9": "yì", + "8CFA": "zhuàn", + "8CFB": "fù", + "8CFC": "gòu", + "8CFD": "sài", + "8CFE": "zé", + "8CFF": "liáo", + "8D00": "yì", + "8D01": "bài", + "8D02": "chěn", + "8D03": "wàn", + "8D04": "zhì", + "8D05": "zhuì", + "8D06": "biāo", + "8D07": "yūn", + "8D08": "zèng", + "8D09": "dàn", + "8D0A": "zàn", + "8D0B": "yàn", + "8D0C": "pu", + "8D0D": "shàn", + "8D0E": "wàn", + "8D0F": "yíng", + "8D10": "jìn", + "8D11": "gàn", + "8D12": "xián", + "8D13": "zāng", + "8D14": "bì", + "8D15": "dú", + "8D16": "shú", + "8D17": "yàn", + "8D18": "shǎng", + "8D19": "xuàn", + "8D1A": "lòng", + "8D1B": "gàn", + "8D1C": "zāng", + "8D1D": "bèi", + "8D1E": "zhēn", + "8D1F": "fù", + "8D20": "yuán", + "8D21": "gòng", + "8D22": "cái", + "8D23": "zé", + "8D24": "xián", + "8D25": "bài", + "8D26": "zhàng", + "8D27": "huò", + "8D28": "zhì", + "8D29": "fàn", + "8D2A": "tān", + "8D2B": "pín", + "8D2C": "biǎn", + "8D2D": "gòu", + "8D2E": "zhù", + "8D2F": "guàn", + "8D30": "èr", + "8D31": "jiàn", + "8D32": "bēn", + "8D33": "shì", + "8D34": "tiē", + "8D35": "guì", + "8D36": "kuàng", + "8D37": "dài", + "8D38": "mào", + "8D39": "fèi", + "8D3A": "hè", + "8D3B": "yí", + "8D3C": "zéi", + "8D3D": "zhì", + "8D3E": "jiǎ", + "8D3F": "huì", + "8D40": "zī", + "8D41": "lìn", + "8D42": "lù", + "8D43": "zāng", + "8D44": "zī", + "8D45": "gāi", + "8D46": "jìn", + "8D47": "qiú", + "8D48": "zhèn", + "8D49": "lài", + "8D4A": "shē", + "8D4B": "fù", + "8D4C": "dǔ", + "8D4D": "jī", + "8D4E": "shú", + "8D4F": "shǎng", + "8D50": "cì", + "8D51": "bì", + "8D52": "zhōu", + "8D53": "gēng", + "8D54": "péi", + "8D55": "dǎn", + "8D56": "lài", + "8D57": "fèng", + "8D58": "zhuì", + "8D59": "fù", + "8D5A": "zhuàn", + "8D5B": "sài", + "8D5C": "zé", + "8D5D": "yàn", + "8D5E": "zàn", + "8D5F": "yūn", + "8D60": "zèng", + "8D61": "shàn", + "8D62": "yíng", + "8D63": "gàn", + "8D64": "chì", + "8D65": "xī", + "8D66": "shè", + "8D67": "nǎn", + "8D68": "tóng", + "8D69": "xì", + "8D6A": "chēng", + "8D6B": "hè", + "8D6C": "chēng", + "8D6D": "zhě", + "8D6E": "xiá", + "8D6F": "táng", + "8D70": "zǒu", + "8D71": "zǒu", + "8D72": "lì", + "8D73": "jiū", + "8D74": "fù", + "8D75": "zhào", + "8D76": "gǎn", + "8D77": "qǐ", + "8D78": "shàn", + "8D79": "qióng", + "8D7A": "yǐn", + "8D7B": "xiǎn", + "8D7C": "cī", + "8D7D": "jué", + "8D7E": "qǐn", + "8D7F": "chí", + "8D80": "cī", + "8D81": "chèn", + "8D82": "chèn", + "8D83": "dié", + "8D84": "jū", + "8D85": "chāo", + "8D86": "dī", + "8D87": "xì", + "8D88": "zhān", + "8D89": "jué", + "8D8A": "yuè", + "8D8B": "qū", + "8D8C": "jí", + "8D8D": "chí", + "8D8E": "chú", + "8D8F": "guā", + "8D90": "xuè", + "8D91": "zī", + "8D92": "tiáo", + "8D93": "duǒ", + "8D94": "liè", + "8D95": "gǎn", + "8D96": "suō", + "8D97": "cù", + "8D98": "xí", + "8D99": "zhào", + "8D9A": "sù", + "8D9B": "yǐn", + "8D9C": "jú", + "8D9D": "jiàn", + "8D9E": "què", + "8D9F": "tàng", + "8DA0": "chuò", + "8DA1": "cuǐ", + "8DA2": "lù", + "8DA3": "qù", + "8DA4": "dàng", + "8DA5": "qiū", + "8DA6": "zī", + "8DA7": "tí", + "8DA8": "qū", + "8DA9": "chì", + "8DAA": "huáng", + "8DAB": "qiáo", + "8DAC": "qiāo", + "8DAD": "jiào", + "8DAE": "zào", + "8DAF": "tì", + "8DB0": "ěr", + "8DB1": "zǎn", + "8DB2": "zǎn", + "8DB3": "zú", + "8DB4": "pā", + "8DB5": "bào", + "8DB6": "kù", + "8DB7": "kē", + "8DB8": "dǔn", + "8DB9": "jué", + "8DBA": "fū", + "8DBB": "chěn", + "8DBC": "jiǎn", + "8DBD": "fàng", + "8DBE": "zhǐ", + "8DBF": "tā", + "8DC0": "yuè", + "8DC1": "bà", + "8DC2": "qí", + "8DC3": "yuè", + "8DC4": "qiāng", + "8DC5": "tuò", + "8DC6": "tái", + "8DC7": "yì", + "8DC8": "niǎn", + "8DC9": "líng", + "8DCA": "mèi", + "8DCB": "bá", + "8DCC": "diē", + "8DCD": "kū", + "8DCE": "tuó", + "8DCF": "jiā", + "8DD0": "cī", + "8DD1": "pǎo", + "8DD2": "qiǎ", + "8DD3": "zhù", + "8DD4": "jū", + "8DD5": "diǎn", + "8DD6": "zhí", + "8DD7": "fū", + "8DD8": "pán", + "8DD9": "jù", + "8DDA": "shān", + "8DDB": "bǒ", + "8DDC": "ní", + "8DDD": "jù", + "8DDE": "lì", + "8DDF": "gēn", + "8DE0": "yí", + "8DE1": "jī", + "8DE2": "duò", + "8DE3": "xiǎn", + "8DE4": "jiāo", + "8DE5": "duò", + "8DE6": "zhū", + "8DE7": "quán", + "8DE8": "kuà", + "8DE9": "zhuǎi", + "8DEA": "guì", + "8DEB": "qióng", + "8DEC": "kuǐ", + "8DED": "xiáng", + "8DEE": "chì", + "8DEF": "lù", + "8DF0": "pián", + "8DF1": "zhì", + "8DF2": "jiá", + "8DF3": "tiào", + "8DF4": "cǎi", + "8DF5": "jiàn", + "8DF6": "tà", + "8DF7": "qiāo", + "8DF8": "bì", + "8DF9": "xiān", + "8DFA": "duò", + "8DFB": "jī", + "8DFC": "jú", + "8DFD": "jì", + "8DFE": "shū", + "8DFF": "tú", + "8E00": "chù", + "8E01": "jìng", + "8E02": "niè", + "8E03": "xiāo", + "8E04": "bù", + "8E05": "xué", + "8E06": "cūn", + "8E07": "mǔ", + "8E08": "shū", + "8E09": "liáng", + "8E0A": "yǒng", + "8E0B": "jiǎo", + "8E0C": "chóu", + "8E0D": "qiāo", + "8E0E": "móu", + "8E0F": "tà", + "8E10": "jiàn", + "8E11": "qí", + "8E12": "wō", + "8E13": "wěi", + "8E14": "chuō", + "8E15": "jié", + "8E16": "jí", + "8E17": "niè", + "8E18": "jū", + "8E19": "jū", + "8E1A": "lún", + "8E1B": "lù", + "8E1C": "lèng", + "8E1D": "huái", + "8E1E": "jù", + "8E1F": "chí", + "8E20": "wǎn", + "8E21": "quán", + "8E22": "tī", + "8E23": "bó", + "8E24": "zú", + "8E25": "qiè", + "8E26": "yǐ", + "8E27": "cù", + "8E28": "zōng", + "8E29": "cǎi", + "8E2A": "zōng", + "8E2B": "pèng", + "8E2C": "zhì", + "8E2D": "zhēng", + "8E2E": "diǎn", + "8E2F": "zhí", + "8E30": "yú", + "8E31": "duó", + "8E32": "dùn", + "8E33": "chuǎn", + "8E34": "yǒng", + "8E35": "zhǒng", + "8E36": "dì", + "8E37": "zhǎ", + "8E38": "chěn", + "8E39": "chuài", + "8E3A": "jiàn", + "8E3B": "guā", + "8E3C": "táng", + "8E3D": "jǔ", + "8E3E": "fú", + "8E3F": "zú", + "8E40": "dié", + "8E41": "pián", + "8E42": "róu", + "8E43": "nuò", + "8E44": "tí", + "8E45": "chǎ", + "8E46": "tuǐ", + "8E47": "jiǎn", + "8E48": "dǎo", + "8E49": "cuō", + "8E4A": "qī", + "8E4B": "tà", + "8E4C": "qiāng", + "8E4D": "niǎn", + "8E4E": "diān", + "8E4F": "tí", + "8E50": "jí", + "8E51": "niè", + "8E52": "pán", + "8E53": "liū", + "8E54": "zàn", + "8E55": "bì", + "8E56": "chōng", + "8E57": "lù", + "8E58": "liáo", + "8E59": "cù", + "8E5A": "tāng", + "8E5B": "dài", + "8E5C": "sù", + "8E5D": "xǐ", + "8E5E": "kuǐ", + "8E5F": "jī", + "8E60": "zhí", + "8E61": "qiāng", + "8E62": "dí", + "8E63": "pán", + "8E64": "zōng", + "8E65": "lián", + "8E66": "bèng", + "8E67": "zāo", + "8E68": "niǎn", + "8E69": "bié", + "8E6A": "tuí", + "8E6B": "jú", + "8E6C": "dēng", + "8E6D": "cèng", + "8E6E": "xiān", + "8E6F": "fán", + "8E70": "chú", + "8E71": "zhōng", + "8E72": "dūn", + "8E73": "bō", + "8E74": "cù", + "8E75": "cù", + "8E76": "jué", + "8E77": "jué", + "8E78": "lìn", + "8E79": "tá", + "8E7A": "qiāo", + "8E7B": "juē", + "8E7C": "pǔ", + "8E7D": "liāo", + "8E7E": "dūn", + "8E7F": "cuān", + "8E80": "kuàng", + "8E81": "zào", + "8E82": "dá", + "8E83": "bì", + "8E84": "bì", + "8E85": "zhú", + "8E86": "jù", + "8E87": "chú", + "8E88": "qiào", + "8E89": "dǔn", + "8E8A": "chóu", + "8E8B": "jī", + "8E8C": "wǔ", + "8E8D": "yuè", + "8E8E": "niǎn", + "8E8F": "lìn", + "8E90": "liè", + "8E91": "zhí", + "8E92": "lì", + "8E93": "zhì", + "8E94": "chán", + "8E95": "chú", + "8E96": "duàn", + "8E97": "wèi", + "8E98": "lóng", + "8E99": "lìn", + "8E9A": "xiān", + "8E9B": "wèi", + "8E9C": "zuān", + "8E9D": "lán", + "8E9E": "xiè", + "8E9F": "ráng", + "8EA0": "sǎ", + "8EA1": "niè", + "8EA2": "tà", + "8EA3": "qú", + "8EA4": "jiè", + "8EA5": "cuān", + "8EA6": "cuó", + "8EA7": "xǐ", + "8EA8": "kuí", + "8EA9": "jué", + "8EAA": "lìn", + "8EAB": "shēn", + "8EAC": "gōng", + "8EAD": "dān", + "8EAE": "fen", + "8EAF": "qū", + "8EB0": "tǐ", + "8EB1": "duǒ", + "8EB2": "duǒ", + "8EB3": "gōng", + "8EB4": "láng", + "8EB5": "ren", + "8EB6": "luǒ", + "8EB7": "ǎi", + "8EB8": "jī", + "8EB9": "jú", + "8EBA": "tǎng", + "8EBB": "kong", + "8EBC": "lào", + "8EBD": "yǎn", + "8EBE": "mei", + "8EBF": "kāng", + "8EC0": "qū", + "8EC1": "lóu", + "8EC2": "lào", + "8EC3": "duǒ", + "8EC4": "zhí", + "8EC5": "yan", + "8EC6": "tǐ", + "8EC7": "dào", + "8EC8": "ying", + "8EC9": "yù", + "8ECA": "chē", + "8ECB": "yà", + "8ECC": "guǐ", + "8ECD": "jūn", + "8ECE": "wèi", + "8ECF": "yuè", + "8ED0": "xìn", + "8ED1": "dài", + "8ED2": "xuān", + "8ED3": "fàn", + "8ED4": "rèn", + "8ED5": "shān", + "8ED6": "kuáng", + "8ED7": "shū", + "8ED8": "tún", + "8ED9": "chén", + "8EDA": "dài", + "8EDB": "è", + "8EDC": "nà", + "8EDD": "qí", + "8EDE": "máo", + "8EDF": "ruǎn", + "8EE0": "rèn", + "8EE1": "qián", + "8EE2": "zhuǎn", + "8EE3": "hōng", + "8EE4": "hū", + "8EE5": "qú", + "8EE6": "kuàng", + "8EE7": "dǐ", + "8EE8": "líng", + "8EE9": "dài", + "8EEA": "āo", + "8EEB": "zhěn", + "8EEC": "fàn", + "8EED": "kuāng", + "8EEE": "yǎng", + "8EEF": "pēng", + "8EF0": "bèi", + "8EF1": "gū", + "8EF2": "gū", + "8EF3": "páo", + "8EF4": "zhù", + "8EF5": "rǒng", + "8EF6": "è", + "8EF7": "bá", + "8EF8": "zhóu", + "8EF9": "zhǐ", + "8EFA": "yáo", + "8EFB": "kē", + "8EFC": "yì", + "8EFD": "zhì", + "8EFE": "shì", + "8EFF": "píng", + "8F00": "ér", + "8F01": "gǒng", + "8F02": "jú", + "8F03": "jiào", + "8F04": "guāng", + "8F05": "hé", + "8F06": "kǎi", + "8F07": "quán", + "8F08": "zhōu", + "8F09": "zài", + "8F0A": "zhì", + "8F0B": "shē", + "8F0C": "liàng", + "8F0D": "yù", + "8F0E": "shāo", + "8F0F": "yóu", + "8F10": "wàn", + "8F11": "yǐn", + "8F12": "zhé", + "8F13": "wǎn", + "8F14": "fǔ", + "8F15": "qīng", + "8F16": "zhōu", + "8F17": "ní", + "8F18": "léng", + "8F19": "zhé", + "8F1A": "zhàn", + "8F1B": "liàng", + "8F1C": "zī", + "8F1D": "huī", + "8F1E": "wǎng", + "8F1F": "chuò", + "8F20": "guǒ", + "8F21": "kǎn", + "8F22": "yǐ", + "8F23": "péng", + "8F24": "qiàn", + "8F25": "gǔn", + "8F26": "niǎn", + "8F27": "píng", + "8F28": "guǎn", + "8F29": "bèi", + "8F2A": "lún", + "8F2B": "pái", + "8F2C": "liáng", + "8F2D": "ruǎn", + "8F2E": "róu", + "8F2F": "ji", + "8F30": "yáng", + "8F31": "xián", + "8F32": "chuán", + "8F33": "còu", + "8F34": "chūn", + "8F35": "gé", + "8F36": "yóu", + "8F37": "hōng", + "8F38": "shū", + "8F39": "fù", + "8F3A": "zī", + "8F3B": "fú", + "8F3C": "wēn", + "8F3D": "bèn", + "8F3E": "zhǎn", + "8F3F": "yú", + "8F40": "wēn", + "8F41": "tāo", + "8F42": "gǔ", + "8F43": "zhēn", + "8F44": "xiá", + "8F45": "yuán", + "8F46": "lù", + "8F47": "jiāo", + "8F48": "cháo", + "8F49": "zhuǎn", + "8F4A": "wèi", + "8F4B": "hún", + "8F4C": "xue", + "8F4D": "zhé", + "8F4E": "jiào", + "8F4F": "zhàn", + "8F50": "bú", + "8F51": "lǎo", + "8F52": "fén", + "8F53": "fān", + "8F54": "lín", + "8F55": "gé", + "8F56": "sè", + "8F57": "kǎn", + "8F58": "huán", + "8F59": "yǐ", + "8F5A": "jí", + "8F5B": "zhuì", + "8F5C": "ér", + "8F5D": "yù", + "8F5E": "jiàn", + "8F5F": "hōng", + "8F60": "léi", + "8F61": "pèi", + "8F62": "lì", + "8F63": "lì", + "8F64": "lú", + "8F65": "lìn", + "8F66": "chē", + "8F67": "yà", + "8F68": "guǐ", + "8F69": "xuān", + "8F6A": "dài", + "8F6B": "rèn", + "8F6C": "zhuǎn", + "8F6D": "è", + "8F6E": "lún", + "8F6F": "ruǎn", + "8F70": "hōng", + "8F71": "gū", + "8F72": "kē", + "8F73": "lú", + "8F74": "zhóu", + "8F75": "zhǐ", + "8F76": "yì", + "8F77": "hū", + "8F78": "zhěn", + "8F79": "lì", + "8F7A": "yáo", + "8F7B": "qīng", + "8F7C": "shì", + "8F7D": "zài", + "8F7E": "zhì", + "8F7F": "jiào", + "8F80": "zhōu", + "8F81": "quán", + "8F82": "lù", + "8F83": "jiào", + "8F84": "zhé", + "8F85": "fǔ", + "8F86": "liàng", + "8F87": "niǎn", + "8F88": "bèi", + "8F89": "huī", + "8F8A": "gǔn", + "8F8B": "wǎng", + "8F8C": "liáng", + "8F8D": "chuò", + "8F8E": "zī", + "8F8F": "còu", + "8F90": "fú", + "8F91": "ji", + "8F92": "wēn", + "8F93": "shū", + "8F94": "pèi", + "8F95": "yuán", + "8F96": "xiá", + "8F97": "niǎn", + "8F98": "lù", + "8F99": "zhé", + "8F9A": "lín", + "8F9B": "xīn", + "8F9C": "gū", + "8F9D": "cí", + "8F9E": "cí", + "8F9F": "pì", + "8FA0": "zuì", + "8FA1": "biàn", + "8FA2": "là", + "8FA3": "là", + "8FA4": "cí", + "8FA5": "xuē", + "8FA6": "bàn", + "8FA7": "biàn", + "8FA8": "biàn", + "8FA9": "biàn", + "8FAA": "xuē", + "8FAB": "biàn", + "8FAC": "bān", + "8FAD": "cí", + "8FAE": "biàn", + "8FAF": "biàn", + "8FB0": "chén", + "8FB1": "rǔ", + "8FB2": "nóng", + "8FB3": "nóng", + "8FB4": "chǎn", + "8FB5": "chuò", + "8FB6": "chuò", + "8FB7": "yi", + "8FB8": "réng", + "8FB9": "biān", + "8FBA": "biān", + "8FBB": "shí", + "8FBC": "ru", + "8FBD": "liáo", + "8FBE": "dá", + "8FBF": "chān", + "8FC0": "gān", + "8FC1": "qiān", + "8FC2": "yū", + "8FC3": "yū", + "8FC4": "qì", + "8FC5": "xùn", + "8FC6": "yí", + "8FC7": "guò", + "8FC8": "mài", + "8FC9": "qī", + "8FCA": "zā", + "8FCB": "wàng", + "8FCC": "tù", + "8FCD": "zhūn", + "8FCE": "yíng", + "8FCF": "tì", + "8FD0": "yùn", + "8FD1": "jìn", + "8FD2": "háng", + "8FD3": "yà", + "8FD4": "fǎn", + "8FD5": "wù", + "8FD6": "dá", + "8FD7": "é", + "8FD8": "hái", + "8FD9": "zhè", + "8FDA": "zhong", + "8FDB": "jìn", + "8FDC": "yuǎn", + "8FDD": "wéi", + "8FDE": "lián", + "8FDF": "chí", + "8FE0": "chè", + "8FE1": "nì", + "8FE2": "tiáo", + "8FE3": "zhì", + "8FE4": "yí", + "8FE5": "jiǒng", + "8FE6": "jiā", + "8FE7": "chén", + "8FE8": "dài", + "8FE9": "ěr", + "8FEA": "dí", + "8FEB": "pò", + "8FEC": "zhù", + "8FED": "dié", + "8FEE": "zé", + "8FEF": "táo", + "8FF0": "shù", + "8FF1": "tuó", + "8FF2": "qu", + "8FF3": "jìng", + "8FF4": "huí", + "8FF5": "dòng", + "8FF6": "yòu", + "8FF7": "mí", + "8FF8": "bèng", + "8FF9": "jī", + "8FFA": "nǎi", + "8FFB": "yí", + "8FFC": "jié", + "8FFD": "zhuī", + "8FFE": "liè", + "8FFF": "xùn", + "9000": "tuì", + "9001": "sòng", + "9002": "shì", + "9003": "táo", + "9004": "páng", + "9005": "hòu", + "9006": "nì", + "9007": "dùn", + "9008": "jiǒng", + "9009": "xuǎn", + "900A": "xùn", + "900B": "bū", + "900C": "yōu", + "900D": "xiāo", + "900E": "qiú", + "900F": "tòu", + "9010": "zhú", + "9011": "qiú", + "9012": "dì", + "9013": "dì", + "9014": "tú", + "9015": "jìng", + "9016": "tì", + "9017": "dòu", + "9018": "yǐ", + "9019": "zhè", + "901A": "tōng", + "901B": "guàng", + "901C": "wù", + "901D": "shì", + "901E": "chěng", + "901F": "sù", + "9020": "zào", + "9021": "qūn", + "9022": "féng", + "9023": "lián", + "9024": "suò", + "9025": "huí", + "9026": "lǐ", + "9027": "gu", + "9028": "lái", + "9029": "bèn", + "902A": "cuò", + "902B": "jué", + "902C": "bèng", + "902D": "huàn", + "902E": "dǎi", + "902F": "lù", + "9030": "yóu", + "9031": "zhōu", + "9032": "jìn", + "9033": "yù", + "9034": "chuō", + "9035": "kuí", + "9036": "wēi", + "9037": "tì", + "9038": "yì", + "9039": "dá", + "903A": "yuǎn", + "903B": "luó", + "903C": "bī", + "903D": "nuò", + "903E": "yú", + "903F": "dàng", + "9040": "suí", + "9041": "dùn", + "9042": "suì", + "9043": "yǎn", + "9044": "chuán", + "9045": "chí", + "9046": "tí", + "9047": "yù", + "9048": "shí", + "9049": "zhēn", + "904A": "yóu", + "904B": "yùn", + "904C": "è", + "904D": "biàn", + "904E": "guò", + "904F": "è", + "9050": "xiá", + "9051": "huáng", + "9052": "qiú", + "9053": "dào", + "9054": "dá", + "9055": "wéi", + "9056": "nan", + "9057": "yí", + "9058": "gòu", + "9059": "yáo", + "905A": "chòu", + "905B": "liú", + "905C": "xùn", + "905D": "tà", + "905E": "dì", + "905F": "chí", + "9060": "yuǎn", + "9061": "sù", + "9062": "tà", + "9063": "qiǎn", + "9064": "ma", + "9065": "yáo", + "9066": "guàn", + "9067": "zhāng", + "9068": "áo", + "9069": "shì", + "906A": "cà", + "906B": "chì", + "906C": "sù", + "906D": "zāo", + "906E": "zhē", + "906F": "dùn", + "9070": "dì", + "9071": "lóu", + "9072": "chí", + "9073": "cuō", + "9074": "lín", + "9075": "zūn", + "9076": "rào", + "9077": "qiān", + "9078": "xuǎn", + "9079": "yù", + "907A": "yí", + "907B": "wù", + "907C": "liáo", + "907D": "jù", + "907E": "shì", + "907F": "bì", + "9080": "yāo", + "9081": "mài", + "9082": "xiè", + "9083": "suì", + "9084": "hái", + "9085": "zhān", + "9086": "téng", + "9087": "ěr", + "9088": "miǎo", + "9089": "biān", + "908A": "biān", + "908B": "lā", + "908C": "lí", + "908D": "yuán", + "908E": "yáo", + "908F": "luó", + "9090": "lǐ", + "9091": "yì", + "9092": "tíng", + "9093": "dèng", + "9094": "qǐ", + "9095": "yōng", + "9096": "shān", + "9097": "hán", + "9098": "yú", + "9099": "máng", + "909A": "rú", + "909B": "qióng", + "909C": "wan", + "909D": "kuàng", + "909E": "fū", + "909F": "kàng", + "90A0": "bīn", + "90A1": "fāng", + "90A2": "xíng", + "90A3": "nà", + "90A4": "xīn", + "90A5": "shěn", + "90A6": "bāng", + "90A7": "yuán", + "90A8": "cūn", + "90A9": "huǒ", + "90AA": "xié", + "90AB": "bāng", + "90AC": "wū", + "90AD": "jù", + "90AE": "yóu", + "90AF": "hán", + "90B0": "tái", + "90B1": "qiū", + "90B2": "bì", + "90B3": "pī", + "90B4": "bǐng", + "90B5": "shào", + "90B6": "bèi", + "90B7": "wǎ", + "90B8": "dǐ", + "90B9": "zōu", + "90BA": "yè", + "90BB": "lín", + "90BC": "kuāng", + "90BD": "guī", + "90BE": "zhū", + "90BF": "shī", + "90C0": "kū", + "90C1": "yù", + "90C2": "gāi", + "90C3": "hé", + "90C4": "qiè", + "90C5": "zhì", + "90C6": "jí", + "90C7": "huán", + "90C8": "hòu", + "90C9": "xíng", + "90CA": "jiāo", + "90CB": "xí", + "90CC": "guī", + "90CD": "nuó", + "90CE": "láng", + "90CF": "jiá", + "90D0": "kuài", + "90D1": "zhèng", + "90D2": "lang", + "90D3": "yùn", + "90D4": "yán", + "90D5": "chéng", + "90D6": "dòu", + "90D7": "xī", + "90D8": "lǘ", + "90D9": "fǔ", + "90DA": "wú", + "90DB": "fú", + "90DC": "gào", + "90DD": "hǎo", + "90DE": "láng", + "90DF": "jiá", + "90E0": "gěng", + "90E1": "jùn", + "90E2": "yǐng", + "90E3": "bó", + "90E4": "xì", + "90E5": "bèi", + "90E6": "lì", + "90E7": "yún", + "90E8": "bù", + "90E9": "xiáo", + "90EA": "qī", + "90EB": "pí", + "90EC": "qīng", + "90ED": "guō", + "90EE": "zhōu", + "90EF": "tán", + "90F0": "zōu", + "90F1": "píng", + "90F2": "lái", + "90F3": "ní", + "90F4": "chēn", + "90F5": "yóu", + "90F6": "bù", + "90F7": "xiāng", + "90F8": "dān", + "90F9": "jú", + "90FA": "yōng", + "90FB": "qiāo", + "90FC": "yī", + "90FD": "dōu", + "90FE": "yǎn", + "90FF": "méi", + "9100": "ruò", + "9101": "bèi", + "9102": "è", + "9103": "shū", + "9104": "juàn", + "9105": "yǔ", + "9106": "yùn", + "9107": "hóu", + "9108": "kuí", + "9109": "xiāng", + "910A": "xiāng", + "910B": "sōu", + "910C": "táng", + "910D": "míng", + "910E": "xī", + "910F": "rǔ", + "9110": "chù", + "9111": "zī", + "9112": "zōu", + "9113": "yè", + "9114": "wū", + "9115": "xiāng", + "9116": "yún", + "9117": "hào", + "9118": "yōng", + "9119": "bǐ", + "911A": "mào", + "911B": "cháo", + "911C": "fū", + "911D": "liǎo", + "911E": "yín", + "911F": "zhuān", + "9120": "hù", + "9121": "qiāo", + "9122": "yān", + "9123": "zhāng", + "9124": "màn", + "9125": "qiāo", + "9126": "xǔ", + "9127": "dèng", + "9128": "bì", + "9129": "xún", + "912A": "bì", + "912B": "zēng", + "912C": "wéi", + "912D": "zhèng", + "912E": "mào", + "912F": "shàn", + "9130": "lín", + "9131": "pó", + "9132": "dān", + "9133": "méng", + "9134": "yè", + "9135": "cào", + "9136": "kuài", + "9137": "fēng", + "9138": "méng", + "9139": "zōu", + "913A": "kuàng", + "913B": "liǎn", + "913C": "zàn", + "913D": "chán", + "913E": "yōu", + "913F": "jī", + "9140": "yàn", + "9141": "chán", + "9142": "cuó", + "9143": "líng", + "9144": "huān", + "9145": "xī", + "9146": "fēng", + "9147": "zàn", + "9148": "lì", + "9149": "yǒu", + "914A": "dīng", + "914B": "qiú", + "914C": "zhuó", + "914D": "pèi", + "914E": "zhòu", + "914F": "yǐ", + "9150": "gān", + "9151": "yú", + "9152": "jiǔ", + "9153": "yǎn", + "9154": "zuì", + "9155": "máo", + "9156": "zhèn", + "9157": "xù", + "9158": "dòu", + "9159": "zhēn", + "915A": "fēn", + "915B": "yuan", + "915C": "fu", + "915D": "yùn", + "915E": "tài", + "915F": "tiān", + "9160": "qiǎ", + "9161": "tuó", + "9162": "cù", + "9163": "hān", + "9164": "gū", + "9165": "sū", + "9166": "fā", + "9167": "chóu", + "9168": "zài", + "9169": "mǐng", + "916A": "lào", + "916B": "chuò", + "916C": "chou", + "916D": "yòu", + "916E": "tóng", + "916F": "zhǐ", + "9170": "xiān", + "9171": "jiàng", + "9172": "chéng", + "9173": "yìn", + "9174": "tú", + "9175": "jiào", + "9176": "méi", + "9177": "kù", + "9178": "suān", + "9179": "lèi", + "917A": "pú", + "917B": "zuì", + "917C": "hǎi", + "917D": "yàn", + "917E": "shāi", + "917F": "niàng", + "9180": "wéi", + "9181": "lù", + "9182": "lǎn", + "9183": "yān", + "9184": "táo", + "9185": "pēi", + "9186": "zhǎn", + "9187": "chún", + "9188": "tán", + "9189": "zuì", + "918A": "zhuì", + "918B": "cù", + "918C": "kūn", + "918D": "tí", + "918E": "xián", + "918F": "dū", + "9190": "hú", + "9191": "xǔ", + "9192": "xǐng", + "9193": "tǎn", + "9194": "qiú", + "9195": "chún", + "9196": "yùn", + "9197": "pò", + "9198": "kē", + "9199": "sōu", + "919A": "mí", + "919B": "quán", + "919C": "chǒu", + "919D": "cuō", + "919E": "yùn", + "919F": "yòng", + "91A0": "àng", + "91A1": "zhà", + "91A2": "hǎi", + "91A3": "táng", + "91A4": "jiàng", + "91A5": "piǎo", + "91A6": "chěn", + "91A7": "yù", + "91A8": "lí", + "91A9": "zāo", + "91AA": "láo", + "91AB": "yī", + "91AC": "jiàng", + "91AD": "bú", + "91AE": "jiào", + "91AF": "xī", + "91B0": "tán", + "91B1": "fā", + "91B2": "nóng", + "91B3": "yì", + "91B4": "lǐ", + "91B5": "jù", + "91B6": "yàn", + "91B7": "yì", + "91B8": "niàng", + "91B9": "rú", + "91BA": "xūn", + "91BB": "chóu", + "91BC": "yàn", + "91BD": "líng", + "91BE": "mí", + "91BF": "mí", + "91C0": "niàng", + "91C1": "xìn", + "91C2": "jiào", + "91C3": "shāi", + "91C4": "mí", + "91C5": "yàn", + "91C6": "biàn", + "91C7": "cǎi", + "91C8": "shì", + "91C9": "yòu", + "91CA": "shì", + "91CB": "shì", + "91CC": "lǐ", + "91CD": "zhòng", + "91CE": "yě", + "91CF": "liàng", + "91D0": "lí", + "91D1": "jīn", + "91D2": "jin", + "91D3": "qiú", + "91D4": "yǐ", + "91D5": "liǎo", + "91D6": "dāo", + "91D7": "zhāo", + "91D8": "dīng", + "91D9": "pò", + "91DA": "qiú", + "91DB": "bā", + "91DC": "fǔ", + "91DD": "zhēn", + "91DE": "zhí", + "91DF": "bā", + "91E0": "luàn", + "91E1": "fǔ", + "91E2": "nǎi", + "91E3": "diào", + "91E4": "shàn", + "91E5": "qiǎo", + "91E6": "kòu", + "91E7": "chuàn", + "91E8": "zǐ", + "91E9": "fǎn", + "91EA": "huá", + "91EB": "huá", + "91EC": "hàn", + "91ED": "gāng", + "91EE": "qí", + "91EF": "máng", + "91F0": "rì", + "91F1": "dì", + "91F2": "sì", + "91F3": "xì", + "91F4": "yì", + "91F5": "chāi", + "91F6": "shī", + "91F7": "tǔ", + "91F8": "xī", + "91F9": "nǚ", + "91FA": "qiān", + "91FB": "qiu", + "91FC": "jiàn", + "91FD": "pì", + "91FE": "yé", + "91FF": "jīn", + "9200": "bǎ", + "9201": "fāng", + "9202": "chén", + "9203": "xíng", + "9204": "dǒu", + "9205": "yuè", + "9206": "qiān", + "9207": "fū", + "9208": "pī", + "9209": "nà", + "920A": "xīn", + "920B": "é", + "920C": "jué", + "920D": "dùn", + "920E": "gōu", + "920F": "yǐn", + "9210": "qián", + "9211": "bǎn", + "9212": "sà", + "9213": "rén", + "9214": "chāo", + "9215": "niǔ", + "9216": "fēn", + "9217": "yǔn", + "9218": "jǐ", + "9219": "qín", + "921A": "pī", + "921B": "guō", + "921C": "hóng", + "921D": "yín", + "921E": "jūn", + "921F": "shī", + "9220": "yì", + "9221": "zhōng", + "9222": "xǐ", + "9223": "gài", + "9224": "rì", + "9225": "huǒ", + "9226": "tài", + "9227": "kàng", + "9228": "yuan", + "9229": "lu", + "922A": "è", + "922B": "wen", + "922C": "duó", + "922D": "zī", + "922E": "nǐ", + "922F": "tú", + "9230": "shì", + "9231": "mín", + "9232": "gū", + "9233": "kē", + "9234": "líng", + "9235": "bǐng", + "9236": "sì", + "9237": "gǔ", + "9238": "bó", + "9239": "pī", + "923A": "yù", + "923B": "sì", + "923C": "zuó", + "923D": "bū", + "923E": "yóu", + "923F": "tián", + "9240": "jiǎ", + "9241": "zhēn", + "9242": "shǐ", + "9243": "shì", + "9244": "zhí", + "9245": "jù", + "9246": "chān", + "9247": "shī", + "9248": "shī", + "9249": "xuàn", + "924A": "zhāo", + "924B": "bào", + "924C": "hé", + "924D": "bì", + "924E": "shēng", + "924F": "chú", + "9250": "shí", + "9251": "bó", + "9252": "zhù", + "9253": "chì", + "9254": "zā", + "9255": "pō", + "9256": "tóng", + "9257": "qián", + "9258": "fú", + "9259": "zhǎi", + "925A": "liǔ", + "925B": "qiān", + "925C": "fú", + "925D": "lì", + "925E": "yuè", + "925F": "pī", + "9260": "yāng", + "9261": "bàn", + "9262": "bō", + "9263": "jié", + "9264": "gōu", + "9265": "shù", + "9266": "zhēng", + "9267": "mǔ", + "9268": "xǐ", + "9269": "xǐ", + "926A": "dì", + "926B": "jiā", + "926C": "mù", + "926D": "tǎn", + "926E": "huán", + "926F": "yǐ", + "9270": "sī", + "9271": "kuàng", + "9272": "kǎ", + "9273": "běi", + "9274": "jiàn", + "9275": "tóng", + "9276": "xíng", + "9277": "hóng", + "9278": "jiǎo", + "9279": "chǐ", + "927A": "èr", + "927B": "luò", + "927C": "bǐng", + "927D": "shì", + "927E": "móu", + "927F": "jiā", + "9280": "yín", + "9281": "jūn", + "9282": "zhōu", + "9283": "chòng", + "9284": "xiǎng", + "9285": "tóng", + "9286": "mò", + "9287": "lèi", + "9288": "jī", + "9289": "yù", + "928A": "xù", + "928B": "rén", + "928C": "zùn", + "928D": "zhì", + "928E": "qióng", + "928F": "shàn", + "9290": "chì", + "9291": "xiǎn", + "9292": "xíng", + "9293": "quán", + "9294": "pī", + "9295": "tiě", + "9296": "zhū", + "9297": "xiàng", + "9298": "míng", + "9299": "kuǎ", + "929A": "yáo", + "929B": "xiān", + "929C": "xián", + "929D": "xiū", + "929E": "jūn", + "929F": "chā", + "92A0": "lǎo", + "92A1": "jí", + "92A2": "pǐ", + "92A3": "rú", + "92A4": "mǐ", + "92A5": "yī", + "92A6": "yīn", + "92A7": "guāng", + "92A8": "ǎn", + "92A9": "diū", + "92AA": "yǒu", + "92AB": "sè", + "92AC": "kào", + "92AD": "qián", + "92AE": "luán", + "92AF": "si", + "92B0": "āi", + "92B1": "diào", + "92B2": "hàn", + "92B3": "ruì", + "92B4": "shì", + "92B5": "kēng", + "92B6": "qiú", + "92B7": "xiāo", + "92B8": "zhé", + "92B9": "xiù", + "92BA": "zàng", + "92BB": "tí", + "92BC": "cuò", + "92BD": "guā", + "92BE": "hòng", + "92BF": "zhōng", + "92C0": "tōu", + "92C1": "lǚ", + "92C2": "méi", + "92C3": "láng", + "92C4": "wǎn", + "92C5": "xīn", + "92C6": "yún", + "92C7": "bèi", + "92C8": "wù", + "92C9": "sù", + "92CA": "yù", + "92CB": "chán", + "92CC": "dìng", + "92CD": "bó", + "92CE": "hàn", + "92CF": "jiá", + "92D0": "hóng", + "92D1": "cuān", + "92D2": "fēng", + "92D3": "chān", + "92D4": "wǎn", + "92D5": "zhì", + "92D6": "sī", + "92D7": "xuān", + "92D8": "huá", + "92D9": "yǔ", + "92DA": "tiáo", + "92DB": "gǒng", + "92DC": "zhuó", + "92DD": "lüè", + "92DE": "xíng", + "92DF": "qǐn", + "92E0": "shèn", + "92E1": "hán", + "92E2": "lüè", + "92E3": "yé", + "92E4": "chú", + "92E5": "zèng", + "92E6": "jū", + "92E7": "xiàn", + "92E8": "tiě", + "92E9": "máng", + "92EA": "pù", + "92EB": "lí", + "92EC": "pàn", + "92ED": "ruì", + "92EE": "chéng", + "92EF": "gào", + "92F0": "lǐ", + "92F1": "tè", + "92F2": "bing", + "92F3": "zhù", + "92F4": "zhen", + "92F5": "tū", + "92F6": "liǔ", + "92F7": "zuì", + "92F8": "jù", + "92F9": "chǎng", + "92FA": "yuǎn", + "92FB": "jiàn", + "92FC": "gāng", + "92FD": "diào", + "92FE": "táo", + "92FF": "cháng", + "9300": "lún", + "9301": "guǒ", + "9302": "líng", + "9303": "bēi", + "9304": "lù", + "9305": "lí", + "9306": "qiāng", + "9307": "póu", + "9308": "juǎn", + "9309": "mín", + "930A": "zuì", + "930B": "péng", + "930C": "àn", + "930D": "pī", + "930E": "xiàn", + "930F": "yā", + "9310": "zhuī", + "9311": "lèi", + "9312": "kē", + "9313": "kōng", + "9314": "tà", + "9315": "kūn", + "9316": "dú", + "9317": "nèi", + "9318": "chuí", + "9319": "zī", + "931A": "zhēng", + "931B": "bēn", + "931C": "niè", + "931D": "zòng", + "931E": "chún", + "931F": "tán", + "9320": "dìng", + "9321": "qí", + "9322": "qián", + "9323": "zhuì", + "9324": "jī", + "9325": "yù", + "9326": "jǐn", + "9327": "guǎn", + "9328": "máo", + "9329": "chāng", + "932A": "tiǎn", + "932B": "xī", + "932C": "liàn", + "932D": "táo", + "932E": "gù", + "932F": "cuò", + "9330": "shù", + "9331": "zhēn", + "9332": "lù", + "9333": "měng", + "9334": "lù", + "9335": "huā", + "9336": "biǎo", + "9337": "gá", + "9338": "lái", + "9339": "kěn", + "933A": "fang", + "933B": "wu", + "933C": "nài", + "933D": "wàn", + "933E": "zàn", + "933F": "hu", + "9340": "dé", + "9341": "xiān", + "9342": "piān", + "9343": "huō", + "9344": "liàng", + "9345": "fa", + "9346": "mén", + "9347": "kǎi", + "9348": "yīng", + "9349": "dī", + "934A": "liàn", + "934B": "guō", + "934C": "xiǎn", + "934D": "dù", + "934E": "tú", + "934F": "wéi", + "9350": "zōng", + "9351": "fù", + "9352": "róu", + "9353": "jí", + "9354": "è", + "9355": "jūn", + "9356": "chěn", + "9357": "tí", + "9358": "zhá", + "9359": "hù", + "935A": "yáng", + "935B": "duàn", + "935C": "xiá", + "935D": "yú", + "935E": "kēng", + "935F": "xīng", + "9360": "huáng", + "9361": "wěi", + "9362": "fù", + "9363": "zhāo", + "9364": "chā", + "9365": "qiè", + "9366": "shī", + "9367": "hōng", + "9368": "kuí", + "9369": "tiǎn", + "936A": "móu", + "936B": "qiāo", + "936C": "qiāo", + "936D": "hóu", + "936E": "tōu", + "936F": "cōng", + "9370": "huán", + "9371": "yè", + "9372": "mín", + "9373": "jiàn", + "9374": "duān", + "9375": "jiàn", + "9376": "sōng", + "9377": "kuí", + "9378": "hú", + "9379": "xuān", + "937A": "duǒ", + "937B": "jié", + "937C": "zhēn", + "937D": "biān", + "937E": "zhōng", + "937F": "zī", + "9380": "xiū", + "9381": "yé", + "9382": "měi", + "9383": "pài", + "9384": "āi", + "9385": "jiè", + "9386": "qian", + "9387": "méi", + "9388": "suǒ", + "9389": "dá", + "938A": "bàng", + "938B": "xiá", + "938C": "lián", + "938D": "suǒ", + "938E": "kài", + "938F": "liú", + "9390": "yáo", + "9391": "yè", + "9392": "nòu", + "9393": "wēng", + "9394": "róng", + "9395": "táng", + "9396": "suǒ", + "9397": "qiāng", + "9398": "lì", + "9399": "shuò", + "939A": "chuí", + "939B": "bó", + "939C": "pán", + "939D": "dā", + "939E": "bī", + "939F": "sǎng", + "93A0": "gāng", + "93A1": "zī", + "93A2": "wū", + "93A3": "yíng", + "93A4": "huàng", + "93A5": "tiáo", + "93A6": "liú", + "93A7": "kǎi", + "93A8": "sǔn", + "93A9": "shā", + "93AA": "sōu", + "93AB": "wàn", + "93AC": "hào", + "93AD": "zhèn", + "93AE": "zhèn", + "93AF": "láng", + "93B0": "yì", + "93B1": "yuán", + "93B2": "tǎng", + "93B3": "niè", + "93B4": "xí", + "93B5": "jiā", + "93B6": "gē", + "93B7": "mǎ", + "93B8": "juān", + "93B9": "song", + "93BA": "zu", + "93BB": "suǒ", + "93BC": "xià", + "93BD": "feng", + "93BE": "wen", + "93BF": "ná", + "93C0": "lǔ", + "93C1": "suǒ", + "93C2": "ōu", + "93C3": "zú", + "93C4": "tuán", + "93C5": "xiū", + "93C6": "guàn", + "93C7": "xuàn", + "93C8": "liàn", + "93C9": "shòu", + "93CA": "ào", + "93CB": "mǎn", + "93CC": "mò", + "93CD": "luó", + "93CE": "bì", + "93CF": "wèi", + "93D0": "liú", + "93D1": "dí", + "93D2": "sǎn", + "93D3": "zǒng", + "93D4": "yí", + "93D5": "lù", + "93D6": "áo", + "93D7": "kēng", + "93D8": "qiāng", + "93D9": "cuī", + "93DA": "qī", + "93DB": "cháng", + "93DC": "tāng", + "93DD": "màn", + "93DE": "yōng", + "93DF": "chǎn", + "93E0": "fēng", + "93E1": "jìng", + "93E2": "biāo", + "93E3": "shù", + "93E4": "lòu", + "93E5": "xiù", + "93E6": "cōng", + "93E7": "lóng", + "93E8": "zàn", + "93E9": "jiàn", + "93EA": "cáo", + "93EB": "lí", + "93EC": "xià", + "93ED": "xī", + "93EE": "kāng", + "93EF": "shuang", + "93F0": "bèng", + "93F1": "zhang", + "93F2": "qian", + "93F3": "chēng", + "93F4": "lù", + "93F5": "huá", + "93F6": "jí", + "93F7": "pú", + "93F8": "huì", + "93F9": "qiǎng", + "93FA": "pō", + "93FB": "lín", + "93FC": "sè", + "93FD": "xiù", + "93FE": "sǎn", + "93FF": "chēng", + "9400": "kuì", + "9401": "sī", + "9402": "liù", + "9403": "náo", + "9404": "huáng", + "9405": "piě", + "9406": "suì", + "9407": "fán", + "9408": "qiáo", + "9409": "quān", + "940A": "yáng", + "940B": "tāng", + "940C": "xiàng", + "940D": "jué", + "940E": "jiāo", + "940F": "zūn", + "9410": "liáo", + "9411": "qiè", + "9412": "láo", + "9413": "duì", + "9414": "xín", + "9415": "zān", + "9416": "jī", + "9417": "jiǎn", + "9418": "zhōng", + "9419": "dèng", + "941A": "yā", + "941B": "yìng", + "941C": "duī", + "941D": "jué", + "941E": "nòu", + "941F": "zān", + "9420": "pǔ", + "9421": "tiě", + "9422": "fán", + "9423": "zhang", + "9424": "dǐng", + "9425": "shàn", + "9426": "kāi", + "9427": "jiān", + "9428": "fèi", + "9429": "suì", + "942A": "lǔ", + "942B": "juān", + "942C": "huì", + "942D": "yù", + "942E": "lián", + "942F": "zhuó", + "9430": "qiāo", + "9431": "jiàn", + "9432": "zhuó", + "9433": "léi", + "9434": "bì", + "9435": "tiě", + "9436": "huán", + "9437": "yè", + "9438": "duó", + "9439": "guǒ", + "943A": "dang", + "943B": "jù", + "943C": "fén", + "943D": "dá", + "943E": "bèi", + "943F": "yì", + "9440": "ài", + "9441": "zōng", + "9442": "xùn", + "9443": "diào", + "9444": "zhù", + "9445": "héng", + "9446": "zhuì", + "9447": "jī", + "9448": "niè", + "9449": "hé", + "944A": "huò", + "944B": "qīng", + "944C": "bīn", + "944D": "yīng", + "944E": "kuì", + "944F": "níng", + "9450": "xū", + "9451": "jiàn", + "9452": "jiàn", + "9453": "qian", + "9454": "chǎ", + "9455": "zhì", + "9456": "miè", + "9457": "lí", + "9458": "léi", + "9459": "jī", + "945A": "zuàn", + "945B": "kuàng", + "945C": "shǎng", + "945D": "péng", + "945E": "là", + "945F": "dú", + "9460": "shuò", + "9461": "chuò", + "9462": "lǜ", + "9463": "biāo", + "9464": "bào", + "9465": "lǔ", + "9466": "xian", + "9467": "kuan", + "9468": "lóng", + "9469": "è", + "946A": "lú", + "946B": "xīn", + "946C": "jiàn", + "946D": "làn", + "946E": "bó", + "946F": "jiān", + "9470": "yào", + "9471": "chán", + "9472": "xiāng", + "9473": "jiàn", + "9474": "xī", + "9475": "guàn", + "9476": "cáng", + "9477": "niè", + "9478": "lěi", + "9479": "cuān", + "947A": "qú", + "947B": "pàn", + "947C": "luó", + "947D": "zuān", + "947E": "luán", + "947F": "záo", + "9480": "niè", + "9481": "jué", + "9482": "tǎng", + "9483": "shǔ", + "9484": "lán", + "9485": "jīn", + "9486": "gá", + "9487": "yǐ", + "9488": "zhēn", + "9489": "dīng", + "948A": "zhāo", + "948B": "pō", + "948C": "liǎo", + "948D": "tǔ", + "948E": "qiān", + "948F": "chuàn", + "9490": "shān", + "9491": "jí", + "9492": "fán", + "9493": "diào", + "9494": "mén", + "9495": "nǚ", + "9496": "yáng", + "9497": "chāi", + "9498": "xíng", + "9499": "gài", + "949A": "bù", + "949B": "tài", + "949C": "jù", + "949D": "dùn", + "949E": "chāo", + "949F": "zhōng", + "94A0": "nà", + "94A1": "bèi", + "94A2": "gāng", + "94A3": "bǎn", + "94A4": "qián", + "94A5": "yào", + "94A6": "qīn", + "94A7": "jūn", + "94A8": "wū", + "94A9": "gōu", + "94AA": "kàng", + "94AB": "fāng", + "94AC": "huǒ", + "94AD": "tǒu", + "94AE": "niǔ", + "94AF": "bǎ", + "94B0": "yù", + "94B1": "qián", + "94B2": "zhēng", + "94B3": "qián", + "94B4": "gǔ", + "94B5": "bō", + "94B6": "ē", + "94B7": "pǒ", + "94B8": "bù", + "94B9": "bó", + "94BA": "yuè", + "94BB": "zuān", + "94BC": "mù", + "94BD": "tǎn", + "94BE": "jiǎ", + "94BF": "diàn", + "94C0": "yóu", + "94C1": "tiě", + "94C2": "bó", + "94C3": "líng", + "94C4": "shuò", + "94C5": "qiān", + "94C6": "mǎo", + "94C7": "bào", + "94C8": "shì", + "94C9": "xuàn", + "94CA": "tā", + "94CB": "bì", + "94CC": "ní", + "94CD": "pī", + "94CE": "duó", + "94CF": "xíng", + "94D0": "kào", + "94D1": "lǎo", + "94D2": "ěr", + "94D3": "máng", + "94D4": "yà", + "94D5": "yǒu", + "94D6": "chéng", + "94D7": "jiá", + "94D8": "yé", + "94D9": "náo", + "94DA": "zhì", + "94DB": "dang", + "94DC": "tóng", + "94DD": "lǚ", + "94DE": "diào", + "94DF": "yīn", + "94E0": "kǎi", + "94E1": "zhá", + "94E2": "zhū", + "94E3": "xǐ", + "94E4": "dìng", + "94E5": "diū", + "94E6": "xiān", + "94E7": "huá", + "94E8": "quán", + "94E9": "shā", + "94EA": "hā", + "94EB": "diào", + "94EC": "gè", + "94ED": "míng", + "94EE": "zhēng", + "94EF": "sè", + "94F0": "jiǎo", + "94F1": "yī", + "94F2": "chǎn", + "94F3": "chòng", + "94F4": "tāng", + "94F5": "ǎn", + "94F6": "yín", + "94F7": "rú", + "94F8": "zhù", + "94F9": "láo", + "94FA": "pù", + "94FB": "wú", + "94FC": "lái", + "94FD": "tè", + "94FE": "liàn", + "94FF": "kēng", + "9500": "xiāo", + "9501": "suǒ", + "9502": "lǐ", + "9503": "zèng", + "9504": "chú", + "9505": "guō", + "9506": "gào", + "9507": "é", + "9508": "xiù", + "9509": "cuò", + "950A": "lüè", + "950B": "fēng", + "950C": "xīn", + "950D": "liǔ", + "950E": "kāi", + "950F": "jiǎn", + "9510": "ruì", + "9511": "tī", + "9512": "láng", + "9513": "qǐn", + "9514": "jū", + "9515": "ā", + "9516": "qiāng", + "9517": "zhě", + "9518": "nuò", + "9519": "cuò", + "951A": "máo", + "951B": "bēn", + "951C": "qí", + "951D": "dé", + "951E": "kè", + "951F": "kūn", + "9520": "chāng", + "9521": "xī", + "9522": "gù", + "9523": "luó", + "9524": "chuí", + "9525": "zhuī", + "9526": "jǐn", + "9527": "zhì", + "9528": "xiān", + "9529": "juǎn", + "952A": "huō", + "952B": "péi", + "952C": "tán", + "952D": "dìng", + "952E": "jiàn", + "952F": "jù", + "9530": "měng", + "9531": "zī", + "9532": "qiè", + "9533": "yīng", + "9534": "kǎi", + "9535": "qiāng", + "9536": "sī", + "9537": "è", + "9538": "chā", + "9539": "qiāo", + "953A": "zhōng", + "953B": "duàn", + "953C": "sōu", + "953D": "huáng", + "953E": "huán", + "953F": "āi", + "9540": "dù", + "9541": "měi", + "9542": "lòu", + "9543": "zī", + "9544": "fèi", + "9545": "méi", + "9546": "mò", + "9547": "zhèn", + "9548": "bó", + "9549": "gé", + "954A": "niè", + "954B": "tǎng", + "954C": "juān", + "954D": "niè", + "954E": "ná", + "954F": "liú", + "9550": "gǎo", + "9551": "bàng", + "9552": "yì", + "9553": "jiā", + "9554": "bīn", + "9555": "róng", + "9556": "biāo", + "9557": "tāng", + "9558": "màn", + "9559": "luó", + "955A": "bèng", + "955B": "yōng", + "955C": "jìng", + "955D": "dī", + "955E": "zú", + "955F": "xuàn", + "9560": "liú", + "9561": "chán", + "9562": "jué", + "9563": "liào", + "9564": "pú", + "9565": "lǔ", + "9566": "duì", + "9567": "lán", + "9568": "pǔ", + "9569": "cuān", + "956A": "qiāng", + "956B": "dèng", + "956C": "huò", + "956D": "léi", + "956E": "huán", + "956F": "zhuó", + "9570": "lián", + "9571": "yì", + "9572": "chǎ", + "9573": "biāo", + "9574": "là", + "9575": "chán", + "9576": "xiāng", + "9577": "zhǎng", + "9578": "cháng", + "9579": "jiǔ", + "957A": "ǎo", + "957B": "dié", + "957C": "qū", + "957D": "liǎo", + "957E": "mí", + "957F": "zhǎng", + "9580": "mén", + "9581": "mà", + "9582": "shuān", + "9583": "shǎn", + "9584": "huò", + "9585": "mén", + "9586": "yán", + "9587": "bì", + "9588": "hàn", + "9589": "bì", + "958A": "shan", + "958B": "kāi", + "958C": "kàng", + "958D": "bēng", + "958E": "hóng", + "958F": "rùn", + "9590": "sàn", + "9591": "xián", + "9592": "xián", + "9593": "jiān", + "9594": "mǐn", + "9595": "xiā", + "9596": "shui", + "9597": "dòu", + "9598": "zhá", + "9599": "nào", + "959A": "zhān", + "959B": "pēng", + "959C": "xiǎ", + "959D": "líng", + "959E": "biàn", + "959F": "bì", + "95A0": "rùn", + "95A1": "ài", + "95A2": "guān", + "95A3": "gé", + "95A4": "gé", + "95A5": "fá", + "95A6": "chù", + "95A7": "hòng", + "95A8": "guī", + "95A9": "mǐn", + "95AA": "sē", + "95AB": "kǔn", + "95AC": "làng", + "95AD": "lǘ", + "95AE": "tíng", + "95AF": "shà", + "95B0": "jú", + "95B1": "yuè", + "95B2": "yuè", + "95B3": "chǎn", + "95B4": "qù", + "95B5": "lìn", + "95B6": "chāng", + "95B7": "shài", + "95B8": "kǔn", + "95B9": "yān", + "95BA": "wén", + "95BB": "yán", + "95BC": "è", + "95BD": "hūn", + "95BE": "yù", + "95BF": "wén", + "95C0": "xiàng", + "95C1": "bāo", + "95C2": "hòng", + "95C3": "qù", + "95C4": "yǎo", + "95C5": "wén", + "95C6": "bǎn", + "95C7": "àn", + "95C8": "wéi", + "95C9": "yīn", + "95CA": "kuò", + "95CB": "què", + "95CC": "lán", + "95CD": "dū", + "95CE": "quan", + "95CF": "fēng", + "95D0": "tián", + "95D1": "niè", + "95D2": "tà", + "95D3": "kǎi", + "95D4": "hé", + "95D5": "què", + "95D6": "chuǎng", + "95D7": "guān", + "95D8": "dòu", + "95D9": "qǐ", + "95DA": "kuī", + "95DB": "táng", + "95DC": "guān", + "95DD": "piáo", + "95DE": "kàn", + "95DF": "xì", + "95E0": "huì", + "95E1": "chǎn", + "95E2": "pì", + "95E3": "dàng", + "95E4": "huán", + "95E5": "tà", + "95E6": "wén", + "95E7": "tā", + "95E8": "mén", + "95E9": "shuān", + "95EA": "shǎn", + "95EB": "yàn", + "95EC": "hàn", + "95ED": "bì", + "95EE": "wèn", + "95EF": "chuǎng", + "95F0": "rùn", + "95F1": "wéi", + "95F2": "xián", + "95F3": "hóng", + "95F4": "jiān", + "95F5": "mǐn", + "95F6": "kāng", + "95F7": "mèn", + "95F8": "zhá", + "95F9": "nào", + "95FA": "guī", + "95FB": "wén", + "95FC": "tà", + "95FD": "mǐn", + "95FE": "lǘ", + "95FF": "kǎi", + "9600": "fá", + "9601": "gé", + "9602": "hé", + "9603": "kǔn", + "9604": "jiū", + "9605": "yuè", + "9606": "láng", + "9607": "dū", + "9608": "yù", + "9609": "yān", + "960A": "chāng", + "960B": "xì", + "960C": "wén", + "960D": "hūn", + "960E": "yán", + "960F": "è", + "9610": "chǎn", + "9611": "lán", + "9612": "qù", + "9613": "huì", + "9614": "kuò", + "9615": "què", + "9616": "hé", + "9617": "tián", + "9618": "dá", + "9619": "quē", + "961A": "hǎn", + "961B": "huán", + "961C": "fù", + "961D": "fù", + "961E": "lè", + "961F": "duì", + "9620": "xìn", + "9621": "qiān", + "9622": "wù", + "9623": "gài", + "9624": "zhì", + "9625": "yīn", + "9626": "yáng", + "9627": "dǒu", + "9628": "è", + "9629": "shēng", + "962A": "bǎn", + "962B": "péi", + "962C": "kēng", + "962D": "yǔn", + "962E": "ruǎn", + "962F": "zhǐ", + "9630": "pí", + "9631": "jǐng", + "9632": "fáng", + "9633": "yáng", + "9634": "yīn", + "9635": "zhèn", + "9636": "jiē", + "9637": "chēng", + "9638": "è", + "9639": "qū", + "963A": "dǐ", + "963B": "zǔ", + "963C": "zuò", + "963D": "diàn", + "963E": "lǐng", + "963F": "ā", + "9640": "tuó", + "9641": "tuó", + "9642": "bēi", + "9643": "bǐng", + "9644": "fù", + "9645": "jì", + "9646": "lù", + "9647": "lǒng", + "9648": "chén", + "9649": "xíng", + "964A": "duò", + "964B": "lòu", + "964C": "mò", + "964D": "jiàng", + "964E": "shū", + "964F": "duò", + "9650": "xiàn", + "9651": "ér", + "9652": "guǐ", + "9653": "yū", + "9654": "gāi", + "9655": "shǎn", + "9656": "jùn", + "9657": "qiào", + "9658": "xíng", + "9659": "chún", + "965A": "fù", + "965B": "bì", + "965C": "xiá", + "965D": "shǎn", + "965E": "shēng", + "965F": "zhì", + "9660": "pū", + "9661": "dǒu", + "9662": "yuàn", + "9663": "zhèn", + "9664": "chú", + "9665": "xiàn", + "9666": "dao", + "9667": "niè", + "9668": "yǔn", + "9669": "xiǎn", + "966A": "péi", + "966B": "fèi", + "966C": "zōu", + "966D": "yì", + "966E": "duì", + "966F": "lún", + "9670": "yīn", + "9671": "jū", + "9672": "chuí", + "9673": "chén", + "9674": "pí", + "9675": "líng", + "9676": "táo", + "9677": "xiàn", + "9678": "lù", + "9679": "shēng", + "967A": "xiǎn", + "967B": "yīn", + "967C": "zhǔ", + "967D": "yáng", + "967E": "réng", + "967F": "xiá", + "9680": "chóng", + "9681": "yàn", + "9682": "yīn", + "9683": "shù", + "9684": "dī", + "9685": "yú", + "9686": "lóng", + "9687": "wēi", + "9688": "wēi", + "9689": "niè", + "968A": "duì", + "968B": "suí", + "968C": "ǎn", + "968D": "huáng", + "968E": "jiē", + "968F": "suí", + "9690": "yǐn", + "9691": "gài", + "9692": "yǎn", + "9693": "huī", + "9694": "gé", + "9695": "yǔn", + "9696": "wù", + "9697": "kuí", + "9698": "ài", + "9699": "xì", + "969A": "táng", + "969B": "jì", + "969C": "zhàng", + "969D": "dǎo", + "969E": "áo", + "969F": "xì", + "96A0": "yǐn", + "96A1": "sa", + "96A2": "rǎo", + "96A3": "lín", + "96A4": "tuí", + "96A5": "dèng", + "96A6": "jiǎo", + "96A7": "suì", + "96A8": "suí", + "96A9": "ào", + "96AA": "xiǎn", + "96AB": "fén", + "96AC": "nǐ", + "96AD": "ér", + "96AE": "jī", + "96AF": "dǎo", + "96B0": "xí", + "96B1": "yǐn", + "96B2": "é", + "96B3": "huī", + "96B4": "lǒng", + "96B5": "xī", + "96B6": "lì", + "96B7": "lì", + "96B8": "lì", + "96B9": "zhuī", + "96BA": "hú", + "96BB": "zhī", + "96BC": "sǔn", + "96BD": "juàn", + "96BE": "nán", + "96BF": "yì", + "96C0": "què", + "96C1": "yàn", + "96C2": "qín", + "96C3": "qiān", + "96C4": "xióng", + "96C5": "yǎ", + "96C6": "jí", + "96C7": "gù", + "96C8": "huán", + "96C9": "zhì", + "96CA": "gòu", + "96CB": "juàn", + "96CC": "cí", + "96CD": "yōng", + "96CE": "jū", + "96CF": "chú", + "96D0": "hū", + "96D1": "zá", + "96D2": "luò", + "96D3": "yú", + "96D4": "chóu", + "96D5": "diāo", + "96D6": "suī", + "96D7": "hàn", + "96D8": "wò", + "96D9": "shuāng", + "96DA": "guàn", + "96DB": "chú", + "96DC": "zá", + "96DD": "yōng", + "96DE": "jī", + "96DF": "xī", + "96E0": "chóu", + "96E1": "liù", + "96E2": "lí", + "96E3": "nán", + "96E4": "xué", + "96E5": "zá", + "96E6": "jí", + "96E7": "jí", + "96E8": "yǔ", + "96E9": "yú", + "96EA": "xuě", + "96EB": "nǎ", + "96EC": "fǒu", + "96ED": "sè", + "96EE": "mù", + "96EF": "wén", + "96F0": "fēn", + "96F1": "pāng", + "96F2": "yún", + "96F3": "lì", + "96F4": "chì", + "96F5": "yāng", + "96F6": "líng", + "96F7": "léi", + "96F8": "án", + "96F9": "báo", + "96FA": "wù", + "96FB": "diàn", + "96FC": "dàng", + "96FD": "hù", + "96FE": "wù", + "96FF": "diào", + "9700": "xū", + "9701": "jì", + "9702": "mù", + "9703": "chén", + "9704": "xiāo", + "9705": "zhà", + "9706": "tíng", + "9707": "zhèn", + "9708": "pèi", + "9709": "méi", + "970A": "líng", + "970B": "qī", + "970C": "zhōu", + "970D": "huò", + "970E": "shà", + "970F": "fēi", + "9710": "hóng", + "9711": "zhān", + "9712": "yīn", + "9713": "ní", + "9714": "zhù", + "9715": "tún", + "9716": "lín", + "9717": "ling", + "9718": "dòng", + "9719": "yīng", + "971A": "wù", + "971B": "líng", + "971C": "shuāng", + "971D": "líng", + "971E": "xiá", + "971F": "hóng", + "9720": "yīn", + "9721": "mài", + "9722": "mài", + "9723": "yǔn", + "9724": "liù", + "9725": "mèng", + "9726": "bīn", + "9727": "wù", + "9728": "wèi", + "9729": "kuò", + "972A": "yín", + "972B": "xí", + "972C": "yì", + "972D": "ǎi", + "972E": "dàn", + "972F": "tèng", + "9730": "sǎn", + "9731": "yù", + "9732": "lù", + "9733": "lóng", + "9734": "dài", + "9735": "jí", + "9736": "pāng", + "9737": "yáng", + "9738": "bà", + "9739": "pī", + "973A": "wéi", + "973B": "fēng", + "973C": "xì", + "973D": "jì", + "973E": "mái", + "973F": "méng", + "9740": "méng", + "9741": "léi", + "9742": "lì", + "9743": "huò", + "9744": "ǎi", + "9745": "fèi", + "9746": "dài", + "9747": "lóng", + "9748": "líng", + "9749": "ài", + "974A": "fēng", + "974B": "lì", + "974C": "bǎo", + "974D": "he", + "974E": "hè", + "974F": "hè", + "9750": "bìng", + "9751": "qīng", + "9752": "qīng", + "9753": "jìng", + "9754": "tiān", + "9755": "zhēn", + "9756": "jìng", + "9757": "chēng", + "9758": "qìng", + "9759": "jìng", + "975A": "jìng", + "975B": "diàn", + "975C": "jìng", + "975D": "tiān", + "975E": "fēi", + "975F": "fēi", + "9760": "kào", + "9761": "mí", + "9762": "miàn", + "9763": "miàn", + "9764": "bào", + "9765": "yè", + "9766": "tiǎn", + "9767": "huì", + "9768": "yè", + "9769": "gé", + "976A": "dīng", + "976B": "chá", + "976C": "qián", + "976D": "rèn", + "976E": "dí", + "976F": "dù", + "9770": "wù", + "9771": "rèn", + "9772": "qín", + "9773": "jìn", + "9774": "xuē", + "9775": "niǔ", + "9776": "bǎ", + "9777": "yǐn", + "9778": "sǎ", + "9779": "nà", + "977A": "mò", + "977B": "zǔ", + "977C": "dá", + "977D": "bàn", + "977E": "yì", + "977F": "yào", + "9780": "táo", + "9781": "bèi", + "9782": "jiá", + "9783": "hóng", + "9784": "páo", + "9785": "yāng", + "9786": "bing", + "9787": "yīn", + "9788": "gé", + "9789": "táo", + "978A": "jié", + "978B": "xié", + "978C": "ān", + "978D": "ān", + "978E": "hén", + "978F": "gǒng", + "9790": "qia", + "9791": "dá", + "9792": "qiáo", + "9793": "tīng", + "9794": "mán", + "9795": "yìng", + "9796": "suī", + "9797": "tiáo", + "9798": "qiào", + "9799": "xuàn", + "979A": "kòng", + "979B": "běng", + "979C": "tà", + "979D": "shàng", + "979E": "bǐng", + "979F": "kuò", + "97A0": "jū", + "97A1": "la", + "97A2": "xiè", + "97A3": "róu", + "97A4": "bāng", + "97A5": "ēng", + "97A6": "qiū", + "97A7": "qiū", + "97A8": "hé", + "97A9": "xiào", + "97AA": "mù", + "97AB": "jū", + "97AC": "jiān", + "97AD": "biān", + "97AE": "dī", + "97AF": "jiān", + "97B0": "wēn", + "97B1": "tāo", + "97B2": "gōu", + "97B3": "tà", + "97B4": "bèi", + "97B5": "xié", + "97B6": "pán", + "97B7": "gé", + "97B8": "bì", + "97B9": "kuò", + "97BA": "tāng", + "97BB": "lóu", + "97BC": "guì", + "97BD": "qiáo", + "97BE": "xuē", + "97BF": "jī", + "97C0": "jiān", + "97C1": "jiāng", + "97C2": "chàn", + "97C3": "dá", + "97C4": "hù", + "97C5": "xiǎn", + "97C6": "qiān", + "97C7": "dú", + "97C8": "wà", + "97C9": "jiān", + "97CA": "lán", + "97CB": "wéi", + "97CC": "rèn", + "97CD": "fú", + "97CE": "mèi", + "97CF": "quàn", + "97D0": "gé", + "97D1": "wěi", + "97D2": "qiào", + "97D3": "hán", + "97D4": "chàng", + "97D5": "kuo", + "97D6": "rǒu", + "97D7": "yùn", + "97D8": "shè", + "97D9": "wěi", + "97DA": "gé", + "97DB": "bài", + "97DC": "tāo", + "97DD": "gōu", + "97DE": "yùn", + "97DF": "gao", + "97E0": "bì", + "97E1": "wěi", + "97E2": "suì", + "97E3": "dú", + "97E4": "wà", + "97E5": "dú", + "97E6": "wéi", + "97E7": "rèn", + "97E8": "fú", + "97E9": "hán", + "97EA": "wěi", + "97EB": "yùn", + "97EC": "tāo", + "97ED": "jiǔ", + "97EE": "jiǔ", + "97EF": "xiān", + "97F0": "xiè", + "97F1": "xiān", + "97F2": "jī", + "97F3": "yīn", + "97F4": "zá", + "97F5": "yùn", + "97F6": "sháo", + "97F7": "lè", + "97F8": "péng", + "97F9": "huáng", + "97FA": "yīng", + "97FB": "yùn", + "97FC": "péng", + "97FD": "ān", + "97FE": "yīn", + "97FF": "xiǎng", + "9800": "hù", + "9801": "yè", + "9802": "dǐng", + "9803": "qǐng", + "9804": "kuí", + "9805": "xiàng", + "9806": "shùn", + "9807": "hān", + "9808": "xū", + "9809": "yí", + "980A": "xū", + "980B": "ě", + "980C": "sòng", + "980D": "kuǐ", + "980E": "qí", + "980F": "háng", + "9810": "yù", + "9811": "wán", + "9812": "bān", + "9813": "dùn", + "9814": "dí", + "9815": "dān", + "9816": "pàn", + "9817": "pō", + "9818": "lǐng", + "9819": "chè", + "981A": "jǐng", + "981B": "lèi", + "981C": "hé", + "981D": "qiāo", + "981E": "è", + "981F": "é", + "9820": "wěi", + "9821": "xié", + "9822": "kuò", + "9823": "shěn", + "9824": "yí", + "9825": "shěn", + "9826": "hái", + "9827": "duǐ", + "9828": "yǔ", + "9829": "pīng", + "982A": "lèi", + "982B": "fǔ", + "982C": "jiá", + "982D": "tóu", + "982E": "huì", + "982F": "kuí", + "9830": "jiá", + "9831": "luō", + "9832": "tǐng", + "9833": "chēng", + "9834": "yǐng", + "9835": "yūn", + "9836": "hú", + "9837": "hàn", + "9838": "jǐng", + "9839": "tuí", + "983A": "tuí", + "983B": "pín", + "983C": "lài", + "983D": "tuí", + "983E": "zī", + "983F": "zī", + "9840": "chuí", + "9841": "dìng", + "9842": "lài", + "9843": "tán", + "9844": "hàn", + "9845": "qiān", + "9846": "kē", + "9847": "cuì", + "9848": "xuǎn", + "9849": "qīn", + "984A": "yí", + "984B": "sāi", + "984C": "tí", + "984D": "é", + "984E": "è", + "984F": "yán", + "9850": "wèn", + "9851": "kǎn", + "9852": "yóng", + "9853": "zhuān", + "9854": "yán", + "9855": "xiǎn", + "9856": "xìn", + "9857": "yǐ", + "9858": "yuàn", + "9859": "sǎng", + "985A": "diān", + "985B": "diān", + "985C": "jiǎng", + "985D": "kuī", + "985E": "lèi", + "985F": "láo", + "9860": "piǎo", + "9861": "wài", + "9862": "mán", + "9863": "cù", + "9864": "yáo", + "9865": "hào", + "9866": "qiáo", + "9867": "gù", + "9868": "xùn", + "9869": "yǎn", + "986A": "huì", + "986B": "chàn", + "986C": "rú", + "986D": "méng", + "986E": "bīn", + "986F": "xiǎn", + "9870": "pín", + "9871": "lú", + "9872": "lǎn", + "9873": "niè", + "9874": "quán", + "9875": "yè", + "9876": "dǐng", + "9877": "qǐng", + "9878": "hān", + "9879": "xiàng", + "987A": "shùn", + "987B": "xū", + "987C": "xū", + "987D": "wán", + "987E": "gù", + "987F": "dùn", + "9880": "qí", + "9881": "bān", + "9882": "sòng", + "9883": "háng", + "9884": "yù", + "9885": "lú", + "9886": "lǐng", + "9887": "pō", + "9888": "jǐng", + "9889": "jié", + "988A": "jiá", + "988B": "tǐng", + "988C": "hé", + "988D": "yǐng", + "988E": "jiǒng", + "988F": "kē", + "9890": "yí", + "9891": "pín", + "9892": "huì", + "9893": "tuí", + "9894": "hàn", + "9895": "yǐng", + "9896": "yǐng", + "9897": "kē", + "9898": "tí", + "9899": "yóng", + "989A": "è", + "989B": "zhuān", + "989C": "yán", + "989D": "é", + "989E": "niè", + "989F": "mān", + "98A0": "diān", + "98A1": "sǎng", + "98A2": "hào", + "98A3": "lèi", + "98A4": "chàn", + "98A5": "rú", + "98A6": "pín", + "98A7": "quán", + "98A8": "fēng", + "98A9": "biāo", + "98AA": "gua", + "98AB": "fú", + "98AC": "xiā", + "98AD": "zhǎn", + "98AE": "biāo", + "98AF": "sà", + "98B0": "bá", + "98B1": "tái", + "98B2": "liè", + "98B3": "guā", + "98B4": "xuàn", + "98B5": "shāo", + "98B6": "jù", + "98B7": "biāo", + "98B8": "sī", + "98B9": "wěi", + "98BA": "yáng", + "98BB": "yáo", + "98BC": "sōu", + "98BD": "kǎi", + "98BE": "sōu", + "98BF": "fān", + "98C0": "liú", + "98C1": "xí", + "98C2": "liù", + "98C3": "piāo", + "98C4": "piāo", + "98C5": "liú", + "98C6": "biāo", + "98C7": "biāo", + "98C8": "biāo", + "98C9": "liáo", + "98CA": "biao", + "98CB": "sè", + "98CC": "fēng", + "98CD": "xiū", + "98CE": "fēng", + "98CF": "yáng", + "98D0": "zhǎn", + "98D1": "biāo", + "98D2": "sà", + "98D3": "jù", + "98D4": "sī", + "98D5": "sōu", + "98D6": "yáo", + "98D7": "liú", + "98D8": "piāo", + "98D9": "biāo", + "98DA": "biāo", + "98DB": "fēi", + "98DC": "fān", + "98DD": "fēi", + "98DE": "fēi", + "98DF": "shí", + "98E0": "shí", + "98E1": "cān", + "98E2": "jī", + "98E3": "dìng", + "98E4": "sì", + "98E5": "tuō", + "98E6": "zhān", + "98E7": "sūn", + "98E8": "xiǎng", + "98E9": "tún", + "98EA": "rèn", + "98EB": "yù", + "98EC": "juàn", + "98ED": "chì", + "98EE": "yǐn", + "98EF": "fàn", + "98F0": "fàn", + "98F1": "sūn", + "98F2": "yǐn", + "98F3": "tǒu", + "98F4": "yí", + "98F5": "zuò", + "98F6": "bì", + "98F7": "jiě", + "98F8": "tāo", + "98F9": "liǔ", + "98FA": "cí", + "98FB": "tiè", + "98FC": "sì", + "98FD": "bǎo", + "98FE": "shì", + "98FF": "duò", + "9900": "hài", + "9901": "rèn", + "9902": "tiǎn", + "9903": "jiǎo", + "9904": "jiá", + "9905": "bǐng", + "9906": "yáo", + "9907": "tóng", + "9908": "cí", + "9909": "xiǎng", + "990A": "yǎng", + "990B": "juàn", + "990C": "ěr", + "990D": "yàn", + "990E": "le", + "990F": "xī", + "9910": "cān", + "9911": "bō", + "9912": "něi", + "9913": "è", + "9914": "bù", + "9915": "jùn", + "9916": "dòu", + "9917": "sù", + "9918": "yú", + "9919": "shì", + "991A": "yáo", + "991B": "hún", + "991C": "guǒ", + "991D": "shì", + "991E": "jiàn", + "991F": "zhuì", + "9920": "bǐng", + "9921": "xiàn", + "9922": "bù", + "9923": "yè", + "9924": "tán", + "9925": "fēi", + "9926": "zhāng", + "9927": "wèi", + "9928": "guǎn", + "9929": "è", + "992A": "nuǎn", + "992B": "yùn", + "992C": "hú", + "992D": "huáng", + "992E": "tiè", + "992F": "huì", + "9930": "jiān", + "9931": "hóu", + "9932": "ài", + "9933": "táng", + "9934": "fēn", + "9935": "wèi", + "9936": "gǔ", + "9937": "chā", + "9938": "sòng", + "9939": "táng", + "993A": "bó", + "993B": "gāo", + "993C": "xì", + "993D": "kuì", + "993E": "liù", + "993F": "sōu", + "9940": "táo", + "9941": "yè", + "9942": "yún", + "9943": "mó", + "9944": "táng", + "9945": "mán", + "9946": "bì", + "9947": "yù", + "9948": "xiū", + "9949": "jǐn", + "994A": "sǎn", + "994B": "kuì", + "994C": "zhuàn", + "994D": "shàn", + "994E": "chì", + "994F": "dàn", + "9950": "yì", + "9951": "jī", + "9952": "ráo", + "9953": "chēng", + "9954": "yōng", + "9955": "tāo", + "9956": "wèi", + "9957": "xiǎng", + "9958": "zhān", + "9959": "fēn", + "995A": "hài", + "995B": "méng", + "995C": "yàn", + "995D": "mó", + "995E": "chán", + "995F": "xiǎng", + "9960": "luó", + "9961": "zàn", + "9962": "náng", + "9963": "shí", + "9964": "dìng", + "9965": "jī", + "9966": "tuō", + "9967": "táng", + "9968": "tún", + "9969": "xì", + "996A": "rèn", + "996B": "yù", + "996C": "chì", + "996D": "fàn", + "996E": "yǐn", + "996F": "jiàn", + "9970": "shì", + "9971": "bǎo", + "9972": "sì", + "9973": "duò", + "9974": "yí", + "9975": "ěr", + "9976": "ráo", + "9977": "xiǎng", + "9978": "hé", + "9979": "le", + "997A": "jiǎo", + "997B": "xī", + "997C": "bǐng", + "997D": "bō", + "997E": "dòu", + "997F": "è", + "9980": "yú", + "9981": "něi", + "9982": "jùn", + "9983": "guǒ", + "9984": "hún", + "9985": "xiàn", + "9986": "guǎn", + "9987": "chā", + "9988": "kuì", + "9989": "gǔ", + "998A": "sōu", + "998B": "chán", + "998C": "yè", + "998D": "mó", + "998E": "bó", + "998F": "liú", + "9990": "xiū", + "9991": "jǐn", + "9992": "mán", + "9993": "sǎn", + "9994": "zhuàn", + "9995": "náng", + "9996": "shǒu", + "9997": "kuí", + "9998": "guó", + "9999": "xiāng", + "999A": "fén", + "999B": "bó", + "999C": "nǐ", + "999D": "bì", + "999E": "bó", + "999F": "tú", + "99A0": "hān", + "99A1": "fēi", + "99A2": "jiān", + "99A3": "ān", + "99A4": "ài", + "99A5": "fù", + "99A6": "xiān", + "99A7": "yūn", + "99A8": "xīn", + "99A9": "fén", + "99AA": "pīn", + "99AB": "xīn", + "99AC": "mǎ", + "99AD": "yù", + "99AE": "féng", + "99AF": "hàn", + "99B0": "dí", + "99B1": "tuó", + "99B2": "zhé", + "99B3": "chí", + "99B4": "xún", + "99B5": "zhù", + "99B6": "zhī", + "99B7": "pèi", + "99B8": "xìn", + "99B9": "rì", + "99BA": "sà", + "99BB": "yǔn", + "99BC": "wén", + "99BD": "zhí", + "99BE": "dàn", + "99BF": "lǘ", + "99C0": "yóu", + "99C1": "bó", + "99C2": "bǎo", + "99C3": "jué", + "99C4": "tuó", + "99C5": "yì", + "99C6": "qū", + "99C7": "pu", + "99C8": "qū", + "99C9": "jiōng", + "99CA": "pǒ", + "99CB": "zhāo", + "99CC": "yuān", + "99CD": "péi", + "99CE": "zhòu", + "99CF": "jù", + "99D0": "zhù", + "99D1": "nú", + "99D2": "jū", + "99D3": "pī", + "99D4": "zǎng", + "99D5": "jià", + "99D6": "líng", + "99D7": "zhěn", + "99D8": "tái", + "99D9": "fù", + "99DA": "yǎng", + "99DB": "shǐ", + "99DC": "bì", + "99DD": "tuo", + "99DE": "tuó", + "99DF": "sì", + "99E0": "liú", + "99E1": "mà", + "99E2": "pián", + "99E3": "táo", + "99E4": "zhì", + "99E5": "róng", + "99E6": "téng", + "99E7": "dòng", + "99E8": "xūn", + "99E9": "quān", + "99EA": "shēn", + "99EB": "jiōng", + "99EC": "ěr", + "99ED": "hài", + "99EE": "bó", + "99EF": "zhū", + "99F0": "yīn", + "99F1": "luò", + "99F2": "zhou", + "99F3": "dàn", + "99F4": "xiè", + "99F5": "liú", + "99F6": "jú", + "99F7": "sǒng", + "99F8": "qīn", + "99F9": "máng", + "99FA": "láng", + "99FB": "hàn", + "99FC": "tú", + "99FD": "xuān", + "99FE": "tuì", + "99FF": "jùn", + "9A00": "ě", + "9A01": "chěng", + "9A02": "xīng", + "9A03": "ái", + "9A04": "lù", + "9A05": "zhuī", + "9A06": "zhōu", + "9A07": "shè", + "9A08": "pián", + "9A09": "kūn", + "9A0A": "táo", + "9A0B": "lái", + "9A0C": "zōng", + "9A0D": "kè", + "9A0E": "qí", + "9A0F": "qí", + "9A10": "yàn", + "9A11": "fēi", + "9A12": "sāo", + "9A13": "yǎn", + "9A14": "gé", + "9A15": "yǎo", + "9A16": "wù", + "9A17": "piàn", + "9A18": "cōng", + "9A19": "piàn", + "9A1A": "qián", + "9A1B": "fēi", + "9A1C": "huáng", + "9A1D": "qián", + "9A1E": "huō", + "9A1F": "yú", + "9A20": "tí", + "9A21": "quán", + "9A22": "xiá", + "9A23": "zōng", + "9A24": "kuí", + "9A25": "róu", + "9A26": "sī", + "9A27": "guā", + "9A28": "tuó", + "9A29": "guī", + "9A2A": "sōu", + "9A2B": "qiān", + "9A2C": "chéng", + "9A2D": "zhì", + "9A2E": "liú", + "9A2F": "péng", + "9A30": "téng", + "9A31": "xí", + "9A32": "cǎo", + "9A33": "dú", + "9A34": "yàn", + "9A35": "yuán", + "9A36": "zōu", + "9A37": "sāo", + "9A38": "shàn", + "9A39": "lí", + "9A3A": "zhì", + "9A3B": "shuāng", + "9A3C": "lù", + "9A3D": "xí", + "9A3E": "luó", + "9A3F": "zhāng", + "9A40": "mò", + "9A41": "ào", + "9A42": "cān", + "9A43": "biāo", + "9A44": "cōng", + "9A45": "qū", + "9A46": "bì", + "9A47": "zhì", + "9A48": "yù", + "9A49": "xū", + "9A4A": "huá", + "9A4B": "bō", + "9A4C": "sù", + "9A4D": "xiāo", + "9A4E": "lín", + "9A4F": "zhàn", + "9A50": "dūn", + "9A51": "liú", + "9A52": "tuó", + "9A53": "céng", + "9A54": "diàn", + "9A55": "jiāo", + "9A56": "tiě", + "9A57": "yàn", + "9A58": "luó", + "9A59": "zhān", + "9A5A": "jīng", + "9A5B": "yì", + "9A5C": "yè", + "9A5D": "tuō", + "9A5E": "pīn", + "9A5F": "zhòu", + "9A60": "yàn", + "9A61": "lóng", + "9A62": "lǘ", + "9A63": "téng", + "9A64": "xiāng", + "9A65": "jì", + "9A66": "shuāng", + "9A67": "jú", + "9A68": "xí", + "9A69": "huān", + "9A6A": "lí", + "9A6B": "biāo", + "9A6C": "mǎ", + "9A6D": "yù", + "9A6E": "tuó", + "9A6F": "xún", + "9A70": "chí", + "9A71": "qū", + "9A72": "rì", + "9A73": "bó", + "9A74": "lǘ", + "9A75": "zǎng", + "9A76": "shǐ", + "9A77": "sì", + "9A78": "fù", + "9A79": "jū", + "9A7A": "zōu", + "9A7B": "zhù", + "9A7C": "tuo", + "9A7D": "nú", + "9A7E": "jià", + "9A7F": "yì", + "9A80": "dài", + "9A81": "xiāo", + "9A82": "mà", + "9A83": "yīn", + "9A84": "jiāo", + "9A85": "huá", + "9A86": "luò", + "9A87": "hài", + "9A88": "pián", + "9A89": "biāo", + "9A8A": "lí", + "9A8B": "chěng", + "9A8C": "yàn", + "9A8D": "xīng", + "9A8E": "qīn", + "9A8F": "jùn", + "9A90": "qí", + "9A91": "qí", + "9A92": "kè", + "9A93": "zhuī", + "9A94": "zōng", + "9A95": "sù", + "9A96": "cān", + "9A97": "piàn", + "9A98": "zhì", + "9A99": "kuí", + "9A9A": "sāo", + "9A9B": "wù", + "9A9C": "ào", + "9A9D": "liú", + "9A9E": "qiān", + "9A9F": "shàn", + "9AA0": "biāo", + "9AA1": "luó", + "9AA2": "cōng", + "9AA3": "chǎn", + "9AA4": "zhòu", + "9AA5": "jì", + "9AA6": "shuāng", + "9AA7": "xiāng", + "9AA8": "gǔ", + "9AA9": "wěi", + "9AAA": "wěi", + "9AAB": "wěi", + "9AAC": "yú", + "9AAD": "gàn", + "9AAE": "yì", + "9AAF": "āng", + "9AB0": "tóu", + "9AB1": "jiè", + "9AB2": "bào", + "9AB3": "bèi", + "9AB4": "cī", + "9AB5": "tǐ", + "9AB6": "dǐ", + "9AB7": "kū", + "9AB8": "hái", + "9AB9": "qiāo", + "9ABA": "hóu", + "9ABB": "kuà", + "9ABC": "gé", + "9ABD": "tuǐ", + "9ABE": "gěng", + "9ABF": "pián", + "9AC0": "bì", + "9AC1": "kē", + "9AC2": "qià", + "9AC3": "yú", + "9AC4": "suǐ", + "9AC5": "lóu", + "9AC6": "bó", + "9AC7": "xiāo", + "9AC8": "bǎng", + "9AC9": "bó", + "9ACA": "cī", + "9ACB": "kuān", + "9ACC": "bìn", + "9ACD": "mó", + "9ACE": "liáo", + "9ACF": "lóu", + "9AD0": "xiāo", + "9AD1": "dú", + "9AD2": "zāng", + "9AD3": "suǐ", + "9AD4": "tǐ", + "9AD5": "bìn", + "9AD6": "kuān", + "9AD7": "lú", + "9AD8": "gāo", + "9AD9": "gāo", + "9ADA": "qiào", + "9ADB": "kāo", + "9ADC": "qiǎo", + "9ADD": "láo", + "9ADE": "sào", + "9ADF": "biāo", + "9AE0": "kūn", + "9AE1": "kūn", + "9AE2": "dí", + "9AE3": "fǎng", + "9AE4": "xiū", + "9AE5": "rán", + "9AE6": "máo", + "9AE7": "dàn", + "9AE8": "kūn", + "9AE9": "bìn", + "9AEA": "fà", + "9AEB": "tiáo", + "9AEC": "pī", + "9AED": "zī", + "9AEE": "fà", + "9AEF": "rán", + "9AF0": "tì", + "9AF1": "bào", + "9AF2": "bì", + "9AF3": "máo", + "9AF4": "fú", + "9AF5": "ér", + "9AF6": "róng", + "9AF7": "qū", + "9AF8": "gōng", + "9AF9": "xiū", + "9AFA": "kuò", + "9AFB": "jì", + "9AFC": "péng", + "9AFD": "zhuā", + "9AFE": "shāo", + "9AFF": "suō", + "9B00": "tì", + "9B01": "lì", + "9B02": "bìn", + "9B03": "zōng", + "9B04": "dí", + "9B05": "péng", + "9B06": "sōng", + "9B07": "zhēng", + "9B08": "quán", + "9B09": "zōng", + "9B0A": "shùn", + "9B0B": "jiǎn", + "9B0C": "tuǒ", + "9B0D": "hú", + "9B0E": "là", + "9B0F": "jiū", + "9B10": "qí", + "9B11": "lián", + "9B12": "zhěn", + "9B13": "bìn", + "9B14": "péng", + "9B15": "mà", + "9B16": "sān", + "9B17": "mán", + "9B18": "mán", + "9B19": "sēng", + "9B1A": "xū", + "9B1B": "liè", + "9B1C": "qiān", + "9B1D": "qiān", + "9B1E": "náng", + "9B1F": "huán", + "9B20": "kuò", + "9B21": "níng", + "9B22": "bìn", + "9B23": "liè", + "9B24": "ráng", + "9B25": "dòu", + "9B26": "dòu", + "9B27": "nào", + "9B28": "hòng", + "9B29": "xì", + "9B2A": "dòu", + "9B2B": "hǎn", + "9B2C": "dòu", + "9B2D": "dòu", + "9B2E": "jiū", + "9B2F": "chàng", + "9B30": "yù", + "9B31": "yù", + "9B32": "gé", + "9B33": "yàn", + "9B34": "fǔ", + "9B35": "qín", + "9B36": "guī", + "9B37": "zōng", + "9B38": "liù", + "9B39": "guī", + "9B3A": "shāng", + "9B3B": "yù", + "9B3C": "guǐ", + "9B3D": "mèi", + "9B3E": "jì", + "9B3F": "qí", + "9B40": "gà", + "9B41": "kuí", + "9B42": "hún", + "9B43": "bá", + "9B44": "pò", + "9B45": "mèi", + "9B46": "xū", + "9B47": "yǎn", + "9B48": "xiāo", + "9B49": "liǎng", + "9B4A": "yù", + "9B4B": "tuí", + "9B4C": "qī", + "9B4D": "wǎng", + "9B4E": "liǎng", + "9B4F": "wèi", + "9B50": "gān", + "9B51": "chī", + "9B52": "piāo", + "9B53": "bì", + "9B54": "mó", + "9B55": "jǐ", + "9B56": "xū", + "9B57": "chǒu", + "9B58": "yǎn", + "9B59": "zhān", + "9B5A": "yú", + "9B5B": "dāo", + "9B5C": "rén", + "9B5D": "jié", + "9B5E": "ba", + "9B5F": "hóng", + "9B60": "tuō", + "9B61": "diào", + "9B62": "jǐ", + "9B63": "xù", + "9B64": "é", + "9B65": "è", + "9B66": "shā", + "9B67": "háng", + "9B68": "tún", + "9B69": "mò", + "9B6A": "jiè", + "9B6B": "shěn", + "9B6C": "bǎn", + "9B6D": "yuán", + "9B6E": "pí", + "9B6F": "lǔ", + "9B70": "wén", + "9B71": "hú", + "9B72": "lú", + "9B73": "zā", + "9B74": "fáng", + "9B75": "fén", + "9B76": "nà", + "9B77": "yóu", + "9B78": "pian", + "9B79": "mo", + "9B7A": "hé", + "9B7B": "xiá", + "9B7C": "qū", + "9B7D": "hán", + "9B7E": "pī", + "9B7F": "líng", + "9B80": "tuó", + "9B81": "bō", + "9B82": "qiú", + "9B83": "píng", + "9B84": "fú", + "9B85": "bì", + "9B86": "cǐ", + "9B87": "wèi", + "9B88": "jū", + "9B89": "diāo", + "9B8A": "bà", + "9B8B": "yóu", + "9B8C": "gǔn", + "9B8D": "pī", + "9B8E": "nián", + "9B8F": "xīng", + "9B90": "tái", + "9B91": "bào", + "9B92": "fù", + "9B93": "zhǎ", + "9B94": "jù", + "9B95": "gū", + "9B96": "shi", + "9B97": "dong", + "9B98": "dai", + "9B99": "tà", + "9B9A": "jié", + "9B9B": "shū", + "9B9C": "hòu", + "9B9D": "xiǎng", + "9B9E": "ér", + "9B9F": "àn", + "9BA0": "wéi", + "9BA1": "zhào", + "9BA2": "zhū", + "9BA3": "yìn", + "9BA4": "liè", + "9BA5": "luò", + "9BA6": "tóng", + "9BA7": "tǐ", + "9BA8": "yì", + "9BA9": "bìng", + "9BAA": "wěi", + "9BAB": "jiāo", + "9BAC": "kū", + "9BAD": "guī", + "9BAE": "xiān", + "9BAF": "gé", + "9BB0": "huí", + "9BB1": "lao", + "9BB2": "fu", + "9BB3": "kào", + "9BB4": "xiu", + "9BB5": "duó", + "9BB6": "jūn", + "9BB7": "tí", + "9BB8": "miǎn", + "9BB9": "shāo", + "9BBA": "zhǎ", + "9BBB": "suō", + "9BBC": "qīn", + "9BBD": "yú", + "9BBE": "něi", + "9BBF": "zhé", + "9BC0": "gǔn", + "9BC1": "gěng", + "9BC2": "su", + "9BC3": "wú", + "9BC4": "qiú", + "9BC5": "shān", + "9BC6": "pū", + "9BC7": "huàn", + "9BC8": "tiáo", + "9BC9": "lǐ", + "9BCA": "shā", + "9BCB": "shā", + "9BCC": "kào", + "9BCD": "méng", + "9BCE": "cheng", + "9BCF": "li", + "9BD0": "zou", + "9BD1": "xi", + "9BD2": "yǒng", + "9BD3": "ní", + "9BD4": "zī", + "9BD5": "qí", + "9BD6": "zhēng", + "9BD7": "xiǎng", + "9BD8": "něi", + "9BD9": "chún", + "9BDA": "jì", + "9BDB": "diāo", + "9BDC": "qiè", + "9BDD": "gù", + "9BDE": "zhǒu", + "9BDF": "dōng", + "9BE0": "lái", + "9BE1": "fèi", + "9BE2": "ní", + "9BE3": "yì", + "9BE4": "kūn", + "9BE5": "lù", + "9BE6": "jiù", + "9BE7": "chāng", + "9BE8": "jīng", + "9BE9": "lún", + "9BEA": "líng", + "9BEB": "zōu", + "9BEC": "lí", + "9BED": "měng", + "9BEE": "zōng", + "9BEF": "zhì", + "9BF0": "nián", + "9BF1": "hu", + "9BF2": "yu", + "9BF3": "di", + "9BF4": "shī", + "9BF5": "shēn", + "9BF6": "hǔn", + "9BF7": "tí", + "9BF8": "hóu", + "9BF9": "xīng", + "9BFA": "zhū", + "9BFB": "là", + "9BFC": "zōng", + "9BFD": "zéi", + "9BFE": "biān", + "9BFF": "biān", + "9C00": "huàn", + "9C01": "quán", + "9C02": "zéi", + "9C03": "wēi", + "9C04": "wēi", + "9C05": "yú", + "9C06": "chūn", + "9C07": "róu", + "9C08": "dié", + "9C09": "huáng", + "9C0A": "liàn", + "9C0B": "yǎn", + "9C0C": "qiū", + "9C0D": "qiū", + "9C0E": "jiǎn", + "9C0F": "bī", + "9C10": "è", + "9C11": "yáng", + "9C12": "fù", + "9C13": "sāi", + "9C14": "gǎn", + "9C15": "xiā", + "9C16": "tuǒ", + "9C17": "hú", + "9C18": "shi", + "9C19": "ruò", + "9C1A": "xuan", + "9C1B": "wēn", + "9C1C": "qiàn", + "9C1D": "hào", + "9C1E": "wū", + "9C1F": "fáng", + "9C20": "sāo", + "9C21": "liú", + "9C22": "mǎ", + "9C23": "shí", + "9C24": "shī", + "9C25": "guān", + "9C26": "zī", + "9C27": "téng", + "9C28": "tǎ", + "9C29": "yáo", + "9C2A": "é", + "9C2B": "yóng", + "9C2C": "qián", + "9C2D": "qí", + "9C2E": "wēn", + "9C2F": "ruò", + "9C30": "shen", + "9C31": "lián", + "9C32": "áo", + "9C33": "lè", + "9C34": "huī", + "9C35": "mǐn", + "9C36": "jì", + "9C37": "tiáo", + "9C38": "qū", + "9C39": "jiān", + "9C3A": "shēn", + "9C3B": "mán", + "9C3C": "xí", + "9C3D": "qiú", + "9C3E": "biào", + "9C3F": "jì", + "9C40": "jì", + "9C41": "zhú", + "9C42": "jiāng", + "9C43": "xiū", + "9C44": "zhuān", + "9C45": "yōng", + "9C46": "zhāng", + "9C47": "kāng", + "9C48": "xuě", + "9C49": "biē", + "9C4A": "yù", + "9C4B": "qū", + "9C4C": "xiàng", + "9C4D": "bō", + "9C4E": "jiǎo", + "9C4F": "xún", + "9C50": "sù", + "9C51": "huáng", + "9C52": "zūn", + "9C53": "shàn", + "9C54": "shàn", + "9C55": "fān", + "9C56": "guì", + "9C57": "lín", + "9C58": "xún", + "9C59": "miáo", + "9C5A": "xǐ", + "9C5B": "zeng", + "9C5C": "xiang", + "9C5D": "fèn", + "9C5E": "guān", + "9C5F": "hòu", + "9C60": "kuài", + "9C61": "zéi", + "9C62": "sāo", + "9C63": "zhān", + "9C64": "gǎn", + "9C65": "guì", + "9C66": "yìng", + "9C67": "lǐ", + "9C68": "cháng", + "9C69": "lei", + "9C6A": "shu", + "9C6B": "ai", + "9C6C": "rú", + "9C6D": "jì", + "9C6E": "xù", + "9C6F": "hù", + "9C70": "shu", + "9C71": "lì", + "9C72": "liè", + "9C73": "lì", + "9C74": "miè", + "9C75": "zhēn", + "9C76": "xiǎng", + "9C77": "è", + "9C78": "lú", + "9C79": "guàn", + "9C7A": "lí", + "9C7B": "xiān", + "9C7C": "yú", + "9C7D": "dāo", + "9C7E": "jǐ", + "9C7F": "yóu", + "9C80": "tún", + "9C81": "lǔ", + "9C82": "fáng", + "9C83": "bā", + "9C84": "hé", + "9C85": "bà", + "9C86": "píng", + "9C87": "nián", + "9C88": "lú", + "9C89": "yóu", + "9C8A": "zhǎ", + "9C8B": "fù", + "9C8C": "bà", + "9C8D": "bào", + "9C8E": "hòu", + "9C8F": "pí", + "9C90": "tái", + "9C91": "guī", + "9C92": "jié", + "9C93": "kǎo", + "9C94": "wěi", + "9C95": "ér", + "9C96": "tóng", + "9C97": "zéi", + "9C98": "hòu", + "9C99": "kuài", + "9C9A": "jì", + "9C9B": "jiāo", + "9C9C": "xiān", + "9C9D": "zhǎ", + "9C9E": "xiǎng", + "9C9F": "xún", + "9CA0": "gěng", + "9CA1": "lí", + "9CA2": "lián", + "9CA3": "jiān", + "9CA4": "lǐ", + "9CA5": "shí", + "9CA6": "tiáo", + "9CA7": "gǔn", + "9CA8": "shā", + "9CA9": "huàn", + "9CAA": "jūn", + "9CAB": "jì", + "9CAC": "yǒng", + "9CAD": "qīng", + "9CAE": "líng", + "9CAF": "qí", + "9CB0": "zōu", + "9CB1": "fēi", + "9CB2": "kūn", + "9CB3": "chāng", + "9CB4": "gù", + "9CB5": "ní", + "9CB6": "nián", + "9CB7": "diāo", + "9CB8": "jīng", + "9CB9": "shēn", + "9CBA": "shī", + "9CBB": "zī", + "9CBC": "fèn", + "9CBD": "dié", + "9CBE": "bī", + "9CBF": "cháng", + "9CC0": "tí", + "9CC1": "wēn", + "9CC2": "wēi", + "9CC3": "sāi", + "9CC4": "è", + "9CC5": "qiū", + "9CC6": "fù", + "9CC7": "huáng", + "9CC8": "quán", + "9CC9": "jiāng", + "9CCA": "biān", + "9CCB": "sāo", + "9CCC": "áo", + "9CCD": "qí", + "9CCE": "tǎ", + "9CCF": "guān", + "9CD0": "yáo", + "9CD1": "páng", + "9CD2": "jiān", + "9CD3": "lè", + "9CD4": "biào", + "9CD5": "xuě", + "9CD6": "biē", + "9CD7": "mán", + "9CD8": "mǐn", + "9CD9": "yōng", + "9CDA": "wèi", + "9CDB": "xí", + "9CDC": "guì", + "9CDD": "shàn", + "9CDE": "lín", + "9CDF": "zūn", + "9CE0": "hù", + "9CE1": "gǎn", + "9CE2": "lǐ", + "9CE3": "zhān", + "9CE4": "guǎn", + "9CE5": "niǎo", + "9CE6": "yǐ", + "9CE7": "fú", + "9CE8": "lì", + "9CE9": "jiū", + "9CEA": "bú", + "9CEB": "yàn", + "9CEC": "fǔ", + "9CED": "diāo", + "9CEE": "jī", + "9CEF": "fèng", + "9CF0": "ru", + "9CF1": "gān", + "9CF2": "shī", + "9CF3": "fèng", + "9CF4": "míng", + "9CF5": "bǎo", + "9CF6": "yuān", + "9CF7": "zhī", + "9CF8": "hù", + "9CF9": "qín", + "9CFA": "fū", + "9CFB": "bān", + "9CFC": "wén", + "9CFD": "jiān", + "9CFE": "shī", + "9CFF": "yù", + "9D00": "fǒu", + "9D01": "yāo", + "9D02": "jué", + "9D03": "jué", + "9D04": "pǐ", + "9D05": "huān", + "9D06": "zhèn", + "9D07": "bǎo", + "9D08": "yàn", + "9D09": "yā", + "9D0A": "zhèng", + "9D0B": "fāng", + "9D0C": "fèng", + "9D0D": "wén", + "9D0E": "ōu", + "9D0F": "dài", + "9D10": "gē", + "9D11": "rú", + "9D12": "líng", + "9D13": "miè", + "9D14": "fú", + "9D15": "tuó", + "9D16": "mín", + "9D17": "lì", + "9D18": "biǎn", + "9D19": "zhì", + "9D1A": "gē", + "9D1B": "yuān", + "9D1C": "cí", + "9D1D": "qú", + "9D1E": "xiāo", + "9D1F": "chī", + "9D20": "dàn", + "9D21": "jū", + "9D22": "yǎo", + "9D23": "gū", + "9D24": "dōng", + "9D25": "yù", + "9D26": "yāng", + "9D27": "ròng", + "9D28": "yā", + "9D29": "tiě", + "9D2A": "yù", + "9D2B": "tian", + "9D2C": "yīng", + "9D2D": "duī", + "9D2E": "wū", + "9D2F": "ér", + "9D30": "guā", + "9D31": "ài", + "9D32": "zhī", + "9D33": "yàn", + "9D34": "héng", + "9D35": "xiāo", + "9D36": "jiá", + "9D37": "liè", + "9D38": "zhū", + "9D39": "yáng", + "9D3A": "tí", + "9D3B": "hóng", + "9D3C": "luò", + "9D3D": "rú", + "9D3E": "móu", + "9D3F": "gē", + "9D40": "rén", + "9D41": "jiāo", + "9D42": "xiū", + "9D43": "zhōu", + "9D44": "zhī", + "9D45": "luò", + "9D46": "heng", + "9D47": "nian", + "9D48": "e", + "9D49": "luán", + "9D4A": "jiá", + "9D4B": "jì", + "9D4C": "tú", + "9D4D": "huān", + "9D4E": "tuǒ", + "9D4F": "bǔ", + "9D50": "wú", + "9D51": "juān", + "9D52": "yù", + "9D53": "bó", + "9D54": "jùn", + "9D55": "xùn", + "9D56": "bī", + "9D57": "xī", + "9D58": "jùn", + "9D59": "jú", + "9D5A": "tū", + "9D5B": "jīng", + "9D5C": "tí", + "9D5D": "é", + "9D5E": "é", + "9D5F": "kuáng", + "9D60": "hú", + "9D61": "wǔ", + "9D62": "shēn", + "9D63": "lài", + "9D64": "jiao", + "9D65": "pan", + "9D66": "lù", + "9D67": "pí", + "9D68": "shū", + "9D69": "fú", + "9D6A": "ān", + "9D6B": "zhuó", + "9D6C": "péng", + "9D6D": "qín", + "9D6E": "qiān", + "9D6F": "bēi", + "9D70": "diāo", + "9D71": "lù", + "9D72": "que", + "9D73": "jiān", + "9D74": "jú", + "9D75": "tù", + "9D76": "yā", + "9D77": "yuān", + "9D78": "qí", + "9D79": "lí", + "9D7A": "yè", + "9D7B": "zhuī", + "9D7C": "kōng", + "9D7D": "duò", + "9D7E": "kūn", + "9D7F": "shēng", + "9D80": "qí", + "9D81": "jīng", + "9D82": "yì", + "9D83": "yì", + "9D84": "jīng", + "9D85": "zī", + "9D86": "lái", + "9D87": "dōng", + "9D88": "qī", + "9D89": "chun", + "9D8A": "gēng", + "9D8B": "jū", + "9D8C": "jué", + "9D8D": "yi", + "9D8E": "zun", + "9D8F": "jī", + "9D90": "shù", + "9D91": "yīng", + "9D92": "chì", + "9D93": "miáo", + "9D94": "róu", + "9D95": "ān", + "9D96": "qiū", + "9D97": "tí", + "9D98": "hú", + "9D99": "tí", + "9D9A": "è", + "9D9B": "jiē", + "9D9C": "máo", + "9D9D": "fú", + "9D9E": "chūn", + "9D9F": "tú", + "9DA0": "yǎn", + "9DA1": "hé", + "9DA2": "yuán", + "9DA3": "piān", + "9DA4": "kūn", + "9DA5": "méi", + "9DA6": "hú", + "9DA7": "yīng", + "9DA8": "chuàn", + "9DA9": "wù", + "9DAA": "jú", + "9DAB": "dong", + "9DAC": "cāng", + "9DAD": "fǎng", + "9DAE": "hè", + "9DAF": "yīng", + "9DB0": "yuán", + "9DB1": "xiān", + "9DB2": "wēng", + "9DB3": "shī", + "9DB4": "hè", + "9DB5": "chú", + "9DB6": "táng", + "9DB7": "xiá", + "9DB8": "ruò", + "9DB9": "liú", + "9DBA": "jí", + "9DBB": "gú", + "9DBC": "jiān", + "9DBD": "sǔn", + "9DBE": "hàn", + "9DBF": "cí", + "9DC0": "cí", + "9DC1": "yì", + "9DC2": "yào", + "9DC3": "yàn", + "9DC4": "jī", + "9DC5": "lì", + "9DC6": "tián", + "9DC7": "kòu", + "9DC8": "tī", + "9DC9": "tī", + "9DCA": "yì", + "9DCB": "tú", + "9DCC": "mǎ", + "9DCD": "jiāo", + "9DCE": "gāo", + "9DCF": "tián", + "9DD0": "chén", + "9DD1": "jí", + "9DD2": "tuán", + "9DD3": "zhè", + "9DD4": "áo", + "9DD5": "yǎo", + "9DD6": "yī", + "9DD7": "ōu", + "9DD8": "chì", + "9DD9": "zhì", + "9DDA": "liù", + "9DDB": "yōng", + "9DDC": "lǘ", + "9DDD": "bì", + "9DDE": "shuāng", + "9DDF": "zhuó", + "9DE0": "yú", + "9DE1": "wú", + "9DE2": "jué", + "9DE3": "yín", + "9DE4": "tí", + "9DE5": "sī", + "9DE6": "jiāo", + "9DE7": "yì", + "9DE8": "huá", + "9DE9": "bì", + "9DEA": "yīng", + "9DEB": "sù", + "9DEC": "huáng", + "9DED": "fán", + "9DEE": "jiāo", + "9DEF": "liáo", + "9DF0": "yàn", + "9DF1": "gāo", + "9DF2": "jiù", + "9DF3": "xián", + "9DF4": "xián", + "9DF5": "tú", + "9DF6": "mǎi", + "9DF7": "zūn", + "9DF8": "yù", + "9DF9": "yīng", + "9DFA": "lù", + "9DFB": "tuán", + "9DFC": "xián", + "9DFD": "xué", + "9DFE": "yì", + "9DFF": "pì", + "9E00": "chǔ", + "9E01": "luó", + "9E02": "xī", + "9E03": "yí", + "9E04": "jī", + "9E05": "zé", + "9E06": "yú", + "9E07": "zhān", + "9E08": "yè", + "9E09": "yáng", + "9E0A": "pì", + "9E0B": "níng", + "9E0C": "hù", + "9E0D": "mí", + "9E0E": "yīng", + "9E0F": "méng", + "9E10": "dí", + "9E11": "yuè", + "9E12": "yù", + "9E13": "lěi", + "9E14": "bào", + "9E15": "lú", + "9E16": "hè", + "9E17": "lóng", + "9E18": "shuāng", + "9E19": "yuè", + "9E1A": "yīng", + "9E1B": "guàn", + "9E1C": "qú", + "9E1D": "lí", + "9E1E": "luán", + "9E1F": "niǎo", + "9E20": "jiū", + "9E21": "jī", + "9E22": "yuān", + "9E23": "míng", + "9E24": "shī", + "9E25": "ōu", + "9E26": "yā", + "9E27": "cāng", + "9E28": "bǎo", + "9E29": "zhèn", + "9E2A": "gū", + "9E2B": "dōng", + "9E2C": "lú", + "9E2D": "yā", + "9E2E": "xiāo", + "9E2F": "yāng", + "9E30": "líng", + "9E31": "chī", + "9E32": "qú", + "9E33": "yuān", + "9E34": "xué", + "9E35": "tuó", + "9E36": "sī", + "9E37": "zhì", + "9E38": "ér", + "9E39": "guā", + "9E3A": "xiū", + "9E3B": "héng", + "9E3C": "zhōu", + "9E3D": "gē", + "9E3E": "luán", + "9E3F": "hóng", + "9E40": "wú", + "9E41": "bó", + "9E42": "lí", + "9E43": "juān", + "9E44": "gǔ", + "9E45": "é", + "9E46": "yù", + "9E47": "xián", + "9E48": "tí", + "9E49": "wǔ", + "9E4A": "que", + "9E4B": "miáo", + "9E4C": "ān", + "9E4D": "kūn", + "9E4E": "bēi", + "9E4F": "péng", + "9E50": "qiān", + "9E51": "chun", + "9E52": "gēng", + "9E53": "yuān", + "9E54": "sù", + "9E55": "hú", + "9E56": "hé", + "9E57": "è", + "9E58": "gǔ", + "9E59": "qiū", + "9E5A": "cí", + "9E5B": "méi", + "9E5C": "wù", + "9E5D": "yì", + "9E5E": "yào", + "9E5F": "wēng", + "9E60": "liú", + "9E61": "jí", + "9E62": "yì", + "9E63": "jiān", + "9E64": "hè", + "9E65": "yī", + "9E66": "yīng", + "9E67": "zhè", + "9E68": "liù", + "9E69": "liáo", + "9E6A": "jiāo", + "9E6B": "jiù", + "9E6C": "yù", + "9E6D": "lù", + "9E6E": "huán", + "9E6F": "zhān", + "9E70": "yīng", + "9E71": "hù", + "9E72": "méng", + "9E73": "guàn", + "9E74": "shuāng", + "9E75": "lǔ", + "9E76": "jīn", + "9E77": "líng", + "9E78": "jiǎn", + "9E79": "xián", + "9E7A": "cuó", + "9E7B": "jiǎn", + "9E7C": "jiǎn", + "9E7D": "yán", + "9E7E": "cuó", + "9E7F": "lù", + "9E80": "yōu", + "9E81": "cū", + "9E82": "jǐ", + "9E83": "páo", + "9E84": "cū", + "9E85": "páo", + "9E86": "zhù", + "9E87": "jūn", + "9E88": "zhǔ", + "9E89": "jiān", + "9E8A": "mí", + "9E8B": "mí", + "9E8C": "yǔ", + "9E8D": "liú", + "9E8E": "chén", + "9E8F": "jūn", + "9E90": "lín", + "9E91": "ní", + "9E92": "qí", + "9E93": "lù", + "9E94": "jiù", + "9E95": "jūn", + "9E96": "jīng", + "9E97": "lì", + "9E98": "xiāng", + "9E99": "xián", + "9E9A": "jiā", + "9E9B": "mí", + "9E9C": "lì", + "9E9D": "shè", + "9E9E": "zhāng", + "9E9F": "lín", + "9EA0": "jīng", + "9EA1": "qí", + "9EA2": "líng", + "9EA3": "yán", + "9EA4": "cū", + "9EA5": "mài", + "9EA6": "mài", + "9EA7": "hé", + "9EA8": "chǎo", + "9EA9": "fū", + "9EAA": "miàn", + "9EAB": "miǎn", + "9EAC": "fū", + "9EAD": "pào", + "9EAE": "qù", + "9EAF": "qū", + "9EB0": "móu", + "9EB1": "fū", + "9EB2": "xiàn", + "9EB3": "lái", + "9EB4": "qū", + "9EB5": "miàn", + "9EB6": "chi", + "9EB7": "fēng", + "9EB8": "fū", + "9EB9": "qū", + "9EBA": "miàn", + "9EBB": "má", + "9EBC": "me", + "9EBD": "mó", + "9EBE": "huī", + "9EBF": "mo", + "9EC0": "zōu", + "9EC1": "nún", + "9EC2": "fén", + "9EC3": "huáng", + "9EC4": "huáng", + "9EC5": "jīn", + "9EC6": "guāng", + "9EC7": "tiān", + "9EC8": "tǒu", + "9EC9": "hóng", + "9ECA": "huà", + "9ECB": "kuàng", + "9ECC": "hóng", + "9ECD": "shǔ", + "9ECE": "lí", + "9ECF": "nián", + "9ED0": "chī", + "9ED1": "hēi", + "9ED2": "hēi", + "9ED3": "yì", + "9ED4": "qián", + "9ED5": "dǎn", + "9ED6": "xì", + "9ED7": "tūn", + "9ED8": "mò", + "9ED9": "mò", + "9EDA": "qián", + "9EDB": "dài", + "9EDC": "chù", + "9EDD": "yǒu", + "9EDE": "diǎn", + "9EDF": "yī", + "9EE0": "xiá", + "9EE1": "yǎn", + "9EE2": "qū", + "9EE3": "měi", + "9EE4": "yǎn", + "9EE5": "qíng", + "9EE6": "yuè", + "9EE7": "lí", + "9EE8": "dǎng", + "9EE9": "dú", + "9EEA": "cǎn", + "9EEB": "yān", + "9EEC": "yán", + "9EED": "yǎn", + "9EEE": "dǎn", + "9EEF": "àn", + "9EF0": "zhěn", + "9EF1": "dài", + "9EF2": "cǎn", + "9EF3": "yī", + "9EF4": "méi", + "9EF5": "zhǎn", + "9EF6": "yǎn", + "9EF7": "dú", + "9EF8": "lú", + "9EF9": "zhǐ", + "9EFA": "fěn", + "9EFB": "fú", + "9EFC": "fǔ", + "9EFD": "miǎn", + "9EFE": "miǎn", + "9EFF": "yuán", + "9F00": "cù", + "9F01": "qù", + "9F02": "cháo", + "9F03": "wā", + "9F04": "zhū", + "9F05": "zhī", + "9F06": "méng", + "9F07": "áo", + "9F08": "biē", + "9F09": "tuó", + "9F0A": "bì", + "9F0B": "yuán", + "9F0C": "cháo", + "9F0D": "tuó", + "9F0E": "dǐng", + "9F0F": "mì", + "9F10": "nài", + "9F11": "dǐng", + "9F12": "zī", + "9F13": "gǔ", + "9F14": "gǔ", + "9F15": "dōng", + "9F16": "fén", + "9F17": "táo", + "9F18": "yuān", + "9F19": "pí", + "9F1A": "chāng", + "9F1B": "gāo", + "9F1C": "qì", + "9F1D": "yuān", + "9F1E": "tāng", + "9F1F": "tēng", + "9F20": "shǔ", + "9F21": "shǔ", + "9F22": "fén", + "9F23": "fèi", + "9F24": "wén", + "9F25": "bá", + "9F26": "diāo", + "9F27": "tuó", + "9F28": "zhōng", + "9F29": "qú", + "9F2A": "shēng", + "9F2B": "shí", + "9F2C": "yòu", + "9F2D": "shí", + "9F2E": "tíng", + "9F2F": "wú", + "9F30": "niàn", + "9F31": "jīng", + "9F32": "hún", + "9F33": "jú", + "9F34": "yǎn", + "9F35": "tū", + "9F36": "sī", + "9F37": "xī", + "9F38": "xiàn", + "9F39": "yǎn", + "9F3A": "léi", + "9F3B": "bí", + "9F3C": "yǎo", + "9F3D": "qiú", + "9F3E": "hān", + "9F3F": "wù", + "9F40": "wù", + "9F41": "hōu", + "9F42": "xiè", + "9F43": "è", + "9F44": "zhā", + "9F45": "xiù", + "9F46": "wèng", + "9F47": "zhā", + "9F48": "nòng", + "9F49": "nàng", + "9F4A": "qí", + "9F4B": "zhāi", + "9F4C": "jì", + "9F4D": "zī", + "9F4E": "jī", + "9F4F": "jī", + "9F50": "qí", + "9F51": "jī", + "9F52": "chǐ", + "9F53": "chèn", + "9F54": "chèn", + "9F55": "hé", + "9F56": "yá", + "9F57": "yín", + "9F58": "xiè", + "9F59": "bāo", + "9F5A": "zé", + "9F5B": "xiè", + "9F5C": "chái", + "9F5D": "chī", + "9F5E": "yǎn", + "9F5F": "jǔ", + "9F60": "tiáo", + "9F61": "líng", + "9F62": "líng", + "9F63": "chū", + "9F64": "quán", + "9F65": "xiè", + "9F66": "kěn", + "9F67": "niè", + "9F68": "jiù", + "9F69": "yǎo", + "9F6A": "chuò", + "9F6B": "kǔn", + "9F6C": "yǔ", + "9F6D": "chǔ", + "9F6E": "yǐ", + "9F6F": "ní", + "9F70": "zé", + "9F71": "zōu", + "9F72": "qǔ", + "9F73": "yǔn", + "9F74": "yǎn", + "9F75": "óu", + "9F76": "è", + "9F77": "wò", + "9F78": "yì", + "9F79": "cī", + "9F7A": "zōu", + "9F7B": "diān", + "9F7C": "chǔ", + "9F7D": "jìn", + "9F7E": "yà", + "9F7F": "chǐ", + "9F80": "chèn", + "9F81": "hé", + "9F82": "yín", + "9F83": "jǔ", + "9F84": "líng", + "9F85": "bāo", + "9F86": "tiáo", + "9F87": "zī", + "9F88": "kěn", + "9F89": "yǔ", + "9F8A": "chuò", + "9F8B": "qǔ", + "9F8C": "wò", + "9F8D": "lóng", + "9F8E": "páng", + "9F8F": "gōng", + "9F90": "páng", + "9F91": "yǎn", + "9F92": "lóng", + "9F93": "lǒng", + "9F94": "gōng", + "9F95": "kān", + "9F96": "dá", + "9F97": "líng", + "9F98": "dá", + "9F99": "lóng", + "9F9A": "gōng", + "9F9B": "kān", + "9F9C": "guī", + "9F9D": "qiū", + "9F9E": "biē", + "9F9F": "guī", + "9FA0": "yuè", + "9FA1": "chuì", + "9FA2": "hé", + "9FA3": "jué", + "9FA4": "xié", + "9FA5": "yù", + "9FC3": "shǎn", + "20000": "hē", + "20001": "qī", + "20003": "qiě", + "20005": "hài", + "20009": "qiū", + "2000A": "cāo", + "2000D": "shì", + "20013": "sī", + "20014": "jué", + "2001B": "yù", + "2001D": "kōng", + "20022": "zī", + "20026": "xíng", + "20031": "mǒu", + "20037": "jī", + "20038": "yè", + "20039": "jūn", + "2003C": "qián", + "2003D": "lù", + "20049": "chū", + "20057": "shì", + "20060": "qiè", + "20065": "gǎ", + "2006D": "qí", + "20077": "chǎn", + "20084": "huān", + "20086": "yì", + "20087": "zuǒ", + "20088": "jié", + "20091": "zōu", + "20094": "zǐ", + "2009F": "jīn", + "200A2": "pài", + "200A4": "duī", + "200A5": "cóng", + "200A7": "shèn", + "200B8": "huáng", + "200CA": "yǐn", + "200CC": "gǔn", + "200D6": "jiū", + "200EB": "shēn", + "200FA": "jiù", + "20105": "yè", + "20109": "dòng", + "2010C": "jué", + "2010D": "jié", + "2010F": "diǎo", + "20111": "jué", + "20112": "chuí", + "20116": "líng", + "2011A": "tīng", + "20123": "gèn", + "2012E": "yà", + "20131": "yí", + "2013F": "wéi", + "20142": "jié", + "2014C": "yí", + "20157": "diè", + "2015A": "qí", + "2016C": "bāo", + "20171": "xiè", + "20179": "zhàng", + "2018C": "yōng", + "20190": "xù", + "20199": "diè", + "2019B": "dān", + "2019F": "wěi", + "201A3": "guǎ", + "201A9": "fàn", + "201AE": "mò", + "201B1": "xī", + "201B2": "yǎn", + "201B5": "ní", + "201B6": "dàn", + "201CB": "dǎn", + "201CF": "tāo", + "201D2": "gōng", + "201D7": "kuā", + "201D8": "chù", + "201EF": "qù", + "201F1": "mò", + "201F3": "shī", + "201F5": "gǎn", + "201F7": "shēng", + "20201": "tuō", + "20205": "shōu", + "2020A": "niě", + "20224": "yùn", + "20225": "guǎ", + "2022C": "xiāo", + "2022D": "láo", + "20230": "dàn", + "20231": "suō", + "20235": "mǎng", + "20236": "yí", + "20238": "tè", + "2023A": "bì", + "20242": "tà", + "20257": "luò", + "20262": "xǐ", + "20263": "hūn", + "20264": "dá", + "20267": "jù", + "20269": "dú", + "2026C": "ǎn", + "20289": "mèi", + "2028C": "rán", + "2028E": "ái", + "2028F": "yù", + "20292": "jiàn", + "20294": "qì", + "2029F": "mǐn", + "202A3": "zhòu", + "202A4": "zhì", + "202A5": "zhǒng", + "202A6": "nǎo", + "202A7": "bìng", + "202A9": "zhuàn", + "202AA": "shù", + "202AB": "xùn", + "202AC": "jué", + "202AD": "qiǎn", + "202B0": "guǎ", + "202B2": "tū", + "202B6": "yìng", + "202B7": "zhì", + "202BE": "kuí", + "202C6": "chèn", + "202D6": "liàn", + "202D7": "yā", + "202DC": "guò", + "202DD": "miǎo", + "202DE": "shé", + "202DF": "yǔ", + "202E1": "sì", + "202E2": "sǒu", + "202E4": "zhì", + "202E7": "qiē", + "202E9": "fù", + "202EC": "jú", + "202ED": "bèi", + "202EF": "bì", + "202F2": "suǒ", + "202F5": "qiǎn", + "202F6": "mǐng", + "202F7": "chǎn", + "202FA": "sāo", + "202FB": "jī", + "20315": "gòng", + "20316": "qióng", + "2031A": "ròng", + "2031E": "sǒu", + "2031F": "sǒu", + "20320": "yáo", + "2032A": "chōu", + "2032D": "shuài", + "2032E": "zhē", + "2032F": "lì", + "20330": "gài", + "20331": "suī", + "20332": "zhān", + "20334": "zhuàng", + "2033D": "fù", + "20343": "jī", + "20344": "dōu", + "20357": "huì", + "2035A": "jiǎn", + "2035B": "yǎn", + "2035C": "zhì", + "20368": "měi", + "20369": "yào", + "2036A": "dī", + "2036B": "yí", + "2036F": "bié", + "20372": "qú", + "20373": "yì", + "20375": "yàng", + "20379": "zhá", + "2037D": "shà", + "20399": "lái", + "203AE": "jué", + "203B0": "qī", + "203B3": "yú", + "203B6": "zǎi", + "203B7": "sà", + "203B8": "sè", + "203BB": "dùn", + "203BF": "jiě", + "203C0": "kē", + "203C3": "yuē", + "203C7": "jiǎn", + "203C8": "yáo", + "203D3": "xiān", + "203D5": "xiào", + "203D6": "qiāo", + "203DA": "yù", + "203DB": "qú", + "203E1": "xiān", + "203E2": "luò", + "203E4": "guǎng", + "203E7": "chēng", + "203E8": "chuǎng", + "203E9": "yí", + "203EB": "zhěng", + "203ED": "zòng", + "203EE": "duì", + "203F0": "zhǎi", + "203FF": "fěi", + "20400": "yí", + "20401": "méng", + "20408": "biān", + "20409": "jié", + "2040A": "shù", + "2040B": "liáo", + "2040C": "bǐ", + "2040D": "sú", + "20411": "dì", + "20421": "bèi", + "20422": "wèn", + "20427": "méng", + "20429": "chǎn", + "20435": "dǎo", + "2043A": "pín", + "2043B": "jiǎn", + "2043C": "lìn", + "2043D": "guì", + "2043E": "qī", + "2043F": "hōng", + "20443": "jí", + "20444": "xiè", + "20445": "zhēng", + "20446": "chǎn", + "20450": "yáo", + "20451": "chǎn", + "20458": "diān", + "20459": "chòng", + "2045A": "néi", + "2045B": "néi", + "2045E": "zhài", + "2045F": "biān", + "20461": "chǎn", + "2046A": "xiāo", + "2046F": "cù", + "20470": "xīn", + "20471": "jǐng", + "20472": "qiān", + "20474": "qīng", + "20479": "gǔ", + "20484": "wù", + "2049C": "yuǎn", + "2049D": "bǐng", + "204A2": "wán", + "204B0": "niǎo", + "204B5": "liàn", + "204B8": "rǎo", + "204BE": "fàn", + "204BF": "dí", + "204CA": "huī", + "204CB": "yì", + "204CC": "xián", + "204D6": "lán", + "204D7": "fù", + "204D9": "xiòng", + "204DC": "liǎng", + "204DD": "tāo", + "204DE": "jí", + "204E2": "jiè", + "204E3": "zhá", + "204E4": "shī", + "204EA": "qí", + "204EB": "biǎn", + "204ED": "lǎn", + "204EE": "lǐn", + "204F6": "zhì", + "204F7": "bì", + "204F8": "shèng", + "204FD": "shèng", + "204FF": "qín", + "20502": "biāo", + "20503": "xī", + "20509": "juàn", + "2050B": "jī", + "2050D": "xī", + "2050E": "qǐn", + "20511": "hài", + "20515": "lún", + "20520": "yuè", + "20528": "lián", + "2052F": "bān", + "20532": "héng", + "20536": "qī", + "2053A": "qiān", + "2053B": "zhèng", + "2053C": "mǎo", + "20541": "cóng", + "20544": "nà", + "2054A": "tǐng", + "2054C": "zōng", + "20555": "jiōng", + "20556": "zhǎo", + "2055F": "niǎn", + "20560": "chéng", + "20563": "qià", + "20566": "yù", + "20567": "jiǎo", + "2056D": "zhào", + "20573": "dí", + "20574": "jiū", + "20578": "suǐ", + "2057B": "yāo", + "2057F": "wāng", + "20582": "liáo", + "20584": "tóng", + "20586": "mèng", + "2058B": "yǒu", + "20593": "sī", + "2059B": "lòu", + "2059F": "yīn", + "205A5": "chǒng", + "205AB": "gǎn", + "205AC": "jiū", + "205B6": "qìn", + "205B7": "jiǒng", + "205B9": "xié", + "205C2": "hè", + "205C6": "tāo", + "205C8": "qiú", + "205C9": "xié", + "205CA": "jìng", + "205CB": "niǎn", + "205CC": "jìng", + "205CF": "jí", + "205D8": "tiǎn", + "205DA": "cuì", + "205DB": "dié", + "205DD": "qǐng", + "205E5": "pìng", + "205E6": "píng", + "205E8": "dié", + "205E9": "lòu", + "205F3": "liǎn", + "205F4": "hán", + "205F5": "pāng", + "205F6": "táng", + "205FA": "yí", + "205FB": "xuán", + "205FC": "suò", + "205FD": "liú", + "205FE": "shuǎng", + "205FF": "shèn", + "20601": "bù", + "20602": "sōu", + "20605": "qín", + "20606": "shěn", + "2060A": "nòng", + "2060B": "tǐng", + "2060C": "jiāng", + "20615": "xī", + "20616": "zhì", + "2061D": "lài", + "2061E": "lì", + "2061F": "lì", + "20622": "hé", + "20623": "jiào", + "20625": "yán", + "20627": "shū", + "2062A": "shǐ", + "20631": "zhěn", + "20633": "yōu", + "2063A": "suò", + "2063B": "wú", + "20641": "cháng", + "20642": "cóng", + "20646": "jù", + "2064E": "shū", + "20654": "jiù", + "20655": "wéi", + "2065E": "huò", + "20664": "jiē", + "2066C": "zǎo", + "20676": "ǒu", + "2067C": "guǎ", + "20683": "háo", + "20684": "lǐ", + "20685": "zhì", + "20686": "xiàn", + "20689": "bū", + "2068A": "chàng", + "20693": "yūn", + "20694": "hé", + "2069C": "tāo", + "206A0": "biāo", + "206A5": "diāo", + "206A7": "èr", + "206A8": "jiū", + "206AD": "dì", + "206AE": "yì", + "206AF": "kūn", + "206B1": "zhé", + "206B3": "kuò", + "206B4": "zhōu", + "206B5": "jù", + "206B9": "shàn", + "206BA": "shà", + "206BB": "diāo", + "206BC": "bān", + "206BD": "jī", + "206C0": "zhōng", + "206C3": "yí", + "206C5": "kōu", + "206C6": "wū", + "206CA": "gē", + "206CB": "bā", + "206CE": "gōu", + "206D1": "xián", + "206D2": "guā", + "206D3": "liǔ", + "206D4": "chǐ", + "206D5": "guāi", + "206D6": "chuān", + "206D8": "lí", + "206D9": "cù", + "206DA": "shuā", + "206E1": "bǐ", + "206E5": "bǐng", + "206E6": "lì", + "206E9": "jiǔ", + "206EA": "tiāo", + "206EB": "duǒ", + "206ED": "yān", + "206EE": "quān", + "206F1": "liè", + "206F3": "kè", + "206F5": "gēn", + "206F6": "zhēn", + "206F8": "fén", + "20701": "yí", + "20703": "jiù", + "20704": "xù", + "20705": "jiǎo", + "20708": "lǜ", + "20709": "jiǔ", + "2070B": "chǒu", + "2070E": "xiàn", + "20710": "kuài", + "20711": "duì", + "20716": "luō", + "20717": "xī", + "20718": "qìn", + "20719": "bù", + "20724": "qià", + "20731": "pī", + "20732": "yā", + "20733": "bēng", + "20734": "guǒ", + "20735": "guā", + "20739": "jú", + "2073C": "qiā", + "2073E": "jué", + "20744": "lì", + "20750": "huā", + "20751": "jiāo", + "20758": "qià", + "2075A": "zhá", + "2075B": "qiā", + "2075D": "zhé", + "2075E": "chā", + "2075F": "yǐng", + "20762": "yān", + "20764": "chōng", + "20768": "chǐ", + "2076A": "wān", + "2076C": "sōu", + "20772": "kǎn", + "20773": "yuán", + "2077D": "chóu", + "2077F": "suǒ", + "20780": "tū", + "20783": "zhé", + "20784": "tī", + "20786": "wū", + "20788": "dā", + "20789": "lì", + "2078A": "chā", + "20795": "róng", + "20796": "gòng", + "20797": "què", + "20799": "lí", + "2079E": "tāo", + "207A4": "lì", + "207A7": "mí", + "207A9": "chì", + "207AC": "gùn", + "207AD": "lóu", + "207AE": "chuǎng", + "207AF": "suǒ", + "207B0": "jiǎo", + "207B1": "jìn", + "207B5": "fá", + "207B6": "zhāi", + "207BE": "jìn", + "207BF": "cuì", + "207C2": "cèng", + "207C3": "zǔn", + "207C5": "zhào", + "207C8": "piē", + "207C9": "zhǎn", + "207CA": "xī", + "207CB": "yào", + "207CC": "fǔ", + "207CD": "chōng", + "207D3": "cuì", + "207D7": "guā", + "207E3": "jī", + "207E6": "sè", + "207E7": "zhān", + "207E8": "lìng", + "207E9": "sè", + "207EA": "yè", + "207F0": "jū", + "207F6": "tū", + "207FA": "rú", + "207FB": "zé", + "207FC": "huán", + "20801": "xiǎn", + "20803": "qiān", + "20804": "zhào", + "2080B": "cán", + "2080E": "kuò", + "2080F": "lì", + "20810": "róu", + "20814": "dú", + "20817": "liè", + "2081C": "yīng", + "2081D": "lì", + "20820": "dú", + "20822": "líng", + "2082A": "wān", + "2082F": "dié", + "20833": "jiū", + "20835": "lì", + "20836": "kū", + "20837": "kēng", + "20839": "zhěn", + "20840": "hè", + "20842": "bì", + "20844": "pī", + "2084A": "hāng", + "20851": "zhuó", + "20852": "duǐ", + "20854": "yì", + "2085C": "kè", + "2085D": "yì", + "2085E": "mò", + "20861": "cán", + "20863": "gěng", + "20864": "kè", + "20865": "shì", + "2086D": "líng", + "2086E": "bēng", + "20871": "duàn", + "20876": "juān", + "20877": "nǎo", + "20878": "zǐ", + "2087B": "zòng", + "20883": "táng", + "20886": "xiá", + "20887": "hàn", + "2088C": "lüè", + "2088D": "qián", + "20893": "mò", + "20894": "ōu", + "20895": "háo", + "20899": "zhá", + "2089A": "juàn", + "2089B": "cóng", + "208A0": "lì", + "208A1": "zhá", + "208A2": "yǒu", + "208A3": "diàn", + "208A4": "jué", + "208A5": "bèi", + "208A9": "yǎo", + "208AA": "piē", + "208B1": "jìn", + "208B2": "kǎi", + "208B3": "sè", + "208B4": "yǎng", + "208B5": "jìn", + "208B9": "kè", + "208C4": "chān", + "208C7": "niǎn", + "208C9": "wàn", + "208CA": "lǜ", + "208D0": "yún", + "208D1": "yāo", + "208D2": "bāo", + "208D5": "jūn", + "208D6": "xuán", + "208D8": "zhōu", + "208E0": "kuì", + "208E1": "fèng", + "208EA": "qú", + "208EB": "shào", + "208EC": "sǔn", + "208F0": "dū", + "208F2": "kuǎi", + "208F3": "pào", + "208FA": "bào", + "208FE": "fù", + "208FF": "jiù", + "20900": "rán", + "20904": "jū", + "2090A": "qióng", + "2090D": "zhōu", + "2090E": "huà", + "2090F": "bǎo", + "20915": "yí", + "20917": "yí", + "20918": "yí", + "2091D": "mào", + "20926": "ruǎn", + "2092B": "cí", + "2092E": "hán", + "20930": "cóng", + "20934": "xì", + "20939": "quán", + "2093A": "tiáo", + "2093C": "diào", + "2093E": "hán", + "20947": "yě", + "2094D": "ē", + "2094E": "wéi", + "20950": "cāng", + "20951": "diào", + "20955": "è", + "20956": "dì", + "20958": "suǎn", + "20959": "quán", + "2095C": "è", + "2095D": "ōu", + "2095E": "xuán", + "20962": "wǔ", + "20966": "yì", + "20968": "móu", + "20970": "hū", + "20974": "hán", + "2097F": "shí", + "20983": "sà", + "20988": "bì", + "2098A": "hán", + "2098B": "jìng", + "2098E": "qìn", + "2098F": "cuó", + "20990": "cì", + "20992": "bān", + "20997": "duī", + "2099C": "xì", + "209A7": "zhī", + "209A8": "luàn", + "209AA": "hū", + "209AB": "jí", + "209AC": "guāi", + "209B2": "pāng", + "209C0": "zhū", + "209C5": "bǐ", + "209C7": "yú", + "209D2": "qǐ", + "209D5": "hé", + "209D6": "chǔ", + "209D9": "shào", + "209DA": "chì", + "209DB": "bó", + "209DF": "réng", + "209E0": "yóu", + "209E4": "nǎi", + "209E9": "huì", + "209EA": "tiáo", + "209EB": "bǎn", + "209F0": "xū", + "209F4": "yóu", + "209F5": "chì", + "209FF": "héng", + "20A03": "wài", + "20A06": "xiè", + "20A0A": "jué", + "20A0C": "suī", + "20A0D": "qīng", + "20A0E": "zhuàn", + "20A15": "jì", + "20A18": "bì", + "20A1A": "xī", + "20A20": "jí", + "20A22": "jùn", + "20A25": "liáo", + "20A26": "yōu", + "20A2D": "jú", + "20A32": "yuè", + "20A35": "bàng", + "20A38": "pí", + "20A3B": "zè", + "20A3E": "yì", + "20A3F": "dǐ", + "20A42": "qiè", + "20A44": "suǒ", + "20A46": "cì", + "20A48": "zhù", + "20A49": "yuè", + "20A4F": "jiāo", + "20A54": "shí", + "20A57": "yí", + "20A58": "xiá", + "20A60": "yuán", + "20A65": "guó", + "20A67": "kè", + "20A6A": "cuì", + "20A6B": "yì", + "20A75": "lì", + "20A77": "diǎn", + "20A7A": "xī", + "20A7F": "bì", + "20A82": "biǎn", + "20A83": "méi", + "20A84": "lì", + "20A87": "sǒu", + "20A90": "liú", + "20A91": "guì", + "20A92": "kè", + "20A97": "yí", + "20A99": "xǐ", + "20A9A": "yín", + "20A9F": "kè", + "20AA3": "shè", + "20AA7": "wǒ", + "20AAE": "pì", + "20AB6": "yuè", + "20AB7": "hóng", + "20ABA": "lì", + "20ABB": "fù", + "20AC3": "jué", + "20AC4": "xiān", + "20AC9": "diān", + "20ACC": "lì", + "20AD3": "tū", + "20AD8": "jiān", + "20ADB": "bǎi", + "20ADC": "dì", + "20ADD": "zhǎng", + "20AE3": "yù", + "20AE8": "duì", + "20AED": "cān", + "20AEE": "tú", + "20AF6": "tān", + "20AF7": "jí", + "20AF8": "qí", + "20AF9": "shàn", + "20AFA": "nián", + "20B06": "guàn", + "20B08": "bǐ", + "20B0B": "xīng", + "20B13": "zhěn", + "20B19": "sā", + "20B1B": "mò", + "20B1D": "fú", + "20B22": "tāo", + "20B23": "bàng", + "20B2A": "biào", + "20B2C": "xī", + "20B2E": "jié", + "20B36": "jìn", + "20B3E": "qiān", + "20B48": "sì", + "20B49": "jǐng", + "20B4B": "chǐ", + "20B57": "jǐng", + "20B65": "suì", + "20B6F": "zhā", + "20B70": "lí", + "20B74": "zhuō", + "20B79": "biàn", + "20B7F": "tún", + "20B83": "bì", + "20B86": "fèi", + "20B8A": "dé", + "20B8C": "zhú", + "20B91": "jū", + "20B99": "yǐ", + "20B9C": "yà", + "20B9F": "chì", + "20BA0": "guǎ", + "20BA1": "zhǐ", + "20BA8": "réng", + "20BAB": "yōu", + "20BAD": "bó", + "20BAF": "jǐ", + "20BB0": "pǐn", + "20BB3": "yīng", + "20BB4": "yāng", + "20BB5": "màng", + "20BBD": "lòng", + "20BBE": "ǹ", + "20BBF": "sa", + "20BC0": "chuān", + "20BC2": "cí", + "20BC3": "wǔ", + "20BC4": "rèn", + "20BC8": "dài", + "20BC9": "jí", + "20BCB": "yǐ", + "20BCD": "rán", + "20BD0": "huò", + "20BD1": "guā", + "20BD3": "zhé", + "20BD4": "pì", + "20BD7": "zā", + "20BD8": "bàn", + "20BD9": "jié", + "20BDC": "hōu", + "20BDF": "xiàn", + "20BE0": "huī", + "20BE9": "zhā", + "20BEA": "dāi", + "20BEB": "gē", + "20BED": "pì", + "20BEF": "piàn", + "20BF0": "shí", + "20BF1": "liǎng", + "20BF2": "yuè", + "20BF3": "hù", + "20BF4": "biàn", + "20BF7": "réng", + "20BF9": "réng", + "20C04": "yī", + "20C05": "zhī", + "20C07": "jīn", + "20C08": "wēng", + "20C09": "chāo", + "20C0B": "qiū", + "20C0D": "zhǔ", + "20C0F": "zhá", + "20C10": "pǒ", + "20C11": "àn", + "20C13": "hé", + "20C15": "chū", + "20C16": "yán", + "20C1A": "shì", + "20C1B": "hù", + "20C1C": "è", + "20C34": "shí", + "20C39": "tuō", + "20C3A": "dài", + "20C3B": "wài", + "20C3C": "pō", + "20C3D": "rǒng", + "20C3E": "jū", + "20C40": "bō", + "20C50": "yǔ", + "20C51": "dōu", + "20C53": "guǐ", + "20C54": "shòu", + "20C57": "suō", + "20C58": "nì", + "20C59": "zhōu", + "20C5A": "lòng", + "20C5B": "bǐng", + "20C5C": "zùn", + "20C5D": "yè", + "20C5E": "rǎn", + "20C60": "líng", + "20C61": "sà", + "20C64": "lěi", + "20C65": "è", + "20C67": "zhòng", + "20C68": "jǐ", + "20C6B": "è", + "20C6F": "zuò", + "20C72": "nà", + "20C73": "yǔn", + "20C8A": "xiè", + "20C8B": "zuǐ", + "20C8C": "shù", + "20C8D": "diū", + "20C8E": "fa", + "20C8F": "rěn", + "20C91": "bāng", + "20C92": "hán", + "20C93": "hóng", + "20C94": "yī", + "20C96": "yī", + "20C99": "kē", + "20C9A": "yì", + "20C9B": "huí", + "20C9C": "zhēng", + "20CAE": "jìng", + "20CB1": "gé", + "20CB4": "nóu", + "20CB5": "qiè", + "20CB7": "dié", + "20CB9": "jì", + "20CBA": "yì", + "20CBB": "yí", + "20CBD": "fú", + "20CBE": "shuò", + "20CBF": "shuò", + "20CC0": "yǒng", + "20CC1": "kěn", + "20CC2": "huá", + "20CC3": "hòng", + "20CC7": "hé", + "20CCA": "hē", + "20CCB": "qiǎn", + "20CCC": "qià", + "20CCE": "sì", + "20CD0": "bāng", + "20CEC": "jīng", + "20CED": "kè", + "20CF3": "āi", + "20CF4": "lóu", + "20CF6": "tū", + "20CF9": "chuáng", + "20CFC": "sòng", + "20CFD": "chéng", + "20CFF": "wēi", + "20D02": "nǔ", + "20D04": "jiǔ", + "20D07": "bīn", + "20D21": "xiào", + "20D22": "shēng", + "20D23": "hǒu", + "20D26": "zhù", + "20D28": "guān", + "20D29": "jī", + "20D2B": "jì", + "20D2D": "xī", + "20D2F": "shè", + "20D30": "ǒu", + "20D31": "hú", + "20D32": "tà", + "20D33": "xiáo", + "20D35": "zào", + "20D38": "bò", + "20D39": "qì", + "20D3A": "wā", + "20D3B": "tuō", + "20D3C": "dào", + "20D3E": "nà", + "20D60": "zhāi", + "20D63": "yà", + "20D66": "wǔ", + "20D67": "zhén", + "20D68": "de", + "20D69": "hē", + "20D6B": "āng", + "20D6C": "pí", + "20D6D": "sè", + "20D6E": "fěn", + "20D6F": "guā", + "20D73": "pǒ", + "20D77": "xuàn", + "20D78": "hān", + "20D79": "gāng", + "20D7A": "bā", + "20D7B": "zōng", + "20D7C": "mèng", + "20D7E": "huò", + "20DA7": "diān", + "20DA8": "xī", + "20DAB": "dà", + "20DAC": "nàng", + "20DB0": "diāo", + "20DB1": "luò", + "20DB2": "kè", + "20DB7": "yì", + "20DB8": "jué", + "20DB9": "hé", + "20DBB": "jí", + "20DBE": "hè", + "20DBF": "niè", + "20DC0": "rǔn", + "20DC1": "qián", + "20DC2": "dài", + "20DC3": "shāo", + "20DC4": "kè", + "20DC5": "zhú", + "20DC7": "shī", + "20DC8": "lǜ", + "20DC9": "jiā", + "20DCA": "pián", + "20DCB": "hòu", + "20DCC": "jī", + "20DCD": "tà", + "20DCE": "chóu", + "20DCF": "wō", + "20DD0": "jìng", + "20DD1": "pō", + "20DD2": "zhāi", + "20DD3": "xīn", + "20DD6": "biàn", + "20DD9": "xù", + "20DDE": "gū", + "20DDF": "jiè", + "20DE2": "xián", + "20DF8": "é", + "20DFA": "bó", + "20DFB": "piāo", + "20DFF": "zǎ", + "20E01": "pài", + "20E02": "tū", + "20E04": "yīng", + "20E2E": "xiǎng", + "20E31": "nuò", + "20E32": "gē", + "20E33": "bó", + "20E34": "xiè", + "20E38": "zhēn", + "20E39": "yú", + "20E3A": "nì", + "20E40": "xùn", + "20E41": "wà", + "20E43": "àng", + "20E44": "hàn", + "20E45": "hōng", + "20E46": "dān", + "20E48": "nuó", + "20E4A": "cǎo", + "20E4B": "jí", + "20E4C": "něng", + "20E4D": "yǒng", + "20E4E": "xiāo", + "20E50": "chuǎ", + "20E51": "yào", + "20E53": "gé", + "20E54": "táng", + "20E55": "bào", + "20E56": "chǎn", + "20E58": "xù", + "20E5B": "hái", + "20E5D": "chóu", + "20E5F": "jiǎn", + "20E60": "zuō", + "20E64": "wèi", + "20E65": "dā", + "20E66": "pī", + "20E90": "huàn", + "20E92": "xī", + "20E94": "pèn", + "20E95": "liū", + "20E96": "mǔ", + "20E97": "miē", + "20E98": "làng", + "20E99": "tuì", + "20E9A": "bān", + "20E9D": "gē", + "20E9F": "kù", + "20EA2": "jiā", + "20EA3": "bō", + "20ECD": "huàn", + "20ECF": "zú", + "20ED0": "luò", + "20ED7": "lí", + "20ED9": "hé", + "20EDA": "mó", + "20EDC": "shuì", + "20EDD": "shēn", + "20EDE": "kǎng", + "20EDF": "chì", + "20EE0": "líng", + "20EE1": "luǒ", + "20EE4": "yǎn", + "20EE5": "zhào", + "20EE6": "chuǎ", + "20EE7": "gǔ", + "20EE8": "qǐn", + "20EEA": "tán", + "20EEB": "fèn", + "20EEC": "tú", + "20EF1": "líng", + "20EF4": "lǎng", + "20F16": "lán", + "20F17": "zàn", + "20F18": "wù", + "20F1D": "lí", + "20F1E": "ā", + "20F1F": "lüè", + "20F20": "zhǐ", + "20F21": "chóu", + "20F22": "jiàng", + "20F24": "jiān", + "20F29": "lún", + "20F2A": "yí", + "20F2C": "shāng", + "20F3B": "jī", + "20F5C": "yì", + "20F5D": "nín", + "20F61": "huì", + "20F63": "zhā", + "20F66": "hǎn", + "20F68": "yǐn", + "20F69": "bì", + "20F6A": "ān", + "20F6B": "xiā", + "20F6C": "ní", + "20F70": "dī", + "20F71": "jiǎn", + "20F72": "pán", + "20F75": "yù", + "20F76": "chuài", + "20F77": "zā", + "20F79": "chá", + "20F7B": "zhé", + "20F7C": "sè", + "20F7E": "pēn", + "20F7F": "gū", + "20F80": "zhé", + "20F86": "lí", + "20F87": "dōu", + "20F89": "chóu", + "20F8B": "zuǐ", + "20F8C": "pò", + "20F8F": "shē", + "20F90": "lóng", + "20FA2": "shù", + "20FA4": "jìn", + "20FA5": "líng", + "20FA8": "kāng", + "20FA9": "là", + "20FAB": "xū", + "20FAC": "jìn", + "20FAE": "chuān", + "20FB2": "yuè", + "20FC6": "mǎi", + "20FC7": "xiè", + "20FC8": "jiū", + "20FC9": "jì", + "20FCB": "yuè", + "20FCF": "jiān", + "20FD1": "hán", + "20FD3": "sà", + "20FD4": "huì", + "20FD5": "qiào", + "20FD7": "sè", + "20FD8": "zuǐ", + "20FDB": "lǔ", + "20FDC": "huà", + "20FDD": "chū", + "20FDE": "shǎn", + "20FDF": "wò", + "20FE0": "jí", + "20FE1": "zhuó", + "20FE2": "xián", + "20FE3": "yī", + "20FE4": "guó", + "20FE5": "kuì", + "21011": "zhōu", + "21014": "lù", + "21016": "bō", + "21017": "shí", + "21018": "yìng", + "21019": "kū", + "21039": "zhì", + "2103A": "xié", + "2103D": "yè", + "2103E": "è", + "2103F": "lǜ", + "21040": "hàn", + "21041": "yè", + "21046": "luò", + "21047": "chuò", + "21048": "fàn", + "21049": "zhí", + "2104A": "yìng", + "2104B": "wěn", + "2104C": "wā", + "2104D": "ài", + "2104E": "yú", + "21051": "huā", + "21053": "liè", + "21054": "jīng", + "21055": "zá", + "21067": "zāng", + "21068": "duì", + "2106A": "jì", + "2106E": "wō", + "21070": "jí", + "21071": "xī", + "21073": "zhàn", + "21074": "tuán", + "2108A": "yú", + "2108F": "liè", + "21092": "zhì", + "21093": "shī", + "21095": "lǎo", + "21096": "lài", + "21097": "wěi", + "21098": "páo", + "21099": "chí", + "2109A": "yǐng", + "2109B": "dòu", + "2109D": "dòu", + "2109F": "bào", + "210A0": "qiè", + "210A1": "shù", + "210A3": "zhí", + "210A9": "liè", + "210AB": "péng", + "210AD": "zhē", + "210BF": "ōu", + "210C2": "xiè", + "210C3": "jí", + "210C4": "lài", + "210C5": "yíng", + "210C6": "cēng", + "210D6": "lē", + "210DD": "lùn", + "210E1": "lóng", + "210E2": "xì", + "210E6": "lìn", + "210E9": "guī", + "210F3": "xīng", + "210F7": "lí", + "210F8": "cī", + "21107": "qǐng", + "21111": "jiān", + "21112": "dào", + "21113": "jiǎn", + "21114": "qìng", + "21115": "xiè", + "21116": "yìng", + "2111F": "há", + "21121": "zhe", + "21122": "shē", + "21123": "mí", + "21124": "huán", + "21131": "cù", + "21132": "rú", + "21133": "sǎ", + "21134": "huò", + "21135": "yī", + "21137": "dī", + "21139": "luàn", + "2113B": "yì", + "21142": "bò", + "21143": "páng", + "21144": "tán", + "21145": "é", + "21146": "zāng", + "21147": "cóng", + "21153": "zhāi", + "21155": "xǐ", + "21156": "mǎng", + "21158": "là", + "21159": "yùn", + "21161": "è", + "21165": "dié", + "2116D": "guān", + "21171": "huàn", + "21175": "shì", + "21176": "jiǎn", + "21179": "zhān", + "2117A": "jí", + "2117B": "huàn", + "21185": "wàn", + "21186": "luǒ", + "2118F": "dòu", + "21195": "liàn", + "211A3": "niè", + "211A4": "nǎn", + "211A5": "jiù", + "211A6": "yuè", + "211A9": "yāo", + "211AA": "chuāng", + "211AE": "cǎn", + "211AF": "lǐ", + "211B0": "dùn", + "211B1": "nǎn", + "211B2": "nǎn", + "211B8": "rì", + "211BD": "yuè", + "211C0": "yóu", + "211C2": "yīn", + "211C4": "guó", + "211C8": "dàng", + "211D1": "zhēn", + "211D2": "mí", + "211D3": "dié", + "211D6": "zhēn", + "211DA": "kuā", + "211DC": "hán", + "211DD": "sòng", + "211DE": "hé", + "211DF": "jī", + "211E0": "zhé", + "211E4": "bǐng", + "211E6": "wéi", + "211E7": "tōu", + "211E9": "tú", + "211EC": "gāng", + "211ED": "lóu", + "211EE": "quán", + "211EF": "hùn", + "211F0": "zhuǎn", + "211F1": "què", + "211F3": "hóng", + "211F5": "dàng", + "211F6": "hé", + "211F7": "tài", + "211F8": "guāi", + "211FA": "yù", + "211FC": "yà", + "211FF": "wān", + "21200": "qūn", + "21205": "jué", + "21206": "ōu", + "21209": "quān", + "2120A": "zhí", + "2120D": "líng", + "2120E": "wū", + "2120F": "xìn", + "21210": "dá", + "21212": "yuān", + "21213": "yuàn", + "21217": "mò", + "21219": "yóu", + "2121E": "wǔ", + "21220": "zhāng", + "21223": "xuān", + "21226": "rǎo", + "21227": "gǔn", + "21228": "yù", + "2122E": "xiá", + "2122F": "biǎn", + "21230": "yóu", + "21232": "yīn", + "21234": "xuán", + "21235": "yóu", + "21236": "léi", + "2123C": "tǐng", + "2123F": "zhēn", + "21244": "zài", + "21245": "gā", + "21246": "lá", + "21249": "què", + "2124E": "jú", + "21250": "chūn", + "21251": "dā", + "21252": "tún", + "21253": "āi", + "21257": "zǐ", + "2125A": "huáng", + "2125B": "yì", + "21269": "bào", + "2126A": "chí", + "2126D": "rì", + "21274": "lú", + "21277": "jié", + "21278": "shì", + "2127A": "zuān", + "21281": "yì", + "21284": "fèn", + "21285": "fèn", + "21289": "mò", + "2128D": "shù", + "2129B": "áo", + "2129D": "pǐ", + "2129E": "píng", + "2129F": "pō", + "212A0": "jiá", + "212A1": "zhóu", + "212A3": "qiū", + "212A7": "yǒu", + "212A8": "tán", + "212AB": "rǒng", + "212AD": "mì", + "212B6": "yì", + "212B8": "rǒng", + "212BB": "liè", + "212BC": "qióng", + "212D9": "huí", + "212DA": "jì", + "212DF": "gào", + "212E7": "yóu", + "212E8": "chā", + "212E9": "dé", + "212EA": "yīn", + "212EC": "yù", + "212ED": "bèi", + "212EF": "bó", + "21314": "qiāo", + "2131A": "chǎ", + "2131C": "xīn", + "2131E": "chí", + "21323": "zào", + "21324": "kuí", + "21326": "fèi", + "21329": "tā", + "2132A": "guài", + "2132D": "duō", + "21332": "guī", + "21334": "zhí", + "2134C": "chǎn", + "2134D": "nǎo", + "21350": "hú", + "21352": "táo", + "21361": "yì", + "21364": "niè", + "21365": "zhài", + "21366": "huán", + "21368": "dù", + "2136A": "qì", + "2136B": "cè", + "2136E": "chuí", + "21372": "dā", + "21376": "zhì", + "21377": "gèng", + "2137B": "wèng", + "21389": "dù", + "2138D": "chí", + "21391": "àn", + "21392": "kuò", + "21394": "wò", + "21398": "yīng", + "2139A": "piǎn", + "213AB": "zhá", + "213AC": "zhuǎ", + "213AE": "sù", + "213B3": "nì", + "213BA": "zhú", + "213BB": "chán", + "213BE": "bèng", + "213BF": "ní", + "213C0": "zhí", + "213C1": "huì", + "213D8": "xià", + "213DA": "zhì", + "213DB": "xī", + "213DE": "jiǎng", + "213E9": "duī", + "213EA": "fū", + "213ED": "jiāo", + "213EE": "cháo", + "213EF": "bài", + "213F5": "liè", + "213FC": "áo", + "2140B": "zāo", + "2140C": "chù", + "2140F": "tuǒ", + "21412": "háo", + "21413": "kāng", + "21414": "yín", + "21416": "xiàn", + "2141D": "fù", + "2141E": "biē", + "21420": "kuī", + "21424": "qiè", + "21425": "sà", + "2143F": "dā", + "21440": "yě", + "21444": "zhǎng", + "21446": "liáng", + "21448": "duǐ", + "2144D": "láo", + "2144E": "xūn", + "21458": "zhì", + "2145A": "kū", + "2145E": "suì", + "2145F": "wō", + "21463": "kū", + "2146F": "jiǎn", + "21476": "jiǎng", + "2147B": "zhuì", + "2147D": "shuǎng", + "2147E": "yú", + "21481": "sà", + "21483": "yù", + "21484": "lǎn", + "2148A": "yù", + "2148C": "qiǎn", + "2148D": "jù", + "2148F": "liè", + "21492": "shú", + "21493": "xiàn", + "21496": "gài", + "214A2": "tái", + "214A7": "tiǎn", + "214AF": "mèng", + "214B1": "dí", + "214B3": "mián", + "214BE": "huī", + "214C9": "duò", + "214CD": "liè", + "214D2": "lài", + "214D3": "yín", + "214D4": "lǎn", + "214D6": "jiāo", + "214D8": "huò", + "214E3": "guō", + "214E6": "zhàn", + "214ED": "mǐ", + "214F0": "kuī", + "214F7": "duò", + "214FF": "yín", + "21507": "lèi", + "21515": "gòng", + "2151B": "tǐng", + "2151C": "yáo", + "2151E": "wǎng", + "21523": "jié", + "21528": "xiū", + "2152A": "shù", + "21531": "wèi", + "21534": "yù", + "21541": "zhān", + "21549": "āng", + "2154F": "sǎng", + "21550": "chóu", + "21552": "kuà", + "21556": "jǔ", + "21557": "hài", + "21562": "miǎn", + "21567": "hàng", + "2156A": "chóu", + "2156E": "líng", + "21570": "zōng", + "21589": "kūn", + "2158C": "zhōng", + "2158E": "zhāo", + "21590": "diě", + "21591": "gǒu", + "21592": "yún", + "21593": "dān", + "21594": "nuǒ", + "2159B": "bǐng", + "2159D": "rán", + "2159E": "chān", + "215A2": "rǒng", + "215A3": "yīn", + "215A4": "chān", + "215A7": "zhì", + "215AA": "guài", + "215AB": "nuó", + "215AC": "shēn", + "215AF": "sù", + "215B2": "wǒ", + "215B3": "chǐ", + "215BA": "miè", + "215BB": "zhí", + "215BE": "qī", + "215C1": "gōu", + "215C6": "lǒu", + "215C8": "zī", + "215CD": "dǎng", + "215CF": "xiǎn", + "215D1": "rǒu", + "215D7": "pěng", + "215DE": "xī", + "215E2": "kuā", + "215E4": "guì", + "215E5": "chún", + "215E6": "jiè", + "215F2": "jiè", + "215F3": "xī", + "215F5": "kū", + "215F7": "gū", + "215F8": "zhà", + "215F9": "fàn", + "215FC": "xiè", + "2160D": "huán", + "2160F": "niǎo", + "21610": "xì", + "2161B": "cū", + "2161D": "gǔn", + "21621": "xī", + "21627": "qiá", + "2162A": "māng", + "2162D": "zhé", + "21630": "juàn", + "21634": "biē", + "21640": "biē", + "21645": "quán", + "2164B": "xì", + "2164E": "jiǎo", + "21650": "quán", + "21651": "zhǐ", + "21652": "tiān", + "21653": "kāi", + "21658": "sǎn", + "2165B": "zī", + "21663": "jié", + "2166A": "bié", + "2166C": "dòu", + "2166D": "zuī", + "21676": "yǎn", + "21681": "bì", + "21685": "kuǎi", + "21687": "yàn", + "21688": "wéi", + "2168A": "huān", + "2168C": "hào", + "21691": "gōng", + "21694": "méng", + "21697": "lěi", + "21699": "dì", + "2169B": "bǐng", + "2169C": "huān", + "2169F": "wā", + "216A0": "jué", + "216A8": "chì", + "216AD": "bā", + "216AE": "jiǔ", + "216B7": "dì", + "216B9": "zhàng", + "216BB": "dà", + "216BC": "shí", + "216BD": "hào", + "216CC": "yè", + "216D7": "bì", + "216D8": "pǐ", + "216D9": "yǎo", + "216DC": "dī", + "216DD": "càn", + "216DE": "pín", + "216DF": "yuè", + "216E0": "qiē", + "216E1": "pī", + "216F5": "tuǒ", + "216F6": "xiè", + "216FD": "yè", + "21700": "fàn", + "21701": "guā", + "21702": "hù", + "21703": "rǔ", + "21709": "rǎn", + "2170A": "fǒu", + "2170B": "huāng", + "2171A": "rú", + "21722": "mǎo", + "21725": "duī", + "21726": "huì", + "21727": "xì", + "21728": "xiū", + "2172B": "rǎn", + "2172C": "yī", + "2172F": "zhé", + "21731": "jì", + "21732": "gào", + "21733": "yòu", + "21735": "pū", + "21748": "chù", + "21749": "cū", + "2174A": "zhé", + "2174B": "niǎo", + "2174D": "qiè", + "21750": "chá", + "21752": "niǎo", + "21753": "suī", + "21759": "chá", + "2175A": "chéng", + "2175B": "yáo", + "2175C": "dù", + "2175D": "wāng", + "2175F": "niàn", + "21760": "mí", + "21766": "nǒu", + "21767": "xì", + "21769": "yāo", + "2176B": "chān", + "21798": "xiè", + "21799": "miè", + "2179A": "kěng", + "2179C": "cù", + "2179E": "shěng", + "2179F": "pàn", + "217A0": "hù", + "217A2": "kè", + "217A3": "xiàn", + "217A5": "hóu", + "217A6": "qióng", + "217A7": "zōng", + "217AA": "fú", + "217AB": "nài", + "217AD": "nì", + "217AF": "kǔ", + "217BE": "nèn", + "217CD": "gē", + "217D1": "hóu", + "217D3": "āi", + "217D5": "shī", + "217DE": "xiū", + "217DF": "cōng", + "217E0": "jiāo", + "217E2": "zhá", + "217E3": "xiāo", + "217E4": "liàn", + "217E5": "qǔ", + "217E8": "shǎn", + "217E9": "xiè", + "217EB": "gòng", + "217EC": "miè", + "217ED": "chái", + "217EF": "ēn", + "217F3": "dòu", + "21806": "kòu", + "2180A": "tiáo", + "2180B": "shī", + "2180F": "sāng", + "21812": "guān", + "21816": "hào", + "21817": "zhì", + "21818": "yàng", + "21819": "tōng", + "2181A": "bì", + "2181C": "mó", + "2181E": "fú", + "21825": "qiáng", + "21839": "zhì", + "2183C": "sōu", + "2183F": "niǎo", + "21840": "juàn", + "21842": "yàng", + "21844": "huāng", + "21848": "bēng", + "21849": "mó", + "2184A": "cháo", + "2184E": "lǚ", + "2184F": "shāo", + "21850": "bǔ", + "21851": "zēng", + "21852": "sī", + "21854": "zuì", + "21855": "yuē", + "21856": "zān", + "21857": "luǎn", + "21865": "qú", + "2187A": "miǎo", + "21880": "zhuàn", + "21888": "dàng", + "2188A": "yuān", + "21892": "jǔ", + "21895": "huǐ", + "21896": "qì", + "21898": "yùn", + "2189A": "màn", + "2189C": "mǒ", + "218B1": "piāo", + "218B3": "jìn", + "218B9": "yāo", + "218C0": "chì", + "218C1": "nì", + "218C2": "sōu", + "218C8": "shù", + "218CB": "piāo", + "218D4": "hàn", + "218E0": "yāo", + "218E2": "néi", + "218EA": "shì", + "218EC": "yuān", + "218EE": "cài", + "218EF": "jié", + "218F9": "xiè", + "218FD": "yán", + "218FE": "xiāo", + "2190B": "xiè", + "2190C": "lì", + "2190E": "fàn", + "21917": "zhù", + "21919": "nà", + "2191B": "zhuǎn", + "2191E": "kuī", + "21922": "luó", + "2192B": "qiā", + "21936": "wān", + "2193D": "shǔ", + "2193F": "chèng", + "21941": "yì", + "21946": "hǎo", + "21948": "jiào", + "2194B": "huì", + "2194D": "xiào", + "2194E": "cí", + "2195E": "jì", + "21966": "nǐ", + "21968": "nǐ", + "21969": "tǐ", + "21976": "jù", + "21978": "mìng", + "2197D": "lí", + "2197F": "zhòng", + "21981": "xù", + "21983": "qióng", + "21984": "fú", + "21986": "bìn", + "2198A": "jì", + "2198D": "qí", + "2198E": "xì", + "21994": "dèng", + "21995": "ér", + "2199B": "shú", + "2199C": "tóng", + "2199D": "xiáo", + "2199F": "pí", + "219A8": "dǎn", + "219AA": "jí", + "219B3": "xiào", + "219B7": "cóng", + "219BB": "bīn", + "219BC": "rǒng", + "219CD": "miàn", + "219D2": "miàn", + "219D4": "shū", + "219D5": "xiáo", + "219D6": "bǎo", + "219D7": "wà", + "219D9": "pào", + "219E3": "gǎi", + "219E5": "hū", + "219E6": "héng", + "219E8": "zhú", + "219E9": "guāi", + "219ED": "guì", + "219F9": "dài", + "219FC": "bīn", + "219FD": "huǎng", + "21A00": "chá", + "21A04": "xià", + "21A05": "jú", + "21A07": "yǎo", + "21A16": "fěn", + "21A17": "zào", + "21A1B": "fēng", + "21A22": "jū", + "21A23": "yù", + "21A29": "hūn", + "21A32": "jié", + "21A33": "xiòng", + "21A35": "nài", + "21A3B": "nǒu", + "21A3D": "shěng", + "21A3F": "yù", + "21A42": "huán", + "21A43": "gěng", + "21A44": "wǎn", + "21A46": "tuó", + "21A47": "qiāo", + "21A58": "yìn", + "21A5A": "jiā", + "21A61": "suǒ", + "21A63": "jié", + "21A64": "xī", + "21A65": "wěng", + "21A69": "máng", + "21A76": "yáng", + "21A78": "yáo", + "21A7D": "máng", + "21A7E": "ōu", + "21A81": "án", + "21A85": "lòu", + "21A91": "è", + "21A92": "zǐ", + "21A97": "è", + "21A99": "àn", + "21A9E": "huò", + "21AA0": "céng", + "21AB0": "xiòng", + "21AB1": "jì", + "21AB3": "zuó", + "21AB5": "qí", + "21ABA": "zhēng", + "21AC0": "jī", + "21AC1": "qī", + "21AC2": "juǎn", + "21AC3": "níng", + "21ADF": "sè", + "21AE5": "hè", + "21AE6": "rǒng", + "21AE7": "qǐn", + "21AEC": "jū", + "21AEF": "lì", + "21AF5": "shí", + "21AF8": "nì", + "21AF9": "xián", + "21AFA": "fū", + "21AFD": "rǔ", + "21B01": "xiòng", + "21B02": "guì", + "21B04": "jì", + "21B06": "měng", + "21B07": "fū", + "21B09": "sài", + "21B0A": "yù", + "21B0B": "jiào", + "21B0C": "mèng", + "21B0D": "lóng", + "21B0E": "qiāng", + "21B10": "mí", + "21B13": "yí", + "21B16": "hān", + "21B17": "nì", + "21B18": "lào", + "21B19": "sèng", + "21B1C": "lǐn", + "21B1E": "yù", + "21B25": "nuó", + "21B2B": "wù", + "21B2F": "biǎn", + "21B32": "biǎn", + "21B33": "xuān", + "21B35": "jiān", + "21B38": "biǎn", + "21B42": "dé", + "21B47": "zhuān", + "21B4B": "rǒng", + "21B50": "shuàn", + "21B58": "jiā", + "21B5B": "huǐ", + "21B5E": "zhān", + "21B62": "bài", + "21B63": "liè", + "21B65": "xiē", + "21B6D": "jiǎn", + "21B6E": "shǒu", + "21B73": "kào", + "21B77": "guān", + "21B78": "luàn", + "21B7E": "nǒu", + "21B7F": "chǎng", + "21B8E": "liáng", + "21B99": "nài", + "21B9A": "rǔ", + "21B9E": "zhì", + "21BA6": "cáo", + "21BB0": "lì", + "21BBB": "lán", + "21BBF": "chān", + "21BC1": "wāng", + "21BC4": "lì", + "21BC7": "wù", + "21BC8": "páo", + "21BC9": "yòu", + "21BCB": "gān", + "21BCF": "ān", + "21BD0": "xiū", + "21BD1": "shuǐ", + "21BD2": "ruǐ", + "21BD8": "bǎn", + "21BD9": "yóu", + "21BE2": "huó", + "21BE5": "huī", + "21BE8": "zuò", + "21BE9": "xiāo", + "21BEB": "mián", + "21BF0": "gà", + "21BF1": "yuǎn", + "21BF3": "bò", + "21BF4": "chào", + "21BF5": "tuǐ", + "21BF7": "bò", + "21BFD": "gà", + "21BFF": "tiāo", + "21C00": "ná", + "21C05": "hú", + "21C06": "niè", + "21C0B": "huí", + "21C0C": "lǒu", + "21C0E": "tí", + "21C10": "qiào", + "21C11": "qiáo", + "21C12": "zhǒng", + "21C16": "dī", + "21C1A": "lín", + "21C1D": "quán", + "21C1E": "zhuān", + "21C20": "léi", + "21C22": "xié", + "21C25": "rén", + "21C28": "dāng", + "21C2A": "dū", + "21C2B": "niǎn", + "21C2F": "shǐ", + "21C32": "xián", + "21C39": "zhí", + "21C3D": "ài", + "21C3E": "cī", + "21C3F": "pú", + "21C41": "shǐ", + "21C45": "qū", + "21C46": "shǔ", + "21C47": "diān", + "21C49": "xiǎo", + "21C4A": "shuǐ", + "21C4C": "huán", + "21C50": "yí", + "21C51": "juān", + "21C54": "zhǐ", + "21C5C": "zhào", + "21C63": "xù", + "21C6F": "lòng", + "21C71": "zhù", + "21C73": "suǒ", + "21C77": "dié", + "21C7A": "qú", + "21C7C": "kè", + "21C7D": "hū", + "21C7E": "jū", + "21C80": "qǐng", + "21C8D": "bīng", + "21C95": "tì", + "21C97": "jué", + "21C9A": "qiú", + "21CA3": "jiàng", + "21CAA": "yùn", + "21CAD": "mèi", + "21CAE": "pī", + "21CB0": "qú", + "21CBC": "mì", + "21CBF": "tì", + "21CC2": "kài", + "21CC4": "bǐ", + "21CC6": "qū", + "21CCF": "tiāo", + "21CD1": "chù", + "21CD8": "jú", + "21CDA": "xī", + "21CDE": "lìn", + "21CED": "chǐ", + "21CEE": "jī", + "21CF4": "lú", + "21CF8": "lì", + "21CFE": "jué", + "21D05": "zhū", + "21D06": "lù", + "21D0E": "niè", + "21D14": "quán", + "21D2D": "yà", + "21D2F": "è", + "21D31": "hù", + "21D40": "máng", + "21D49": "wù", + "21D4C": "chā", + "21D51": "qīn", + "21D52": "jié", + "21D53": "hóng", + "21D55": "dān", + "21D56": "ěn", + "21D57": "zè", + "21D58": "hù", + "21D59": "àng", + "21D5A": "jiè", + "21D5B": "fù", + "21D5C": "yòng", + "21D5E": "fēng", + "21D6C": "mù", + "21D76": "sè", + "21D77": "cóng", + "21D7B": "kāng", + "21D82": "yào", + "21D83": "ài", + "21D84": "bāo", + "21D86": "pǒ", + "21D88": "shǐ", + "21D89": "fàn", + "21D8B": "jú", + "21D8C": "pí", + "21D8E": "wèi", + "21D8F": "kū", + "21D90": "qié", + "21D91": "gān", + "21DA2": "kuàng", + "21DA3": "suì", + "21DA4": "bēng", + "21DA5": "jiā", + "21DA6": "yà", + "21DAA": "kàn", + "21DAB": "niè", + "21DAD": "xíng", + "21DAF": "xì", + "21DB1": "lìn", + "21DB2": "duǒ", + "21DB4": "chǎn", + "21DC8": "shì", + "21DCB": "duì", + "21DCD": "jiāng", + "21DCE": "yǔ", + "21DCF": "lù", + "21DD0": "ěn", + "21DD3": "gǔ", + "21DD5": "wěi", + "21DD6": "chē", + "21DD7": "huàn", + "21DD8": "bié", + "21DDB": "hàn", + "21DDC": "tuí", + "21DDD": "nà", + "21DDE": "qǐ", + "21DE0": "tóu", + "21DE1": "yuān", + "21DE2": "wáng", + "21DE4": "wú", + "21DE5": "gào", + "21DE8": "kēng", + "21DEA": "yí", + "21DF8": "xiāo", + "21DFA": "guǐ", + "21DFB": "yà", + "21DFC": "suì", + "21DFD": "sǒng", + "21DFF": "zhuó", + "21E02": "tū", + "21E03": "xiǎn", + "21E08": "zè", + "21E09": "lì", + "21E0C": "zhù", + "21E0E": "jié", + "21E11": "tì", + "21E14": "xié", + "21E15": "qióng", + "21E17": "yà", + "21E18": "jū", + "21E1B": "yín", + "21E1C": "zhí", + "21E1E": "kǎn", + "21E1F": "zī", + "21E21": "kē", + "21E23": "niè", + "21E24": "qiáng", + "21E25": "wǎn", + "21E26": "zé", + "21E28": "jū", + "21E2A": "zì", + "21E44": "yà", + "21E47": "lín", + "21E49": "qí", + "21E4E": "huí", + "21E53": "qì", + "21E55": "yáng", + "21E56": "suì", + "21E58": "qǐ", + "21E59": "guī", + "21E62": "qìn", + "21E63": "ē", + "21E65": "zuò", + "21E68": "zè", + "21E69": "qì", + "21E6A": "jí", + "21E6C": "tuó", + "21E6D": "dié", + "21E6F": "huì", + "21E70": "máo", + "21E72": "xǔ", + "21E75": "hóu", + "21E76": "yǎn", + "21E77": "xiáng", + "21E78": "cōng", + "21E79": "hú", + "21E7C": "àn", + "21E7E": "bǐng", + "21E87": "duǒ", + "21E90": "zhǔ", + "21E91": "dié", + "21E92": "yōu", + "21E93": "qǐ", + "21E94": "shí", + "21E95": "xūn", + "21E96": "yōu", + "21E97": "kān", + "21E98": "qiǎo", + "21E9B": "qiāng", + "21E9C": "pén", + "21E9F": "quán", + "21EA1": "yíng", + "21EA7": "shā", + "21EAB": "tāo", + "21EAD": "hòng", + "21EAE": "pǐ", + "21EAF": "yáo", + "21EB4": "tú", + "21EB5": "chái", + "21EB7": "xià", + "21EB8": "qí", + "21EBA": "qióng", + "21EBD": "jìn", + "21EC8": "zhēn", + "21ECC": "zhū", + "21ECE": "xī", + "21ED0": "wēng", + "21ED1": "zhǒng", + "21ED5": "suì", + "21ED8": "kē", + "21ED9": "kuò", + "21EDA": "kǎng", + "21EDD": "cháo", + "21EDE": "bì", + "21EDF": "mò", + "21EE0": "zhù", + "21EE1": "hàn", + "21EE2": "yǔ", + "21EE3": "yí", + "21EE4": "má", + "21EE7": "qì", + "21EE8": "gùn", + "21EE9": "màn", + "21EEA": "liáo", + "21EEB": "lín", + "21EEC": "zú", + "21EED": "lěi", + "21EEE": "hù", + "21EEF": "chuǎng", + "21EF0": "qì", + "21EF1": "léi", + "21F01": "chī", + "21F03": "pó", + "21F04": "dié", + "21F0A": "lěi", + "21F0E": "yǐ", + "21F13": "diàn", + "21F16": "dūn", + "21F17": "gāo", + "21F18": "hū", + "21F1A": "xiāo", + "21F1B": "gá", + "21F1C": "pēng", + "21F2C": "shěn", + "21F31": "wéi", + "21F3B": "duì", + "21F3C": "cháo", + "21F3D": "yǐn", + "21F3E": "kuài", + "21F3F": "kū", + "21F41": "zuì", + "21F42": "gǔ", + "21F45": "yùn", + "21F46": "zhì", + "21F49": "jì", + "21F4A": "chēng", + "21F56": "xiè", + "21F5B": "zuǐ", + "21F5C": "án", + "21F5D": "hāo", + "21F60": "pǒ", + "21F62": "dí", + "21F63": "yè", + "21F67": "náo", + "21F71": "jié", + "21F72": "bàng", + "21F73": "lǎn", + "21F74": "cáng", + "21F76": "bì", + "21F7B": "zhǎn", + "21F7C": "qì", + "21F82": "náo", + "21F85": "lǜ", + "21F87": "kuàng", + "21F89": "mó", + "21F8B": "lěi", + "21F8C": "páo", + "21F92": "lì", + "21F93": "céng", + "21F95": "dàng", + "21F96": "lěi", + "21F99": "è", + "21F9B": "bèng", + "21F9C": "jué", + "21FA5": "xuán", + "21FA6": "niè", + "21FA8": "hài", + "21FAE": "xiǎn", + "21FB0": "jiǎn", + "21FB1": "mí", + "21FB2": "niè", + "21FBB": "cáng", + "21FBC": "sǒng", + "21FBD": "zēng", + "21FBE": "yì", + "21FC2": "chóng", + "21FC4": "cáng", + "21FC9": "lěi", + "21FCA": "nuó", + "21FCB": "lì", + "21FCE": "lí", + "21FCF": "luó", + "21FD3": "tǎng", + "21FD6": "niè", + "21FD7": "niè", + "21FD9": "jī", + "21FDB": "lěi", + "21FDD": "nàng", + "21FE0": "lín", + "21FE1": "líng", + "21FE4": "xián", + "21FE5": "yù", + "21FE7": "zāi", + "21FE8": "quǎn", + "21FE9": "liè", + "21FEF": "yù", + "21FF0": "huāng", + "21FFA": "nǎo", + "21FFC": "xùn", + "21FFE": "jú", + "21FFF": "huò", + "22001": "yì", + "2200A": "xī", + "2200B": "sè", + "2200C": "jiǎo", + "2200D": "yōng", + "22015": "shī", + "22016": "jīng", + "22017": "wàn", + "22018": "yě", + "22019": "jiū", + "2201C": "gǒng", + "22021": "huī", + "2202A": "ěr", + "22035": "hàn", + "2203C": "fú", + "22040": "fú", + "22041": "zhuó", + "22042": "jī", + "2204F": "bāng", + "22052": "qí", + "22053": "shǐ", + "22055": "diǎo", + "22056": "pèi", + "22057": "xiǎn", + "22058": "sān", + "2205D": "cháng", + "2205E": "yuē", + "22060": "gōng", + "22062": "wū", + "22064": "fēn", + "22067": "chǎn", + "22069": "nèi", + "2206A": "jué", + "2206C": "zhǎo", + "2206E": "qián", + "22071": "ǎo", + "22076": "wǎng", + "22077": "zhōng", + "22079": "huāng", + "2207B": "bù", + "2207C": "zhǔ", + "2207D": "bì", + "2207E": "chāo", + "2207F": "zhēng", + "22080": "fú", + "22081": "kōu", + "22083": "zuó", + "22084": "xuàn", + "22086": "fù", + "2208A": "yǎo", + "2208D": "bō", + "2208F": "bèi", + "22090": "xié", + "22091": "shì", + "22092": "yí", + "22094": "hóng", + "22095": "cuì", + "22097": "yì", + "22098": "zhuān", + "2209D": "chì", + "220A4": "pō", + "220A8": "yín", + "220B1": "yuàn", + "220B6": "jiōng", + "220B9": "mào", + "220BA": "qiàn", + "220BC": "yì", + "220C0": "wú", + "220CD": "bēi", + "220CE": "huò", + "220CF": "cóng", + "220D0": "kōng", + "220D5": "tà", + "220D7": "hàn", + "220D8": "qiàn", + "220DC": "zhí", + "220E2": "sè", + "220E5": "qiān", + "220E6": "guǒ", + "220E9": "gǔn", + "220EC": "jiān", + "220ED": "zhōng", + "220EE": "miǎn", + "220EF": "guǐ", + "220F0": "shì", + "220F1": "móu", + "220F2": "è", + "220F3": "bǎ", + "220F4": "là", + "220F8": "zhòu", + "220FA": "jí", + "22100": "zǎo", + "22104": "zhā", + "22105": "yì", + "22107": "gǒu", + "2210A": "guī", + "2210B": "yīng", + "2210C": "shǎi", + "2210D": "hé", + "2210E": "bàng", + "2210F": "mò", + "22110": "méng", + "22113": "wù", + "22114": "dài", + "22117": "jiǒng", + "2211C": "hàn", + "2211F": "tōng", + "22120": "kōu", + "22121": "lí", + "22122": "zhì", + "22123": "huì", + "22124": "zǎn", + "22126": "diǎo", + "22127": "cù", + "22131": "zhì", + "22133": "kuǎ", + "22135": "xiàng", + "22136": "huà", + "22137": "liáo", + "22138": "cuì", + "22139": "qiāo", + "2213A": "jiǎo", + "2213C": "xū", + "2213D": "èr", + "2213F": "tuō", + "22140": "tán", + "22141": "zhì", + "22148": "nǎo", + "22149": "mào", + "2214A": "dì", + "2214B": "céng", + "2214E": "jiǎo", + "2214F": "lián", + "22151": "shā", + "22152": "dàn", + "22155": "suì", + "22156": "lián", + "22157": "guò", + "2215A": "biǎo", + "2215C": "cì", + "2215D": "diàn", + "2215E": "lǜ", + "2215F": "nǐ", + "22160": "yǎn", + "22161": "lán", + "22164": "gài", + "22165": "chú", + "22169": "bì", + "2216A": "zú", + "2216B": "huì", + "2216D": "lǎi", + "2216E": "xián", + "2216F": "fèn", + "22170": "hè", + "22179": "yào", + "2217A": "zhǎn", + "2217C": "néi", + "2217E": "luǒ", + "22180": "yuán", + "22182": "néng", + "22189": "rěn", + "2219C": "gé", + "2219E": "jiǎn", + "2219F": "píng", + "221A3": "biè", + "221A6": "jiàn", + "221A9": "bìng", + "221AF": "mì", + "221B0": "hù", + "221B4": "diǎo", + "221B6": "yōu", + "221B7": "yāo", + "221B8": "bēng", + "221BA": "chén", + "221BB": "jī", + "221BD": "yāo", + "221C7": "guān", + "221C8": "yàn", + "221D5": "chǐ", + "221D7": "shà", + "221D8": "yǎn", + "221D9": "yì", + "221DA": "yì", + "221DB": "chè", + "221DE": "hàn", + "221DF": "huāng", + "221E4": "shuì", + "221E5": "suì", + "221E6": "rén", + "221E7": "tán", + "221E8": "zhǐ", + "221EA": "fàn", + "221EB": "fěng", + "221F0": "tán", + "221F2": "mí", + "221F3": "pí", + "221F4": "bù", + "221F5": "nà", + "221F6": "tián", + "221F7": "bá", + "221F8": "yì", + "22202": "yǎn", + "22204": "tiāo", + "22206": "yáo", + "22207": "shěn", + "22208": "kē", + "22209": "tóng", + "2220B": "xuǎn", + "22213": "yòu", + "22215": "bài", + "22219": "xiá", + "2221A": "lǚ", + "2221B": "kùn", + "2221C": "zāng", + "2221D": "qiú", + "22220": "cù", + "22221": "zuī", + "22222": "lǒu", + "22224": "xiá", + "2222F": "shēn", + "22232": "pú", + "22234": "jīng", + "22235": "qiāng", + "22236": "yì", + "22238": "niè", + "22239": "duī", + "2223B": "jié", + "2223C": "suì", + "2223D": "zhàn", + "2223E": "cōu", + "22241": "bēng", + "22242": "guān", + "22243": "shě", + "22245": "jìn", + "22246": "dì", + "22251": "dān", + "22253": "nǎi", + "22255": "nóu", + "22257": "jí", + "22258": "yán", + "2225A": "nòu", + "2225C": "dù", + "2225D": "wèi", + "2225E": "piān", + "22262": "hú", + "22264": "jià", + "22265": "yè", + "22266": "jǔn", + "22267": "lán", + "22268": "là", + "22269": "yīn", + "2226D": "tuí", + "22275": "nǎo", + "2227A": "zǔ", + "2227F": "mà", + "22280": "sī", + "22281": "zhì", + "22284": "huī", + "22285": "zhuì", + "22287": "huì", + "2228D": "chú", + "2228F": "chè", + "22292": "xiū", + "22293": "lán", + "22295": "cōng", + "22296": "shèn", + "22297": "mò", + "22298": "yī", + "22299": "yáo", + "2229A": "xǐ", + "2229B": "zuǐ", + "2229C": "bìng", + "222A7": "yú", + "222A9": "lù", + "222AE": "tuí", + "222AF": "wěi", + "222B1": "fén", + "222B2": "shěn", + "222BB": "liáo", + "222C2": "shǔ", + "222C3": "dǎn", + "222C4": "juǎn", + "222C5": "yú", + "222C6": "xìn", + "222C7": "yáo", + "222C8": "sū", + "222D2": "huó", + "222D4": "qiān", + "222DA": "má", + "222DD": "kǎi", + "222E1": "lǔ", + "222E3": "yōu", + "222EE": "xiàn", + "222F9": "wú", + "222FB": "yǐn", + "222FC": "xī", + "222FF": "zhāi", + "22300": "xiè", + "22304": "qú", + "22308": "lí", + "2230D": "qiān", + "22314": "líng", + "22315": "luán", + "2231A": "chān", + "22326": "zhèng", + "22328": "yán", + "22332": "yìn", + "22333": "kuí", + "22337": "qū", + "22339": "fú", + "2233B": "yù", + "22341": "qí", + "22346": "qì", + "22347": "jì", + "22348": "yuān", + "2234E": "gào", + "2234F": "juàn", + "22351": "qí", + "22353": "gǎi", + "22355": "quàn", + "2235A": "wèi", + "22367": "zhì", + "2236B": "jiǎn", + "2236D": "sì", + "22370": "yì", + "22371": "qiān", + "2237C": "lì", + "2237F": "zāng", + "22380": "yì", + "22382": "cái", + "22383": "yì", + "22384": "gē", + "22386": "dié", + "22388": "zhī", + "22389": "yì", + "2238B": "zāi", + "2238C": "dài", + "2238E": "sù", + "22394": "jié", + "22395": "chèn", + "22396": "qú", + "22398": "hàn", + "22399": "xián", + "223A0": "quán", + "223A1": "jié", + "223A5": "juàn", + "223AA": "dàn", + "223AD": "jīn", + "223B4": "bīng", + "223B5": "hú", + "223B9": "jué", + "223BB": "yú", + "223C3": "lǐ", + "223C4": "qiáng", + "223C5": "shuǐ", + "223C6": "kū", + "223C8": "zhěn", + "223CD": "fú", + "223CE": "shēn", + "223D2": "chuí", + "223D5": "tóng", + "223D7": "yì", + "223D9": "yáng", + "223DC": "tuó", + "223DD": "zhōu", + "223DE": "jí", + "223E4": "xùn", + "223E6": "shěn", + "223E7": "xuān", + "223ED": "liú", + "223EE": "yuān", + "223EF": "hú", + "223F0": "zhèng", + "223F3": "pēng", + "223F7": "jué", + "22402": "zhì", + "22403": "piān", + "22404": "yuàn", + "22406": "jiān", + "2240A": "páng", + "2240E": "zhuàn", + "22410": "xián", + "22412": "bēng", + "22414": "cōng", + "22416": "mò", + "2241A": "guó", + "2241E": "chéng", + "2241F": "qiāo", + "22426": "bì", + "22429": "qiǎng", + "2242B": "zhōu", + "22432": "fán", + "22433": "biē", + "2243E": "bó", + "2243F": "rǒng", + "22445": "dǐng", + "22446": "quán", + "22447": "jiù", + "22448": "yáo", + "22453": "xiá", + "22456": "zǎo", + "2245D": "dān", + "2245F": "wǔ", + "22460": "tuó", + "22462": "hū", + "22467": "xī", + "2246C": "lái", + "2246E": "fēi", + "22479": "hú", + "22486": "xiān", + "22489": "shǎn", + "2248D": "fèi", + "22490": "cuò", + "22492": "fú", + "22494": "chù", + "2249D": "diū", + "2249E": "làn", + "224A9": "xǐ", + "224AF": "biāo", + "224B0": "yù", + "224B1": "suì", + "224B2": "xǐ", + "224B7": "póu", + "224BE": "jiào", + "224C0": "yì", + "224C3": "wán", + "224C4": "jǐ", + "224C6": "wán", + "224C7": "tuì", + "224CB": "àng", + "224CD": "tiān", + "224CE": "chí", + "224D2": "rán", + "224D4": "sà", + "224D5": "yín", + "224D6": "pī", + "224D7": "cǐ", + "224D8": "tóng", + "224D9": "yǐn", + "224DC": "gé", + "224DD": "tiāo", + "224DE": "zhēng", + "224DF": "zhòu", + "224E1": "yí", + "224E2": "kuà", + "224E3": "sōng", + "224E7": "dì", + "224EC": "xié", + "224EE": "xiāo", + "224EF": "guàng", + "224F0": "tuǒ", + "224F1": "fēng", + "224F2": "wú", + "224F5": "xiù", + "224FF": "yóu", + "22501": "líng", + "22502": "yàn", + "22505": "dōng", + "22506": "qì", + "22507": "táo", + "22508": "hán", + "2250A": "chí", + "2250B": "sōng", + "22511": "quǎn", + "22514": "hàn", + "2251F": "rǒu", + "22520": "qì", + "22521": "kāi", + "22522": "yú", + "22523": "chā", + "22524": "chèng", + "22525": "yù", + "22527": "bìng", + "22529": "cōng", + "2252A": "zhū", + "2252C": "yù", + "22531": "jué", + "22532": "liù", + "22533": "sāo", + "22534": "yù", + "22545": "shuài", + "2254B": "yuàn", + "2254E": "zhāng", + "22551": "shuài", + "22553": "chǔ", + "22554": "zhāng", + "22555": "sǎn", + "22556": "xiān", + "22558": "cuī", + "22559": "měng", + "2255A": "dí", + "2255E": "zhì", + "2255F": "ào", + "22566": "xiū", + "22568": "pián", + "2256A": "jiào", + "2256B": "kuǎn", + "2256C": "sà", + "2256D": "xiàn", + "2256E": "zhà", + "2256F": "diàn", + "22577": "yí", + "2257A": "huì", + "2257B": "shàn", + "22584": "chóng", + "22585": "yí", + "22586": "xiè", + "22587": "zhì", + "22588": "tiào", + "2258A": "pīng", + "2258B": "xián", + "2258E": "xiān", + "2258F": "sù", + "22591": "cuán", + "22597": "sǒng", + "2259B": "hēi", + "2259D": "xiàn", + "2259F": "yóu", + "225A1": "yù", + "225A4": "tái", + "225A6": "jué", + "225A7": "nàng", + "225A9": "diān", + "225AB": "yì", + "225AC": "bì", + "225B3": "xū", + "225B4": "yì", + "225B5": "rù", + "225B7": "gōng", + "225BA": "yì", + "225BF": "zhì", + "225C0": "xīn", + "225C2": "jì", + "225C4": "xià", + "225C8": "zhāo", + "225C9": "nè", + "225CA": "xiè", + "225CE": "yì", + "225EB": "fǔ", + "225ED": "shè", + "225EF": "yuán", + "225F0": "fǎn", + "225F2": "fū", + "225F3": "wù", + "225F4": "xī", + "225F5": "hǒng", + "225F9": "jì", + "225FA": "chàng", + "225FF": "mò", + "22600": "pèi", + "22603": "mú", + "22604": "qiú", + "22605": "mào", + "22607": "dá", + "22609": "xiá", + "2260A": "shēn", + "2260B": "tè", + "2260C": "hóng", + "2260D": "bì", + "2261D": "nǐ", + "2261F": "qiáo", + "22627": "ruǎn", + "22638": "jiàng", + "22639": "chā", + "2263A": "mǐ", + "2263D": "yì", + "2263F": "suō", + "22641": "wù", + "22642": "xuān", + "22645": "xí", + "22647": "yǐ", + "22650": "náo", + "22653": "wèi", + "2266E": "kàn", + "22671": "lòng", + "22672": "lǚ", + "22673": "zhuǎng", + "2267A": "zhì", + "2267C": "xìng", + "2267E": "gěng", + "2267F": "jìn", + "22680": "xiàn", + "22681": "jì", + "22682": "cuò", + "22684": "láo", + "22685": "fěn", + "22686": "jù", + "2268B": "miào", + "2268C": "xiá", + "22691": "sù", + "226A8": "zhì", + "226AA": "hù", + "226AB": "kòu", + "226AD": "suǒ", + "226AE": "nì", + "226BA": "tēng", + "226BB": "zhù", + "226C1": "dá", + "226C3": "qiú", + "226C4": "yà", + "226C6": "xián", + "226C9": "nèi", + "226CD": "zhǐ", + "226CE": "bié", + "226D2": "chǒng", + "226D3": "lán", + "226D4": "dōng", + "226D5": "qūn", + "226D6": "xiàng", + "226D8": "xiáo", + "226D9": "wǎn", + "226DA": "rù", + "226DB": "wàng", + "226DC": "nì", + "226DE": "bāi", + "226DF": "yà", + "226E5": "sī", + "226E6": "yǐn", + "226E8": "yù", + "226EE": "lí", + "226EF": "huò", + "22717": "bàng", + "22723": "xī", + "22725": "jiū", + "22728": "xiè", + "22729": "qiān", + "2272A": "nuò", + "2272B": "xǐng", + "2272C": "duó", + "2272D": "jǐ", + "2272E": "wǔ", + "2272F": "mú", + "22730": "yàn", + "22731": "qì", + "22732": "ná", + "22733": "chì", + "22734": "hóu", + "22736": "sào", + "22738": "náo", + "2273B": "chěng", + "2273C": "chěng", + "2273D": "kuǐ", + "2273F": "jià", + "22740": "tú", + "22742": "dú", + "22745": "xiá", + "22746": "zhòng", + "22747": "huò", + "22748": "chóng", + "22749": "dá", + "2274C": "mào", + "2274D": "yào", + "22753": "juān", + "2276C": "shì", + "2276F": "yín", + "22773": "gǔ", + "22774": "wù", + "22778": "guò", + "22779": "tì", + "2277B": "hōng", + "22787": "rě", + "22789": "yí", + "2278B": "tǔn", + "2278F": "qióng", + "22790": "hài", + "22792": "qì", + "22795": "huò", + "22796": "tì", + "22797": "pī", + "2279A": "gěng", + "2279C": "xiè", + "2279E": "mì", + "2279F": "gào", + "227A0": "tā", + "227A1": "xiǎng", + "227A3": "shū", + "227A6": "fú", + "227AC": "zhuān", + "227AD": "liù", + "227C5": "yóu", + "227CA": "chěng", + "227CB": "duī", + "227E2": "lí", + "227E3": "yàng", + "227E4": "lí", + "227E7": "lǔ", + "227E8": "mǔ", + "227E9": "suì", + "227EA": "ài", + "227ED": "kòu", + "227EF": "zhé", + "227F0": "ài", + "227F1": "téng", + "227F3": "lǜ", + "227F4": "tuí", + "227F5": "bī", + "227FE": "huì", + "227FF": "huán", + "2281B": "kuò", + "2281D": "xīn", + "22821": "sào", + "2282B": "shù", + "2282C": "què", + "2282D": "bā", + "2282E": "tuì", + "22832": "fù", + "22833": "biē", + "22835": "tǎng", + "22837": "xiàng", + "22839": "sī", + "2283A": "bó", + "2283C": "mái", + "2283D": "dàng", + "2283F": "guì", + "22840": "hēi", + "22841": "xī", + "22842": "dàng", + "22843": "yì", + "22845": "bī", + "22847": "gū", + "22848": "cuì", + "22849": "sè", + "2284D": "gé", + "2284E": "yù", + "2284F": "nǎ", + "22851": "lì", + "22852": "zhì", + "22870": "zhào", + "22874": "jī", + "22875": "ruǎn", + "22879": "chòng", + "22882": "jié", + "2288C": "chàng", + "2288D": "zhé", + "22892": "sù", + "22893": "yōng", + "22896": "qì", + "22897": "zhuó", + "2289A": "kài", + "2289C": "yè", + "2289E": "qì", + "228B9": "xiòng", + "228C9": "yī", + "228CA": "chǒu", + "228CE": "tuǎn", + "228CF": "ài", + "228D0": "pīn", + "228D3": "liè", + "228D4": "mián", + "228D5": "ài", + "228D7": "mǒ", + "228D8": "wèi", + "228D9": "yìng", + "228DA": "nǐ", + "228DE": "bó", + "228E0": "liù", + "228F3": "ruì", + "228FB": "lǘ", + "228FC": "chá", + "228FF": "chù", + "22901": "sào", + "22902": "lí", + "22904": "sōng", + "22906": "lì", + "2290B": "xì", + "2290D": "yān", + "2290E": "cuō", + "22910": "liú", + "22918": "méng", + "2291A": "zhàn", + "22924": "zhuàng", + "22927": "miǎo", + "22929": "lì", + "2292B": "jǔ", + "2292F": "xiè", + "22930": "xiè", + "22931": "lǒng", + "22932": "lóng", + "22942": "téng", + "22943": "zhù", + "2294B": "chán", + "2294C": "xiǎn", + "2294F": "yíng", + "22950": "pèi", + "22958": "xié", + "2295A": "jiào", + "2295E": "chōng", + "22973": "hē", + "2297D": "tǔn", + "22985": "hǒng", + "22988": "mán", + "2298A": "jīn", + "2298C": "qú", + "2298D": "dǒu", + "2298E": "qiú", + "2298F": "zāi", + "22991": "shēng", + "22992": "zāi", + "22995": "yǐ", + "2299A": "huà", + "2299F": "kān", + "229B0": "yuè", + "229B1": "nì", + "229B2": "sī", + "229B4": "wǒ", + "229B8": "cán", + "229BA": "jiān", + "229BC": "miè", + "229BD": "sháo", + "229BF": "rǒng", + "229C0": "gān", + "229C5": "qiáng", + "229C7": "shú", + "229C8": "zhuó", + "229CF": "shī", + "229D1": "tì", + "229D6": "zhá", + "229D7": "zhān", + "229DD": "fèn", + "229DE": "miè", + "229E0": "zè", + "229E4": "zhì", + "229E5": "qiān", + "229E6": "hàn", + "229E7": "gé", + "229EE": "cán", + "229F0": "guó", + "229F1": "jiāo", + "229F3": "yōng", + "229F4": "áo", + "229FB": "zhá", + "229FD": "xì", + "22A01": "xū", + "22A02": "wǔ", + "22A0F": "jué", + "22A10": "jī", + "22A12": "chì", + "22A14": "wǎn", + "22A16": "miè", + "22A17": "zéi", + "22A1C": "jié", + "22A1D": "shí", + "22A1F": "xī", + "22A21": "è", + "22A25": "hù", + "22A26": "hù", + "22A28": "lì", + "22A2B": "chù", + "22A2E": "yī", + "22A2F": "mǎo", + "22A30": "xū", + "22A31": "zhōng", + "22A33": "yì", + "22A3A": "liáo", + "22A3F": "jiān", + "22A40": "jiǎn", + "22A41": "jú", + "22A44": "zhù", + "22A48": "wǔ", + "22A4F": "kè", + "22A50": "kě", + "22A51": "lì", + "22A52": "bǐ", + "22A53": "gé", + "22A55": "xū", + "22A56": "shā", + "22A57": "líng", + "22A58": "kē", + "22A5E": "bó", + "22A5F": "biān", + "22A60": "shuān", + "22A61": "qí", + "22A62": "shàn", + "22A66": "jī", + "22A68": "qiǎo", + "22A6E": "yì", + "22A6F": "jué", + "22A70": "zhǎng", + "22A72": "xìn", + "22A77": "tuō", + "22A78": "hài", + "22A79": "xià", + "22A7B": "tuó", + "22A7C": "yí", + "22A83": "cù", + "22A87": "jiāng", + "22A88": "nán", + "22A8B": "pěng", + "22A8D": "jié", + "22A8E": "xuē", + "22A8F": "hú", + "22AA5": "yǒu", + "22AA6": "nǔ", + "22AA7": "yè", + "22AAA": "yìn", + "22AAC": "kǒng", + "22AB6": "xiāo", + "22AB7": "xiāng", + "22ABC": "náo", + "22ABE": "zhàng", + "22AD0": "jié", + "22AD3": "nǔ", + "22AD4": "shàn", + "22AE2": "jiá", + "22AE7": "zhǒu", + "22AE8": "rǒng", + "22AEB": "lù", + "22AEC": "sà", + "22AED": "nù", + "22AEF": "bó", + "22AF0": "zhé", + "22AF2": "qǐn", + "22AF4": "cī", + "22AF5": "zú", + "22AF7": "wǒ", + "22AF8": "wǔ", + "22AFB": "nié", + "22AFF": "xiān", + "22B00": "hóng", + "22B2B": "tìng", + "22B2C": "jǐn", + "22B31": "jié", + "22B32": "hè", + "22B33": "tū", + "22B34": "zhé", + "22B35": "pīn", + "22B36": "jìn", + "22B37": "nàn", + "22B3C": "dùn", + "22B3E": "xī", + "22B3F": "xiè", + "22B41": "xì", + "22B42": "láo", + "22B43": "duǎn", + "22B44": "jì", + "22B45": "chā", + "22B46": "chōu", + "22B48": "gāng", + "22B4E": "xiáng", + "22B4F": "dǎo", + "22B65": "biàn", + "22B66": "xiāo", + "22B67": "xīn", + "22B81": "yǔ", + "22B82": "xián", + "22B83": "lí", + "22B84": "qiǎn", + "22B87": "měi", + "22B89": "qiāo", + "22B8A": "yà", + "22B8C": "qiā", + "22B8D": "qiòng", + "22B8F": "bàng", + "22B90": "zhēng", + "22B9A": "zè", + "22B9B": "shuàn", + "22B9E": "sào", + "22BC5": "lù", + "22BC9": "xié", + "22BCB": "fǔ", + "22BCC": "zhài", + "22BE9": "zè", + "22BEB": "duàn", + "22BED": "dèng", + "22BEE": "yù", + "22BF0": "lǜ", + "22BF2": "wàn", + "22BF3": "xué", + "22BF4": "jiǎo", + "22BF5": "yuě", + "22BF6": "zhì", + "22BF7": "wěi", + "22BF9": "gé", + "22BFA": "jǔ", + "22BFC": "yǎn", + "22BFD": "cuò", + "22BFE": "mào", + "22C06": "fú", + "22C07": "āi", + "22C0A": "xuān", + "22C0C": "gāng", + "22C0D": "ān", + "22C12": "jí", + "22C18": "pí", + "22C19": "zhǐ", + "22C1C": "nuó", + "22C3F": "pàn", + "22C41": "yí", + "22C44": "jié", + "22C46": "zī", + "22C48": "jià", + "22C49": "wǎi", + "22C4C": "jià", + "22C5F": "chǎn", + "22C61": "suǒ", + "22C62": "suǒ", + "22C63": "jí", + "22C64": "sǒng", + "22C66": "tī", + "22C67": "pī", + "22C68": "pó", + "22C6E": "mì", + "22C74": "yè", + "22C76": "qìn", + "22C77": "jìn", + "22C7A": "juē", + "22C7D": "yuān", + "22C7E": "ruán", + "22C94": "bàn", + "22CB0": "bīn", + "22CB4": "wèi", + "22CB5": "zào", + "22CB6": "qiè", + "22CB7": "sōu", + "22CB8": "lǔ", + "22CBC": "dié", + "22CBD": "chuāi", + "22CBE": "bì", + "22CBF": "zhú", + "22CC0": "mā", + "22CC1": "fèi", + "22CC2": "piē", + "22CC3": "yìn", + "22CC4": "xuàn", + "22CC6": "ào", + "22CC7": "zhuó", + "22CC8": "zú", + "22CCB": "bǐ", + "22CD1": "làng", + "22CD3": "tì", + "22CD9": "tiǎo", + "22CDA": "jiān", + "22CDF": "tǒng", + "22CFD": "duō", + "22CFE": "dòng", + "22D02": "biǎn", + "22D20": "zhì", + "22D22": "fén", + "22D26": "káng", + "22D27": "zhì", + "22D28": "zhāi", + "22D29": "bì", + "22D2A": "kuǎn", + "22D2C": "bàn", + "22D2D": "juē", + "22D2E": "qū", + "22D30": "qī", + "22D31": "léi", + "22D32": "xié", + "22D33": "tāng", + "22D3C": "sōu", + "22D3E": "bèi", + "22D47": "yàng", + "22D48": "jiǎn", + "22D65": "zào", + "22D80": "zhuài", + "22D83": "fán", + "22D85": "shé", + "22D87": "qióng", + "22D89": "pò", + "22D8B": "tiě", + "22D8C": "shā", + "22D8D": "zá", + "22D91": "niǎo", + "22D92": "guài", + "22D93": "cuǐ", + "22DA1": "qiào", + "22DA3": "dié", + "22DB3": "pīn", + "22DB4": "cí", + "22DB6": "bàng", + "22DCD": "yìn", + "22DD1": "xiǎn", + "22DD4": "yǐ", + "22DD5": "miǎo", + "22DD6": "duǎn", + "22DD7": "zhòu", + "22DD9": "kōng", + "22DE2": "zhāng", + "22DF6": "liú", + "22DF8": "zhǐ", + "22DF9": "chǎn", + "22DFA": "dú", + "22DFB": "yuán", + "22DFE": "suò", + "22DFF": "jié", + "22E00": "lì", + "22E01": "gǒng", + "22E0C": "bāng", + "22E17": "guó", + "22E18": "liáo", + "22E19": "shěn", + "22E23": "niǎo", + "22E25": "cuàn", + "22E26": "wěi", + "22E28": "tuō", + "22E2B": "sū", + "22E2D": "lóng", + "22E33": "xiāo", + "22E34": "yǎn", + "22E43": "qǐng", + "22E4D": "xī", + "22E4F": "yú", + "22E51": "zhèng", + "22E52": "xiè", + "22E53": "chāi", + "22E54": "fèn", + "22E56": "guó", + "22E58": "jǐng", + "22E59": "làn", + "22E5A": "xiān", + "22E5D": "líng", + "22E6E": "lěi", + "22E72": "jùn", + "22E73": "xiào", + "22E7C": "zá", + "22E84": "guān", + "22E85": "qiè", + "22E86": "luò", + "22E87": "yào", + "22E88": "luán", + "22E89": "tà", + "22E91": "luò", + "22E9E": "bǎ", + "22E9F": "chàn", + "22EA1": "zhuó", + "22EAB": "tiǎo", + "22EAF": "wān", + "22EB0": "líng", + "22EB4": "yù", + "22EB5": "qì", + "22EB7": "qí", + "22EBC": "jì", + "22EBD": "bó", + "22EBF": "shī", + "22EC0": "fǔ", + "22EC2": "guī", + "22EC5": "diǎn", + "22EC7": "hāo", + "22EC9": "gǎi", + "22ECB": "qí", + "22ED3": "chéng", + "22ED4": "huì", + "22ED7": "xiá", + "22ED8": "shí", + "22ED9": "zhì", + "22EDA": "qí", + "22EDC": "hài", + "22EDF": "jiǎo", + "22EE0": "lì", + "22EE2": "liǎo", + "22EE4": "qiāo", + "22EE8": "sà", + "22EEA": "qī", + "22EEB": "shī", + "22EEE": "jié", + "22EF5": "bèi", + "22EF6": "biān", + "22EF7": "bā", + "22EF8": "jūn", + "22EF9": "pī", + "22EFC": "dǎn", + "22EFF": "táng", + "22F00": "kuǐ", + "22F01": "kū", + "22F03": "kǒu", + "22F09": "shī", + "22F0A": "shī", + "22F0B": "jī", + "22F0C": "bào", + "22F10": "kě", + "22F11": "kuāng", + "22F16": "mǐn", + "22F19": "liáo", + "22F1A": "è", + "22F1B": "gé", + "22F1F": "wǎng", + "22F20": "duó", + "22F23": "qià", + "22F24": "huá", + "22F26": "hǒng", + "22F29": "pēng", + "22F2B": "jiào", + "22F30": "qū", + "22F31": "zì", + "22F32": "zhòu", + "22F33": "kuāng", + "22F35": "shā", + "22F37": "jì", + "22F38": "wēi", + "22F39": "pū", + "22F3A": "xué", + "22F3C": "shāo", + "22F42": "láng", + "22F43": "zhǐ", + "22F44": "tǐng", + "22F47": "dà", + "22F55": "yáng", + "22F56": "jìn", + "22F57": "zhǐ", + "22F5A": "zhuó", + "22F5C": "zá", + "22F5D": "chán", + "22F62": "mào", + "22F66": "kōng", + "22F67": "zhōu", + "22F68": "hū", + "22F69": "pēng", + "22F6D": "jiù", + "22F78": "chuò", + "22F79": "mǐn", + "22F7E": "xiào", + "22F80": "dǔ", + "22F81": "wéi", + "22F83": "cán", + "22F84": "yú", + "22F85": "dù", + "22F86": "kāi", + "22F87": "pì", + "22F8A": "chéng", + "22F8E": "chǔn", + "22F90": "shǎo", + "22F91": "yǎn", + "22F92": "kuài", + "22F94": "yuē", + "22FA6": "qí", + "22FA7": "zhēng", + "22FA9": "kè", + "22FAA": "qí", + "22FAB": "zhǐ", + "22FAC": "lù", + "22FB1": "pī", + "22FB2": "nuò", + "22FB3": "pǎo", + "22FBA": "fěi", + "22FBF": "wén", + "22FC2": "méng", + "22FC8": "shǎn", + "22FCC": "xiòng", + "22FCE": "duò", + "22FCF": "biào", + "22FDA": "yōu", + "22FDC": "màn", + "22FDE": "liǎo", + "22FE1": "xié", + "22FE2": "luàn", + "22FE3": "qiāo", + "22FE4": "dèng", + "22FE6": "chéng", + "22FE7": "chéng", + "22FED": "chuò", + "22FF8": "cè", + "23000": "léi", + "23001": "zhǎn", + "23002": "lǐ", + "23003": "lián", + "23004": "qún", + "2300D": "chén", + "2300F": "chéng", + "23010": "gū", + "23012": "zòng", + "23013": "chóu", + "23014": "chuàn", + "2301C": "lèi", + "2301D": "shuò", + "2301E": "lǜ", + "23023": "fú", + "23025": "lì", + "23027": "sàn", + "2302B": "sān", + "2302F": "sà", + "23033": "niè", + "23036": "zuān", + "23037": "lǐ", + "2303B": "shǔ", + "2303E": "fú", + "23049": "bì", + "2304D": "dào", + "23052": "shī", + "23056": "gàn", + "23057": "tàn", + "2305C": "màn", + "2305F": "lí", + "23062": "bì", + "23066": "pán", + "23068": "yōu", + "2306D": "jiū", + "2306F": "guō", + "23070": "liáo", + "23073": "wò", + "23074": "qià", + "23075": "dǒu", + "23077": "liè", + "23079": "jiǎo", + "2307B": "liè", + "23081": "tiāo", + "23084": "guō", + "23086": "pāng", + "23087": "qiāo", + "23089": "dí", + "2308A": "yùn", + "23092": "lè", + "23096": "sī", + "23097": "xīn", + "2309C": "xīn", + "2309D": "xiàng", + "2309E": "luǒ", + "230A4": "bēng", + "230A5": "tiāo", + "230AC": "xiào", + "230AE": "dōu", + "230B3": "dàng", + "230B4": "tíng", + "230B5": "zhuàn", + "230BB": "ōu", + "230BD": "wò", + "230C4": "xīn", + "230C5": "ruǎn", + "230C8": "zhuó", + "230C9": "dàng", + "230CD": "cuì", + "230D1": "zhuó", + "230D7": "cóng", + "230D8": "chǎn", + "230DD": "yǎng", + "230E7": "yǎn", + "230F3": "yǎn", + "230F5": "zhèn", + "230FD": "nuǒ", + "230FE": "yàn", + "23105": "fǎng", + "23109": "yǎn", + "2310A": "yú", + "2310D": "tí", + "2310E": "fù", + "2310F": "běn", + "23111": "yǎn", + "23113": "huī", + "23119": "huǎng", + "2311C": "guì", + "2311D": "yàn", + "2311F": "hú", + "23120": "biāo", + "23127": "suì", + "2312E": "zì", + "2312F": "jì", + "23130": "ě", + "23131": "jì", + "23132": "kuǐ", + "23134": "liàng", + "23138": "huò", + "2313A": "wéi", + "2313B": "zhuō", + "2313F": "tǐng", + "23143": "zǎi", + "23144": "yòu", + "23149": "rèn", + "2314D": "miàn", + "2315A": "nà", + "2315D": "tū", + "2315F": "dān", + "23161": "jué", + "23164": "xū", + "23165": "dī", + "23170": "xiàng", + "23177": "xiòng", + "2317A": "yǒu", + "2317B": "guǎ", + "2317E": "xī", + "23188": "hè", + "2318D": "dǐng", + "23190": "lú", + "23192": "xú", + "23194": "zhòu", + "23195": "xiàn", + "23196": "huāng", + "23197": "chā", + "23198": "shǐ", + "23199": "gàn", + "2319A": "nuǒ", + "2319B": "àn", + "2319F": "xiē", + "231A7": "hào", + "231B2": "qīn", + "231B3": "gěng", + "231B4": "shān", + "231B5": "fú", + "231BD": "zè", + "231C7": "dàn", + "231D6": "diǎn", + "231D7": "shēn", + "231D9": "zǔ", + "231E2": "biē", + "231E6": "chuí", + "231E7": "zhè", + "231E8": "dài", + "231EB": "wǒ", + "231EC": "qióng", + "231F0": "lín", + "231F2": "hūn", + "231F3": "jī", + "23205": "cáo", + "2320A": "mù", + "2320D": "dié", + "2320E": "wèi", + "23220": "biàn", + "23221": "tǐ", + "23225": "tú", + "23236": "gèng", + "23244": "chí", + "23245": "còu", + "23246": "tǐ", + "23252": "huò", + "23253": "qī", + "23254": "sāo", + "23255": "sàng", + "23256": "xuǎn", + "23257": "àng", + "23258": "nài", + "2325A": "yáng", + "2325B": "shū", + "2325C": "shā", + "23261": "tǐng", + "23269": "yà", + "2326A": "huǎng", + "2326E": "bīn", + "2327E": "òu", + "2327F": "cáo", + "23281": "áo", + "23283": "mào", + "23294": "méng", + "23296": "tiān", + "2329D": "sàng", + "2329E": "xù", + "2329F": "kàn", + "232A7": "lǎng", + "232B6": "biē", + "232B7": "cóng", + "232BA": "xián", + "232C4": "tūn", + "232C9": "yù", + "232CA": "dàn", + "232CB": "yìng", + "232CD": "zhāo", + "232CF": "pù", + "232D8": "huì", + "232DE": "ài", + "232DF": "mǒ", + "232E2": "jīng", + "232E3": "lán", + "232F2": "liè", + "232F3": "piǎo", + "232F5": "bó", + "232F6": "qióng", + "232F9": "bì", + "232FF": "yōng", + "23305": "lì", + "2330D": "niè", + "2330F": "dé", + "23313": "huān", + "23317": "yuè", + "2331A": "chūn", + "2331C": "lì", + "2331E": "zhāng", + "2331F": "líng", + "23320": "chún", + "23327": "cè", + "23328": "xún", + "2332C": "jǔ", + "2332D": "hui", + "2333E": "tōng", + "23346": "níng", + "23347": "jù", + "2334F": "chà", + "23356": "zāo", + "2335B": "yù", + "2335F": "kěn", + "23366": "kuàng", + "23367": "fěi", + "2336F": "yùn", + "23370": "qiǎn", + "23374": "quán", + "23378": "pò", + "2337A": "pěi", + "23384": "gèng", + "23385": "yì", + "23386": "luò", + "23391": "kuān", + "23393": "xuǎn", + "23394": "niàn", + "2339A": "hú", + "2339B": "jú", + "233A9": "yè", + "233AE": "xī", + "233B1": "yuè", + "233B2": "tǎng", + "233B3": "pìn", + "233B4": "dǔn", + "233B5": "bèi", + "233B8": "liǎo", + "233C0": "yǒng", + "233CE": "yā", + "233D1": "jiǎo", + "233D4": "kùn", + "233D6": "zhèn", + "233D7": "shù", + "233DA": "shí", + "233DE": "yóu", + "233DF": "pài", + "233E0": "xiáo", + "233E1": "jí", + "233F6": "qī", + "233F7": "hé", + "233FA": "kǒng", + "23402": "yè", + "23403": "chì", + "2340A": "kǎo", + "2340B": "yuè", + "2340E": "wǎ", + "2340F": "niǎn", + "23411": "cí", + "23413": "yí", + "23424": "jiu", + "2342B": "yāng", + "2342C": "lí", + "2342E": "dāi", + "2342F": "chóng", + "23435": "yí", + "2343A": "hàn", + "2343F": "yī", + "23441": "chòng", + "23442": "hù", + "23443": "zhuǎ", + "23466": "qióng", + "23467": "duò", + "23478": "tóng", + "23479": "xiān", + "2347F": "fú", + "23482": "diàn", + "23483": "xí", + "23484": "xiē", + "23485": "zhèn", + "23486": "qiào", + "23487": "tū", + "234B7": "hàn", + "234B8": "kuàng", + "234B9": "suō", + "234BB": "shòu", + "234BC": "tiáo", + "234C0": "zhēn", + "234C3": "nèi", + "234C5": "qiǎn", + "234C6": "yín", + "234C8": "liǎng", + "234C9": "shà", + "234CA": "zì", + "234CB": "pí", + "234CC": "gāo", + "234CF": "jìn", + "234D0": "yóu", + "234D2": "shàn", + "234D4": "mì", + "234D5": "òu", + "234D7": "hū", + "234DB": "yòu", + "234DD": "měng", + "23510": "zhǐ", + "23513": "bǐ", + "23517": "shēn", + "23518": "qì", + "23519": "xiān", + "2351A": "pán", + "2351B": "kǎng", + "2352B": "shuān", + "2352C": "pí", + "2352E": "zāi", + "2352F": "zhǔ", + "23531": "sōu", + "23532": "jiǒng", + "23535": "chán", + "23536": "fán", + "23537": "xiáo", + "23538": "yǐn", + "23539": "hóu", + "2353A": "mào", + "2353B": "tú", + "2353D": "jì", + "23541": "yí", + "23543": "yù", + "23544": "jiōng", + "23545": "pào", + "23547": "xiāo", + "23549": "gǒu", + "2354C": "gōu", + "2354D": "sǔn", + "2354E": "xiǎn", + "2354F": "zhuǎn", + "2357E": "chóu", + "23584": "qiāo", + "23585": "tí", + "23586": "yún", + "23589": "shān", + "2358A": "liè", + "2358C": "zhǐ", + "23590": "pāi", + "235A3": "jú", + "235A4": "lái", + "235A8": "zǐ", + "235AA": "qú", + "235AB": "gǔ", + "235AC": "jué", + "235AD": "zhí", + "235AE": "àng", + "235AF": "qìn", + "235B0": "pí", + "235B1": "zuī", + "235B3": "qián", + "235B5": "cuó", + "235B7": "jí", + "235B8": "tí", + "235B9": "rú", + "235BB": "hǎi", + "235BC": "xún", + "235BE": "bèi", + "235BF": "zhí", + "235C1": "dùn", + "235CB": "dǎng", + "235D0": "réng", + "235F2": "gān", + "235F5": "gàng", + "235F6": "tà", + "235F8": "tuò", + "235F9": "yàng", + "235FA": "kū", + "235FB": "zhì", + "23616": "jiān", + "23617": "nì", + "23618": "shēn", + "23619": "bàng", + "2361A": "shuài", + "2361B": "dōu", + "2361D": "qiān", + "2361E": "hán", + "2361F": "qiā", + "23620": "gǎn", + "23623": "chún", + "23624": "chá", + "23625": "bì", + "23626": "yī", + "23627": "fū", + "23628": "ě", + "2362A": "láo", + "2362B": "háo", + "2362C": "lí", + "23631": "tè", + "23632": "shēn", + "23634": "yín", + "23637": "jiān", + "2363B": "chá", + "23657": "niè", + "23658": "còu", + "2365B": "yí", + "2365F": "táng", + "23662": "juàn", + "23670": "chì", + "23671": "gǒu", + "23674": "jié", + "23675": "zhé", + "23676": "hú", + "23677": "máng", + "2367B": "zōu", + "2367C": "sì", + "2367F": "fèi", + "23680": "zī", + "23681": "zī", + "23683": "jié", + "23684": "sī", + "23686": "chūn", + "23687": "pào", + "2368B": "yé", + "2368C": "dī", + "2368E": "léi", + "2368F": "xū", + "23690": "rú", + "23692": "pá", + "23693": "juàn", + "23694": "xì", + "23695": "yè", + "23696": "ān", + "23698": "yì", + "23699": "jiān", + "2369C": "sōng", + "2369D": "wǒ", + "2369F": "sè", + "236A0": "zhǐ", + "236A1": "bī", + "236A2": "zhuàn", + "236A6": "jiàng", + "236A7": "hào", + "236A9": "chì", + "236AA": "dùn", + "236D3": "bó", + "236D4": "jí", + "236D5": "chuǎ", + "236D7": "luò", + "236DA": "ruǐ", + "236EB": "hú", + "236F1": "dàn", + "236F4": "hǎn", + "236F5": "què", + "236F6": "shā", + "236F7": "zhǎn", + "236F8": "zé", + "236F9": "chuán", + "236FA": "qī", + "236FB": "dié", + "236FD": "zhà", + "236FE": "tòu", + "23701": "cī", + "23702": "sà", + "23704": "luó", + "23707": "jí", + "23722": "luǒ", + "23723": "qín", + "23727": "qióng", + "23728": "juàn", + "2372C": "ài", + "2372D": "jiǎn", + "23739": "tì", + "2373A": "wén", + "2373D": "qiāo", + "23741": "pái", + "23742": "hún", + "23745": "ài", + "23747": "shuò", + "23748": "lián", + "23749": "duì", + "2374B": "tà", + "2374C": "jǐn", + "2374D": "bì", + "2374E": "yǎn", + "2374F": "gào", + "23750": "piáo", + "23751": "yù", + "23752": "shè", + "23755": "jiān", + "23757": "hú", + "2375A": "liè", + "2375C": "biàn", + "2375D": "sù", + "2375E": "jiāo", + "23778": "zhuì", + "2377D": "hān", + "23787": "dùn", + "23790": "xiě", + "23791": "méng", + "23792": "fū", + "23793": "lù", + "23794": "tàn", + "23797": "liú", + "23798": "xiān", + "23799": "sǎng", + "2379C": "còu", + "2379D": "zhuāng", + "2379F": "chēn", + "237B0": "liàn", + "237B4": "lí", + "237C0": "pèng", + "237C1": "tuǒ", + "237C4": "tuò", + "237C6": "liáo", + "237C7": "xiào", + "237C8": "chuì", + "237C9": "huài", + "237CA": "niǎo", + "237CB": "qiān", + "237CC": "lì", + "237CF": "pāo", + "237D0": "tiáo", + "237D1": "liú", + "237D2": "wú", + "237E4": "yǐng", + "237E6": "zhá", + "237F0": "yú", + "237F2": "xiǎn", + "237F3": "xuán", + "237F4": "shuān", + "237F5": "xī", + "237F8": "méi", + "237F9": "sēn", + "237FA": "liàn", + "237FC": "jiū", + "237FD": "lào", + "2380E": "xiāo", + "2380F": "zōu", + "2381A": "liú", + "2381C": "zhào", + "2381E": "zhé", + "23820": "lěi", + "2382D": "duǎn", + "23837": "jiǎn", + "23838": "shuān", + "23839": "zuó", + "2383A": "qiè", + "2383C": "lǎo", + "23849": "yù", + "2384A": "yì", + "2384B": "nǐ", + "2384E": "cén", + "23855": "yàn", + "23857": "ruǎn", + "2385E": "yán", + "2385F": "dié", + "23860": "mián", + "23867": "léi", + "23869": "wān", + "23870": "nǎ", + "23876": "yán", + "2387A": "lěi", + "2387D": "shā", + "2387E": "hū", + "23881": "xī", + "23882": "xī", + "23884": "yǒu", + "23885": "hān", + "23887": "hāi", + "23889": "wā", + "2388A": "xù", + "2388B": "pī", + "2388C": "tān", + "2388D": "xī", + "2388E": "xī", + "2388F": "bīn", + "23890": "qīn", + "23891": "xī", + "23892": "yú", + "23893": "xì", + "23895": "cì", + "23896": "qiàn", + "23897": "xiā", + "2389A": "wá", + "2389B": "è", + "2389C": "yǒu", + "2389D": "xìng", + "2389E": "ní", + "2389F": "hán", + "238A0": "bì", + "238A1": "shēng", + "238A4": "zhān", + "238A5": "diàn", + "238A6": "yǔ", + "238A8": "ǒu", + "238AA": "guǐ", + "238AB": "wǎng", + "238AC": "qiān", + "238AD": "yí", + "238B0": "zú", + "238B2": "qiān", + "238B3": "dìng", + "238B4": "kēng", + "238B6": "chù", + "238B7": "yī", + "238BA": "hān", + "238BB": "kuǎn", + "238C8": "diàn", + "238C9": "xì", + "238CA": "zī", + "238CB": "líng", + "238CC": "zì", + "238CE": "yù", + "238CF": "hūn", + "238D1": "sǐ", + "238D2": "kǎn", + "238DA": "àn", + "238DC": "yǒu", + "238DD": "jí", + "238DE": "hùn", + "238DF": "qiā", + "238E0": "hóu", + "238E1": "hóu", + "238E3": "diàn", + "238E9": "xiē", + "238ED": "shè", + "238EE": "shà", + "238F2": "xié", + "238F3": "yáo", + "238F4": "dà", + "238F6": "xiè", + "238F7": "chī", + "238F8": "yǒu", + "238F9": "hē", + "238FA": "shà", + "238FF": "tái", + "23901": "zhú", + "23903": "ǎi", + "23907": "què", + "23908": "zé", + "2390A": "lā", + "2390B": "lòu", + "2390C": "chuài", + "2390E": "yǒu", + "23916": "tì", + "23918": "shī", + "23921": "xiào", + "23922": "xì", + "23928": "huò", + "23929": "chì", + "2392A": "yì", + "2392F": "shú", + "23930": "yuè", + "23931": "chán", + "23932": "è", + "23933": "xī", + "23934": "xī", + "23935": "yǐng", + "23936": "zú", + "23937": "zā", + "2393A": "zā", + "23942": "tà", + "23943": "wàn", + "23947": "xìn", + "2394A": "wàng", + "2394B": "fǔ", + "23950": "lǔ", + "2395E": "jiǎn", + "23961": "yán", + "23963": "bì", + "23964": "kěn", + "23965": "guàn", + "23968": "zī", + "2396E": "kuǐ", + "2396F": "zhǒu", + "23970": "zhì", + "23973": "tú", + "23977": "tà", + "23979": "chù", + "2397A": "chēng", + "2397B": "chěng", + "2397C": "zhù", + "2397E": "dà", + "23987": "bì", + "23989": "jiǎ", + "2398C": "yì", + "2398F": "yuè", + "23990": "gāng", + "23996": "gān", + "2399C": "qiāo", + "239A0": "chú", + "239A1": "chú", + "239A2": "bì", + "239A6": "guì", + "239A9": "gǔ", + "239AA": "bǐng", + "239AB": "yìn", + "239AC": "zhuì", + "239AD": "gǔ", + "239AF": "lì", + "239B5": "è", + "239B6": "dǎi", + "239BC": "cán", + "239C2": "tì", + "239C3": "dù", + "239C4": "yì", + "239C8": "dié", + "239CA": "niǔ", + "239CC": "xuè", + "239CD": "nè", + "239CE": "guì", + "239CF": "kǎo", + "239D2": "chuǎn", + "239D6": "zhá", + "239D7": "yóu", + "239D9": "bài", + "239DA": "shí", + "239DB": "diàn", + "239DC": "pā", + "239DD": "qiú", + "239E1": "xuè", + "239E3": "mò", + "239E4": "kē", + "239E5": "yǒu", + "239E6": "jiǎo", + "239E7": "bó", + "239EC": "xiǔ", + "239F2": "mǐ", + "239F3": "luò", + "239F5": "xuè", + "239F7": "duò", + "239F9": "èr", + "239FA": "shān", + "239FC": "kuì", + "239FD": "nào", + "239FE": "miǎn", + "239FF": "lì", + "23A00": "luàn", + "23A02": "dié", + "23A04": "qià", + "23A05": "lèi", + "23A07": "mào", + "23A09": "hēng", + "23A0A": "chè", + "23A0B": "zhì", + "23A0D": "gǔ", + "23A0E": "cuō", + "23A13": "wù", + "23A14": "tào", + "23A17": "xī", + "23A18": "yāo", + "23A19": "wěi", + "23A1B": "zú", + "23A1C": "mà", + "23A1D": "yǔ", + "23A1E": "pěng", + "23A1F": "yì", + "23A20": "qìn", + "23A21": "yuè", + "23A22": "juè", + "23A23": "jiàng", + "23A24": "xù", + "23A25": "bēng", + "23A2A": "luǒ", + "23A2B": "zhuī", + "23A32": "dù", + "23A33": "xiàng", + "23A36": "huì", + "23A3A": "gǔ", + "23A3B": "kǎo", + "23A3E": "xīng", + "23A3F": "hún", + "23A40": "biān", + "23A44": "kè", + "23A45": "kǎo", + "23A48": "cuó", + "23A4F": "lù", + "23A51": "zuì", + "23A52": "zāo", + "23A53": "jiǎo", + "23A54": "guàn", + "23A59": "yān", + "23A5A": "ér", + "23A5C": "qíng", + "23A5F": "dèng", + "23A60": "sì", + "23A61": "suì", + "23A62": "liào", + "23A67": "shàn", + "23A69": "bì", + "23A6A": "wèi", + "23A6B": "yè", + "23A6D": "zhài", + "23A6F": "yé", + "23A70": "diào", + "23A71": "ài", + "23A74": "jiàng", + "23A77": "sū", + "23A79": "huài", + "23A7A": "yù", + "23A7D": "rǎng", + "23A80": "diān", + "23A81": "zuān", + "23A82": "bān", + "23A84": "qín", + "23A87": "jiā", + "23A89": "pí", + "23A8C": "tóu", + "23A90": "chóu", + "23A95": "guǐ", + "23AA0": "jī", + "23AA8": "xuè", + "23AAA": "diàn", + "23AAD": "biàn", + "23AAE": "zǎi", + "23AAF": "tóng", + "23AB6": "shǎn", + "23AB8": "gù", + "23AB9": "què", + "23AC0": "gǔ", + "23AC8": "hú", + "23AC9": "kuǎi", + "23ACC": "gòu", + "23ACE": "sù", + "23AD0": "chóu", + "23AD2": "kēng", + "23AD4": "dū", + "23AD9": "yì", + "23ADC": "dào", + "23ADD": "qiāng", + "23AE3": "lóng", + "23AE5": "lí", + "23AE7": "lì", + "23AE8": "qīng", + "23AEA": "wēi", + "23AEC": "móu", + "23AF1": "qì", + "23AF3": "jiǎng", + "23AF4": "xié", + "23AF9": "dài", + "23AFB": "lóu", + "23B02": "guàn", + "23B06": "péi", + "23B09": "pí", + "23B0B": "juàn", + "23B0D": "bēi", + "23B0E": "jué", + "23B0F": "juàn", + "23B10": "shì", + "23B15": "xiě", + "23B18": "ruí", + "23B19": "jìng", + "23B1A": "pò", + "23B1B": "sān", + "23B20": "jī", + "23B29": "fēn", + "23B2A": "bèi", + "23B2B": "jiè", + "23B2C": "sā", + "23B2E": "pī", + "23B34": "dì", + "23B35": "máo", + "23B36": "ba", + "23B37": "ba", + "23B38": "tiáo", + "23B39": "líng", + "23B3A": "shēng", + "23B3B": "zhěn", + "23B3C": "pī", + "23B3D": "wù", + "23B3F": "zè", + "23B40": "bào", + "23B47": "lǚ", + "23B56": "hāo", + "23B57": "dǒu", + "23B58": "fú", + "23B59": "ní", + "23B5D": "gé", + "23B60": "rú", + "23B61": "xiǎn", + "23B64": "bì", + "23B6E": "máo", + "23B72": "rǒng", + "23B73": "qiú", + "23B77": "bó", + "23B79": "hāo", + "23B7A": "nǎo", + "23B7B": "yán", + "23B83": "páo", + "23B84": "suī", + "23B86": "tuò", + "23B88": "qū", + "23B89": "lí", + "23B8A": "dé", + "23B8C": "jié", + "23B8D": "jié", + "23B8E": "gǔn", + "23B8F": "jiān", + "23B90": "bì", + "23BA0": "sàn", + "23BA1": "bāng", + "23BA2": "chún", + "23BA6": "nài", + "23BA7": "bǎng", + "23BAA": "róng", + "23BAB": "jiā", + "23BAC": "sōu", + "23BB0": "dé", + "23BBE": "xiān", + "23BBF": "zhān", + "23BC0": "mào", + "23BC3": "zī", + "23BC5": "jì", + "23BC6": "qí", + "23BCB": "rù", + "23BCC": "suō", + "23BCD": "rǒng", + "23BCE": "wù", + "23BCF": "róng", + "23BD0": "róng", + "23BDA": "tà", + "23BDC": "sōu", + "23BE4": "lí", + "23BE7": "cuǐ", + "23BE8": "zōng", + "23BE9": "mén", + "23BEA": "xǐ", + "23BEC": "mǎng", + "23BED": "niè", + "23BEF": "suī", + "23BF1": "péi", + "23BF4": "bì", + "23BF5": "dì", + "23BF8": "qú", + "23BF9": "qiáo", + "23BFB": "fēn", + "23BFC": "sù", + "23C03": "xū", + "23C07": "rǒng", + "23C08": "jī", + "23C0B": "qú", + "23C0C": "liè", + "23C15": "sào", + "23C18": "kùn", + "23C1A": "cuì", + "23C1B": "yè", + "23C1C": "bìng", + "23C1E": "jié", + "23C20": "qú", + "23C21": "qú", + "23C25": "méng", + "23C26": "rán", + "23C28": "bīn", + "23C29": "cháo", + "23C2C": "dú", + "23C36": "ráng", + "23C37": "xiān", + "23C3A": "táo", + "23C3B": "qú", + "23C3C": "niè", + "23C3F": "shū", + "23C40": "lǔ", + "23C42": "kùn", + "23C48": "mín", + "23C49": "mǐn", + "23C4D": "dàn", + "23C50": "yìn", + "23C53": "xiào", + "23C57": "jì", + "23C5C": "yīn", + "23C66": "fēn", + "23C67": "zhòng", + "23C6B": "gǔ", + "23C71": "chá", + "23C73": "liú", + "23C76": "bǔ", + "23C7A": "pā", + "23C7B": "sì", + "23C7C": "dāo", + "23C7D": "zhěn", + "23C80": "shān", + "23C82": "chuǎi", + "23C84": "jiǔ", + "23C8A": "kè", + "23C8B": "chí", + "23C91": "hù", + "23C92": "lì", + "23C93": "shā", + "23C96": "pài", + "23C97": "wéi", + "23C98": "wǔ", + "23C9C": "yíng", + "23CA1": "shā", + "23CA2": "dī", + "23CA5": "dān", + "23CB1": "tū", + "23CB2": "hé", + "23CB3": "pǒ", + "23CB5": "zhǐ", + "23CB6": "niǔ", + "23CB7": "nì", + "23CBD": "rǒng", + "23CBE": "guài", + "23CC0": "zhí", + "23CC3": "jí", + "23CDC": "fàn", + "23CDF": "jié", + "23CE0": "hǎi", + "23CE4": "zhàn", + "23CE6": "xì", + "23CE9": "zī", + "23CEC": "xí", + "23CED": "piào", + "23CF0": "bēn", + "23CF2": "jiǎn", + "23D13": "jiàn", + "23D16": "zá", + "23D1E": "bèn", + "23D1F": "mào", + "23D22": "zào", + "23D23": "zhuàng", + "23D25": "kuáng", + "23D28": "bí", + "23D2A": "pài", + "23D3C": "mào", + "23D3D": "tàn", + "23D5E": "tǔn", + "23D5F": "luǒ", + "23D62": "tān", + "23D71": "án", + "23D77": "hán", + "23D78": "zhú", + "23D7A": "duò", + "23D7B": "duò", + "23D7C": "gàn", + "23D86": "qiòng", + "23D88": "wǎng", + "23D8A": "mò", + "23D8B": "zhè", + "23D8C": "wěn", + "23D8D": "zhuàng", + "23D8F": "jiē", + "23D90": "pào", + "23D98": "sù", + "23D9D": "jù", + "23DA0": "qī", + "23DA1": "càn", + "23DA3": "tuán", + "23DA4": "shā", + "23DA6": "tuó", + "23DA9": "huà", + "23DAB": "yì", + "23DE0": "mín", + "23DE1": "zhōng", + "23DE5": "shuò", + "23DE9": "yì", + "23DEA": "wǎng", + "23DEB": "áo", + "23DF6": "sǔ", + "23DFE": "guǐ", + "23DFF": "tuǒ", + "23E00": "huǐ", + "23E03": "xù", + "23E04": "zǎn", + "23E06": "zǐ", + "23E07": "biàn", + "23E09": "dá", + "23E0A": "yīn", + "23E0B": "quǎn", + "23E0E": "huài", + "23E0F": "ná", + "23E10": "zá", + "23E12": "tí", + "23E18": "yí", + "23E19": "tān", + "23E1A": "shé", + "23E1B": "shuò", + "23E1D": "xíng", + "23E20": "yǒu", + "23E23": "fén", + "23E47": "kè", + "23E4B": "fú", + "23E52": "mǐn", + "23E5A": "pì", + "23E5C": "jí", + "23E5D": "qiào", + "23E5E": "zhǒng", + "23E5F": "gàn", + "23E60": "yuān", + "23E61": "chí", + "23E65": "qiàn", + "23E67": "zuó", + "23E69": "xié", + "23E6A": "máo", + "23E6C": "hú", + "23E6E": "pì", + "23E6F": "xùn", + "23E71": "xiá", + "23E72": "tí", + "23E75": "nà", + "23E76": "chuǎ", + "23E80": "wǔ", + "23EAC": "huāng", + "23EAD": "xuè", + "23EAE": "tào", + "23EB0": "qiào", + "23EB3": "jiāo", + "23EBC": "dǎng", + "23EBD": "bài", + "23ECD": "dàng", + "23ECE": "kòu", + "23ED0": "jū", + "23ED1": "shā", + "23ED2": "jīng", + "23ED5": "mó", + "23ED6": "nóu", + "23ED8": "shuò", + "23EDA": "shù", + "23EDB": "zhuāng", + "23EDC": "fú", + "23EDF": "zāng", + "23EE0": "xié", + "23EE1": "làng", + "23EE2": "tōng", + "23EE9": "zhé", + "23EEC": "càn", + "23EEE": "yuè", + "23EF1": "zhòu", + "23F1A": "tān", + "23F1E": "yán", + "23F1F": "lù", + "23F20": "yǎn", + "23F26": "zé", + "23F27": "shuài", + "23F45": "guō", + "23F46": "zhú", + "23F48": "rú", + "23F49": "rú", + "23F4C": "kǎn", + "23F4D": "jì", + "23F4E": "gāo", + "23F52": "xiè", + "23F55": "òu", + "23F56": "jiān", + "23F5A": "zhí", + "23F5B": "zhá", + "23F5D": "hǒng", + "23F5F": "kuǎn", + "23F61": "bó", + "23F64": "sè", + "23F65": "àn", + "23F66": "jiàn", + "23F68": "téng", + "23F6B": "sōng", + "23F6D": "mèng", + "23F6E": "yín", + "23F6F": "tān", + "23F70": "guō", + "23F73": "ruán", + "23F74": "wèi", + "23F77": "sì", + "23FA4": "qì", + "23FA6": "zhǎng", + "23FC5": "dǒng", + "23FC6": "fú", + "23FC7": "shěn", + "23FC8": "sù", + "23FC9": "yì", + "23FCA": "liàn", + "23FCC": "hé", + "23FCE": "zhēn", + "23FD0": "zé", + "23FD2": "cuǐ", + "23FD3": "cuǐ", + "23FDD": "fèng", + "23FDE": "lǐ", + "23FDF": "kòu", + "23FE3": "xiào", + "23FE4": "yǒu", + "24003": "háo", + "24009": "hàn", + "2400A": "kěn", + "2401D": "yù", + "24023": "huǎn", + "24024": "suō", + "24026": "là", + "24028": "dòu", + "24029": "jiàn", + "2402A": "pō", + "2402B": "biǎn", + "24030": "xuè", + "24032": "biàn", + "24037": "wèi", + "24061": "dàn", + "24062": "jié", + "24063": "bài", + "24065": "niǎn", + "24066": "xiàn", + "24067": "sè", + "2406A": "huá", + "2406B": "chuā", + "2406E": "òu", + "2406F": "liè", + "24070": "dí", + "24071": "cài", + "24073": "zhá", + "24075": "lǘ", + "24079": "huò", + "2407C": "lì", + "2407D": "yǐng", + "2407F": "wěi", + "24080": "bì", + "24081": "guó", + "24083": "pì", + "24086": "biāo", + "240A0": "yǎn", + "240A4": "zhuàn", + "240B2": "hóng", + "240B6": "lìn", + "240B7": "è", + "240B9": "yǐn", + "240BA": "làn", + "240BC": "yào", + "240BF": "xuàn", + "240C0": "lì", + "240E8": "làn", + "240E9": "líng", + "240EA": "xī", + "240EB": "hōng", + "240ED": "jiǎo", + "240EE": "zhuó", + "240F2": "zhí", + "240F5": "bó", + "240F6": "tēng", + "240F7": "ǎn", + "240FA": "xún", + "240FB": "lěi", + "240FC": "zāng", + "240FD": "huǐ", + "2410E": "xì", + "2410F": "hóng", + "24111": "fàn", + "24112": "jiǎn", + "24113": "cóng", + "24114": "zá", + "24116": "cā", + "24118": "yōu", + "2411B": "duì", + "2411C": "pān", + "24125": "tà", + "24127": "pàn", + "2412B": "fān", + "2412C": "xī", + "24136": "yào", + "24137": "luó", + "2413A": "biān", + "2413C": "jìn", + "2413D": "lì", + "2414A": "yàn", + "2414B": "dòu", + "2414E": "màn", + "24150": "gōng", + "24151": "rǎng", + "24152": "càn", + "24163": "mén", + "24171": "gǔ", + "24172": "shuàn", + "24178": "yán", + "24179": "bì", + "24180": "biāo", + "24181": "chéng", + "24182": "kuì", + "24184": "huǒ", + "2418D": "chì", + "2418F": "wò", + "24191": "còu", + "24192": "zhì", + "24199": "shuǐ", + "2419C": "guà", + "2419D": "pū", + "2419E": "xù", + "2419F": "sī", + "241A1": "wǔ", + "241AE": "fū", + "241B0": "shì", + "241B3": "huì", + "241B4": "huāng", + "241B5": "pā", + "241BC": "zhǔ", + "241BE": "yí", + "241C3": "lì", + "241C4": "shǎn", + "241DC": "mín", + "241DE": "gē", + "241E0": "hū", + "241EF": "ēn", + "241F0": "fá", + "241F3": "xù", + "241F4": "yí", + "241FE": "yìng", + "24214": "chí", + "24219": "yí", + "24225": "dí", + "24226": "huǐ", + "24227": "hé", + "24229": "zhǎ", + "24236": "yún", + "24237": "xiān", + "2424C": "xián", + "2424D": "lào", + "2424E": "shào", + "2424F": "shì", + "24250": "zhuó", + "24264": "biē", + "24265": "jiǔ", + "24266": "wō", + "24267": "jiǎo", + "24268": "fú", + "2426A": "xiāng", + "2426B": "kài", + "242B2": "nǎo", + "242B4": "huò", + "242B5": "jí", + "242B6": "là", + "242BB": "fōu", + "242BC": "shǎn", + "242BD": "liào", + "242BE": "miè", + "242BF": "chè", + "242C2": "mó", + "242CF": "lóu", + "242E8": "duò", + "242EB": "nǎo", + "242ED": "jī", + "242F0": "zhù", + "24302": "sù", + "24303": "duò", + "24307": "jiǒng", + "2430A": "zǎi", + "2430B": "huǐ", + "2430C": "yǐng", + "2430D": "hú", + "2430E": "lìn", + "2430F": "wěng", + "24310": "hàn", + "24314": "nán", + "24337": "xì", + "24339": "gàn", + "2433E": "hè", + "2433F": "jī", + "24340": "xiǎng", + "24341": "shā", + "24350": "tuì", + "24352": "zhāo", + "24353": "shù", + "24355": "yǒu", + "24356": "jiān", + "2435C": "zào", + "24364": "zhāng", + "2437D": "ruò", + "24384": "yān", + "2438B": "cuì", + "24397": "jí", + "24398": "shāng", + "243A3": "è", + "243A4": "láo", + "243A5": "tǎn", + "243A7": "zhù", + "243AD": "lǐn", + "243AF": "zēng", + "243B1": "juǎn", + "243B2": "hū", + "243D7": "shěn", + "243D8": "huò", + "243DC": "kuì", + "243F1": "chù", + "243F2": "zhòu", + "243F6": "āo", + "243F8": "zhuó", + "243FD": "xīng", + "243FF": "miè", + "24400": "hū", + "24414": "tán", + "24419": "bì", + "24423": "dǐng", + "24429": "kài", + "2442B": "biāo", + "24430": "huò", + "24431": "liè", + "24432": "cuàn", + "24443": "xiàn", + "24444": "rè", + "24453": "yuè", + "24455": "xūn", + "24457": "liǎo", + "24463": "shā", + "24466": "shì", + "2446A": "xiè", + "24473": "xiāo", + "24477": "yé", + "24478": "lǎn", + "24479": "yì", + "2447F": "liǎn", + "24494": "bó", + "24495": "cāo", + "2449D": "yào", + "244A6": "liàn", + "244BB": "tà", + "244D1": "jì", + "244D4": "xī", + "244D5": "zhì", + "244DA": "xī", + "244DD": "yuè", + "244E4": "xiǎn", + "244E6": "zhuò", + "244EF": "zhǎng", + "244F5": "zǔ", + "244F7": "ná", + "244FE": "dào", + "244FF": "liè", + "24500": "ná", + "24509": "páo", + "2450B": "jù", + "24516": "luǒ", + "24519": "shuǎ", + "2451A": "shàng", + "2451D": "luǒ", + "2451F": "fēn", + "24523": "bào", + "24528": "lì", + "2452B": "xiòng", + "24536": "dāng", + "24540": "chèng", + "24544": "zhǎng", + "24547": "sǒu", + "2454A": "shén", + "24552": "gě", + "24558": "yū", + "2455A": "huī", + "2455B": "chè", + "2455D": "jiào", + "2455E": "zhù", + "2455F": "shū", + "24562": "xiáo", + "24566": "níng", + "2456D": "jiāng", + "2456F": "jiāng", + "24577": "diào", + "2457D": "qiáng", + "2457E": "qiú", + "24580": "fēng", + "24586": "zhàn", + "24587": "kē", + "24592": "dié", + "24593": "zé", + "24596": "guāng", + "24597": "sè", + "24598": "fèn", + "2459B": "jiǎng", + "2459D": "yán", + "2459E": "zhì", + "245A2": "lì", + "245A6": "líng", + "245AA": "yí", + "245AC": "qǔ", + "245AD": "pán", + "245AE": "gōu", + "245B0": "jiǎ", + "245B1": "hé", + "245B3": "pèng", + "245B5": "jù", + "245B7": "chè", + "245BA": "liè", + "245BB": "shì", + "245BC": "pò", + "245BD": "xiàng", + "245BF": "pì", + "245C0": "luǒ", + "245C1": "cù", + "245C3": "yǔ", + "245C7": "kòng", + "245C8": "xiè", + "245CD": "wǎn", + "245CE": "yǎn", + "245CF": "péi", + "245D3": "chéng", + "245D8": "tí", + "245D9": "chè", + "245DA": "bì", + "245DB": "liàn", + "245DC": "jiǎ", + "245DE": "tíng", + "245E2": "tī", + "245E8": "dié", + "245EA": "shù", + "245EB": "lí", + "245EC": "lǘ", + "245ED": "xiā", + "245EF": "cuī", + "245F3": "bō", + "245F4": "tuí", + "245F5": "pú", + "245F7": "lìn", + "245F8": "fèn", + "245FA": "bó", + "245FB": "chàn", + "245FE": "dāng", + "245FF": "tǎi", + "24600": "dào", + "24603": "lì", + "24605": "yá", + "24606": "yá", + "24607": "zhān", + "2460A": "yí", + "2460C": "qī", + "24614": "hù", + "24616": "tīng", + "24618": "kǒu", + "2461B": "chún", + "2461C": "yóu", + "2461D": "fèn", + "2461F": "nuó", + "24620": "tiàn", + "24621": "jìn", + "24622": "pí", + "24623": "chén", + "24624": "pì", + "24626": "jiè", + "24627": "guǐ", + "24632": "zhuàng", + "24635": "hú", + "24636": "chǒu", + "24637": "shù", + "24638": "tāo", + "24639": "pí", + "2463A": "rǒng", + "2463B": "rǒng", + "2463D": "hǒu", + "2463E": "pēng", + "24645": "bài", + "24647": "xiá", + "2464B": "qǐn", + "2464C": "nǐ", + "2464E": "tāo", + "2464F": "qù", + "24652": "xié", + "24654": "zhào", + "24655": "huā", + "24656": "xīn", + "24658": "shōu", + "2465B": "tú", + "2465D": "liáng", + "2465E": "bì", + "2465F": "chū", + "24661": "xīng", + "24663": "xīn", + "24664": "fū", + "24669": "jiè", + "2466D": "fǔ", + "24670": "tè", + "24671": "shè", + "24674": "chāo", + "24675": "chuī", + "2467C": "rán", + "2467D": "hǒu", + "2467E": "bēng", + "24680": "cǎi", + "24685": "mú", + "24689": "xū", + "2468A": "dié", + "2468D": "chǎn", + "2468E": "yú", + "2468F": "zhòng", + "24693": "lí", + "24694": "shōu", + "2469A": "dú", + "2469C": "māo", + "2469D": "huáng", + "2469F": "táo", + "246A1": "dù", + "246A2": "tí", + "246A3": "shēng", + "246A4": "méi", + "246A8": "zhēn", + "246A9": "qín", + "246AA": "pì", + "246AB": "táng", + "246AC": "cāng", + "246AD": "yáo", + "246AF": "xiù", + "246B0": "bāng", + "246B1": "gǔ", + "246B5": "bù", + "246BC": "gòu", + "246BD": "bó", + "246C1": "wèn", + "246C4": "jì", + "246CA": "lā", + "246CD": "cuī", + "246CE": "mǐn", + "246CF": "cǔ", + "246D0": "ōu", + "246D1": "yōng", + "246D6": "máo", + "246D7": "kè", + "246D8": "māng", + "246D9": "dǐng", + "246DA": "huān", + "246DB": "duǒ", + "246DC": "jiāng", + "246DD": "sù", + "246E2": "céng", + "246E3": "tà", + "246E5": "huáng", + "246E6": "jué", + "246E7": "xún", + "246EA": "xiòng", + "246EC": "mì", + "246ED": "qún", + "246EE": "láo", + "246F1": "zhì", + "246F2": "wěi", + "246F7": "sè", + "246FB": "zāng", + "24701": "ǎn", + "24702": "wèi", + "24704": "huài", + "24707": "zhàn", + "24709": "yīng", + "2470A": "gē", + "2470B": "huì", + "2470D": "quán", + "24713": "liè", + "24714": "jú", + "24715": "bà", + "24716": "léi", + "24718": "mán", + "24719": "líng", + "2471C": "lì", + "2471D": "jǐ", + "24721": "huí", + "24722": "xìn", + "24723": "shì", + "24724": "zhé", + "24727": "bō", + "2472B": "chā", + "2472F": "chā", + "24730": "jīng", + "24731": "bā", + "24732": "bèi", + "24735": "yàn", + "24737": "hù", + "24739": "yú", + "2473B": "bì", + "2473C": "chuán", + "2473E": "jǐ", + "24742": "mù", + "24744": "máo", + "24745": "zhōng", + "24747": "yè", + "24748": "dōu", + "24749": "yě", + "2474D": "rì", + "2474E": "yīn", + "24750": "hào", + "24752": "nà", + "24753": "tiè", + "24754": "fù", + "24755": "mǔ", + "24756": "zǎi", + "24758": "hú", + "2475A": "chēn", + "2475B": "tuó", + "2475E": "chù", + "2475F": "fú", + "24767": "bào", + "2476C": "dǐ", + "2476D": "cǎi", + "2476E": "lù", + "2476F": "pǒ", + "24770": "dá", + "24771": "yè", + "24773": "yǐ", + "24777": "xiáng", + "24778": "bī", + "24779": "zhū", + "2477B": "yí", + "2477D": "lǜ", + "2477F": "kuāng", + "24782": "zhì", + "24787": "wá", + "24788": "dī", + "24789": "shù", + "2478A": "liè", + "2478B": "zǎo", + "2478C": "zhì", + "2478D": "náo", + "24797": "chái", + "2479A": "xiāo", + "2479B": "zàng", + "2479E": "yù", + "2479F": "dòu", + "247A0": "chà", + "247A1": "xié", + "247A2": "yáng", + "247A4": "xiǎn", + "247A5": "bǎo", + "247AE": "zhāi", + "247B0": "qiú", + "247B2": "hú", + "247B3": "zài", + "247B4": "jué", + "247B6": "hān", + "247BF": "àn", + "247C0": "zào", + "247C3": "shà", + "247C5": "xiàn", + "247C6": "chǐ", + "247C7": "yǎn", + "247C9": "àn", + "247CD": "zhé", + "247CE": "jué", + "247D1": "lì", + "247D3": "lè", + "247D6": "cǎi", + "247D8": "lù", + "247DA": "jiā", + "247DD": "xià", + "247DE": "xiào", + "247DF": "yān", + "247E0": "xū", + "247E2": "dùn", + "247E3": "yíng", + "247E4": "huī", + "247E5": "tí", + "247E6": "nóu", + "247E7": "xǐ", + "247EA": "tú", + "247F7": "wāi", + "247F8": "chēn", + "247FC": "hōng", + "247FE": "tí", + "247FF": "xuān", + "24800": "zá", + "24807": "gé", + "2480B": "lóu", + "2480C": "chái", + "2480D": "pán", + "2480E": "jí", + "24810": "tà", + "24813": "xī", + "24816": "xiāo", + "24818": "sāo", + "24819": "jiā", + "2481A": "sù", + "2481B": "huāng", + "2481D": "cuō", + "2481F": "tà", + "24820": "shuāi", + "2482A": "fú", + "2482B": "lì", + "2482D": "shè", + "2482F": "táng", + "24836": "diān", + "2483A": "bì", + "2483C": "gòu", + "2483D": "cù", + "2483F": "qiān", + "24842": "léi", + "24843": "sù", + "24846": "zòng", + "24847": "hāo", + "2484F": "chì", + "24850": "cáo", + "24853": "wò", + "24854": "xiāo", + "24855": "liè", + "24856": "yān", + "2485D": "bì", + "2485F": "huàn", + "24861": "xī", + "24862": "chī", + "24863": "xū", + "24864": "náo", + "24865": "yán", + "24867": "xiè", + "24868": "zhá", + "2486A": "suì", + "2486C": "xì", + "2486D": "bēng", + "2486E": "rán", + "2486F": "shuò", + "24870": "bān", + "24871": "guì", + "24872": "kāi", + "24873": "chēn", + "24876": "xù", + "2487E": "è", + "2487F": "lì", + "24880": "xī", + "24881": "huàn", + "24882": "sù", + "24884": "chǎng", + "2488A": "lù", + "2488B": "yán", + "2488E": "dāng", + "2488F": "dǎn", + "24890": "yāng", + "24892": "zhǎi", + "24893": "jù", + "24895": "duó", + "24896": "sāo", + "24897": "lái", + "24898": "sù", + "2489F": "zé", + "248A3": "bì", + "248A6": "yìn", + "248A8": "hāo", + "248AA": "liè", + "248AD": "háo", + "248AE": "yáng", + "248B4": "shuò", + "248B5": "ài", + "248B6": "qióng", + "248B9": "lěi", + "248BA": "xié", + "248BC": "shì", + "248C3": "lǔ", + "248C5": "què", + "248C6": "lián", + "248CC": "xiào", + "248CE": "yīng", + "248D1": "xié", + "248D8": "líng", + "248D9": "yōu", + "248DE": "dǎng", + "248DF": "lǎn", + "248E0": "xiāo", + "248E8": "yì", + "248EC": "wū", + "248EE": "yì", + "248EF": "tuō", + "248F0": "bǔ", + "248F2": "xìn", + "248F5": "sī", + "248F6": "jīn", + "248F8": "bā", + "248F9": "fǎ", + "248FB": "mò", + "248FC": "ruò", + "2490A": "dà", + "2490B": "jì", + "24910": "sù", + "24911": "qióng", + "24912": "bā", + "24926": "tián", + "24927": "yóu", + "24929": "tuó", + "2492B": "wài", + "2492C": "yòu", + "2492E": "dōng", + "24931": "xǐ", + "24932": "kǒng", + "24936": "qióng", + "24937": "duī", + "24938": "duò", + "2493A": "yì", + "24952": "xī", + "24953": "qīn", + "24954": "sù", + "24957": "liú", + "24959": "wán", + "2496D": "chē", + "2496E": "zhū", + "24970": "mào", + "24977": "quán", + "2497D": "yū", + "2497F": "yì", + "24980": "mí", + "24983": "lái", + "24984": "zhì", + "249A4": "ní", + "249A6": "bān", + "249AA": "dōng", + "249AE": "zhì", + "249D5": "yì", + "249D8": "líng", + "249D9": "yú", + "249DA": "cōng", + "249DB": "dì", + "249DC": "zhì", + "249E0": "ruǎn", + "249E3": "jiàn", + "249E9": "wàn", + "249EB": "jìn", + "249ED": "páng", + "24A0D": "lù", + "24A0E": "qú", + "24A10": "xǐ", + "24A11": "dá", + "24A16": "hù", + "24A17": "luǒ", + "24A19": "lè", + "24A36": "gǒng", + "24A3B": "lìng", + "24A42": "láo", + "24A44": "zhuàn", + "24A68": "zǎo", + "24A69": "hào", + "24A6A": "xiàng", + "24A6D": "hào", + "24A6E": "lì", + "24A71": "diàn", + "24A72": "gé", + "24A7D": "huán", + "24A84": "è", + "24A86": "xiá", + "24A8B": "jiān", + "24A8C": "qí", + "24A8D": "xiá", + "24A8E": "yǒu", + "24AA1": "zhēng", + "24AAA": "zhuàn", + "24AAE": "chàn", + "24AC9": "xiè", + "24AD5": "náo", + "24ADD": "jì", + "24ADE": "tián", + "24AE3": "yǎn", + "24AE7": "hǎo", + "24AE8": "xín", + "24AE9": "líng", + "24AEB": "bān", + "24AEC": "běng", + "24AF1": "gōu", + "24AF2": "líng", + "24AF5": "kuò", + "24AF6": "qià", + "24AF7": "jiào", + "24AF9": "ēn", + "24AFA": "yáo", + "24AFB": "dū", + "24B01": "huǒ", + "24B02": "dǔ", + "24B03": "pēi", + "24B0C": "yuán", + "24B0F": "lóu", + "24B10": "xíng", + "24B13": "lián", + "24B14": "yáo", + "24B15": "xī", + "24B16": "yáo", + "24B18": "xī", + "24B1B": "lú", + "24B1D": "yàn", + "24B20": "quán", + "24B25": "ráng", + "24B26": "wà", + "24B27": "zú", + "24B28": "fàn", + "24B29": "yì", + "24B2A": "dù", + "24B2B": "suì", + "24B2D": "pī", + "24B2F": "hán", + "24B31": "xù", + "24B33": "gǒng", + "24B35": "dì", + "24B37": "nà", + "24B3E": "duò", + "24B3F": "wā", + "24B42": "niè", + "24B48": "diào", + "24B49": "huāng", + "24B4C": "tí", + "24B4D": "fàn", + "24B51": "wú", + "24B52": "áng", + "24B54": "píng", + "24B59": "hán", + "24B5B": "gāng", + "24B5C": "lí", + "24B5E": "dūn", + "24B5F": "fù", + "24B60": "nà", + "24B62": "cèi", + "24B67": "jiē", + "24B69": "qìng", + "24B6B": "yīng", + "24B6C": "xiáng", + "24B71": "hú", + "24B74": "sù", + "24B7B": "gē", + "24B7C": "è", + "24B7D": "xù", + "24B86": "xī", + "24B8A": "kāng", + "24B8B": "guó", + "24B8C": "jiē", + "24B8D": "chuán", + "24B8E": "léi", + "24B8F": "héng", + "24B90": "zūn", + "24B95": "piè", + "24B98": "dēng", + "24B99": "xī", + "24B9A": "léi", + "24B9C": "shàn", + "24BA7": "lú", + "24BA9": "duì", + "24BAA": "jùn", + "24BAD": "chàn", + "24BAF": "xié", + "24BB0": "wā", + "24BB1": "zhé", + "24BB3": "zhuān", + "24BB7": "liù", + "24BB8": "léi", + "24BBC": "dài", + "24BBD": "gān", + "24BC4": "shì", + "24BC7": "yǎn", + "24BCC": "gān", + "24BD0": "yán", + "24BD6": "suī", + "24BDA": "zhōng", + "24BDC": "shì", + "24BE1": "shèng", + "24BE5": "chǎn", + "24BF7": "huáng", + "24BF8": "yìn", + "24BFB": "měng", + "24C02": "ráng", + "24C05": "xiáng", + "24C08": "bèi", + "24C0C": "chuán", + "24C11": "pú", + "24C19": "kē", + "24C1A": "lā", + "24C1D": "quǎn", + "24C1F": "hàng", + "24C20": "chì", + "24C21": "máng", + "24C26": "zhà", + "24C2A": "fèn", + "24C2C": "chào", + "24C33": "jǐng", + "24C43": "liè", + "24C45": "nà", + "24C46": "nà", + "24C47": "tóng", + "24C4B": "rán", + "24C4C": "zǔ", + "24C4D": "pī", + "24C4E": "yǒu", + "24C50": "shū", + "24C5B": "liè", + "24C5C": "shōu", + "24C5D": "tuǎn", + "24C5F": "gǎo", + "24C60": "sháo", + "24C61": "tuó", + "24C63": "nán", + "24C67": "tuǒ", + "24C68": "gōng", + "24C69": "diào", + "24C74": "měng", + "24C75": "bāng", + "24C77": "xié", + "24C78": "sì", + "24C79": "tǐng", + "24C7A": "guì", + "24C7D": "fú", + "24C7E": "guì", + "24C89": "guì", + "24C91": "zhǔ", + "24C93": "lái", + "24C95": "lǔn", + "24C96": "tiǎn", + "24C97": "rǎn", + "24C9A": "dōng", + "24CA8": "juàn", + "24CA9": "yán", + "24CAC": "ruán", + "24CAD": "dǎn", + "24CB0": "mào", + "24CB6": "luán", + "24CB8": "xù", + "24CBA": "xī", + "24CC2": "má", + "24CC3": "qī", + "24CC5": "chà", + "24CC8": "shāng", + "24CC9": "hàn", + "24CCA": "píng", + "24CCE": "jī", + "24CD3": "lì", + "24CD5": "yù", + "24CD6": "bān", + "24CD8": "tēng", + "24CDD": "chóu", + "24CE0": "chóu", + "24CE4": "qī", + "24CE5": "xī", + "24CE6": "bèi", + "24CEA": "yè", + "24CED": "guǎng", + "24CEF": "zhù", + "24CF3": "léi", + "24CF4": "léi", + "24CF5": "chā", + "24D00": "guǎng", + "24D0D": "dié", + "24D13": "yǎ", + "24D18": "niè", + "24D19": "shū", + "24D1B": "zhì", + "24D1F": "zhì", + "24D22": "zhì", + "24D23": "pǐ", + "24D25": "jiū", + "24D26": "jiū", + "24D27": "yì", + "24D28": "yòu", + "24D2A": "jiū", + "24D2F": "huàn", + "24D31": "dù", + "24D3B": "táo", + "24D3C": "qiè", + "24D3D": "qín", + "24D3E": "xìn", + "24D3F": "chān", + "24D40": "jì", + "24D42": "qìn", + "24D4A": "dù", + "24D4B": "zhī", + "24D4E": "ǒu", + "24D50": "wù", + "24D52": "wén", + "24D58": "bì", + "24D5B": "bēi", + "24D5D": "mǔ", + "24D5E": "jìn", + "24D5F": "táo", + "24D60": "liáo", + "24D65": "cáo", + "24D66": "zhá", + "24D6C": "chǐ", + "24D6D": "yā", + "24D6E": "kuí", + "24D6F": "yìn", + "24D78": "lóng", + "24D79": "qià", + "24D7B": "hāng", + "24D7C": "shàng", + "24D7D": "hài", + "24D7E": "chā", + "24D80": "jiǎo", + "24D81": "lǎo", + "24D88": "xī", + "24D8B": "bó", + "24D93": "zhǐ", + "24D95": "tùn", + "24D96": "fú", + "24D98": "hū", + "24D9A": "niè", + "24D9B": "yì", + "24D9C": "zhuàng", + "24DA0": "chá", + "24DA4": "suān", + "24DA7": "yùn", + "24DAE": "dù", + "24DB0": "xī", + "24DB1": "chuàn", + "24DB2": "xíng", + "24DB3": "jiǎo", + "24DB4": "shēn", + "24DC0": "wāng", + "24DC1": "bēi", + "24DC2": "féi", + "24DC3": "jiàn", + "24DC4": "quán", + "24DC5": "yì", + "24DC6": "dōng", + "24DC7": "xù", + "24DC8": "nà", + "24DC9": "jí", + "24DCC": "zhěn", + "24DCD": "qí", + "24DCE": "duī", + "24DCF": "yín", + "24DD1": "jiù", + "24DD2": "pí", + "24DD3": "xìn", + "24DD4": "lún", + "24DD5": "cǎi", + "24DD6": "lìng", + "24DD7": "biē", + "24DD8": "dào", + "24DD9": "dé", + "24DDF": "la", + "24DE1": "xī", + "24DE2": "jù", + "24DE4": "xiáo", + "24DE6": "jīng", + "24DF9": "wài", + "24DFB": "nǎo", + "24DFC": "xiāng", + "24DFD": "què", + "24DFE": "qiè", + "24DFF": "tū", + "24E00": "xǔ", + "24E01": "huì", + "24E05": "mín", + "24E06": "wěi", + "24E08": "yóu", + "24E09": "tuí", + "24E0A": "dài", + "24E0E": "kě", + "24E0F": "nà", + "24E11": "fù", + "24E12": "yù", + "24E13": "zhǐ", + "24E15": "hān", + "24E16": "āi", + "24E17": "fù", + "24E21": "yāng", + "24E24": "shí", + "24E26": "chán", + "24E2A": "chì", + "24E2B": "yùn", + "24E2C": "shuāi", + "24E2E": "sù", + "24E2F": "sǎng", + "24E31": "è", + "24E32": "zhěng", + "24E33": "ái", + "24E34": "suǒ", + "24E35": "bù", + "24E37": "qún", + "24E38": "yì", + "24E39": "yǎn", + "24E3B": "nà", + "24E3C": "wǔ", + "24E47": "lì", + "24E48": "lì", + "24E4A": "xī", + "24E4B": "jué", + "24E4C": "shī", + "24E4E": "yǎ", + "24E5B": "chén", + "24E5C": "yíng", + "24E5D": "bì", + "24E5E": "chè", + "24E61": "zhā", + "24E62": "tuǒ", + "24E63": "hù", + "24E64": "téng", + "24E65": "yìng", + "24E66": "bǐ", + "24E67": "níng", + "24E68": "liàn", + "24E69": "xìn", + "24E6A": "yǔ", + "24E72": "bèi", + "24E74": "mó", + "24E75": "duī", + "24E77": "dǎo", + "24E78": "qí", + "24E80": "shuāi", + "24E83": "xiāo", + "24E84": "zhǒng", + "24E85": "zhuì", + "24E87": "biàn", + "24E89": "wěi", + "24E8A": "xī", + "24E8C": "dēng", + "24E8E": "xiē", + "24E8F": "pān", + "24E90": "niè", + "24E93": "bié", + "24E94": "shè", + "24E95": "fèi", + "24E96": "mǐn", + "24E97": "qì", + "24EAA": "shàn", + "24EAB": "suǒ", + "24EB7": "jí", + "24EBA": "dǎn", + "24EBB": "juàn", + "24EBC": "lù", + "24EBE": "ào", + "24EC2": "yì", + "24EC3": "shǔ", + "24EC4": "suì", + "24EC5": "wèi", + "24EC6": "wán", + "24EC7": "chǔ", + "24ECC": "wò", + "24ED6": "bì", + "24ED8": "yǐn", + "24ED9": "huó", + "24EDC": "kài", + "24EDD": "níng", + "24EE2": "ài", + "24EE4": "lì", + "24EE6": "zhāi", + "24EF1": "lù", + "24EF6": "biàn", + "24EF7": "pán", + "24EFF": "guì", + "24F00": "sū", + "24F01": "méng", + "24F02": "xiǎn", + "24F03": "lòng", + "24F05": "qì", + "24F0B": "chàn", + "24F0C": "yì", + "24F0D": "háng", + "24F0F": "liǎn", + "24F10": "guàn", + "24F12": "wěi", + "24F17": "jué", + "24F18": "léi", + "24F19": "luán", + "24F1A": "lì", + "24F1C": "pí", + "24F22": "huǎn", + "24F2E": "guī", + "24F33": "jú", + "24F36": "dēng", + "24F3A": "fèi", + "24F41": "zhī", + "24F43": "mèi", + "24F45": "huàn", + "24F49": "pā", + "24F4A": "bǐ", + "24F4C": "pō", + "24F53": "ér", + "24F55": "huàn", + "24F63": "chàng", + "24F65": "luò", + "24F66": "fǒu", + "24F6F": "chóu", + "24F71": "zú", + "24F72": "nán", + "24F73": "xiǎo", + "24F79": "bài", + "24F7A": "lù", + "24F7C": "luò", + "24F7F": "niàn", + "24F80": "zé", + "24F84": "zhù", + "24F85": "hú", + "24F88": "huī", + "24F89": "tǎng", + "24F8A": "chóu", + "24F91": "huáng", + "24F92": "dōu", + "24F9B": "miào", + "24F9D": "bó", + "24FA0": "dì", + "24FA2": "děng", + "24FA3": "pū", + "24FA5": "sōng", + "24FA6": "chóu", + "24FAB": "yào", + "24FAC": "měng", + "24FAD": "lóng", + "24FB2": "lián", + "24FB5": "bié", + "24FBA": "lǚ", + "24FBF": "sè", + "24FC0": "zuó", + "24FC4": "cún", + "24FC5": "líng", + "24FC6": "zhěng", + "24FC7": "pǐ", + "24FC8": "báo", + "24FCB": "què", + "24FCE": "pī", + "24FCF": "nàn", + "24FD0": "pī", + "24FD1": "bǒ", + "24FD2": "bèi", + "24FD3": "fā", + "24FD5": "mǐn", + "24FD6": "mò", + "24FD7": "wà", + "24FD8": "zhāo", + "24FD9": "zhì", + "24FDA": "cū", + "24FDF": "xún", + "24FE0": "jí", + "24FE1": "guì", + "24FE3": "chéng", + "24FE7": "hàn", + "24FE8": "xiào", + "24FE9": "què", + "24FEB": "chuò", + "24FED": "fǔ", + "24FF3": "qǐn", + "24FF4": "lù", + "24FF5": "què", + "24FF6": "diǎn", + "24FF7": "qiān", + "24FFC": "chǎng", + "24FFD": "tà", + "24FFE": "bēi", + "25001": "dù", + "25002": "běng", + "25003": "hòu", + "25008": "zhǎ", + "25009": "zhǎ", + "2500E": "què", + "2500F": "má", + "25010": "hán", + "25013": "liú", + "25014": "lù", + "25016": "zī", + "25018": "pǐ", + "25019": "zhòu", + "2501B": "zāo", + "2501D": "niǔ", + "25020": "huì", + "25023": "xué", + "25025": "là", + "2502B": "nóu", + "2502C": "yǎn", + "2502D": "rǎn", + "2502E": "nǎo", + "25030": "là", + "25031": "guǎng", + "25032": "dú", + "25035": "lú", + "25039": "jiǎn", + "2503A": "xiè", + "2503B": "qì", + "2503E": "xiàng", + "25041": "guǒ", + "25042": "jié", + "25043": "màng", + "25046": "xiā", + "25047": "kuī", + "2504E": "yòng", + "25050": "hǎi", + "25051": "mì", + "25052": "yào", + "25055": "wēn", + "2505F": "lì", + "25060": "juàn", + "25061": "wū", + "25062": "qiáo", + "2506E": "diào", + "2506F": "chù", + "25072": "suō", + "25075": "chōng", + "25078": "quān", + "25079": "shè", + "25082": "měng", + "25083": "jù", + "2508B": "tú", + "25092": "nóng", + "25093": "mó", + "25099": "fèn", + "250A2": "áo", + "250A3": "guō", + "250A4": "hú", + "250A5": "cán", + "250A6": "dūn", + "250A7": "hǎi", + "250A8": "jiǎo", + "250B0": "gū", + "250B5": "jīn", + "250B8": "yáng", + "250C0": "chà", + "250CC": "huī", + "250D4": "qú", + "250D5": "kē", + "250DF": "qīng", + "250E0": "yì", + "250E3": "kǎi", + "250E4": "jiǎo", + "250E7": "chōu", + "250E8": "bǔ", + "250E9": "gèn", + "250EA": "jiāo", + "250EB": "zhī", + "250EE": "wèn", + "250F0": "bīn", + "250F4": "xiòng", + "250F5": "fàn", + "250F8": "yí", + "250F9": "chuàn", + "250FA": "yào", + "250FD": "yāng", + "250FE": "dù", + "250FF": "yǎn", + "25101": "méng", + "25107": "chī", + "25108": "mù", + "25109": "jiāo", + "2510B": "nǜ", + "2510D": "guó", + "2510E": "xuè", + "25111": "fú", + "25112": "xuē", + "25113": "fū", + "25114": "pèi", + "25115": "mò", + "25116": "xī", + "25117": "wò", + "25118": "shǎn", + "2511B": "xī", + "2511C": "qì", + "2511D": "miàn", + "25126": "dǎn", + "25128": "chǒu", + "25131": "fèi", + "25132": "mié", + "25134": "xuè", + "25135": "xù", + "25136": "sī", + "25137": "jǔ", + "25138": "mǎo", + "25139": "bào", + "2513B": "yí", + "2513C": "guā", + "2513D": "nì", + "2513F": "yí", + "25141": "zuò", + "25144": "nǔ", + "25151": "diàn", + "25152": "fàn", + "25153": "yì", + "25154": "shì", + "25157": "cū", + "25158": "zhěn", + "2515E": "shì", + "2515F": "jiǎo", + "25160": "hòu", + "25161": "ér", + "25166": "lèi", + "25167": "xuè", + "25168": "gèng", + "2516A": "shōu", + "2516C": "juān", + "25174": "jié", + "25175": "wéi", + "25177": "shǒu", + "25178": "jìng", + "2517A": "xú", + "2517B": "chòng", + "25185": "jiāng", + "25186": "mòu", + "25189": "yù", + "2518C": "jué", + "25191": "tìng", + "25194": "xiāo", + "25196": "dōu", + "25198": "guó", + "25199": "máng", + "2519A": "wāng", + "2519B": "xù", + "2519C": "wàng", + "2519D": "suō", + "2519E": "juàn", + "2519F": "yuè", + "251A1": "hán", + "251A3": "shēn", + "251A5": "xié", + "251A6": "liú", + "251A7": "rún", + "251AF": "bì", + "251B2": "nào", + "251B6": "wàn", + "251B7": "jiù", + "251B8": "quē", + "251C4": "nì", + "251C6": "mí", + "251C7": "suō", + "251C9": "qiǎng", + "251CC": "hàn", + "251CD": "zhuó", + "251CE": "mí", + "251CF": "xù", + "251D1": "lǎng", + "251D2": "jié", + "251D3": "dìng", + "251D4": "chàng", + "251D5": "zhì", + "251D6": "fēi", + "251D7": "jiá", + "251D8": "jùn", + "251D9": "huò", + "251DA": "qī", + "251DB": "jū", + "251DC": "zhūn", + "251DE": "diàn", + "251DF": "jiǎo", + "251E0": "yā", + "251E2": "zhǎn", + "251ED": "zhī", + "251EF": "mài", + "251F0": "hū", + "251F1": "xiè", + "251F2": "shí", + "251F3": "guī", + "251FF": "xù", + "25202": "jí", + "25204": "chuàng", + "25206": "mào", + "25207": "ruán", + "25208": "xū", + "25209": "huàn", + "2520A": "shà", + "2520B": "jǔ", + "2520F": "kuàng", + "25211": "hóu", + "25212": "guān", + "25213": "guā", + "25215": "mí", + "25216": "dié", + "25217": "bì", + "25218": "liǎng", + "25219": "là", + "2521A": "shǎn", + "2521B": "lù", + "2521C": "xì", + "2521F": "sǒu", + "2522C": "ōu", + "2522E": "léng", + "25237": "kū", + "25238": "guī", + "2523B": "xī", + "2523C": "pán", + "2523D": "sè", + "2523E": "juè", + "2523F": "hòng", + "25240": "guàn", + "25241": "jù", + "25243": "nài", + "25244": "huá", + "25245": "gé", + "25246": "lì", + "25247": "gòu", + "25248": "tì", + "2524A": "mà", + "2524B": "téng", + "2524C": "dá", + "25250": "qī", + "25251": "yù", + "25252": "jiǎo", + "25253": "miè", + "25254": "gěng", + "25255": "mèng", + "25256": "wèi", + "25258": "tí", + "25259": "qí", + "2525C": "chén", + "2525D": "dōu", + "2525F": "pán", + "25270": "hàn", + "25274": "mì", + "25275": "má", + "25276": "lù", + "25277": "qī", + "25278": "kēng", + "2527A": "dié", + "2527B": "qì", + "2527C": "jiāo", + "2527D": "kāng", + "2527E": "qiāo", + "2527F": "mì", + "25280": "shān", + "25287": "jiān", + "25288": "lí", + "25289": "kè", + "2528A": "xù", + "25291": "mán", + "25292": "fèng", + "25293": "chàn", + "25294": "huǐ", + "252A7": "kòu", + "252AA": "wěi", + "252AB": "guàn", + "252AC": "jí", + "252AD": "zùn", + "252AE": "huò", + "252AF": "xié", + "252B4": "suì", + "252B6": "ruǎn", + "252B8": "tè", + "252BC": "zhèng", + "252BD": "kūn", + "252BE": "xiǎng", + "252BF": "mián", + "252C1": "xì", + "252CC": "sā", + "252D9": "è", + "252DA": "miè", + "252DB": "zhǔ", + "252DC": "zōu", + "252DD": "měng", + "252DF": "xī", + "252E1": "táng", + "252E3": "jià", + "252E4": "cháng", + "252E5": "jí", + "252EE": "zhuó", + "252FF": "hè", + "25300": "chá", + "25301": "qì", + "25302": "mián", + "25303": "zhěn", + "25304": "kū", + "25305": "yè", + "25306": "zhōu", + "25308": "jiān", + "2530A": "pàn", + "2530D": "huī", + "2530F": "míng", + "25310": "liù", + "25318": "shuì", + "2531A": "mài", + "2531B": "lí", + "2531E": "shuò", + "2531F": "yí", + "25324": "lì", + "25328": "xiē", + "25329": "tè", + "2532A": "xiū", + "2532D": "xuàn", + "2532E": "lì", + "2532F": "méng", + "25330": "wéi", + "25331": "méng", + "2533A": "yào", + "2533B": "lán", + "2533C": "líng", + "2533D": "yīng", + "2533E": "yīng", + "2533F": "lì", + "25340": "jiǎn", + "25341": "guī", + "25345": "guān", + "25346": "xiè", + "25349": "shè", + "2534B": "zuī", + "25353": "kàn", + "25354": "léi", + "2535A": "biàn", + "2535D": "shǔ", + "2535E": "nǜ", + "2535F": "xù", + "25363": "hào", + "25368": "guǐ", + "2536A": "zhài", + "2536B": "láng", + "2536C": "cuān", + "2536D": "zhì", + "2536E": "féng", + "2536F": "qīn", + "25371": "zé", + "25372": "nà", + "25373": "niǔ", + "25374": "yì", + "25377": "cōng", + "25378": "shī", + "25379": "jiǎn", + "2537A": "zōng", + "2537B": "yǎn", + "2537C": "yīng", + "25380": "ruǎn", + "25382": "róng", + "25383": "xì", + "25385": "guān", + "25386": "kài", + "25388": "wù", + "2538A": "qín", + "2538B": "cōng", + "2538D": "zé", + "2538E": "xiè", + "25390": "yù", + "25391": "zàn", + "25392": "chuāng", + "25393": "lǐ", + "25394": "lǐ", + "25395": "xù", + "25396": "mí", + "25397": "xù", + "25398": "ruǎn", + "2539B": "guì", + "2539C": "rǒng", + "2539F": "máo", + "253A1": "qín", + "253A2": "cuàn", + "253A3": "cuàn", + "253A4": "cuàn", + "253AE": "wū", + "253B0": "fǎ", + "253B1": "bá", + "253B8": "qià", + "253B9": "zhì", + "253BA": "tiào", + "253C4": "zhì", + "253C5": "zhí", + "253C7": "huàn", + "253C8": "chóu", + "253CA": "zhì", + "253CE": "yǐng", + "253D2": "wù", + "253D3": "bēi", + "253D5": "hóng", + "253D6": "shěn", + "253D8": "jué", + "253D9": "kuì", + "253DC": "yǐ", + "253DD": "yà", + "253E0": "bī", + "253E4": "kuà", + "253E5": "qiān", + "253E8": "zhāo", + "253EA": "kǎi", + "253EB": "shāng", + "253EE": "àn", + "253EF": "zhé", + "253F0": "zhì", + "253F7": "zhì", + "253F9": "jiǎo", + "25400": "sī", + "25401": "pú", + "25402": "ǒu", + "2540A": "zhuó", + "25411": "yīng", + "25413": "huān", + "25415": "yà", + "25418": "shí", + "25419": "pā", + "2541A": "pǔ", + "2541E": "máng", + "2541F": "chāi", + "25429": "yún", + "2542C": "gǔ", + "25439": "dǎn", + "2543B": "náo", + "2543D": "zhé", + "2543F": "hú", + "25445": "kēng", + "25447": "dié", + "25448": "tīng", + "2544B": "guài", + "2544E": "qiōng", + "2544F": "shǐ", + "25450": "jiǎ", + "25451": "ào", + "25452": "nǎ", + "25453": "pǐn", + "25454": "jiá", + "25461": "zhè", + "25462": "bù", + "25463": "wǒ", + "25465": "chǎ", + "2546A": "náo", + "2546B": "kǎn", + "2546F": "dú", + "25470": "guài", + "25471": "qióng", + "25473": "róng", + "25474": "yǐ", + "25475": "duī", + "25476": "lěi", + "25478": "zhōu", + "25479": "kuā", + "2547A": "ē", + "2547B": "xiān", + "2547C": "diàn", + "2547D": "nuò", + "2547E": "è", + "2547F": "yōng", + "25480": "wù", + "25481": "kēng", + "25493": "zhì", + "25497": "zhǐ", + "25498": "xún", + "2549B": "zhèng", + "2549E": "yáng", + "254A0": "huò", + "254A1": "jí", + "254A2": "nǎo", + "254A7": "yà", + "254A8": "lù", + "254AB": "fū", + "254AC": "sǎn", + "254AD": "chù", + "254AE": "wěi", + "254B0": "fǔ", + "254B1": "kēng", + "254B2": "sì", + "254B3": "kàng", + "254B5": "yì", + "254B6": "huà", + "254BE": "yǔ", + "254C3": "lì", + "254C6": "lǐn", + "254C7": "dǔ", + "254C8": "è", + "254CC": "qiǎng", + "254CD": "dú", + "254D0": "jié", + "254D1": "chuò", + "254D2": "xiàn", + "254D6": "gǎo", + "254EC": "dào", + "254F0": "hōng", + "254FB": "zōng", + "254FE": "qì", + "254FF": "tuó", + "25500": "hōng", + "25501": "pǐ", + "25502": "gèng", + "25504": "niè", + "25507": "kōng", + "2550A": "zhǐ", + "25511": "xiǎo", + "25521": "shè", + "25522": "yú", + "25523": "jiāng", + "25529": "qǐ", + "2552A": "chěn", + "2552B": "sǎng", + "2552D": "suǒ", + "2552E": "qián", + "2552F": "huì", + "25531": "shàn", + "25532": "è", + "2553B": "qiū", + "2553D": "kè", + "25540": "wēng", + "25541": "zī", + "25542": "jí", + "25547": "dǎ", + "25549": "cuò", + "2554D": "lǒu", + "2554E": "kāng", + "2554F": "kuò", + "25550": "dí", + "25551": "qiē", + "25553": "mò", + "25556": "guǒ", + "25557": "hōng", + "25558": "cháo", + "25559": "hēi", + "25562": "cáo", + "25563": "zhé", + "25566": "gǔn", + "25570": "xū", + "25571": "péng", + "25572": "jué", + "25575": "gǎn", + "25576": "sī", + "25578": "suì", + "25579": "què", + "2557B": "wú", + "2557C": "yán", + "2557D": "pèng", + "2557E": "xiǎo", + "2557F": "pān", + "2558D": "là", + "25597": "bèng", + "25598": "zhěn", + "25599": "jí", + "2559C": "jǐn", + "2559D": "lián", + "2559E": "kěn", + "255A0": "zhóu", + "255A8": "zào", + "255AA": "lè", + "255AB": "qī", + "255AC": "bìng", + "255B5": "yǐn", + "255B6": "pīn", + "255BB": "sǒu", + "255BC": "lǜ", + "255BE": "dí", + "255BF": "dú", + "255C0": "liǎo", + "255C1": "zhuó", + "255CA": "chǎng", + "255D2": "chèn", + "255D3": "tà", + "255D9": "què", + "255DA": "dào", + "255DD": "rǎng", + "255DF": "pò", + "255E6": "zhōng", + "255E7": "xiē", + "255EA": "jiāng", + "255EB": "qú", + "255EC": "lěi", + "255ED": "cà", + "255EE": "quē", + "255F5": "xiàng", + "255F6": "lèi", + "255FA": "làn", + "255FF": "lǎ", + "25601": "lǎ", + "25604": "yù", + "2560A": "jiào", + "2560B": "qín", + "2560C": "jī", + "2560F": "gǎn", + "25612": "yì", + "25620": "yì", + "25621": "zhī", + "25624": "biǎo", + "25625": "shēng", + "25626": "jiù", + "2562B": "hē", + "2562C": "fú", + "2562E": "jū", + "25640": "zuǒ", + "25641": "yí", + "25646": "xiàn", + "25647": "yí", + "25649": "sì", + "2564B": "chuì", + "2564E": "mò", + "25661": "zhān", + "25663": "xún", + "25666": "rú", + "25668": "huò", + "2566C": "shāo", + "25670": "shòu", + "2567E": "yòu", + "2567F": "yù", + "25682": "jùn", + "25689": "zī", + "2568A": "lù", + "2569A": "chǐ", + "2569B": "kūn", + "256A0": "zhùn", + "256A6": "hóu", + "256A9": "xǔ", + "256BE": "zōng", + "256BF": "yìng", + "256C2": "zhū", + "256C5": "liù", + "256D1": "nù", + "256D8": "bì", + "256DA": "chì", + "256DC": "zǔ", + "256DD": "féng", + "256DE": "lù", + "256DF": "pǔ", + "256E5": "zhuàn", + "256E7": "zhé", + "256E8": "shī", + "256E9": "yǔ", + "256EA": "lù", + "256EB": "liáng", + "256EF": "jué", + "256F0": "liào", + "256F1": "bēng", + "25703": "yì", + "25704": "guān", + "2570C": "ǎo", + "2570F": "guì", + "25710": "mǐn", + "25712": "yǎn", + "25713": "lán", + "25716": "bó", + "25719": "zàn", + "2571A": "yǒu", + "25725": "yì", + "25726": "nǐ", + "2572C": "nǐ", + "2572D": "guǒ", + "2572E": "jùn", + "25730": "shī", + "25732": "xiǎn", + "25734": "qiān", + "25735": "què", + "25736": "kuí", + "25740": "shé", + "25742": "huò", + "25744": "wàn", + "2574A": "fèi", + "2574B": "fèi", + "2574D": "yù", + "25751": "zhī", + "25752": "guà", + "25754": "jié", + "25755": "máng", + "25756": "hé", + "25758": "yǒu", + "2575F": "dù", + "25760": "sī", + "25762": "lì", + "25765": "jié", + "25766": "niǔ", + "25767": "bà", + "25768": "yú", + "2576E": "zhī", + "25778": "hé", + "25779": "kē", + "2577E": "dù", + "2577F": "jiā", + "25781": "chēn", + "25783": "chuì", + "25784": "hé", + "25785": "zhǎi", + "2578A": "mèi", + "2578D": "hé", + "2578E": "zǐ", + "2578F": "zhú", + "25792": "tuó", + "25798": "zùn", + "2579A": "rú", + "2579B": "duò", + "2579C": "jiàng", + "257A7": "héng", + "257A9": "bēng", + "257AA": "mò", + "257AF": "zú", + "257B2": "biē", + "257B4": "kù", + "257B5": "jiá", + "257BA": "zhuō", + "257BC": "xiū", + "257C3": "hé", + "257C5": "qiāo", + "257CD": "fěi", + "257CE": "shēng", + "257D2": "zhuì", + "257D3": "kuǎn", + "257D4": "zè", + "257D5": "xiān", + "257D7": "bì", + "257D8": "yì", + "257DA": "chàng", + "257EA": "mào", + "257F6": "wǎn", + "257FD": "wū", + "257FE": "kū", + "257FF": "wǒ", + "25800": "xīng", + "25801": "kē", + "25803": "jiū", + "25804": "duān", + "25805": "huàn", + "25808": "zhì", + "25809": "cè", + "2580A": "róu", + "2580B": "jí", + "2580D": "yè", + "2581B": "jīng", + "2581C": "yàng", + "25821": "zǒng", + "25829": "cǎn", + "25831": "sī", + "25832": "lì", + "25833": "gǔ", + "25834": "chàng", + "25836": "fěi", + "25837": "liú", + "25839": "jié", + "2583A": "yūn", + "2583D": "zhì", + "25840": "chóu", + "25841": "biē", + "25852": "jī", + "2585C": "luó", + "2585D": "jiān", + "2585F": "chuāng", + "25860": "shuǎng", + "25862": "lǜ", + "25863": "jùn", + "25864": "jiào", + "25866": "tì", + "25867": "zhā", + "2586A": "yì", + "2586C": "cōng", + "2586D": "něi", + "2586E": "jiā", + "25874": "jì", + "2587D": "ài", + "25887": "jiǎn", + "2588A": "bèn", + "2588C": "fán", + "2588D": "suì", + "2588E": "zùn", + "25890": "gāo", + "25891": "gǎo", + "25892": "láo", + "25894": "zhuó", + "2589F": "hù", + "258A2": "tuí", + "258A6": "bì", + "258A7": "jú", + "258AE": "huá", + "258B2": "chéng", + "258B6": "kuài", + "258B7": "dāng", + "258B8": "gé", + "258B9": "xié", + "258BB": "jié", + "258BD": "cān", + "258C6": "zú", + "258C8": "pú", + "258CB": "shǔ", + "258CC": "bǔ", + "258D7": "níng", + "258D8": "yǎn", + "258D9": "zhòu", + "258DB": "méng", + "258DD": "biǎn", + "258DF": "xiàng", + "258E4": "lù", + "258E5": "lí", + "258E9": "jì", + "258EB": "miè", + "258EC": "lèi", + "258EE": "zhì", + "258EF": "yōu", + "258F0": "biǎn", + "258F8": "mù", + "258F9": "ràn", + "25902": "niǎo", + "2590A": "quán", + "2590B": "zhé", + "25910": "lèi", + "25917": "dǎng", + "25918": "jué", + "2591C": "líng", + "2591E": "líng", + "2591F": "yán", + "25923": "yǎo", + "25924": "zhèn", + "25925": "qī", + "25926": "ài", + "25928": "nú", + "25929": "mǎng", + "25931": "kǎn", + "25933": "jiū", + "25934": "yǎn", + "25935": "miàn", + "25937": "yín", + "25938": "wán", + "25939": "yào", + "2593A": "wā", + "2593B": "pí", + "2593C": "suì", + "25945": "kǒng", + "25948": "hóng", + "2594A": "mǐng", + "2594B": "líng", + "2594C": "yì", + "2594D": "shēn", + "2594F": "zuò", + "2595B": "tū", + "2595D": "yòng", + "2595F": "wà", + "25960": "guǐ", + "25961": "hòng", + "25965": "shì", + "25967": "xiòng", + "25969": "ā", + "25971": "chéng", + "25973": "kēng", + "25974": "yì", + "25975": "yàng", + "25976": "tíng", + "25977": "dòu", + "25978": "chá", + "25979": "liù", + "2597D": "qiú", + "2597E": "xuǎn", + "2597F": "shēn", + "25980": "kuān", + "25981": "tòng", + "25983": "qiǎn", + "25985": "chòu", + "2598A": "wěn", + "2598C": "lòng", + "2598D": "ǎn", + "25994": "kǎn", + "25996": "yǎo", + "25998": "fú", + "2599C": "bèng", + "2599D": "lǎn", + "2599E": "qià", + "2599F": "diàn", + "259A2": "jiào", + "259A3": "guī", + "259A5": "xiòng", + "259A8": "kè", + "259B6": "xiàn", + "259B7": "wòng", + "259C2": "gǒng", + "259C6": "ǒu", + "259C7": "kē", + "259CB": "kū", + "259D1": "tián", + "259D2": "gòu", + "259D3": "mǎ", + "259D5": "liù", + "259D9": "wèi", + "259DA": "wěn", + "259E1": "gòng", + "259E3": "tú", + "259E4": "níng", + "259E7": "mì", + "259EB": "láng", + "259EC": "qiǎn", + "259ED": "mán", + "259EE": "zhé", + "259F0": "huà", + "259F1": "yōng", + "259F2": "jìn", + "259F4": "mèi", + "259F7": "fú", + "259FB": "qú", + "25A0C": "liù", + "25A0D": "fù", + "25A0E": "dàn", + "25A10": "gǒng", + "25A12": "cuì", + "25A15": "xǐng", + "25A1C": "tū", + "25A1D": "shòu", + "25A2A": "qióng", + "25A33": "róng", + "25A3B": "lì", + "25A3F": "jī", + "25A40": "tuò", + "25A4C": "tóng", + "25A52": "tán", + "25A54": "líng", + "25A56": "yì", + "25A57": "ruǎn", + "25A59": "pǎ", + "25A5D": "cà", + "25A61": "yuè", + "25A62": "què", + "25A63": "zhù", + "25A64": "hài", + "25A71": "fá", + "25A72": "hài", + "25A80": "bū", + "25A81": "pīng", + "25A82": "liè", + "25A8A": "kuǐ", + "25A8B": "fú", + "25A8C": "tiǎn", + "25A8D": "wò", + "25A8F": "jū", + "25A98": "zhēn", + "25A9A": "fú", + "25AA2": "lóng", + "25AA6": "xì", + "25AA7": "tián", + "25AAB": "jì", + "25AAF": "yào", + "25AB1": "cù", + "25AB4": "pàng", + "25AB5": "qiè", + "25ABB": "lóng", + "25ABC": "jǐ", + "25AC2": "tóng", + "25AC3": "yí", + "25AC5": "chāng", + "25ACB": "gōng", + "25ACE": "dòng", + "25AD6": "xiāng", + "25AD9": "tǐng", + "25ADB": "zhuān", + "25ADC": "yǐ", + "25ADD": "yì", + "25ADE": "zǐ", + "25ADF": "qǐ", + "25AE2": "chǎ", + "25AEC": "dùn", + "25AEF": "chōng", + "25AF0": "lù", + "25AF1": "dùn", + "25AF3": "fāng", + "25AF4": "shì", + "25AF5": "tì", + "25AF6": "jī", + "25AF7": "qiū", + "25AF8": "shuǐ", + "25AF9": "chén", + "25AFC": "huàng", + "25AFD": "shi", + "25B00": "yún", + "25B06": "lóng", + "25B08": "mǎn", + "25B09": "gōu", + "25B0D": "xiān", + "25B0E": "mò", + "25B10": "shěn", + "25B12": "pō", + "25B13": "yào", + "25B14": "qū", + "25B15": "rǎn", + "25B19": "jù", + "25B1C": "yǐn", + "25B1D": "bái", + "25B1E": "niè", + "25B20": "chōu", + "25B2A": "róng", + "25B2B": "chuǎn", + "25B2C": "niè", + "25B2D": "lì", + "25B2E": "jiāng", + "25B2F": "kǎo", + "25B30": "cè", + "25B31": "chòng", + "25B32": "zhuā", + "25B33": "zǐ", + "25B34": "yáng", + "25B3C": "wěn", + "25B4B": "jì", + "25B4C": "jì", + "25B50": "lǜ", + "25B51": "qiú", + "25B52": "dùn", + "25B53": "báo", + "25B54": "chān", + "25B56": "bó", + "25B58": "chī", + "25B59": "zhè", + "25B5A": "màng", + "25B5C": "jì", + "25B5D": "miào", + "25B5E": "yuàn", + "25B60": "wú", + "25B61": "zhì", + "25B62": "pīng", + "25B65": "chōng", + "25B6B": "mí", + "25B6C": "féi", + "25B6D": "cuō", + "25B6E": "méng", + "25B8D": "yín", + "25B8E": "mǎng", + "25B8F": "diǎn", + "25B90": "diāo", + "25B92": "qián", + "25B95": "hàng", + "25B96": "zhí", + "25B97": "jú", + "25B98": "niàn", + "25B9C": "mí", + "25B9D": "gǔ", + "25BA3": "zhuā", + "25BA4": "niè", + "25BA5": "zhuó", + "25BA7": "yè", + "25BA8": "còng", + "25BAA": "xū", + "25BAC": "xì", + "25BAF": "bō", + "25BBE": "cǎn", + "25BC3": "yǎn", + "25BD1": "jǐn", + "25BD4": "jǔ", + "25BD5": "dàng", + "25BD6": "dù", + "25BD8": "yé", + "25BD9": "jìng", + "25BDA": "kè", + "25BDB": "luò", + "25BDC": "wěi", + "25BDD": "tū", + "25BDE": "yóu", + "25BDF": "pài", + "25BE1": "pí", + "25BE2": "dìng", + "25BE4": "wěi", + "25BE5": "chè", + "25BE6": "jiàn", + "25BE8": "sī", + "25BE9": "zhuó", + "25BEA": "sòu", + "25BEC": "ruǎn", + "25BEE": "yú", + "25BF3": "è", + "25BF6": "kǔ", + "25BF8": "zhù", + "25BFE": "xiá", + "25C1B": "fú", + "25C1C": "táo", + "25C1D": "xī", + "25C1E": "chōu", + "25C1F": "gǎn", + "25C20": "lǘ", + "25C21": "cè", + "25C22": "shàn", + "25C23": "liú", + "25C25": "xì", + "25C26": "jī", + "25C27": "yǐ", + "25C28": "tán", + "25C2A": "hú", + "25C2D": "cuō", + "25C2E": "gě", + "25C30": "shì", + "25C31": "sāo", + "25C32": "hòng", + "25C33": "xiàn", + "25C36": "xiá", + "25C3B": "mù", + "25C3C": "suǒ", + "25C3E": "zhài", + "25C40": "fū", + "25C41": "sè", + "25C42": "nú", + "25C43": "yì", + "25C67": "qín", + "25C68": "qìng", + "25C75": "huì", + "25C76": "shuǎng", + "25C77": "dǎn", + "25C78": "ōu", + "25C79": "mò", + "25C7A": "qiān", + "25C7B": "chì", + "25C7C": "pái", + "25C7D": "juàn", + "25C80": "cháo", + "25C81": "liè", + "25C82": "bīng", + "25C83": "kòu", + "25C84": "dàn", + "25C85": "chóu", + "25C86": "tōng", + "25C87": "dàn", + "25C88": "mǎn", + "25C89": "hù", + "25C8A": "liáo", + "25C8B": "xián", + "25C8D": "cáo", + "25C8E": "lù", + "25C8F": "chuàn", + "25C90": "wú", + "25C91": "mán", + "25C95": "zǐ", + "25C97": "dù", + "25C9A": "shuàng", + "25C9B": "fù", + "25C9C": "jù", + "25C9D": "zhòu", + "25C9F": "diào", + "25CA0": "wàng", + "25CA1": "chuāng", + "25CA2": "qiān", + "25CA3": "tuì", + "25CA5": "lián", + "25CA6": "biāo", + "25CA7": "lí", + "25CAA": "lí", + "25CC6": "bì", + "25CC7": "fù", + "25CC8": "cuì", + "25CC9": "dū", + "25CCB": "zàn", + "25CCC": "lóng", + "25CCD": "xún", + "25CCE": "qióng", + "25CCF": "jī", + "25CD0": "qiǎn", + "25CD2": "jiǎn", + "25CD3": "shāo", + "25CD4": "duò", + "25CD5": "shū", + "25CD6": "bù", + "25CD7": "xū", + "25CD8": "dǒng", + "25CDA": "rán", + "25CDC": "yáng", + "25CDD": "ruǐ", + "25CDE": "lìn", + "25CDF": "jiǎn", + "25CE0": "dì", + "25CE1": "fén", + "25CE2": "diàn", + "25CE3": "zuì", + "25CE5": "nǐng", + "25CEA": "suàn", + "25CEB": "tiǎn", + "25CEC": "àn", + "25CEF": "cè", + "25CF0": "dìng", + "25CF1": "shēn", + "25CF2": "dù", + "25CF3": "tí", + "25CF4": "jiǎo", + "25CF5": "zuì", + "25CF6": "zhǎng", + "25CF7": "jiǎn", + "25CF8": "dàn", + "25CF9": "dǎn", + "25CFA": "sǒng", + "25D10": "zhǎn", + "25D11": "tíng", + "25D12": "zhì", + "25D15": "yóu", + "25D16": "pái", + "25D21": "lǐ", + "25D24": "qián", + "25D26": "suì", + "25D27": "jǔ", + "25D28": "ài", + "25D29": "gé", + "25D2A": "jù", + "25D2B": "tún", + "25D2C": "bì", + "25D2D": "qià", + "25D2E": "bó", + "25D2F": "huì", + "25D31": "jiàn", + "25D34": "gōu", + "25D35": "suàn", + "25D3A": "cí", + "25D3B": "qiàng", + "25D3F": "yán", + "25D4F": "diàn", + "25D52": "miè", + "25D5C": "pò", + "25D5D": "lǐng", + "25D5E": "jié", + "25D5F": "zhù", + "25D60": "gǔ", + "25D63": "duān", + "25D64": "zhào", + "25D66": "shǎo", + "25D67": "qǐn", + "25D68": "mí", + "25D6A": "píng", + "25D6B": "cóng", + "25D6C": "chōu", + "25D6F": "sà", + "25D76": "tiǎn", + "25D85": "liú", + "25D86": "lǘ", + "25D87": "lǔ", + "25D88": "zōu", + "25D8C": "lǜ", + "25D8D": "huǎn", + "25D8F": "tiáo", + "25D90": "tuí", + "25D91": "qiǎng", + "25D92": "lìn", + "25D93": "bēi", + "25D94": "páo", + "25D95": "zhān", + "25D97": "lì", + "25D9B": "tí", + "25D9C": "hú", + "25DA2": "liè", + "25DB5": "huǐ", + "25DB6": "qū", + "25DB7": "xuǎn", + "25DB9": "jìng", + "25DBA": "dié", + "25DBB": "suí", + "25DBD": "wèi", + "25DBF": "yán", + "25DC0": "yān", + "25DC1": "bàn", + "25DC3": "jiǎng", + "25DC4": "nǐ", + "25DC5": "lì", + "25DC6": "hú", + "25DC7": "qì", + "25DC8": "zhōng", + "25DD1": "bì", + "25DD4": "yú", + "25DD5": "dié", + "25DD6": "lìn", + "25DD7": "lì", + "25DD8": "zhuó", + "25DD9": "jì", + "25DDA": "jū", + "25DDC": "fēng", + "25DDE": "yù", + "25DE8": "liè", + "25DE9": "zá", + "25DEA": "qián", + "25DEB": "jiē", + "25DEC": "guān", + "25DEE": "zhuó", + "25DF1": "fù", + "25DF9": "sè", + "25DFC": "cù", + "25E03": "huǐ", + "25E08": "dàng", + "25E09": "lóng", + "25E0A": "yì", + "25E17": "sǎ", + "25E18": "yuè", + "25E1A": "dí", + "25E21": "gǎn", + "25E22": "zān", + "25E23": "shàn", + "25E24": "yù", + "25E25": "bǒ", + "25E27": "dìng", + "25E28": "fán", + "25E2A": "yù", + "25E2C": "shēn", + "25E32": "gōng", + "25E34": "miè", + "25E35": "tún", + "25E38": "liè", + "25E41": "zhā", + "25E42": "pēi", + "25E44": "mí", + "25E46": "míng", + "25E47": "fàn", + "25E49": "nà", + "25E4A": "sì", + "25E4B": "yí", + "25E4C": "jiā", + "25E4D": "zhù", + "25E53": "bān", + "25E54": "yù", + "25E56": "pǒ", + "25E5A": "huān", + "25E5B": "càn", + "25E5C": "jiāo", + "25E60": "tán", + "25E69": "zhì", + "25E6B": "mǐ", + "25E6C": "kǎo", + "25E71": "yāo", + "25E72": "duì", + "25E73": "quǎn", + "25E74": "bù", + "25E75": "chù", + "25E76": "qiǎo", + "25E77": "liú", + "25E78": "bó", + "25E7A": "kāng", + "25E7B": "fèn", + "25E85": "dào", + "25E89": "dòu", + "25E8A": "gé", + "25E99": "líng", + "25E9A": "xí", + "25E9C": "nì", + "25E9D": "zhōu", + "25E9E": "zhōu", + "25EA3": "chōu", + "25EB4": "niān", + "25EB5": "jī", + "25EB7": "qū", + "25EC4": "kāi", + "25EC7": "xiàn", + "25EC9": "hé", + "25ECB": "lín", + "25ECD": "zī", + "25ED1": "ǒu", + "25ED2": "cù", + "25ED7": "chá", + "25EDD": "zhòng", + "25EDE": "bú", + "25EE4": "chōu", + "25EE5": "xì", + "25EE6": "sà", + "25EE7": "xián", + "25EE8": "sè", + "25EE9": "miàn", + "25EEB": "fán", + "25EEC": "zhī", + "25EEE": "cuì", + "25EF4": "xià", + "25EFE": "nuò", + "25EFF": "lí", + "25F00": "zú", + "25F02": "cuī", + "25F03": "zé", + "25F05": "lí", + "25F18": "qí", + "25F1A": "zhuō", + "25F1B": "cuì", + "25F1C": "pū", + "25F1E": "fán", + "25F1F": "tán", + "25F29": "zī", + "25F2A": "zǔ", + "25F2B": "zhōu", + "25F2C": "róng", + "25F2D": "lín", + "25F2E": "tán", + "25F36": "shì", + "25F3A": "cuǐ", + "25F3B": "zī", + "25F3C": "fū", + "25F41": "xiào", + "25F48": "fēng", + "25F4F": "xiàn", + "25F50": "jiàn", + "25F52": "fèn", + "25F57": "lì", + "25F58": "mò", + "25F5F": "yōu", + "25F65": "huò", + "25F67": "qū", + "25F6C": "niàng", + "25F70": "mí", + "25F73": "qì", + "25F76": "hé", + "25F78": "liàn", + "25F7F": "zuò", + "25F82": "líng", + "25F85": "zhú", + "25F87": "niǎo", + "25F8A": "jǐ", + "25F8B": "réng", + "25F8C": "jié", + "25F8D": "gǎn", + "25F90": "yì", + "25F93": "zhóu", + "25F95": "wù", + "25F9A": "gěng", + "25F9B": "cù", + "25F9D": "miè", + "25FA1": "xún", + "25FA3": "zhī", + "25FA4": "xiáo", + "25FA7": "fú", + "25FA8": "hú", + "25FAC": "dī", + "25FAE": "jué", + "25FAF": "diào", + "25FB9": "shǒu", + "25FBC": "wǎng", + "25FC3": "nà", + "25FC4": "dī", + "25FC5": "shì", + "25FC6": "cí", + "25FC7": "shū", + "25FC9": "wà", + "25FCA": "chè", + "25FCB": "fán", + "25FCD": "gū", + "25FCE": "yuān", + "25FD1": "guān", + "25FDA": "qiè", + "25FDC": "zhǎn", + "25FDD": "dài", + "25FDE": "shē", + "25FE6": "zhōu", + "25FE7": "xiǎng", + "25FE8": "míng", + "25FE9": "zì", + "25FEA": "huāng", + "25FEB": "mí", + "25FED": "xì", + "25FEE": "zhì", + "25FEF": "pài", + "25FF0": "duǒ", + "25FF4": "cì", + "25FF5": "móu", + "25FF7": "chào", + "25FF9": "yì", + "25FFA": "gōu", + "26007": "jīng", + "26013": "zēng", + "26014": "pīng", + "26015": "yè", + "26016": "jié", + "26018": "pī", + "2601B": "shā", + "2601C": "zhuàng", + "2601D": "jiǒng", + "26020": "liú", + "26021": "yǔ", + "26023": "jū", + "26028": "nuò", + "26038": "mào", + "26044": "chēn", + "26046": "zhuàn", + "26047": "niàn", + "26048": "kòng", + "26049": "jiē", + "2604A": "huà", + "2604D": "xīn", + "2604E": "zuó", + "2604F": "yàn", + "26050": "jué", + "26055": "hū", + "26056": "zhòu", + "26057": "shè", + "26059": "yǎn", + "2605B": "xiè", + "2605C": "dié", + "2605F": "chēn", + "26072": "jiǎn", + "26073": "jì", + "26076": "chuò", + "26077": "hóng", + "26080": "dá", + "26084": "kāi", + "26085": "xīng", + "26086": "huì", + "26087": "jiǎn", + "26088": "zhòu", + "26089": "zhǎ", + "2608A": "fù", + "2608B": "chì", + "2608C": "běng", + "2608D": "nuò", + "26091": "jì", + "26092": "qián", + "26094": "wàn", + "26095": "óu", + "26096": "bì", + "26097": "shuò", + "260A0": "jīng", + "260A1": "yè", + "260C4": "fěi", + "260C7": "lí", + "260CA": "lì", + "260CB": "pí", + "260D2": "suì", + "260D3": "liú", + "260D4": "hé", + "260D5": "hǔn", + "260D6": "tǎn", + "260D7": "shuò", + "260D8": "zhì", + "260D9": "bó", + "260DD": "xì", + "260E1": "pó", + "260E2": "qǔn", + "260E4": "mù", + "260FD": "yōng", + "26102": "dài", + "2610A": "qǐ", + "2610B": "diǎo", + "2610C": "niè", + "2610D": "shuǎng", + "2610F": "shāo", + "26110": "kǔn", + "26111": "suì", + "26113": "dōu", + "26114": "dié", + "2611C": "gōng", + "2612F": "zhuǎn", + "26130": "guó", + "2613C": "xū", + "2613D": "qú", + "26140": "xún", + "26143": "jiāo", + "26144": "zhé", + "26146": "diàn", + "26147": "sāng", + "26148": "bēng", + "2614A": "suǒ", + "2614B": "qiǎn", + "2614F": "xū", + "26151": "xún", + "26154": "mò", + "26175": "suì", + "26176": "là", + "26177": "zhǔ", + "26178": "zhòu", + "2617A": "lì", + "2617C": "dān", + "2617D": "jú", + "2617F": "yùn", + "26180": "chǎn", + "26181": "luó", + "26184": "sè", + "26186": "lián", + "26188": "zuǎn", + "2618B": "lài", + "2618C": "shuǎng", + "2618D": "qiè", + "26198": "dōu", + "2619E": "wù", + "2619F": "méng", + "261A1": "jì", + "261A4": "chī", + "261A6": "nǐ", + "261B8": "yáo", + "261BB": "là", + "261BE": "lǜ", + "261C0": "suì", + "261C1": "fū", + "261C4": "lěi", + "261C5": "wěi", + "261CE": "cōng", + "261D4": "lì", + "261D6": "pín", + "261D8": "jūn", + "261D9": "jǔ", + "261DB": "là", + "261E7": "jì", + "261EA": "miè", + "261EC": "yào", + "261ED": "biān", + "261F1": "cóng", + "261F2": "sī", + "261F5": "sī", + "261F8": "hé", + "26203": "nàng", + "26205": "dié", + "26208": "chè", + "26209": "yùn", + "2620B": "xiǔ", + "2620C": "shū", + "2620E": "chǎn", + "2620F": "mín", + "26210": "lián", + "26211": "yīn", + "26212": "xīng", + "26213": "wēi", + "26214": "gǔ", + "26215": "tóu", + "26216": "tā", + "26217": "fěi", + "26218": "dā", + "26219": "niè", + "2621A": "cù", + "2621B": "zuǒ", + "2621C": "jié", + "2621D": "xuàn", + "2621E": "bó", + "2621F": "jīn", + "26220": "yǐn", + "26221": "xū", + "26223": "yú", + "26224": "xiòng", + "26226": "qì", + "26227": "bēi", + "26228": "xíng", + "26229": "gǒng", + "2622C": "zuǐ", + "26230": "jiē", + "26232": "kāi", + "26235": "xíng", + "26236": "bēi", + "26237": "shū", + "26238": "yù", + "2623A": "zhǒu", + "2623B": "zhǎn", + "26242": "zhōng", + "26246": "chá", + "26248": "chuí", + "26249": "liù", + "2624E": "suī", + "26250": "zhǔ", + "26259": "biàn", + "2625D": "xìn", + "2625F": "yà", + "26262": "líng", + "26267": "yà", + "2626C": "tīng", + "26279": "dí", + "26281": "pí", + "26282": "hù", + "26283": "cén", + "2628A": "tiān", + "2628B": "mǒu", + "2628C": "juǎn", + "2628E": "mǒu", + "26290": "jù", + "26291": "liǔ", + "26293": "lǐng", + "26297": "liǔ", + "26298": "hù", + "262A6": "fú", + "262A7": "hú", + "262AA": "è", + "262AB": "gōng", + "262AC": "gū", + "262B1": "guà", + "262B9": "lüè", + "262BB": "fán", + "262BC": "lǜ", + "262BD": "méng", + "262BE": "fú", + "262BF": "liú", + "262C5": "xié", + "262C6": "gū", + "262C8": "xiàn", + "262C9": "bó", + "262CB": "jì", + "262D3": "quān", + "262D4": "lù", + "262DE": "shuò", + "262E1": "mǒu", + "262E2": "yù", + "262E3": "hàn", + "262E9": "yuè", + "262EA": "dàn", + "262EF": "yú", + "262F0": "jiān", + "262F3": "gāng", + "262FF": "cáo", + "26300": "shèn", + "26301": "liǔ", + "26306": "jiāo", + "26309": "sù", + "2630A": "sù", + "2630B": "zhòng", + "26312": "liào", + "26314": "xuǎn", + "26315": "lù", + "26317": "jì", + "2631A": "yán", + "2631F": "lù", + "26321": "mǐn", + "26322": "tí", + "26326": "huàn", + "26329": "yì", + "2632A": "tǎn", + "2632C": "wǔ", + "26330": "jī", + "26337": "dú", + "26338": "kūn", + "2633A": "jūn", + "2633F": "shī", + "26340": "nàn", + "26341": "pò", + "26344": "shū", + "26345": "quàn", + "2634C": "rèn", + "2634F": "fén", + "26352": "tà", + "26353": "tún", + "26355": "yáng", + "26366": "duō", + "26367": "cī", + "26369": "gǔ", + "2636A": "fén", + "2636D": "róu", + "26371": "gāo", + "26372": "xiáng", + "26374": "xiáng", + "26375": "hǒu", + "26377": "tāo", + "26378": "shàn", + "26379": "yáng", + "2637A": "zì", + "2637C": "yuán", + "26384": "sú", + "26387": "chuàn", + "26388": "xiáng", + "2638A": "bān", + "2638C": "mǎn", + "2638E": "fǔ", + "2638F": "lǎ", + "26390": "lǐ", + "26392": "jié", + "26393": "yōu", + "26398": "yù", + "2639A": "chì", + "2639C": "chuàn", + "2639D": "yì", + "2639E": "shān", + "263A2": "jí", + "263A3": "yān", + "263A6": "wù", + "263A7": "chún", + "263A8": "máng", + "263AD": "fú", + "263AE": "jiā", + "263AF": "gòu", + "263B0": "gú", + "263B1": "jiá", + "263B5": "xián", + "263B7": "jìn", + "263B8": "zì", + "263B9": "lóu", + "263BC": "gòu", + "263C0": "rén", + "263C2": "shān", + "263C5": "jué", + "263C6": "tóng", + "263C7": "yǒu", + "263D4": "jiān", + "263D5": "dú", + "263D7": "hú", + "263DB": "sāo", + "263DC": "yù", + "263E2": "mài", + "263E4": "zhī", + "263E5": "yān", + "263E6": "gāo", + "263E8": "huài", + "263EE": "quán", + "263F1": "yǎng", + "263F3": "zuǐ", + "263F7": "xiāo", + "263F8": "yì", + "263F9": "yǎn", + "263FA": "hóng", + "263FB": "yú", + "263FF": "chì", + "26401": "chí", + "26404": "háng", + "26405": "sè", + "26406": "pā", + "26407": "tà", + "26408": "fēn", + "26409": "chī", + "2640C": "hóng", + "2640D": "xuè", + "26416": "zhǐ", + "2641B": "qú", + "26420": "xī", + "26421": "fú", + "26423": "shū", + "26424": "hài", + "26426": "pò", + "26428": "cǐ", + "26430": "chài", + "26433": "hōng", + "26438": "pǎo", + "26439": "shēn", + "2643A": "xiāo", + "2643D": "xuān", + "2643E": "cǐ", + "2643F": "tíng", + "26440": "pò", + "26447": "tà", + "26448": "chā", + "2644B": "zú", + "2644C": "huò", + "2644D": "xù", + "2644E": "yàn", + "2644F": "chài", + "26451": "tuó", + "26458": "xián", + "26459": "xuān", + "2645A": "hóu", + "2645B": "huǎn", + "2645C": "gé", + "2645D": "chǒng", + "2645E": "bì", + "2645F": "hōng", + "26460": "hōng", + "26461": "chí", + "26463": "chá", + "2646F": "zhǎ", + "26471": "zhái", + "26472": "tà", + "26475": "pò", + "26476": "tà", + "26478": "yóu", + "26479": "fú", + "2647A": "cī", + "2647B": "dá", + "2647C": "tǎ", + "2647E": "liú", + "26481": "cī", + "26483": "hōng", + "26485": "hàn", + "26486": "lā", + "26488": "shī", + "2648D": "tóng", + "2648E": "huì", + "2648F": "hé", + "26490": "piē", + "26491": "yù", + "2649C": "xiān", + "2649D": "hǎn", + "2649F": "pò", + "264A6": "là", + "264A7": "huò", + "264B0": "tài", + "264B4": "lǎo", + "264B6": "shù", + "264BA": "dào", + "264BB": "diǎn", + "264C8": "xiòng", + "264CB": "wàng", + "264CD": "chě", + "264CE": "nài", + "264D0": "jué", + "264D3": "ér", + "264D4": "ér", + "264D5": "nǘ", + "264D6": "nǜ", + "264DD": "zhuǎn", + "264E2": "nuò", + "264E4": "liè", + "264E5": "lěi", + "264E7": "bā", + "264EC": "chēng", + "264EF": "guī", + "264F0": "quán", + "264F1": "gè", + "264F3": "gǒng", + "264F4": "shào", + "264F9": "lái", + "264FA": "zhēng", + "264FB": "yì", + "264FC": "gǔn", + "264FD": "wēi", + "264FE": "lǔn", + "26502": "shí", + "26503": "yīng", + "26504": "shěng", + "26505": "tú", + "26506": "bì", + "26508": "zé", + "26509": "zhòng", + "2650B": "rǒng", + "2650C": "qí", + "2650D": "fù", + "2650E": "cè", + "26513": "lí", + "26514": "mán", + "26516": "lián", + "26517": "biāo", + "2651B": "chuáng", + "2651C": "yì", + "26520": "pài", + "26525": "yì", + "26526": "kuài", + "26529": "biāo", + "2652B": "chì", + "2652C": "qú", + "2652D": "mò", + "2652E": "zhé", + "2652F": "shà", + "26530": "shà", + "26537": "yāo", + "26538": "gōng", + "26539": "nài", + "2653C": "xiè", + "2653F": "tiàn", + "26546": "yé", + "26549": "shā", + "2654F": "sào", + "26552": "diān", + "26553": "xù", + "26559": "qú", + "26560": "hōng", + "26561": "shèng", + "26562": "tìng", + "26570": "duo", + "26575": "liáo", + "26577": "hòng", + "26578": "lǐ", + "2657A": "xiǎng", + "2657D": "shèn", + "26580": "fū", + "26588": "yǎn", + "26589": "wǎng", + "2658A": "qī", + "2658B": "duǒ", + "2658D": "huà", + "2658E": "qiān", + "26590": "xiè", + "2659D": "cì", + "2659E": "shēng", + "265A2": "èr", + "265A4": "xīng", + "265A6": "tuì", + "265A7": "yàn", + "265A9": "liè", + "265AC": "mí", + "265B8": "zòng", + "265BA": "zī", + "265BC": "hú", + "265BD": "yíng", + "265BE": "lián", + "265BF": "dā", + "265C0": "tián", + "265C1": "tiàn", + "265CB": "róng", + "265CD": "ài", + "265D0": "ài", + "265D1": "zhé", + "265D2": "guō", + "265D3": "lù", + "265D4": "zhāo", + "265D5": "mí", + "265D6": "liáo", + "265D7": "zhé", + "265DB": "qǔ", + "265DC": "cōng", + "265DF": "tīng", + "265E1": "tán", + "265E2": "zhǎn", + "265E3": "hú", + "265E5": "piē", + "265E7": "dā", + "265E8": "róng", + "265EE": "nǎo", + "265F3": "náng", + "265F4": "dāng", + "265F5": "jiǎo", + "265FB": "jù", + "265FC": "ěr", + "2660A": "lì", + "2660C": "guō", + "2660D": "wài", + "26612": "niè", + "26614": "jīn", + "26629": "pǐ", + "2662A": "chì", + "26632": "pǐ", + "26633": "yì", + "26634": "dū", + "26635": "wǎ", + "26636": "xūn", + "26638": "qì", + "26639": "shàn", + "2663C": "xū", + "2663F": "hē", + "26640": "pàn", + "26642": "pēi", + "26644": "xiōng", + "26646": "chǐ", + "26647": "tān", + "26648": "zuì", + "26649": "zuǎn", + "2664A": "qì", + "2664B": "dū", + "26659": "shuǐ", + "2665C": "nǎ", + "2665D": "xī", + "26667": "chǎo", + "26668": "yì", + "2666B": "zhēng", + "2666E": "jú", + "2666F": "dài", + "26671": "sān", + "26674": "zhù", + "26675": "wàn", + "26678": "sān", + "26679": "bàn", + "2667A": "jià", + "2667B": "mài", + "26688": "tuò", + "2668A": "qì", + "2668F": "zhuāng", + "26690": "tuó", + "26693": "píng", + "2669D": "pēng", + "2669E": "kuāng", + "2669F": "yí", + "266A1": "xiè", + "266A2": "yuē", + "266A3": "hén", + "266A5": "hóu", + "266A6": "zhēng", + "266A7": "chǔn", + "266A8": "shì", + "266A9": "wǎ", + "266AB": "xié", + "266B8": "gèng", + "266C5": "è", + "266CF": "kú", + "266D0": "nà", + "266D3": "jū", + "266D4": "xuàn", + "266D5": "qū", + "266D6": "chè", + "266D7": "lǚ", + "266D8": "hé", + "266D9": "shèng", + "266DA": "nàn", + "266DC": "hé", + "266DD": "chá", + "266DE": "yān", + "266DF": "gěng", + "266E0": "niè", + "266E2": "guó", + "266E3": "yán", + "266E4": "guǎn", + "266E7": "zhì", + "266E8": "lao", + "266EF": "dǔ", + "266F0": "qì", + "266F1": "qū", + "266F2": "jué", + "26701": "fēng", + "26703": "xù", + "26704": "tuì", + "26706": "hán", + "26707": "kū", + "2670A": "shēn", + "2670B": "zhì", + "2670D": "pàng", + "2670E": "zhēng", + "2670F": "lì", + "26710": "wǎn", + "26712": "fǎn", + "26713": "xìn", + "26716": "yà", + "2671B": "jū", + "2671C": "shèn", + "2672D": "mǎng", + "2672F": "tǔn", + "26730": "zhuó", + "26731": "xī", + "26732": "yìn", + "26733": "jīng", + "26734": "tún", + "26737": "gèng", + "26738": "jì", + "2674F": "zhuǎn", + "26752": "tiē", + "26754": "zhī", + "26756": "jí", + "2675A": "yíng", + "2675B": "wèi", + "2675D": "huàn", + "2675E": "tíng", + "2675F": "chán", + "26762": "kuí", + "26763": "qià", + "26764": "bàn", + "26765": "chā", + "26766": "tuǒ", + "26767": "nǎn", + "26768": "jiē", + "2676A": "yān", + "2676C": "tú", + "2676E": "wěn", + "26770": "cōng", + "26773": "xù", + "26774": "yìn", + "26777": "bèng", + "2677C": "lǘ", + "26781": "zāi", + "26782": "dā", + "26786": "niè", + "26787": "jǔ", + "26788": "hóu", + "2678C": "gèng", + "26795": "hóu", + "26796": "kān", + "26797": "gōng", + "26799": "huǐ", + "2679A": "xiè", + "2679D": "xì", + "2679E": "hán", + "2679F": "mí", + "267A1": "wěng", + "267A2": "hùn", + "267A3": "sāo", + "267A4": "xìn", + "267A5": "zhé", + "267A6": "huò", + "267A8": "gōng", + "267AB": "sài", + "267AC": "jīn", + "267AD": "wā", + "267B1": "duǐ", + "267B2": "chī", + "267BD": "xī", + "267C2": "mí", + "267C3": "zāng", + "267C4": "sǎng", + "267D3": "tún", + "267D4": "zhì", + "267D5": "wěn", + "267D8": "yín", + "267D9": "tǔn", + "267DB": "chōng", + "267DC": "zé", + "267DE": "xiāo", + "267DF": "mó", + "267E0": "cù", + "267E3": "biǎn", + "267E4": "xiū", + "267E7": "yí", + "267EE": "huǎng", + "267F0": "zhā", + "267F1": "suō", + "267F2": "hún", + "267F3": "jù", + "26801": "cù", + "26804": "jī", + "26805": "xún", + "26806": "sǔn", + "26807": "céng", + "26809": "yì", + "2680E": "biāo", + "26812": "jué", + "26813": "lì", + "26816": "pào", + "2681B": "zā", + "2681C": "yè", + "2681E": "bì", + "2681F": "zhè", + "26820": "zhè", + "26822": "jiù", + "26823": "zhé", + "26826": "shù", + "2682A": "xī", + "26837": "xǔ", + "26838": "nǎi", + "26839": "xián", + "2683A": "gǔn", + "2683B": "wèi", + "2683E": "jí", + "2683F": "sà", + "26842": "dǒng", + "26843": "nuó", + "26844": "dù", + "26845": "zhēng", + "26846": "kū", + "26849": "míng", + "26855": "báo", + "26856": "huì", + "26859": "zōng", + "26868": "sàn", + "2686A": "tēng", + "2686B": "yí", + "2686D": "yù", + "26871": "yào", + "26872": "nǐng", + "26874": "chóu", + "26875": "hùn", + "26877": "duì", + "26879": "qì", + "2687A": "yǐng", + "2687B": "bìng", + "2687C": "níng", + "2687D": "huáng", + "26886": "yǐng", + "2688A": "báo", + "2688E": "guàng", + "2688F": "lěi", + "26890": "zǔn", + "26899": "chǎn", + "268A3": "jiǎn", + "268A7": "méng", + "268A9": "xiào", + "268AF": "xìn", + "268B1": "lí", + "268BA": "qiǎo", + "268BF": "wěi", + "268C0": "nà", + "268C2": "pāng", + "268C4": "léi", + "268C7": "luó", + "268CB": "luán", + "268CD": "gēng", + "268CF": "luán", + "268D2": "qú", + "268D6": "luó", + "268D8": "náng", + "268DB": "luó", + "268DC": "yuè", + "268E2": "shuì", + "268E5": "mì", + "268E6": "wáng", + "268E7": "cè", + "268E8": "jiān", + "268E9": "wǎng", + "268EF": "jiā", + "268F4": "huán", + "268F8": "liàn", + "268F9": "zì", + "268FA": "bái", + "268FB": "shǒu", + "268FE": "wǎn", + "26902": "shū", + "26907": "guī", + "26908": "xī", + "2690A": "rú", + "2690B": "yào", + "2690E": "gāo", + "26915": "yuè", + "26918": "yōng", + "26919": "wà", + "2691A": "bó", + "2691F": "xìn", + "26922": "pì", + "26923": "bó", + "26926": "hài", + "26927": "zhài", + "26928": "wò", + "2692A": "yè", + "2692B": "bì", + "2692C": "hài", + "26938": "chì", + "2693B": "zhì", + "2693D": "ní", + "26941": "wú", + "26942": "ǎi", + "26948": "ǎi", + "26949": "yǔ", + "2694A": "chì", + "2694D": "jìng", + "2694E": "zhì", + "2694F": "zhì", + "26950": "zhì", + "26951": "jú", + "26956": "hán", + "2695A": "pīng", + "2695D": "yǎo", + "26963": "yóu", + "26964": "pīng", + "26966": "mò", + "2696C": "zuò", + "2696D": "pò", + "2696F": "xué", + "26970": "kuáng", + "26971": "yì", + "26972": "pò", + "2697B": "zhuì", + "26983": "ní", + "26984": "qiǔ", + "26985": "còu", + "2698C": "yǎo", + "26991": "fén", + "26995": "xiá", + "26997": "jiāng", + "26998": "chā", + "2699B": "xiào", + "2699C": "chā", + "269A2": "chéng", + "269A3": "cuì", + "269A7": "qióng", + "269A9": "yù", + "269AB": "yú", + "269AF": "wèn", + "269B1": "chā", + "269B2": "yǔ", + "269B9": "zuó", + "269BA": "dǎo", + "269BD": "juàn", + "269BE": "dǎo", + "269BF": "yīng", + "269C1": "fěng", + "269C5": "wèng", + "269C8": "jìn", + "269C9": "qì", + "269CB": "qìn", + "269CD": "kuò", + "269CF": "tān", + "269D0": "xiān", + "269D2": "tiān", + "269D4": "kuò", + "269D6": "tiàn", + "269D8": "hú", + "269D9": "zhū", + "269DA": "zhān", + "269DB": "tà", + "269DD": "tiān", + "269DE": "tà", + "269DF": "tà", + "269E0": "huá", + "269E1": "yǎn", + "269E2": "tiè", + "269E4": "tiè", + "269E5": "tà", + "269EC": "huài", + "269EE": "jiá", + "269EF": "qì", + "269F1": "tà", + "269F4": "tān", + "269F5": "huà", + "269F8": "zhuàn", + "269F9": "huā", + "269FC": "lán", + "26A06": "zūn", + "26A07": "yì", + "26A08": "fú", + "26A09": "wù", + "26A0B": "fú", + "26A0D": "dīng", + "26A0E": "tà", + "26A16": "chào", + "26A19": "rì", + "26A1A": "quǎn", + "26A1C": "gē", + "26A21": "fú", + "26A22": "dì", + "26A23": "diāo", + "26A24": "yǒng", + "26A26": "jià", + "26A29": "lóng", + "26A2C": "yǒng", + "26A2D": "pí", + "26A2F": "huó", + "26A30": "qióng", + "26A32": "fán", + "26A33": "wú", + "26A34": "tóng", + "26A35": "háng", + "26A38": "tān", + "26A3E": "hēng", + "26A44": "tiāo", + "26A48": "zhōu", + "26A4B": "bài", + "26A4C": "xiè", + "26A4D": "dāo", + "26A4F": "jīn", + "26A55": "hū", + "26A56": "bēi", + "26A58": "dìng", + "26A5C": "nuó", + "26A5D": "wèi", + "26A5E": "yú", + "26A60": "xīng", + "26A61": "fú", + "26A62": "xiàn", + "26A63": "qì", + "26A64": "tū", + "26A67": "jí", + "26A69": "yìng", + "26A6B": "dèng", + "26A6C": "wēi", + "26A6D": "xī", + "26A6F": "pái", + "26A71": "shéng", + "26A72": "yǒu", + "26A74": "ái", + "26A75": "jiàn", + "26A77": "gōu", + "26A78": "ruò", + "26A7C": "gòng", + "26A7F": "shà", + "26A80": "táng", + "26A87": "lù", + "26A88": "áo", + "26A8A": "qì", + "26A8B": "xiū", + "26A8D": "dāi", + "26A91": "fá", + "26A92": "wèi", + "26A94": "dùn", + "26A95": "liáo", + "26A96": "fān", + "26A97": "huáng", + "26A98": "jué", + "26A99": "tà", + "26A9A": "zùn", + "26A9B": "ráo", + "26A9C": "cān", + "26A9D": "téng", + "26AA0": "huà", + "26AA1": "xū", + "26AA3": "zhān", + "26AA7": "gǎn", + "26AAA": "péng", + "26AAB": "cān", + "26AAC": "xiē", + "26AAD": "dá", + "26AB1": "jì", + "26AB6": "lǐ", + "26AB9": "pán", + "26ABD": "lóng", + "26ABE": "lì", + "26ABF": "xí", + "26AC0": "téng", + "26AC3": "líng", + "26AC8": "lǐ", + "26AC9": "rán", + "26ACA": "líng", + "26ACE": "gǔn", + "26AD4": "pō", + "26AD5": "mò", + "26AD6": "pāi", + "26AD9": "bà", + "26AE1": "qí", + "26AE4": "yán", + "26AEA": "wà", + "26AEB": "ǎng", + "26AED": "mìng", + "26AEE": "mǐn", + "26AEF": "xùn", + "26AF0": "méng", + "26AF3": "guǎi", + "26AF6": "jiāo", + "26AFB": "gǎi", + "26B01": "cái", + "26B02": "wù", + "26B03": "zhé", + "26B04": "rěn", + "26B05": "kōu", + "26B14": "zhǎo", + "26B15": "zhōng", + "26B16": "qiú", + "26B17": "guō", + "26B18": "gōng", + "26B19": "pū", + "26B1A": "hù", + "26B1B": "miǎn", + "26B1E": "tiān", + "26B23": "wǎng", + "26B38": "zhú", + "26B39": "dá", + "26B3A": "xiòng", + "26B3B": "ná", + "26B3E": "juān", + "26B41": "niǎn", + "26B48": "hù", + "26B49": "shā", + "26B5C": "zhī", + "26B5F": "tā", + "26B61": "sī", + "26B65": "yì", + "26B6D": "qióng", + "26B6E": "zhì", + "26B6F": "lǚ", + "26B70": "rú", + "26B72": "qí", + "26B73": "yǔ", + "26B74": "zhōu", + "26B75": "yáng", + "26B76": "xiǎn", + "26B77": "móu", + "26B78": "chóu", + "26B79": "huī", + "26B7A": "jiū", + "26B7B": "jiù", + "26B7C": "piǎo", + "26B81": "jiào", + "26B83": "guāi", + "26B85": "mò", + "26B90": "xī", + "26B91": "pú", + "26BAF": "jì", + "26BB6": "wěn", + "26BB7": "bèi", + "26BB8": "yǐ", + "26BB9": "fú", + "26BBA": "sī", + "26BBB": "juān", + "26BBC": "jì", + "26BBE": "nì", + "26BC0": "bèn", + "26BC5": "xù", + "26BC8": "qǐn", + "26BC9": "bó", + "26BCC": "wáng", + "26BCD": "zhè", + "26BCF": "wò", + "26BD0": "sháo", + "26BD1": "zào", + "26BD2": "yǎng", + "26BD5": "sòng", + "26BD6": "niè", + "26BDB": "bì", + "26BE3": "cú", + "26BE4": "qiāng", + "26BEA": "xiào", + "26BEB": "zhī", + "26BEC": "shé", + "26BEF": "zhì", + "26BF0": "pēng", + "26C0F": "diào", + "26C16": "wò", + "26C18": "zhǐ", + "26C19": "bì", + "26C1B": "fén", + "26C25": "bāng", + "26C2A": "qiú", + "26C2B": "nǐ", + "26C2C": "bó", + "26C2D": "dùn", + "26C2F": "shǐ", + "26C30": "xū", + "26C31": "cháng", + "26C32": "xū", + "26C33": "yé", + "26C34": "mí", + "26C38": "xīn", + "26C39": "zhuó", + "26C3A": "fù", + "26C3D": "pǐ", + "26C3E": "xuè", + "26C40": "yù", + "26C41": "xián", + "26C42": "yù", + "26C43": "yú", + "26C45": "jū", + "26C46": "tā", + "26C47": "kōng", + "26C4A": "zhēng", + "26C4B": "méng", + "26C4C": "gāng", + "26C52": "mù", + "26C53": "xǐ", + "26C54": "bì", + "26C56": "fù", + "26C5C": "xiào", + "26C60": "jiū", + "26C63": "gǒu", + "26C70": "chí", + "26C71": "jiū", + "26C72": "jiū", + "26C75": "shā", + "26C77": "fēi", + "26CAB": "fú", + "26CAF": "wàn", + "26CB0": "xū", + "26CB1": "bō", + "26CC1": "hào", + "26CC3": "xié", + "26CC4": "pián", + "26CC5": "yǔ", + "26CC7": "tián", + "26CC8": "pí", + "26CCA": "shǐ", + "26CCB": "kuǎi", + "26CCC": "jī", + "26CCF": "zhā", + "26CD0": "nài", + "26CD1": "mǒu", + "26CD3": "fú", + "26CD4": "dù", + "26CD7": "shěng", + "26CD8": "chá", + "26CDA": "chí", + "26CDB": "guǐ", + "26CDC": "mín", + "26CDD": "tāng", + "26CDE": "bài", + "26CDF": "qiāng", + "26CE1": "zhuó", + "26CE2": "wèi", + "26CE3": "xún", + "26CE5": "miǎo", + "26CE6": "zāi", + "26CE7": "yóu", + "26CE9": "yòu", + "26CEB": "shān", + "26CEC": "hé", + "26CED": "lǚ", + "26CEE": "zhí", + "26CF2": "jìng", + "26CF3": "zhēn", + "26CF6": "méng", + "26CF7": "yóu", + "26CF9": "wò", + "26CFA": "bá", + "26CFD": "juàn", + "26CFE": "rú", + "26CFF": "còu", + "26D00": "zhī", + "26D09": "hú", + "26D0A": "yāng", + "26D0C": "jùn", + "26D0D": "shé", + "26D0E": "kòu", + "26D11": "qián", + "26D14": "méng", + "26D1A": "tiáo", + "26D50": "niè", + "26D5F": "chí", + "26D61": "xiōng", + "26D63": "hùn", + "26D66": "dí", + "26D67": "láng", + "26D69": "zāo", + "26D6A": "cè", + "26D6B": "suǒ", + "26D6C": "zù", + "26D6D": "suī", + "26D6F": "xiá", + "26D71": "xiè", + "26D74": "jié", + "26D75": "yóu", + "26D77": "gòu", + "26D78": "gěng", + "26D7C": "jùn", + "26D7D": "huǎng", + "26D7E": "jí", + "26D7F": "pōu", + "26D80": "wū", + "26D82": "yì", + "26D85": "nǎi", + "26D87": "rǒng", + "26D88": "nán", + "26D8A": "píng", + "26D8B": "shàn", + "26D8C": "diāo", + "26D8D": "jí", + "26D8E": "huā", + "26D8F": "duì", + "26D90": "kǒng", + "26D91": "tà", + "26D93": "hòng", + "26D95": "shū", + "26D99": "héng", + "26D9A": "fěn", + "26DB2": "kòu", + "26DD9": "nián", + "26DDD": "chú", + "26DE6": "qiàng", + "26DF2": "xì", + "26DF3": "hú", + "26DF4": "sòng", + "26DF5": "wò", + "26DF7": "hài", + "26DF8": "rú", + "26DF9": "méng", + "26DFB": "sǎn", + "26DFD": "wú", + "26DFF": "yóu", + "26E01": "tān", + "26E02": "shēn", + "26E06": "qǐ", + "26E08": "guó", + "26E09": "qià", + "26E0A": "xiān", + "26E0F": "suī", + "26E10": "lù", + "26E13": "qī", + "26E14": "diāo", + "26E17": "qí", + "26E18": "jiá", + "26E19": "yóu", + "26E1A": "xí", + "26E1B": "cháo", + "26E21": "mì", + "26E22": "lòu", + "26E23": "bǐ", + "26E2A": "péi", + "26E2E": "zhēn", + "26E2F": "shēn", + "26E30": "chǎn", + "26E31": "fù", + "26E36": "qū", + "26E37": "sī", + "26E3A": "zuī", + "26E6B": "zhào", + "26E7D": "pí", + "26E80": "còu", + "26E86": "gāo", + "26E87": "dú", + "26E89": "fū", + "26E8A": "guān", + "26E8B": "sǎo", + "26E8C": "sǒu", + "26E8D": "jiǎn", + "26E8E": "póu", + "26E90": "cán", + "26E91": "bèng", + "26E92": "mòu", + "26E93": "zhāo", + "26E94": "xiáo", + "26E96": "jú", + "26E97": "shū", + "26E98": "jiǎn", + "26E99": "lí", + "26E9B": "chuàn", + "26E9C": "lào", + "26E9E": "hè", + "26E9F": "hú", + "26EA0": "gū", + "26EA1": "zhǎng", + "26EA2": "jié", + "26EA3": "xiàng", + "26EA5": "dū", + "26EA6": "hán", + "26EA7": "jiá", + "26EA8": "xiàng", + "26EA9": "jí", + "26EAA": "shǔ", + "26EAB": "làng", + "26EAC": "jī", + "26EAD": "shān", + "26EB0": "tāo", + "26EB1": "zī", + "26EB2": "shuàn", + "26EB4": "jí", + "26EB5": "chù", + "26EB6": "jì", + "26EB7": "shēn", + "26EB8": "lìn", + "26EB9": "liáo", + "26EBB": "sǎn", + "26EBD": "ǎn", + "26EBE": "ruǎn", + "26EC0": "tí", + "26EC1": "dàn", + "26EC3": "huán", + "26EC5": "sà", + "26F06": "ruí", + "26F07": "wū", + "26F08": "jù", + "26F09": "huán", + "26F0A": "léng", + "26F0B": "lù", + "26F0E": "tān", + "26F0F": "zēng", + "26F13": "qián", + "26F17": "xī", + "26F21": "cǐ", + "26F22": "shé", + "26F27": "sà", + "26F2A": "mào", + "26F2B": "qú", + "26F2D": "bó", + "26F2E": "gǎn", + "26F30": "qiè", + "26F31": "juàn", + "26F32": "dāng", + "26F33": "cháng", + "26F34": "yáng", + "26F35": "hé", + "26F37": "jī", + "26F39": "bǐng", + "26F3B": "méi", + "26F3F": "dūn", + "26F40": "ǎo", + "26F41": "jīng", + "26F42": "lù", + "26F43": "miàn", + "26F44": "diàn", + "26F45": "hè", + "26F47": "jiān", + "26F4A": "huá", + "26F4B": "gōu", + "26F4E": "lù", + "26F4F": "fú", + "26F50": "huǐ", + "26F52": "zéi", + "26F54": "jìn", + "26F55": "sī", + "26F56": "qūn", + "26F5C": "dàn", + "26F5E": "wàn", + "26F5F": "biǎn", + "26F64": "jiá", + "26F6B": "dǎn", + "26F6C": "jiū", + "26F6D": "xián", + "26F6E": "bó", + "26F8F": "xiá", + "26F91": "biāo", + "26F95": "pò", + "26F98": "sǎo", + "26F99": "bèi", + "26F9A": "shà", + "26F9B": "wěi", + "26F9D": "cāng", + "26F9E": "lù", + "26FA9": "dàn", + "26FAB": "gǔ", + "26FAC": "zā", + "26FAD": "bǎng", + "26FAE": "gàn", + "26FB1": "chāo", + "26FB2": "jì", + "26FB3": "liē", + "26FB5": "qióng", + "26FB6": "jiàn", + "26FB7": "lù", + "26FB8": "duān", + "26FB9": "suān", + "26FBA": "yáo", + "26FBB": "yǐn", + "26FBD": "tà", + "26FBE": "yáo", + "26FBF": "jīng", + "26FC0": "chú", + "26FC1": "fú", + "26FC2": "yuán", + "26FC3": "shǎo", + "26FC5": "bìng", + "26FC6": "dàng", + "26FC7": "shì", + "26FCA": "lú", + "26FCB": "qiè", + "26FCC": "luó", + "26FCD": "pò", + "26FCF": "méng", + "26FD0": "jié", + "26FD3": "jī", + "26FD6": "lù", + "27004": "chàng", + "27005": "miè", + "27006": "méng", + "27007": "jiǎn", + "2700A": "cǎi", + "2700C": "sù", + "27014": "hè", + "27015": "sà", + "27017": "zī", + "27018": "kēng", + "27019": "gěng", + "2701A": "sī", + "27020": "tí", + "27021": "zhàn", + "27022": "xiè", + "27023": "shuí", + "27024": "chǐ", + "27025": "yōu", + "27026": "lǔ", + "27027": "mèng", + "27028": "liè", + "27029": "sì", + "2702C": "xī", + "2702D": "fán", + "2702E": "fū", + "2702F": "shěn", + "27030": "tí", + "27031": "chài", + "27032": "yuè", + "27034": "fū", + "27035": "jiàn", + "27036": "dì", + "2703A": "xié", + "2703B": "dān", + "2703F": "zhí", + "27043": "xù", + "27048": "niè", + "27049": "fàn", + "2704A": "méng", + "2704B": "mǐn", + "2707E": "lóu", + "2707F": "dú", + "27081": "zhàn", + "27082": "jiàn", + "27083": "hàn", + "27084": "dàn", + "27085": "sēn", + "27086": "jiàn", + "27087": "tán", + "27088": "jiǎo", + "27089": "pó", + "2708B": "píng", + "2708D": "zhuàn", + "2708F": "liáo", + "27090": "zì", + "27092": "zhuó", + "27094": "hù", + "27099": "xì", + "2709B": "méng", + "2709C": "jù", + "2709D": "miè", + "2709E": "xián", + "270A0": "kuì", + "270A1": "méng", + "270A2": "jiān", + "270A6": "nóu", + "270A8": "dì", + "270A9": "sāo", + "270CF": "chù", + "270D0": "zhí", + "270D1": "qián", + "270D2": "lǚ", + "270D4": "zhuó", + "270D8": "zuò", + "270D9": "hán", + "270DA": "suǐ", + "270DB": "gòu", + "270DD": "chǒu", + "270DE": "jì", + "270DF": "yì", + "270E0": "yú", + "270E8": "nóu", + "270E9": "nǐ", + "270EA": "ruò", + "270EE": "lín", + "270F1": "níng", + "2710D": "qiáo", + "2710E": "yáo", + "2710F": "fù", + "27110": "shuāng", + "27111": "kuì", + "27112": "qú", + "27113": "dǒng", + "27114": "shǔ", + "2711A": "lí", + "2711B": "jú", + "2711C": "ruǐ", + "27120": "zhá", + "27124": "xiāo", + "27138": "mén", + "27139": "shí", + "2713A": "diān", + "2713B": "lì", + "2713C": "dèng", + "2713D": "zàn", + "2713F": "luó", + "27140": "cán", + "27143": "āo", + "27146": "jiǎn", + "27148": "diào", + "2714B": "yíng", + "27156": "yì", + "27157": "dǎng", + "27158": "nóu", + "2715A": "yuè", + "2716E": "lǐ", + "2716F": "lí", + "27170": "hù", + "27172": "yòu", + "2717A": "nàng", + "27182": "chèn", + "27189": "fēng", + "2718A": "biē", + "2718F": "mǎn", + "27190": "gàn", + "27191": "huò", + "27193": "cū", + "27195": "yǒu", + "27198": "yòu", + "2719C": "xū", + "271A1": "xù", + "271A2": "hǔ", + "271A3": "lú", + "271A5": "xiá", + "271A6": "yì", + "271AE": "hǔ", + "271AF": "hù", + "271B0": "zǐ", + "271B7": "gōng", + "271B8": "tuī", + "271B9": "wū", + "271BA": "líng", + "271BB": "gū", + "271BC": "zhōng", + "271C4": "lú", + "271C8": "zù", + "271CC": "tóng", + "271CD": "xiā", + "271CE": "hé", + "271D3": "yuè", + "271D9": "nán", + "271DA": "bó", + "271DB": "hū", + "271DC": "qì", + "271DD": "shú", + "271DE": "qiāng", + "271DF": "zhōu", + "271E0": "yào", + "271E1": "gū", + "271E5": "bān", + "271E6": "kǎn", + "271EE": "hé", + "271EF": "jì", + "271F0": "hú", + "271F1": "yán", + "271F6": "chūn", + "271F7": "dǐng", + "271F8": "qiū", + "271F9": "hóu", + "271FC": "hào", + "271FF": "zù", + "27201": "xián", + "27204": "xià", + "27205": "xì", + "27208": "sè", + "2720C": "gé", + "2720D": "xì", + "27211": "gé", + "27214": "lǚ", + "27216": "gé", + "27217": "kè", + "27219": "shòu", + "2721A": "zhù", + "2721C": "téng", + "2721D": "yà", + "2721E": "nì", + "27226": "luò", + "27227": "suī", + "2722A": "chǎn", + "2722D": "wù", + "2722F": "yū", + "27239": "zǎo", + "2723B": "yì", + "2723C": "xī", + "2723D": "hóng", + "2723E": "quán", + "2723F": "wǎng", + "27240": "chǐ", + "27241": "xì", + "27242": "tiǎn", + "27243": "yǔn", + "27245": "yī", + "27246": "jí", + "27247": "huī", + "27248": "fóu", + "2724A": "fǔ", + "2724D": "jí", + "2724E": "xuán", + "27251": "tài", + "27253": "dù", + "27257": "yuán", + "2725B": "dì", + "2725E": "zhǔ", + "2725F": "tāi", + "27261": "rǒng", + "27262": "xué", + "27263": "yù", + "27264": "fàn", + "27265": "běi", + "27267": "qǔ", + "27269": "bù", + "2726A": "jiā", + "2726B": "zhá", + "2726D": "nǔ", + "2726E": "shé", + "27272": "lì", + "27284": "guǐ", + "27285": "guǎi", + "27287": "dài", + "2728F": "gāi", + "27292": "cì", + "27294": "yǎn", + "27295": "sōng", + "27296": "shì", + "27298": "kù", + "27299": "zhǐ", + "2729A": "tóng", + "2729B": "qú", + "2729C": "è", + "2729E": "xíng", + "2729F": "rú", + "272A0": "yú", + "272A3": "yì", + "272A4": "yì", + "272A5": "xù", + "272A6": "fǒu", + "272A7": "gé", + "272AC": "hé", + "272AD": "yīn", + "272AF": "hòng", + "272B1": "duǒ", + "272BD": "xíng", + "272BE": "fán", + "272C9": "qī", + "272CA": "shā", + "272CC": "dù", + "272CD": "dì", + "272CE": "lí", + "272CF": "yì", + "272D0": "xí", + "272D1": "gěng", + "272D2": "tóng", + "272D3": "kào", + "272D4": "hòng", + "272D5": "kǔn", + "272D6": "niè", + "272D7": "chí", + "272D8": "tí", + "272DA": "tóng", + "272E0": "lí", + "272E1": "nà", + "272F1": "zhān", + "272F2": "běi", + "27301": "tiáo", + "27303": "zā", + "27304": "è", + "27305": "shòu", + "27306": "kōng", + "27307": "péng", + "27308": "fù", + "27309": "lù", + "2730A": "xiè", + "2730B": "xiè", + "2730C": "xiū", + "2730D": "lù", + "2730E": "tiǎn", + "2730F": "tà", + "27310": "cì", + "27311": "qū", + "27313": "fù", + "27314": "zhī", + "27316": "xiè", + "27317": "zǒu", + "27318": "fèi", + "27319": "mín", + "2731A": "xīng", + "2731D": "tóng", + "2731E": "qí", + "27320": "piāo", + "27322": "suì", + "27323": "ěr", + "27327": "hǔ", + "2733B": "sōng", + "2733D": "biē", + "2733E": "dīng", + "2733F": "bǎn", + "27340": "shī", + "27341": "xiè", + "27342": "xiáo", + "27343": "fěi", + "27352": "chuǎn", + "27353": "shuài", + "27354": "yāo", + "27355": "jué", + "27356": "shěng", + "27358": "yōu", + "27359": "fàn", + "2735C": "kuí", + "2735D": "dì", + "2735F": "máo", + "27360": "jié", + "27362": "yán", + "27365": "wēi", + "27368": "sāng", + "27369": "jié", + "2736A": "yú", + "2736B": "wèi", + "2736C": "è", + "2736D": "quán", + "2736E": "jiǒng", + "2736F": "féng", + "27370": "lóng", + "27371": "dié", + "27372": "pián", + "27374": "liàn", + "27375": "hú", + "27376": "lǜ", + "2737F": "diàn", + "27383": "cuì", + "27384": "móu", + "27395": "wáng", + "27396": "juān", + "27397": "kē", + "27398": "yán", + "27399": "jiǎo", + "273A1": "gōng", + "273A3": "róng", + "273A4": "sūn", + "273A5": "shàn", + "273A8": "chí", + "273AA": "qí", + "273AB": "suǒ", + "273AD": "yè", + "273AE": "zǎo", + "273AF": "quē", + "273B0": "zhǎn", + "273B1": "bā", + "273B2": "zú", + "273B3": "suǒ", + "273B4": "zhé", + "273B5": "xì", + "273B7": "chǔ", + "273B8": "jiǎo", + "273B9": "zuì", + "273BA": "gē", + "273BB": "wù", + "273BE": "lüè", + "273BF": "jí", + "273C2": "xié", + "273C3": "xié", + "273C6": "dǒu", + "273CB": "qiū", + "273D1": "píng", + "273D3": "liú", + "273E5": "jié", + "273E7": "huì", + "273EB": "shà", + "273F8": "zhí", + "273F9": "ài", + "273FA": "xù", + "273FB": "bì", + "273FD": "yē", + "273FE": "nì", + "273FF": "zhú", + "27401": "sù", + "27403": "xié", + "27404": "yù", + "27405": "qū", + "27408": "zú", + "27409": "zhī", + "2740A": "zhāng", + "2740B": "lüè", + "2740C": "wěi", + "2740D": "chōng", + "2740E": "mì", + "27410": "jī", + "27412": "sù", + "27413": "yě", + "27414": "xí", + "27415": "tuán", + "27416": "lián", + "27417": "xuán", + "27419": "wù", + "2741F": "máo", + "2742C": "hóng", + "2742F": "lüè", + "27430": "dú", + "27431": "cóng", + "27432": "chán", + "27433": "lù", + "27434": "sù", + "27440": "lüè", + "27446": "zhōng", + "27447": "lí", + "27448": "fèi", + "2744A": "jǐng", + "2744B": "kuì", + "2744C": "yì", + "2744D": "huá", + "2744E": "cuì", + "27450": "yù", + "27451": "běng", + "27452": "tūn", + "27453": "shǔ", + "27454": "dài", + "27455": "wū", + "27456": "cì", + "27457": "nìng", + "27458": "dàng", + "27459": "zú", + "2745A": "hán", + "2745C": "pí", + "2745D": "chuàn", + "27460": "dù", + "27461": "pá", + "27464": "zhū", + "27466": "xié", + "27467": "zhé", + "27468": "qiè", + "27469": "xuān", + "2746B": "sào", + "27480": "bì", + "27482": "fù", + "27488": "lì", + "2748E": "é", + "27490": "yē", + "27491": "shǔ", + "27493": "sè", + "27495": "qī", + "27496": "guò", + "27497": "sè", + "27499": "fù", + "2749A": "máo", + "2749C": "léi", + "2749D": "zhān", + "274A8": "chài", + "274AD": "wèi", + "274BD": "léi", + "274BF": "zéi", + "274C0": "yīng", + "274C1": "ài", + "274C2": "xiē", + "274C4": "bì", + "274CB": "chán", + "274CE": "pí", + "274CF": "cóng", + "274D0": "liè", + "274D1": "qí", + "274D3": "jì", + "274D4": "jīng", + "274D5": "dōng", + "274D6": "féi", + "274D7": "yí", + "274D8": "tuán", + "274E8": "měng", + "274E9": "cán", + "274EA": "yá", + "274F2": "yǎng", + "274F4": "tíng", + "274F8": "zhí", + "274FA": "xiè", + "274FB": "lǜ", + "274FD": "lì", + "274FF": "máo", + "27502": "xiá", + "27505": "sòu", + "27516": "sū", + "27517": "xuè", + "2751D": "lì", + "2751E": "yuán", + "27521": "zhǎn", + "27523": "tà", + "27524": "xuán", + "27525": "wèi", + "27526": "yè", + "27527": "páng", + "27528": "máo", + "27529": "tí", + "2752A": "pín", + "2752C": "dù", + "2752D": "qiú", + "2752E": "yǐ", + "27533": "tuó", + "27534": "chài", + "27537": "jìn", + "2753C": "é", + "27543": "chán", + "27544": "yīng", + "27545": "líng", + "27547": "xiǎn", + "27549": "qī", + "2754B": "yuè", + "2754C": "lüè", + "2754D": "yíng", + "2754E": "qú", + "27552": "fěi", + "27553": "zī", + "27559": "qīng", + "2755D": "níng", + "2755E": "wèi", + "2755F": "shuāng", + "27561": "fù", + "27564": "mò", + "27565": "mò", + "27566": "tuó", + "27567": "chài", + "27568": "zàng", + "2756E": "lí", + "2756F": "lí", + "27571": "xiá", + "27572": "juǎn", + "27574": "nán", + "27575": "mì", + "27578": "huáng", + "2757A": "shuàng", + "2757C": "xǔ", + "2757F": "fěi", + "27581": "xiè", + "27586": "tà", + "27587": "yǒng", + "27589": "zhǎn", + "27591": "qiáng", + "27592": "náng", + "27594": "lìn", + "27598": "luán", + "27599": "xiǎn", + "2759A": "fú", + "2759C": "líng", + "275A0": "sāo", + "275A2": "huì", + "275A8": "tíng", + "275AA": "qíng", + "275AC": "huāng", + "275AE": "àn", + "275B5": "mǎn", + "275B7": "nì", + "275BB": "guó", + "275BC": "ǒu", + "275BF": "xiàng", + "275C1": "jīn", + "275C6": "zhēng", + "275C8": "n", + "275CB": "sàn", + "275CC": "hù", + "275CE": "zú", + "275CF": "huǐ", + "275D2": "jī", + "275D6": "yè", + "275E6": "xíng", + "275E9": "là", + "275EA": "yù", + "275EB": "jué", + "275F1": "shù", + "275F2": "zhēng", + "275F4": "yǒng", + "275F6": "gē", + "275F8": "jiàn", + "275F9": "xìn", + "275FC": "huī", + "275FF": "shuài", + "27602": "chōng", + "27603": "háng", + "27608": "liǎo", + "2760D": "jiāng", + "2760F": "gōng", + "27611": "zhuó", + "27617": "qǐ", + "2761C": "qiān", + "2761E": "dǒu", + "2761F": "pō", + "27622": "hù", + "27625": "niǔ", + "27627": "qì", + "27628": "diāo", + "27629": "diāo", + "2762B": "lì", + "2762E": "xiōng", + "2763D": "ná", + "2763F": "zhēng", + "27640": "là", + "27641": "zhì", + "27643": "ě", + "27644": "bō", + "27645": "pō", + "27646": "xū", + "27647": "yòng", + "27648": "cí", + "27649": "lì", + "2764C": "páo", + "2764F": "xiù", + "2765B": "pù", + "2765D": "ché", + "2765E": "qì", + "27661": "yì", + "27663": "tí", + "27664": "duǒ", + "27665": "lóng", + "27667": "jiàn", + "2766D": "zhàn", + "2766E": "yuàn", + "27676": "yú", + "27678": "gēng", + "2767A": "hòu", + "2767E": "qǐ", + "27680": "mù", + "27681": "huàn", + "27682": "lòng", + "27683": "xì", + "27684": "é", + "27685": "lǎng", + "27686": "fèi", + "27687": "wǎn", + "27689": "cūn", + "2768B": "péng", + "2768F": "cuò", + "27690": "wēng", + "276A1": "gǎo", + "276A5": "cuì", + "276A8": "qì", + "276A9": "lí", + "276AA": "qiè", + "276AB": "qiàn", + "276AC": "kōng", + "276AD": "běng", + "276AF": "shòu", + "276B7": "wēi", + "276C4": "shān", + "276CF": "zī", + "276D2": "tì", + "276D3": "qiān", + "276D4": "dú", + "276D7": "tú", + "276DA": "wēi", + "276DE": "hú", + "276DF": "xīng", + "276E1": "shān", + "276E2": "zhǐ", + "276E7": "chǐ", + "276F8": "zhòu", + "276F9": "wēng", + "276FA": "chí", + "276FB": "suǒ", + "276FC": "xiè", + "276FE": "kè", + "27701": "shài", + "27702": "shī", + "27703": "shòu", + "27705": "jiè", + "27709": "gǎo", + "2770A": "lǚ", + "27714": "xiè", + "2771A": "zhǐ", + "2771E": "mán", + "27720": "shuài", + "27721": "kè", + "27723": "diǎo", + "27724": "yī", + "27726": "sù", + "27727": "chuāng", + "27731": "cuì", + "27732": "tuò", + "27735": "xiè", + "2773D": "xuán", + "27742": "hè", + "27743": "jué", + "27746": "tì", + "27747": "fèi", + "27749": "zhǐ", + "2774A": "shì", + "2774B": "tuí", + "2774E": "chōng", + "27750": "tì", + "27751": "zhàn", + "27752": "héng", + "27754": "qú", + "27755": "wéi", + "27757": "dūn", + "27758": "bào", + "2775C": "liáo", + "27764": "sī", + "2776A": "biǎo", + "2776B": "xiè", + "2776C": "bié", + "2776E": "cǒng", + "27772": "jù", + "27773": "hé", + "27777": "kuì", + "27778": "yōng", + "27780": "shù", + "2778D": "niè", + "2778F": "yú", + "27790": "zhuó", + "27791": "méng", + "27792": "hú", + "27795": "liè", + "2779D": "jiē", + "2779E": "xióng", + "277A3": "yǎn", + "277A9": "jié", + "277AA": "là", + "277AB": "shù", + "277AC": "jié", + "277AD": "léi", + "277B0": "zú", + "277B2": "shì", + "277B8": "wéi", + "277B9": "dū", + "277BA": "sù", + "277C3": "xié", + "277C4": "ráng", + "277CC": "luò", + "277D1": "qiān", + "277D8": "nàng", + "277D9": "líng", + "277DC": "jì", + "277E0": "mìng", + "277E3": "gǔ", + "277E8": "xuán", + "277EC": "xū", + "277F1": "bó", + "277FC": "wēi", + "27802": "kū", + "27806": "wǎn", + "27808": "chà", + "2780A": "mào", + "2780B": "kè", + "2780E": "cì", + "27812": "xiàn", + "27813": "mò", + "2781A": "hūn", + "2781B": "chàn", + "2781C": "shī", + "2781D": "zhěn", + "2781E": "è", + "2781F": "mí", + "27821": "shī", + "27822": "qū", + "27823": "shū", + "27825": "cī", + "27826": "yǎn", + "27829": "hū", + "2782A": "qī", + "2782B": "zhì", + "2782C": "huāng", + "27834": "zhǐ", + "27836": "yǒu", + "2783C": "gào", + "2783D": "yǎo", + "2783E": "pōu", + "27847": "yí", + "27848": "chèng", + "27849": "jì", + "2784B": "ǎi", + "2784D": "dòng", + "2784F": "suì", + "27851": "jiù", + "27858": "qì", + "27859": "lián", + "2785A": "xuǎn", + "2785C": "liǎo", + "27861": "yùn", + "27862": "xuǎn", + "27863": "cóu", + "27864": "piān", + "27866": "kuí", + "27868": "tí", + "27869": "huǎn", + "2786A": "dān", + "2786B": "guì", + "2786C": "chēn", + "2786E": "shǎng", + "2786F": "jì", + "27874": "liàn", + "27875": "kān", + "27876": "shèng", + "27878": "dōu", + "27879": "yóu", + "2787A": "qí", + "2787C": "xiǎo", + "27882": "yì", + "27883": "lóu", + "27886": "chuāng", + "2788B": "lào", + "2788C": "gāo", + "27890": "zēng", + "27892": "wéi", + "27896": "jiān", + "2789B": "yīng", + "2789C": "fán", + "2789D": "lì", + "2789E": "qiān", + "278A2": "yào", + "278A6": "kuī", + "278A7": "wéi", + "278A9": "què", + "278AC": "xiǎo", + "278AD": "què", + "278B0": "hū", + "278B5": "duō", + "278B6": "chù", + "278B9": "shēn", + "278BC": "zhuó", + "278BD": "é", + "278BE": "jì", + "278C1": "tán", + "278C3": "pā", + "278CB": "jiè", + "278CC": "qiào", + "278D1": "qián", + "278D2": "jù", + "278D5": "qiú", + "278D6": "tuó", + "278DA": "nuò", + "278DB": "sì", + "278DF": "yí", + "278E1": "gǔ", + "278E2": "hùn", + "278E3": "pá", + "278E4": "zī", + "278E6": "jiāo", + "278E9": "xǐ", + "278EA": "shǎo", + "278EC": "yí", + "278ED": "zhì", + "278F5": "lùn", + "278F7": "zhōu", + "278F8": "jué", + "278F9": "tán", + "278FA": "nuò", + "278FB": "jù", + "278FC": "hú", + "278FE": "zhì", + "27903": "bī", + "2790D": "chì", + "2790E": "xuān", + "2790F": "jí", + "27910": "guǎ", + "27911": "jú", + "27912": "wò", + "27913": "tuó", + "27915": "qiú", + "27916": "wēi", + "27917": "duān", + "27919": "shòu", + "2791B": "zhěn", + "2791C": "nè", + "2791F": "xì", + "27920": "zhé", + "27921": "zhì", + "27923": "ná", + "27928": "jiān", + "2792E": "yáo", + "2792F": "guó", + "27932": "dǐ", + "27934": "huò", + "27935": "jīng", + "2793C": "jué", + "2793D": "yuè", + "27944": "jí", + "27946": "sù", + "27948": "jiān", + "2794A": "kūn", + "2794B": "wò", + "2794C": "kuàng", + "2794D": "biāo", + "2794E": "jué", + "27951": "bì", + "27953": "chán", + "27955": "zī", + "27956": "lì", + "2795A": "fó", + "2795B": "qiǎn", + "2795C": "yǎn", + "2795E": "tàn", + "2795F": "mò", + "27963": "kòu", + "27964": "xī", + "2796E": "hù", + "2796F": "hù", + "27971": "fú", + "27974": "yàng", + "27975": "guò", + "27977": "rén", + "27978": "yìn", + "27979": "fēng", + "2797A": "jùn", + "2797C": "yún", + "2797F": "xùn", + "27981": "xì", + "2798E": "xiā", + "27991": "háng", + "2799A": "hù", + "2799D": "hū", + "2799E": "pù", + "2799F": "fān", + "279A4": "jiā", + "279A7": "yí", + "279AD": "tuō", + "279AE": "ná", + "279B8": "yín", + "279B9": "yìn", + "279C3": "jì", + "279C4": "wàng", + "279C5": "shì", + "279C6": "duī", + "279C7": "duò", + "279C9": "tuó", + "279CA": "wā", + "279CB": "lì", + "279CF": "rè", + "279D2": "cì", + "279D3": "xù", + "279D4": "zhōu", + "279D5": "zì", + "279DC": "wǎng", + "279DD": "yǎ", + "279DF": "jì", + "279E0": "chǎo", + "279E9": "jí", + "279F5": "shǎn", + "279F6": "tú", + "279F8": "bié", + "279F9": "xì", + "279FA": "pī", + "279FB": "zhà", + "279FE": "huì", + "27A00": "suō", + "27A02": "hè", + "27A04": "yuē", + "27A06": "wū", + "27A08": "líng", + "27A0A": "zhà", + "27A0B": "huá", + "27A17": "chán", + "27A1F": "è", + "27A21": "chén", + "27A27": "suì", + "27A29": "tiǎn", + "27A30": "zhì", + "27A31": "tì", + "27A32": "āo", + "27A33": "zhuó", + "27A34": "zì", + "27A35": "kē", + "27A37": "sè", + "27A38": "tián", + "27A39": "lù", + "27A3E": "shán", + "27A3F": "zhǎ", + "27A43": "chōng", + "27A45": "yàn", + "27A52": "mǔ", + "27A53": "hū", + "27A5A": "chī", + "27A5D": "sù", + "27A63": "nǎo", + "27A66": "jí", + "27A67": "duó", + "27A68": "hòu", + "27A6A": "còng", + "27A6B": "zhā", + "27A6C": "yín", + "27A6E": "xiǎo", + "27A70": "biàn", + "27A71": "bèng", + "27A72": "là", + "27A74": "chī", + "27A76": "qià", + "27A78": "ān", + "27A79": "shī", + "27A7C": "chì", + "27A85": "nù", + "27A87": "jì", + "27A93": "ǒu", + "27A95": "xiā", + "27A98": "chài", + "27A9A": "ái", + "27A9D": "shèng", + "27A9E": "hé", + "27AA0": "jí", + "27AA1": "chī", + "27AA2": "xì", + "27AA3": "zhēng", + "27AA6": "tā", + "27AA8": "mà", + "27AAB": "pī", + "27AAE": "xū", + "27AAF": "qiǎn", + "27AB9": "xià", + "27ACA": "yù", + "27AD1": "jié", + "27AD2": "xià", + "27AD3": "lǔ", + "27AD5": "qiè", + "27AD7": "chà", + "27ADB": "yàng", + "27ADC": "jì", + "27ADD": "shǎ", + "27ADE": "lòu", + "27AE0": "jī", + "27AE1": "zhì", + "27AE2": "wàng", + "27AE4": "bì", + "27AE5": "ān", + "27AE6": "yī", + "27AE7": "ān", + "27AEC": "lí", + "27AF9": "xiān", + "27AFE": "jiù", + "27AFF": "tǎn", + "27B01": "hào", + "27B02": "hè", + "27B05": "zhā", + "27B06": "zhǎn", + "27B07": "yì", + "27B08": "xì", + "27B0A": "xì", + "27B0B": "fà", + "27B0C": "yán", + "27B0F": "mǔ", + "27B15": "gū", + "27B1E": "yún", + "27B24": "zhòng", + "27B26": "chǎn", + "27B27": "chuáng", + "27B28": "huì", + "27B29": "zá", + "27B2A": "gùn", + "27B2B": "jiǎn", + "27B2C": "yá", + "27B30": "xiàng", + "27B31": "hè", + "27B43": "dàn", + "27B47": "mián", + "27B48": "níng", + "27B4A": "méng", + "27B4C": "liè", + "27B4D": "zhòu", + "27B4E": "pū", + "27B4F": "tāi", + "27B53": "yíng", + "27B54": "téng", + "27B55": "guó", + "27B5A": "qiáng", + "27B5C": "lǜ", + "27B5D": "sà", + "27B5E": "liè", + "27B5F": "chí", + "27B60": "xiě", + "27B63": "guó", + "27B64": "bào", + "27B65": "luò", + "27B66": "juàn", + "27B6A": "è", + "27B73": "hé", + "27B75": "mèi", + "27B78": "xiè", + "27B79": "pín", + "27B7B": "hān", + "27B7C": "chèn", + "27B7D": "shàn", + "27B7E": "huì", + "27B86": "yīng", + "27B88": "jiǎn", + "27B8D": "ān", + "27B91": "tà", + "27B92": "yī", + "27B93": "tuí", + "27B97": "liú", + "27B99": "zuó", + "27B9B": "lí", + "27B9D": "pín", + "27B9E": "xuè", + "27BA0": "nèn", + "27BA1": "dòu", + "27BA4": "lǎn", + "27BAA": "zhān", + "27BAB": "jué", + "27BAC": "zhēn", + "27BAD": "jí", + "27BAE": "qiān", + "27BB0": "hān", + "27BB1": "fén", + "27BB3": "hān", + "27BB4": "hóng", + "27BB5": "hé", + "27BB6": "hóu", + "27BBA": "zhàn", + "27BBB": "chóu", + "27BBC": "tài", + "27BBD": "qiàn", + "27BBF": "shè", + "27BC0": "yīng", + "27BC3": "qīn", + "27BC6": "huò", + "27BC8": "xì", + "27BC9": "hè", + "27BCA": "xì", + "27BCB": "xiā", + "27BCC": "hāo", + "27BCD": "lào", + "27BCF": "lì", + "27BD2": "chēng", + "27BD6": "jùn", + "27BD7": "xī", + "27BD8": "hǎn", + "27BDE": "dòu", + "27BE0": "dōu", + "27BE1": "wān", + "27BE4": "dōu", + "27BE5": "zài", + "27BE6": "juàn", + "27BE8": "lǒu", + "27BE9": "chù", + "27BEB": "zhēng", + "27BEF": "qí", + "27BF0": "kàn", + "27BF1": "huò", + "27BF2": "lái", + "27BFA": "gāi", + "27BFC": "shòu", + "27BFE": "dōng", + "27C03": "lóu", + "27C04": "tuān", + "27C07": "yú", + "27C08": "wù", + "27C0A": "tián", + "27C12": "guó", + "27C18": "tán", + "27C19": "qí", + "27C20": "liè", + "27C21": "lì", + "27C23": "xūn", + "27C28": "gèng", + "27C29": "tīng", + "27C2A": "hàn", + "27C2B": "chù", + "27C2D": "tún", + "27C2F": "xióng", + "27C30": "yóu", + "27C31": "mò", + "27C32": "chǐ", + "27C34": "hǔ", + "27C35": "dū", + "27C37": "mǔ", + "27C39": "nà", + "27C3B": "líng", + "27C3F": "ài", + "27C40": "xiān", + "27C44": "kǎn", + "27C45": "sì", + "27C46": "sān", + "27C4A": "yì", + "27C4F": "yì", + "27C50": "xiào", + "27C52": "zhī", + "27C53": "dòu", + "27C58": "mài", + "27C5C": "lún", + "27C5D": "jué", + "27C61": "qiāng", + "27C62": "líng", + "27C69": "pián", + "27C6A": "còu", + "27C6B": "duò", + "27C6C": "yǔ", + "27C70": "zhuō", + "27C72": "xì", + "27C73": "huài", + "27C74": "míng", + "27C75": "táng", + "27C79": "pū", + "27C7B": "mì", + "27C7C": "mán", + "27C7E": "guāi", + "27C80": "qiān", + "27C82": "lín", + "27C83": "mǐn", + "27C84": "wěi", + "27C85": "céng", + "27C87": "hù", + "27C88": "suí", + "27C8B": "jù", + "27C8C": "shà", + "27C8D": "méng", + "27C97": "wéi", + "27C98": "xī", + "27C99": "lìng", + "27C9C": "bì", + "27C9D": "wèi", + "27CA1": "lì", + "27CA2": "zhé", + "27CA4": "yóng", + "27CA5": "hú", + "27CA6": "wán", + "27CA7": "bā", + "27CA8": "jiān", + "27CAD": "zuǒ", + "27CAE": "zhǎn", + "27CAF": "bō", + "27CB0": "qiū", + "27CB1": "yāng", + "27CB4": "dōng", + "27CB5": "qú", + "27CBA": "pí", + "27CBB": "zhǎi", + "27CBE": "shān", + "27CBF": "gòu", + "27CC0": "biào", + "27CC1": "yí", + "27CC2": "fú", + "27CC4": "xìn", + "27CC5": "shì", + "27CC6": "tōng", + "27CC9": "dīng", + "27CCC": "tū", + "27CCD": "xiāo", + "27CCE": "wú", + "27CCF": "péi", + "27CD0": "huī", + "27CD5": "lái", + "27CD9": "sì", + "27CDA": "cuǐ", + "27CDB": "shà", + "27CDC": "zhǒu", + "27CDD": "zhào", + "27CDE": "wéi", + "27CDF": "lái", + "27CE0": "bì", + "27CE3": "dǒng", + "27CE6": "nǎo", + "27CE7": "xiē", + "27CE8": "rǎo", + "27CE9": "tuàn", + "27CEA": "wèi", + "27CEB": "yóu", + "27CEC": "méi", + "27CED": "yuán", + "27CEE": "zhòng", + "27CF6": "sōu", + "27CF8": "gú", + "27CF9": "shào", + "27CFB": "zhǎo", + "27CFC": "pí", + "27CFF": "tōng", + "27D01": "chī", + "27D02": "péng", + "27D03": "chán", + "27D04": "yōng", + "27D05": "shuǎng", + "27D07": "wǔ", + "27D09": "pí", + "27D0A": "huàn", + "27D0C": "fú", + "27D0E": "biào", + "27D13": "náo", + "27D15": "biào", + "27D16": "wèi", + "27D17": "yōng", + "27D19": "nǎo", + "27D1A": "guài", + "27D20": "lì", + "27D22": "xìn", + "27D23": "yán", + "27D24": "pò", + "27D25": "péi", + "27D2A": "suǒ", + "27D2C": "rèn", + "27D2D": "shǎn", + "27D32": "suǒ", + "27D38": "dān", + "27D3A": "mèn", + "27D43": "shǒu", + "27D48": "gòu", + "27D4A": "hān", + "27D4B": "shì", + "27D4C": "yǎng", + "27D4E": "gǔ", + "27D5B": "kē", + "27D5E": "jū", + "27D60": "pài", + "27D61": "cè", + "27D62": "bāo", + "27D63": "xiōng", + "27D64": "cái", + "27D67": "lǐn", + "27D68": "ài", + "27D6C": "mì", + "27D6D": "lǎi", + "27D71": "xiāo", + "27D73": "shé", + "27D7B": "huó", + "27D7C": "nì", + "27D84": "zhèng", + "27D86": "lìn", + "27D87": "zhá", + "27D8A": "yún", + "27D8D": "xù", + "27D94": "chéng", + "27D95": "wǒ", + "27D96": "xī", + "27D99": "bèi", + "27D9C": "shāng", + "27DA0": "yù", + "27DA1": "mì", + "27DB2": "duǎn", + "27DB5": "chà", + "27DB7": "zé", + "27DB8": "chèng", + "27DBA": "tíng", + "27DC5": "yí", + "27DCB": "yāo", + "27DCE": "kū", + "27DD0": "fén", + "27DD1": "xié", + "27DD2": "chèng", + "27DDB": "kuì", + "27DDF": "bīn", + "27DE1": "lóu", + "27DE5": "yì", + "27DE6": "mì", + "27DE7": "xiè", + "27DF1": "guī", + "27DF3": "luó", + "27DF6": "shàn", + "27DFE": "jú", + "27DFF": "dū", + "27E02": "xiān", + "27E05": "zhǐ", + "27E08": "bìn", + "27E15": "zhǐ", + "27E16": "zhuàn", + "27E17": "xué", + "27E18": "liàn", + "27E19": "suì", + "27E26": "làn", + "27E27": "jù", + "27E28": "mián", + "27E29": "xùn", + "27E2A": "zhàn", + "27E2B": "gùn", + "27E32": "zhì", + "27E3D": "wèi", + "27E3E": "quǎn", + "27E3F": "chài", + "27E48": "réng", + "27E4A": "yuè", + "27E4C": "zī", + "27E50": "luò", + "27E51": "guì", + "27E53": "chéng", + "27E55": "jū", + "27E56": "tiǎn", + "27E57": "wàn", + "27E5B": "zhī", + "27E5E": "nǎn", + "27E63": "hān", + "27E68": "xī", + "27E69": "lín", + "27E6C": "yān", + "27E6D": "xù", + "27E72": "hù", + "27E73": "gàn", + "27E74": "xù", + "27E76": "xì", + "27E7A": "cuì", + "27E7D": "xì", + "27E7E": "hú", + "27E85": "yān", + "27E8E": "yì", + "27E8F": "chí", + "27E90": "jué", + "27E92": "zú", + "27E9C": "jiào", + "27E9D": "yì", + "27E9F": "tǎn", + "27EA0": "chì", + "27EA1": "bá", + "27EA2": "tòu", + "27EA3": "zōng", + "27EA4": "qiú", + "27EA7": "chì", + "27EA8": "xǐ", + "27EB0": "nì", + "27EB2": "cū", + "27EB4": "wǔ", + "27EB6": "chù", + "27EB7": "sū", + "27EB8": "yóng", + "27EB9": "jǔ", + "27EBA": "bá", + "27EBC": "cǐ", + "27EBD": "dì", + "27EBE": "pǎn", + "27EBF": "chì", + "27EC1": "qiǔ", + "27EC3": "yán", + "27ECD": "zhǎi", + "27ED2": "xiàn", + "27ED3": "bèng", + "27ED4": "kuāng", + "27ED5": "qì", + "27ED6": "zhōu", + "27ED7": "jú", + "27ED8": "qiè", + "27ED9": "mò", + "27EDA": "yuán", + "27EDC": "guì", + "27EDD": "zuī", + "27EE7": "qiè", + "27EF0": "hú", + "27EF1": "qiú", + "27EF2": "hái", + "27EF3": "fù", + "27EF4": "làng", + "27EF5": "shà", + "27EF6": "xī", + "27EF7": "bū", + "27EF8": "shì", + "27EF9": "yǒng", + "27EFA": "guāng", + "27EFC": "niè", + "27EFF": "hǒu", + "27F0A": "mì", + "27F0E": "è", + "27F0F": "xián", + "27F10": "yǔn", + "27F11": "xù", + "27F12": "qǐn", + "27F13": "dōng", + "27F14": "léng", + "27F15": "qì", + "27F16": "lán", + "27F17": "fú", + "27F18": "qǐ", + "27F19": "chǒng", + "27F1C": "cù", + "27F1F": "mò", + "27F20": "bēi", + "27F24": "dào", + "27F28": "jié", + "27F29": "chòng", + "27F2A": "chì", + "27F2B": "yù", + "27F2C": "cuī", + "27F2D": "sù", + "27F2E": "tì", + "27F2F": "shù", + "27F30": "zhá", + "27F31": "fú", + "27F33": "chè", + "27F34": "fó", + "27F35": "hóu", + "27F36": "zhá", + "27F44": "jié", + "27F45": "zhá", + "27F46": "zhān", + "27F49": "yǎn", + "27F4A": "hái", + "27F4B": "wǔ", + "27F4C": "huá", + "27F4D": "diān", + "27F4E": "yáo", + "27F4F": "sōu", + "27F50": "qiān", + "27F51": "jí", + "27F52": "xiòng", + "27F53": "qì", + "27F54": "jūn", + "27F56": "hái", + "27F5E": "yǎn", + "27F5F": "jié", + "27F60": "cuī", + "27F62": "tuán", + "27F63": "zhāng", + "27F64": "piāo", + "27F65": "lù", + "27F66": "zhī", + "27F67": "chù", + "27F68": "mì", + "27F69": "qiāng", + "27F6B": "liàn", + "27F72": "lì", + "27F76": "é", + "27F77": "sù", + "27F78": "jué", + "27F7B": "jú", + "27F7C": "tán", + "27F7D": "liáo", + "27F7E": "sān", + "27F7F": "dòng", + "27F81": "zá", + "27F82": "zhí", + "27F86": "xuàn", + "27F87": "líng", + "27F8A": "dēng", + "27F8D": "zhān", + "27F8E": "xuān", + "27F8F": "qǐn", + "27F90": "jiào", + "27F91": "pì", + "27F94": "hǎn", + "27F9A": "yú", + "27F9B": "guó", + "27F9D": "xún", + "27FA0": "xún", + "27FA1": "chán", + "27FA2": "jié", + "27FA3": "jú", + "27FA4": "yǎn", + "27FA5": "dú", + "27FA7": "hòng", + "27FA8": "xiàn", + "27FA9": "xún", + "27FAE": "líng", + "27FAF": "jié", + "27FB0": "yì", + "27FB1": "qú", + "27FB2": "gān", + "27FB3": "fēng", + "27FB5": "jué", + "27FB6": "qū", + "27FBB": "jiù", + "27FBD": "jì", + "27FBE": "jǐ", + "27FC5": "xí", + "27FC6": "pāng", + "27FC8": "kuàng", + "27FC9": "kù", + "27FCB": "kù", + "27FCC": "zhà", + "27FCF": "bà", + "27FD2": "chěn", + "27FD3": "hù", + "27FD4": "nù", + "27FD5": "é", + "27FD6": "xiōng", + "27FD7": "dǔn", + "27FD8": "shēng", + "27FD9": "wán", + "27FDA": "fēn", + "27FDD": "xī", + "27FDE": "zī", + "27FE0": "hù", + "27FE5": "bié", + "27FE7": "tuò", + "27FE8": "bǎn", + "27FE9": "gé", + "27FEB": "kē", + "27FF2": "zhuì", + "27FF3": "fú", + "27FF4": "mò", + "27FF5": "jiá", + "27FF6": "tuó", + "27FF7": "yù", + "27FF9": "mǔ", + "27FFA": "jué", + "27FFB": "jú", + "27FFC": "guā", + "27FFD": "pǒ", + "28000": "nǐ", + "28004": "wǎ", + "28005": "yǎn", + "28014": "chǒu", + "28015": "kuāng", + "28016": "hài", + "28018": "xiáng", + "28019": "xī", + "2801B": "cún", + "2801C": "tōng", + "2801D": "ruò", + "2801F": "duó", + "28020": "chè", + "28024": "lèi", + "28025": "zī", + "28027": "zhěng", + "28028": "zuǒ", + "2802B": "kāng", + "2802C": "zài", + "2802E": "yuān", + "2802F": "qióng", + "28033": "fá", + "28034": "xún", + "28036": "jì", + "28038": "chā", + "28040": "shū", + "28041": "xuàn", + "28042": "xié", + "28043": "tī", + "28044": "hàn", + "28045": "xiān", + "28046": "shān", + "28047": "tùn", + "28048": "háng", + "28049": "kǔn", + "2804A": "cén", + "2804B": "dōu", + "2804C": "nuó", + "2804D": "yàn", + "2804E": "chéng", + "2804F": "pū", + "28050": "qì", + "28051": "yuè", + "28052": "fū", + "28057": "tǐng", + "2805F": "wǒ", + "28060": "shēng", + "28061": "tuǒ", + "28074": "tǎn", + "28076": "yǎ", + "28077": "zhì", + "28078": "lù", + "28079": "yǎn", + "2807A": "jū", + "2807D": "dé", + "2807F": "chù", + "28080": "zǔ", + "28081": "è", + "28082": "zhí", + "28083": "péng", + "28085": "biē", + "28087": "dǐ", + "28090": "lái", + "28092": "yè", + "2809C": "háo", + "2809D": "pán", + "2809E": "tàn", + "2809F": "kāng", + "280A0": "xū", + "280A1": "zòu", + "280A2": "jì", + "280A3": "wù", + "280A6": "chuàn", + "280A9": "pò", + "280AA": "yǎn", + "280AB": "tuò", + "280AD": "dú", + "280AF": "pián", + "280B0": "chì", + "280B1": "hùn", + "280B2": "pīng", + "280B4": "cōng", + "280B5": "zhǎ", + "280BA": "wān", + "280BF": "wǎi", + "280C3": "è", + "280C4": "wèi", + "280C5": "bāi", + "280C7": "jiāng", + "280D3": "chá", + "280D5": "chù", + "280D6": "kuà", + "280D7": "téng", + "280D8": "zōu", + "280D9": "lì", + "280DA": "tà", + "280DB": "sà", + "280DE": "pán", + "280DF": "pán", + "280E3": "sào", + "280E4": "qiāo", + "280ED": "zú", + "280EF": "zhì", + "280F0": "yǎn", + "280F2": "jié", + "280F3": "néng", + "28104": "luán", + "28105": "qū", + "28107": "dèng", + "28108": "liáng", + "28109": "chǎn", + "2810A": "qiè", + "2810B": "lòu", + "2810C": "dié", + "2810D": "cuī", + "28110": "jǐ", + "28113": "cháo", + "28114": "shuàn", + "28115": "zú", + "28117": "kāng", + "2811A": "qiāng", + "2811B": "lí", + "2812E": "shuāi", + "2812F": "yù", + "28130": "zhāng", + "28131": "lěi", + "28145": "pó", + "2814A": "zhé", + "2814B": "xiào", + "2814D": "tǎn", + "2814E": "cuì", + "2814F": "lán", + "28151": "xū", + "28152": "shù", + "28153": "zhǎ", + "28154": "cán", + "28157": "bǐ", + "28158": "pèng", + "2815D": "chéng", + "28163": "qiáo", + "28164": "jī", + "2816A": "zhāi", + "2816C": "lán", + "28181": "tiǎn", + "28182": "sà", + "28183": "jīn", + "28184": "zhù", + "28185": "duò", + "28187": "chà", + "28188": "juàn", + "28189": "táng", + "2818A": "bèng", + "2818C": "fán", + "2818D": "liè", + "2818E": "zéi", + "2818F": "suì", + "28199": "sè", + "281A7": "zhì", + "281A8": "tuí", + "281AA": "qīng", + "281AC": "chuò", + "281B0": "tà", + "281B1": "bìng", + "281B2": "wěn", + "281B5": "pǒ", + "281BD": "mó", + "281BE": "cā", + "281C1": "kuàng", + "281C3": "cuó", + "281C4": "rǎo", + "281C5": "bào", + "281C6": "lài", + "281CD": "niǎn", + "281CE": "lí", + "281D5": "jiǎo", + "281D6": "lú", + "281D7": "lì", + "281D8": "lóng", + "281D9": "guì", + "281DD": "chǎn", + "281E4": "xiān", + "281E6": "chàn", + "281E8": "xiè", + "281E9": "zhàn", + "281EF": "shuāng", + "281FB": "mǐ", + "281FC": "luán", + "281FD": "luò", + "28200": "diān", + "28208": "dié", + "2820A": "wān", + "2820B": "yuè", + "2820C": "luán", + "2820E": "luán", + "28213": "léng", + "28215": "wǎi", + "28216": "dìn", + "28217": "nèn", + "28218": "shǎo", + "28219": "xiè", + "2821A": "pí", + "28225": "máo", + "28227": "yǐn", + "28229": "bó", + "2822B": "zhù", + "2822E": "chōng", + "28236": "mǔ", + "28237": "tuó", + "28239": "tǒng", + "2823A": "yé", + "28241": "huàng", + "28243": "rèn", + "28245": "yè", + "2824B": "tuó", + "28256": "zuān", + "28257": "yù", + "2825A": "ā", + "2825C": "zhōu", + "2825D": "wān", + "28261": "duǒ", + "28262": "zhòng", + "28263": "hā", + "28264": "huáng", + "28265": "miàn", + "28269": "chūn", + "2826A": "qiè", + "2826B": "gōng", + "2826C": "tíng", + "2826D": "méi", + "28271": "tàng", + "28274": "róng", + "28277": "róng", + "28278": "qí", + "28279": "guó", + "2827D": "xiàng", + "2827E": "tián", + "28285": "xiāo", + "28288": "zhān", + "28289": "cuì", + "28294": "lán", + "28298": "shēn", + "2829A": "lěi", + "2829B": "lì", + "2829D": "chān", + "2829E": "niè", + "2829F": "luán", + "282A1": "tīng", + "282A2": "huì", + "282A7": "gōng", + "282B0": "qì", + "282B1": "yú", + "282B3": "xīn", + "282B8": "yuè", + "282B9": "bā", + "282BA": "dài", + "282BB": "jī", + "282BC": "xuàn", + "282BF": "jué", + "282C0": "niǔ", + "282C8": "dù", + "282C9": "jí", + "282D0": "pā", + "282D1": "gǒng", + "282D2": "bèn", + "282D4": "kēng", + "282D5": "yàng", + "282D6": "liǔ", + "282D7": "ní", + "282D8": "zhà", + "282D9": "yìn", + "282DA": "niǎn", + "282DB": "pào", + "282DD": "gōng", + "282DE": "bù", + "282DF": "hé", + "282E0": "rǒng", + "282E1": "guì", + "282E5": "bì", + "282E6": "xī", + "282E7": "jú", + "282E8": "hún", + "282E9": "bì", + "282EB": "tiāo", + "282EC": "zhěng", + "282EF": "yì", + "282F0": "cì", + "282F2": "bìng", + "282F7": "gōng", + "282FA": "fá", + "282FD": "yáng", + "282FE": "xǔ", + "28301": "hōng", + "28304": "zàng", + "28305": "chái", + "28306": "hóng", + "28308": "tián", + "2830C": "zhī", + "2830D": "xīng", + "2830E": "xú", + "28311": "zhèn", + "28314": "wǎn", + "28318": "jùn", + "2831D": "wò", + "28320": "lù", + "28322": "zhēng", + "28323": "rǒng", + "28324": "chéng", + "28325": "fú", + "28327": "è", + "28328": "tāo", + "28329": "táng", + "2832B": "juān", + "2832C": "chào", + "2832D": "tà", + "2832E": "dǐ", + "28330": "zōng", + "28333": "kēng", + "28334": "tuī", + "28336": "kēng", + "28345": "rǒng", + "28346": "yūn", + "28347": "hé", + "28348": "zǒng", + "28349": "cōng", + "2834A": "qiū", + "2834E": "mù", + "2834F": "duó", + "28350": "xǔ", + "28351": "kēng", + "28352": "xiàn", + "2835B": "dú", + "2835C": "kǎn", + "2835E": "yīng", + "28362": "zī", + "28367": "huáng", + "28369": "péng", + "2836B": "lì", + "2836D": "bó", + "2836E": "gé", + "2836F": "jú", + "28370": "kē", + "28372": "hú", + "28373": "yáo", + "28374": "táng", + "28376": "qióng", + "28377": "rǒng", + "28378": "liǔ", + "28379": "huì", + "2837A": "jī", + "28389": "zhì", + "2838B": "táng", + "2838C": "zhǐ", + "2838D": "kāng", + "28394": "yàng", + "28396": "tǎng", + "28397": "hōng", + "2839B": "liáng", + "2839D": "cáo", + "283A1": "nǎi", + "283A2": "zǒng", + "283A4": "dèng", + "283A6": "jiāo", + "283A7": "péng", + "283A9": "guāng", + "283AA": "ér", + "283AB": "jiàn", + "283AC": "jiào", + "283AD": "nuó", + "283AE": "zǎo", + "283B3": "péng", + "283B4": "dāng", + "283B6": "qú", + "283B7": "lián", + "283B8": "mù", + "283B9": "lǎn", + "283BE": "fén", + "283C2": "hún", + "283C6": "kuāng", + "283C8": "yǐn", + "283C9": "shuàn", + "283CA": "jiàn", + "283D2": "luò", + "283D4": "lù", + "283DA": "gé", + "283DB": "rǎng", + "283DE": "pín", + "283E0": "lóng", + "283E4": "zhěn", + "283E5": "xiàn", + "283E8": "lìn", + "283E9": "lián", + "283EA": "shān", + "283EB": "bó", + "283EC": "lì", + "283F3": "xié", + "283F4": "gé", + "283F5": "mǐn", + "283F6": "lián", + "283F9": "jué", + "283FA": "zhōu", + "283FF": "kē", + "28401": "dié", + "28403": "zhé", + "28405": "shū", + "28406": "jī", + "28407": "lóng", + "28408": "guāng", + "28409": "zǎo", + "2840A": "xiàn", + "2840B": "qiān", + "2840D": "shēn", + "28410": "yǐn", + "28411": "jiè", + "28414": "shēn", + "28415": "shēn", + "28416": "sǎ", + "2841B": "xì", + "28421": "kù", + "28423": "qú", + "28425": "gé", + "28426": "bàn", + "28428": "bì", + "28429": "qiān", + "28430": "bīn", + "28431": "bàn", + "28433": "zuò", + "28434": "pì", + "28436": "huò", + "2843E": "bàn", + "2844A": "nóng", + "2844C": "chén", + "2844E": "pēng", + "28451": "fǔ", + "28452": "tú", + "2845C": "pǐ", + "2845D": "pò", + "28460": "chǐ", + "28463": "xuè", + "28464": "qì", + "28465": "wù", + "28468": "zhì", + "28469": "dì", + "2846A": "cōng", + "2846B": "yóu", + "28479": "cōng", + "2847C": "dì", + "2847D": "zhuó", + "2847F": "zǒu", + "28480": "cóng", + "28483": "pàn", + "28484": "yǎn", + "28485": "qì", + "28486": "rǒng", + "28487": "jiá", + "28489": "zhì", + "2848A": "qiú", + "2848B": "yuè", + "2848D": "shì", + "28491": "háo", + "28499": "tuō", + "2849C": "bié", + "2849E": "kàn", + "284A2": "chuò", + "284A4": "cǐ", + "284A6": "yǐn", + "284A7": "shì", + "284A8": "nài", + "284A9": "ruǎn", + "284AB": "yáng", + "284AC": "chī", + "284AE": "cī", + "284B1": "gōng", + "284B2": "mí", + "284B4": "jǐ", + "284BC": "gèn", + "284BD": "zào", + "284C1": "běng", + "284C7": "xǐn", + "284C8": "kuò", + "284CA": "dié", + "284CD": "tíng", + "284DA": "shuì", + "284DE": "dài", + "284E6": "lǐ", + "284E8": "yǒng", + "284E9": "jiāo", + "284EC": "tá", + "284ED": "qǔ", + "284EE": "yín", + "284EF": "yuān", + "284F0": "jié", + "284F2": "qiān", + "284F3": "yāo", + "284F4": "yà", + "284F7": "qīng", + "284FF": "péi", + "28517": "jiā", + "28519": "tòu", + "2851B": "tī", + "28521": "dùn", + "28522": "chǎn", + "28523": "jiā", + "28524": "chì", + "28525": "jiān", + "28526": "shù", + "2852F": "tà", + "28555": "zhī", + "28557": "yuán", + "2855A": "hū", + "2855C": "liè", + "28560": "zé", + "28562": "chù", + "28566": "qiù", + "28567": "bēng", + "28579": "huán", + "2857A": "kuā", + "2857B": "shēng", + "2857D": "jié", + "2857F": "wǎng", + "28583": "hū", + "2858A": "zé", + "2858B": "zǎn", + "2858C": "yàng", + "2858E": "chǐ", + "2858F": "jiù", + "2859A": "liáo", + "2859B": "yū", + "285A0": "biǎn", + "285A2": "kuáng", + "285AC": "chòu", + "285AD": "yá", + "285AE": "zhuó", + "285B0": "qiè", + "285B1": "xiàn", + "285B3": "yuān", + "285B4": "wǔ", + "285B5": "jiǎo", + "285B6": "xiàng", + "285B7": "shà", + "285B9": "zhì", + "285BC": "chòng", + "285BE": "biān", + "285BF": "wēi", + "285D3": "dào", + "285DD": "yù", + "285DE": "tuí", + "285E1": "chào", + "285E5": "huì", + "285E6": "qiǎn", + "285E8": "wěi", + "285F0": "yóu", + "285FC": "dì", + "285FE": "dà", + "28601": "yóu", + "28602": "jiù", + "28603": "tuí", + "28604": "zǎn", + "28607": "huì", + "28609": "shà", + "2860C": "huò", + "28614": "yáo", + "28619": "xiàn", + "2861E": "xiàn", + "2862C": "dì", + "2862E": "jiù", + "28632": "huì", + "28634": "kào", + "28635": "yóu", + "28638": "lì", + "2863C": "chuán", + "2863E": "chí", + "28640": "huò", + "28642": "yóu", + "28644": "yuè", + "2864E": "tà", + "2864F": "zàn", + "28653": "niè", + "28654": "zhù", + "28661": "xiǎn", + "28669": "shí", + "2866B": "kǒu", + "2866C": "qǐ", + "2866D": "tǔ", + "2866E": "fán", + "2866F": "cūn", + "28672": "tún", + "28673": "chā", + "28674": "cái", + "28675": "xiàng", + "28676": "pèi", + "28677": "jǐng", + "28678": "qí", + "28679": "shǎo", + "2867A": "niǔ", + "2867B": "nà", + "2867D": "qín", + "2868D": "bì", + "28693": "bì", + "28694": "bāo", + "28695": "biàn", + "28696": "zī", + "28697": "nà", + "28698": "wèi", + "28699": "háo", + "286A1": "jǐn", + "286A3": "zhèng", + "286A7": "qié", + "286AE": "hào", + "286AF": "tóng", + "286B0": "zǎo", + "286B1": "shèng", + "286B2": "cún", + "286B3": "huāng", + "286B4": "rú", + "286B5": "zài", + "286B6": "nián", + "286BE": "xiān", + "286C8": "quán", + "286C9": "jì", + "286CA": "yín", + "286CB": "lǐ", + "286CC": "máng", + "286CD": "shào", + "286CE": "hàn", + "286CF": "cuò", + "286D0": "jùn", + "286D1": "jì", + "286D2": "bù", + "286D3": "lòng", + "286D4": "fǒu", + "286D5": "yóu", + "286D6": "kuài", + "286DC": "xiàng", + "286E1": "yún", + "286E3": "qín", + "286E4": "huí", + "286E5": "pú", + "286EB": "lí", + "286EC": "péi", + "286ED": "shū", + "286EE": "jū", + "286EF": "yí", + "286F0": "zhēng", + "286F1": "chóng", + "286F3": "xí", + "286F5": "hǔ", + "286F6": "róu", + "2870C": "huàn", + "2870D": "qiào", + "2870E": "zhī", + "2870F": "yíng", + "28710": "xǐ", + "28711": "qiāo", + "28712": "jì", + "28713": "zhēng", + "28714": "huáng", + "28716": "yú", + "28717": "zōu", + "28718": "méi", + "2871C": "shěng", + "28729": "quán", + "28730": "jiāng", + "28731": "hé", + "28733": "tóng", + "28734": "hé", + "28735": "wēn", + "28736": "yì", + "28737": "páng", + "2873A": "wēng", + "2873B": "qián", + "2873C": "lì", + "2873D": "yí", + "2873E": "chuàng", + "2873F": "xù", + "28740": "wěi", + "28746": "gē", + "28748": "yǔ", + "2874B": "zhài", + "2874C": "gān", + "2874D": "qiān", + "2874E": "kāng", + "2874F": "lí", + "28750": "shēn", + "28751": "guàn", + "28753": "piáo", + "28756": "lí", + "28758": "hǔ", + "2875B": "tú", + "2875C": "shùn", + "2875E": "hù", + "2875F": "lí", + "28762": "lòu", + "28766": "dàng", + "28768": "zuò", + "28769": "shān", + "2876B": "shè", + "2876D": "féng", + "2876E": "jù", + "2876F": "tóng", + "28770": "jiǎo", + "28771": "qiáo", + "28772": "gāo", + "28773": "zī", + "28774": "huáng", + "28775": "shān", + "28778": "tán", + "2878C": "tuō", + "2878E": "lìng", + "28790": "chéng", + "28791": "wèng", + "28792": "zuó", + "28793": "yù", + "28795": "zhú", + "28797": "qún", + "28798": "xǐ", + "28799": "qú", + "2879B": "gé", + "287A2": "qī", + "287A3": "xū", + "287A8": "gài", + "287A9": "què", + "287AA": "chóu", + "287AB": "méng", + "287B2": "shēn", + "287B3": "qú", + "287B6": "qiāo", + "287B7": "cán", + "287BA": "lì", + "287BC": "wàn", + "287BD": "léi", + "287BE": "xīng", + "287BF": "láng", + "287C2": "shì", + "287C3": "zhēng", + "287C4": "fán", + "287CA": "zhì", + "287CF": "yín", + "287D1": "lì", + "287D6": "mó", + "287D7": "wěi", + "287D9": "yīng", + "287DA": "ráng", + "287E0": "quān", + "287E5": "luǒ", + "287F2": "dài", + "287F4": "yìn", + "287F5": "bǐ", + "287F6": "gē", + "287F8": "wèn", + "287F9": "yǎn", + "287FA": "miǎn", + "287FC": "gǎng", + "287FD": "qiú", + "287FE": "zhī", + "2880B": "gū", + "2880C": "tóng", + "2880E": "líng", + "2880F": "tí", + "28810": "cí", + "28811": "yí", + "28812": "fàn", + "28813": "pō", + "28814": "bì", + "28816": "bào", + "2881F": "pēng", + "28821": "suān", + "28824": "sōng", + "28825": "wéi", + "28826": "xiáo", + "2882C": "hào", + "2882D": "yǎn", + "28836": "yí", + "28837": "zāo", + "28838": "yǐng", + "28839": "nǎn", + "2883F": "zā", + "28841": "tiǎn", + "28842": "xī", + "28843": "jiào", + "28844": "yán", + "2884C": "néi", + "2884D": "tǎn", + "2884E": "yàn", + "2884F": "tiǎn", + "28850": "zhì", + "28851": "chōu", + "28852": "táo", + "28857": "zhà", + "2885E": "miǎn", + "28861": "wǔ", + "28862": "yǐn", + "28863": "yàn", + "28864": "lǎo", + "28869": "pō", + "2886B": "hùn", + "2886C": "hǎi", + "2886D": "mú", + "2886E": "cōng", + "28871": "kù", + "28872": "chōu", + "28874": "yǒu", + "28878": "zhuó", + "2887B": "sōu", + "28882": "yìn", + "28885": "zuì", + "28886": "sāng", + "28887": "liù", + "28888": "hàn", + "28889": "wèi", + "2888A": "méng", + "2888B": "hú", + "2888C": "lì", + "2888E": "mì", + "28890": "bāng", + "28891": "jiǎn", + "2889C": "què", + "288A0": "méng", + "288A2": "mú", + "288A3": "hǒng", + "288A4": "hù", + "288A5": "mí", + "288A6": "shài", + "288A9": "shāng", + "288AA": "chào", + "288AC": "zhuó", + "288AE": "zhī", + "288AF": "niàn", + "288B5": "jì", + "288B8": "kē", + "288B9": "zhēng", + "288BF": "dān", + "288C0": "liǎo", + "288C1": "zhǎn", + "288C2": "gǒng", + "288C3": "láo", + "288C4": "huā", + "288C5": "chuài", + "288C7": "jiǎn", + "288C8": "kuì", + "288CD": "shē", + "288D4": "chěn", + "288D5": "tǎn", + "288D7": "hú", + "288D8": "méng", + "288D9": "pào", + "288DA": "zhǎn", + "288DB": "cháng", + "288DD": "gǎn", + "288E0": "yì", + "288E2": "suì", + "288E6": "xù", + "288E7": "jì", + "288E8": "làn", + "288EC": "yí", + "288EF": "mì", + "288F1": "miè", + "288F5": "cuán", + "288F8": "lǎn", + "288FB": "yān", + "288FE": "mí", + "28902": "yǒng", + "28903": "cáng", + "28904": "jiǎn", + "28907": "sōu", + "2890E": "yán", + "28911": "juàn", + "28915": "è", + "28918": "fèn", + "2891A": "fèn", + "28921": "guàng", + "28922": "mái", + "28924": "liě", + "28929": "chōng", + "2892B": "lí", + "28931": "zhí", + "28934": "xiè", + "28937": "chóu", + "28939": "jí", + "2893D": "pī", + "28942": "jié", + "28947": "zhǒu", + "2894D": "xiōng", + "28951": "kuàng", + "28959": "jǐng", + "2895B": "hù", + "2895E": "qián", + "28963": "cén", + "28966": "qí", + "28967": "wǎn", + "28968": "máo", + "2896A": "dǒu", + "28974": "kǒu", + "28976": "dài", + "28978": "náo", + "2897A": "hóng", + "28982": "lǎi", + "28983": "duǒ", + "28984": "qiān", + "28986": "yín", + "28996": "lòu", + "28997": "huī", + "2899B": "fù", + "2899C": "máo", + "2899E": "zhōu", + "289A1": "yóng", + "289AD": "láo", + "289AE": "jí", + "289AF": "yì", + "289B0": "liú", + "289B1": "cōng", + "289B3": "nǎn", + "289D0": "tūn", + "289D1": "xiàng", + "289D5": "biàn", + "289D6": "chuáng", + "289D7": "wù", + "289D9": "jū", + "289E5": "xiē", + "289E6": "pī", + "289E7": "zhuó", + "289E8": "ruì", + "289EA": "sào", + "289EB": "zì", + "289ED": "zhèng", + "289F0": "zú", + "289F1": "qū", + "289F3": "chì", + "289F5": "zhì", + "28A17": "quàn", + "28A18": "qiān", + "28A19": "yā", + "28A1A": "chào", + "28A1B": "hé", + "28A1C": "rǔ", + "28A20": "jū", + "28A21": "wù", + "28A2C": "chì", + "28A2D": "kuàng", + "28A2F": "còu", + "28A30": "ruàn", + "28A31": "kuò", + "28A32": "chí", + "28A33": "zú", + "28A34": "jiāo", + "28A36": "yú", + "28A37": "tú", + "28A38": "méng", + "28A39": "dā", + "28A3A": "shuò", + "28A65": "fēng", + "28A66": "gǒu", + "28A67": "dōng", + "28A68": "chǎ", + "28A69": "mào", + "28A6A": "chǎn", + "28A6B": "biān", + "28A6C": "yù", + "28A6F": "wán", + "28A70": "zú", + "28A72": "zī", + "28A74": "chuān", + "28A75": "wǎn", + "28A76": "wā", + "28A78": "quān", + "28A7B": "wǎn", + "28A7D": "xià", + "28A84": "yìng", + "28A85": "jiàn", + "28A88": "wěi", + "28A89": "tí", + "28A8A": "sāo", + "28A8C": "qí", + "28A8D": "shā", + "28A8E": "yù", + "28A8F": "jí", + "28A90": "dòu", + "28A91": "chǎn", + "28A92": "tuán", + "28A95": "liú", + "28A97": "zhuì", + "28AB3": "ruàn", + "28AB6": "yàn", + "28AB7": "gǔ", + "28AB9": "lì", + "28ABA": "chā", + "28ABE": "dì", + "28AC0": "zhǎn", + "28AC1": "pō", + "28AD2": "lòu", + "28AD4": "zhì", + "28B01": "lián", + "28B05": "luǒ", + "28B0D": "duò", + "28B10": "jué", + "28B11": "lì", + "28B12": "lán", + "28B14": "ruàn", + "28B15": "gū", + "28B16": "chán", + "28B17": "xū", + "28B1A": "zhǐ", + "28B41": "xuè", + "28B42": "bō", + "28B43": "chēng", + "28B45": "zhù", + "28B46": "hēi", + "28B49": "bān", + "28B53": "dié", + "28B56": "zhǎn", + "28B57": "guó", + "28B5A": "biāo", + "28B5B": "là", + "28B7A": "jīn", + "28B82": "gǎi", + "28B92": "mèng", + "28B94": "yù", + "28BAA": "xǐ", + "28BAC": "piāo", + "28BAD": "sī", + "28BB4": "dèng", + "28BB8": "chuō", + "28BB9": "dí", + "28BBA": "jī", + "28BBB": "chán", + "28BBF": "zhuó", + "28BD3": "cài", + "28BDE": "jiàng", + "28BF2": "tóu", + "28BFD": "lí", + "28C02": "qiàn", + "28C06": "chuō", + "28C0F": "tà", + "28C11": "diào", + "28C13": "jiǎn", + "28C1B": "zhǐ", + "28C1C": "jué", + "28C1E": "mó", + "28C20": "luó", + "28C26": "bǎo", + "28C2D": "zuǎn", + "28C35": "zhē", + "28C38": "yú", + "28C3B": "bǎo", + "28C3E": "mǎ", + "28C3F": "xì", + "28C40": "hù", + "28C41": "yì", + "28C42": "é", + "28C43": "gū", + "28C44": "tú", + "28C45": "zhēn", + "28C47": "qiú", + "28C48": "sù", + "28C49": "liàng", + "28C4A": "qū", + "28C4B": "líng", + "28C4C": "guàn", + "28C4D": "láng", + "28C4E": "tōu", + "28C4F": "dā", + "28C50": "lòu", + "28C51": "huáng", + "28C52": "shòu", + "28C53": "jiāo", + "28C54": "zūn", + "28C55": "gǎi", + "28C56": "wéi", + "28C59": "kūn", + "28C5A": "duàn", + "28C5B": "sōng", + "28C5C": "qí", + "28C5D": "yǎng", + "28C61": "shì", + "28C63": "gǎi", + "28C66": "dào", + "28C67": "yǎo", + "28C6B": "qián", + "28C6D": "shāo", + "28C6E": "cháng", + "28C6F": "miǔ", + "28C71": "mó", + "28C75": "nǎo", + "28C78": "cōng", + "28C7A": "niè", + "28C7B": "zhāo", + "28C7C": "cén", + "28C7F": "sōng", + "28C80": "niè", + "28C81": "cì", + "28C84": "jùn", + "28C86": "shāo", + "28C88": "zhú", + "28C89": "duǒ", + "28C8A": "àn", + "28C8B": "bī", + "28C8E": "tì", + "28C90": "pǐ", + "28C91": "xiá", + "28C92": "qiú", + "28C93": "shěng", + "28C97": "tāng", + "28C9B": "mán", + "28C9C": "piān", + "28C9E": "tì", + "28C9F": "róng", + "28CA7": "cōng", + "28CAA": "jī", + "28CAB": "féng", + "28CAC": "wù", + "28CAD": "jiào", + "28CAE": "láo", + "28CAF": "zēng", + "28CB0": "péng", + "28CB1": "cǎn", + "28CB3": "nóng", + "28CB5": "chǎn", + "28CBE": "mán", + "28CBF": "guì", + "28CC0": "niào", + "28CC1": "chōng", + "28CC2": "chàn", + "28CC6": "nàng", + "28CC9": "xiā", + "28CCA": "jiū", + "28CCB": "jǐ", + "28CCC": "zhèn", + "28CD1": "tǐng", + "28CD4": "mén", + "28CD5": "yuè", + "28CD7": "zhōng", + "28CD8": "tún", + "28CD9": "ruì", + "28CDA": "xiè", + "28CDB": "xī", + "28CDD": "tǐng", + "28CDE": "niǔ", + "28CE0": "wǎng", + "28CE1": "jiān", + "28CE3": "fēn", + "28CF2": "biàn", + "28CF7": "yí", + "28CFA": "dié", + "28CFB": "jī", + "28CFC": "gǎn", + "28CFF": "jiān", + "28D00": "jiōng", + "28D06": "kāi", + "28D0A": "què", + "28D0C": "nán", + "28D0D": "móu", + "28D0E": "xù", + "28D0F": "sǒng", + "28D10": "shèn", + "28D11": "kuāng", + "28D12": "què", + "28D13": "wéi", + "28D17": "dié", + "28D18": "nán", + "28D1A": "ruò", + "28D1B": "gōng", + "28D1C": "dòu", + "28D1E": "niǎn", + "28D21": "chāo", + "28D22": "hé", + "28D23": "yàn", + "28D29": "tú", + "28D2A": "bǔ", + "28D2C": "hú", + "28D2D": "yǒng", + "28D2F": "shǐ", + "28D30": "chù", + "28D39": "xiāo", + "28D3A": "mén", + "28D3B": "lǐ", + "28D3C": "tí", + "28D3E": "jiān", + "28D42": "zhǐ", + "28D43": "guā", + "28D44": "guǎn", + "28D46": "qì", + "28D48": "fēi", + "28D49": "yǔ", + "28D4A": "zhé", + "28D4B": "wěi", + "28D4C": "ě", + "28D4D": "chān", + "28D4E": "xī", + "28D50": "gǔ", + "28D57": "què", + "28D58": "huì", + "28D5A": "xié", + "28D5B": "yīng", + "28D5D": "tà", + "28D5E": "wāi", + "28D5F": "fú", + "28D60": "jiè", + "28D61": "pì", + "28D65": "shěng", + "28D66": "yú", + "28D67": "kuā", + "28D69": "pì", + "28D6A": "xié", + "28D6B": "nüè", + "28D6C": "xiàn", + "28D6D": "jiàn", + "28D6E": "xù", + "28D70": "bì", + "28D74": "nán", + "28D76": "liáng", + "28D78": "pián", + "28D7C": "jìng", + "28D80": "tǎ", + "28D81": "yàn", + "28D82": "ài", + "28D85": "xiāo", + "28D86": "qiāng", + "28D87": "wǔ", + "28D88": "táng", + "28D8A": "jùn", + "28D90": "kuò", + "28D97": "làng", + "28D99": "něng", + "28D9C": "dòu", + "28D9D": "shú", + "28D9F": "jiǎo", + "28DA0": "niè", + "28DA2": "yú", + "28DA8": "cè", + "28DAA": "jiǎo", + "28DAC": "huà", + "28DAD": "wén", + "28DAE": "yē", + "28DAF": "é", + "28DB0": "guāng", + "28DB1": "huā", + "28DB2": "jiāo", + "28DBA": "lèi", + "28DBC": "shāng", + "28DBD": "yòng", + "28DBF": "dēng", + "28DC0": "guān", + "28DC1": "niú", + "28DC3": "suì", + "28DC4": "xiàng", + "28DC6": "sà", + "28DC7": "chāng", + "28DCE": "rùn", + "28DD0": "yūn", + "28DD2": "fēn", + "28DD3": "jiàn", + "28DD4": "xù", + "28DD8": "xì", + "28DD9": "shú", + "28DE5": "xié", + "28DE6": "lì", + "28DE9": "tóu", + "28DEC": "mǐ", + "28DED": "chǎn", + "28DEE": "huō", + "28DF1": "zhuǎn", + "28DF2": "yuè", + "28DFB": "lán", + "28DFD": "yán", + "28DFE": "dàng", + "28DFF": "xiàng", + "28E00": "yuè", + "28E01": "tǐng", + "28E02": "bēng", + "28E03": "sàn", + "28E04": "xiàn", + "28E05": "dié", + "28E06": "pì", + "28E07": "pián", + "28E09": "tǎ", + "28E0B": "jiāo", + "28E0C": "yē", + "28E0E": "yuè", + "28E10": "réng", + "28E11": "qiǎo", + "28E12": "qí", + "28E13": "diāo", + "28E14": "qí", + "28E17": "hàn", + "28E18": "yuán", + "28E19": "yóu", + "28E1A": "jí", + "28E1B": "gài", + "28E1C": "hāi", + "28E1D": "shì", + "28E1F": "qū", + "28E29": "wèn", + "28E2C": "zhèn", + "28E2D": "pō", + "28E2E": "yán", + "28E2F": "gū", + "28E30": "jù", + "28E31": "tiàn", + "28E37": "è", + "28E3A": "yā", + "28E3B": "lìn", + "28E3C": "bì", + "28E40": "zǐ", + "28E41": "hóng", + "28E43": "duǒ", + "28E45": "duì", + "28E46": "xuàn", + "28E48": "shǎn", + "28E4A": "shǎn", + "28E4B": "yáo", + "28E4C": "rǎn", + "28E54": "tuó", + "28E57": "bīng", + "28E58": "xù", + "28E59": "tūn", + "28E5A": "chéng", + "28E5C": "dòu", + "28E5D": "yì", + "28E61": "chè", + "28E75": "juǎn", + "28E76": "jī", + "28E78": "zhào", + "28E79": "bēng", + "28E7B": "tiǎn", + "28E80": "pēng", + "28E85": "fù", + "28E96": "tuǒ", + "28E98": "xián", + "28E99": "nì", + "28E9A": "lóng", + "28E9D": "zhuó", + "28E9F": "zhēng", + "28EA0": "shǔn", + "28EA1": "zōng", + "28EA2": "fēng", + "28EA3": "duàn", + "28EA4": "pì", + "28EA5": "yǎn", + "28EA6": "sǒu", + "28EA7": "qiú", + "28EA8": "è", + "28EA9": "qián", + "28EAB": "qiǎn", + "28EAD": "cā", + "28EAE": "xùn", + "28EB5": "zhuì", + "28EB8": "mǎo", + "28EB9": "jiǎo", + "28EBF": "zhǎn", + "28EC0": "pí", + "28EC1": "xī", + "28EC2": "yàn", + "28EC3": "fèi", + "28EC4": "niè", + "28EC6": "zhì", + "28EC8": "suǒ", + "28ECA": "yì", + "28ECC": "lěi", + "28ECD": "xù", + "28ECF": "yì", + "28ED2": "wēi", + "28ED5": "jī", + "28ED6": "chēn", + "28ED7": "dié", + "28EE3": "yuán", + "28EE5": "xí", + "28EE7": "liú", + "28EE8": "suǒ", + "28EF1": "bēng", + "28EF2": "xià", + "28EF3": "yàn", + "28EF5": "cuī", + "28EF7": "kāng", + "28EFA": "qīng", + "28EFB": "lóu", + "28EFC": "bī", + "28F08": "zhàn", + "28F09": "cuàn", + "28F0A": "wú", + "28F0B": "xū", + "28F0C": "chēn", + "28F0D": "háo", + "28F0E": "jué", + "28F10": "chèn", + "28F11": "chá", + "28F12": "chǎn", + "28F13": "zhí", + "28F14": "xún", + "28F23": "gé", + "28F24": "chén", + "28F25": "yè", + "28F2A": "chǔ", + "28F2B": "qú", + "28F2C": "xiè", + "28F2E": "zhàn", + "28F2F": "kěn", + "28F31": "jué", + "28F3D": "qú", + "28F3F": "méng", + "28F40": "yè", + "28F41": "zōu", + "28F42": "pú", + "28F44": "shì", + "28F49": "shǔ", + "28F4A": "chán", + "28F4D": "dú", + "28F4F": "guō", + "28F50": "lù", + "28F51": "yān", + "28F56": "niǎo", + "28F57": "bīn", + "28F5F": "tuí", + "28F66": "nì", + "28F67": "huān", + "28F68": "qián", + "28F6F": "xià", + "28F72": "líng", + "28F77": "lián", + "28F79": "yì", + "28F7B": "lì", + "28F7C": "sì", + "28F7F": "dài", + "28F82": "wèi", + "28F85": "cì", + "28F89": "jiǔ", + "28F8A": "hóng", + "28F8C": "yú", + "28F8E": "kuí", + "28F92": "háng", + "28F93": "gē", + "28F94": "fàng", + "28F97": "kuí", + "28F9A": "guī", + "28F9B": "chǐ", + "28F9E": "jiǔ", + "28FA1": "suī", + "28FA4": "dié", + "28FAC": "suǐ", + "28FB0": "qín", + "28FB4": "guī", + "28FBB": "zhuī", + "28FBE": "tiào", + "28FC1": "yuè", + "28FC7": "zuǐ", + "28FCF": "wú", + "28FD0": "cuǐ", + "28FDB": "zhì", + "28FE0": "shuì", + "28FE2": "dōng", + "28FED": "wéi", + "28FFF": "chǒng", + "2900B": "rún", + "29016": "jí", + "2901C": "diāo", + "2901E": "cāng", + "29020": "kòu", + "29023": "wéi", + "29027": "cán", + "2902A": "má", + "2902B": "òu", + "29032": "sǎn", + "29036": "wéi", + "2903C": "sǎn", + "2903F": "jīn", + "2904C": "wéi", + "2905E": "cài", + "2905F": "lí", + "2906F": "yuè", + "29074": "yūn", + "29077": "chēng", + "2907A": "shān", + "29082": "hū", + "29083": "shài", + "29084": "tún", + "29086": "fǒu", + "29088": "qìn", + "29089": "xū", + "2908D": "chuān", + "2908E": "fù", + "29092": "yì", + "29093": "dōng", + "29094": "fú", + "29095": "fú", + "29096": "zé", + "29097": "pù", + "29099": "líng", + "2909D": "shài", + "2909E": "pào", + "290A2": "yín", + "290A3": "luò", + "290A4": "huà", + "290A5": "yìn", + "290A6": "bèng", + "290A7": "yū", + "290A8": "shè", + "290AA": "xiè", + "290AB": "chǔ", + "290B4": "shè", + "290B5": "diàn", + "290B9": "yì", + "290BB": "chè", + "290BC": "gěng", + "290BD": "lóng", + "290BE": "píng", + "290BF": "yǔn", + "290C0": "yàn", + "290C1": "mò", + "290C3": "suī", + "290CB": "jìng", + "290CD": "sòng", + "290CE": "páng", + "290D0": "yá", + "290D1": "sè", + "290D2": "duǒ", + "290D5": "chuáng", + "290D6": "xiè", + "290D8": "tuán", + "290D9": "gōng", + "290DA": "xuàn", + "290DC": "lā", + "290DE": "líng", + "290E0": "dài", + "290E1": "zhá", + "290EC": "yīn", + "290ED": "sōng", + "290EF": "yǔ", + "290F0": "tuó", + "290F1": "tuó", + "290F4": "bà", + "290F5": "rǎn", + "290F6": "bó", + "290F7": "dài", + "290F9": "zhá", + "290FA": "hóu", + "290FE": "huǐ", + "29105": "lú", + "2910A": "lìng", + "2910B": "rú", + "29115": "dàn", + "29116": "méng", + "29117": "xià", + "29118": "wěng", + "29119": "hán", + "2911A": "zī", + "2911B": "zhèn", + "2911C": "sè", + "2911D": "cuó", + "2911E": "lì", + "29120": "diān", + "29121": "lián", + "29122": "gòu", + "29126": "péng", + "2912A": "yīng", + "2912C": "hòu", + "2912E": "duì", + "2912F": "wù", + "29137": "piào", + "29138": "hè", + "2913A": "lóng", + "2913B": "mò", + "2913C": "fěi", + "2913D": "lǚ", + "2913E": "zé", + "2913F": "bó", + "29140": "diàn", + "29141": "mǎng", + "29143": "zhuàng", + "29144": "lù", + "29145": "pāng", + "29146": "duì", + "29147": "bù", + "2914C": "chēn", + "2914D": "màn", + "29156": "xī", + "2915D": "ǎn", + "2915E": "zhōng", + "29160": "nàn", + "29161": "tuò", + "29162": "hé", + "29165": "duì", + "29166": "wān", + "29167": "zhōng", + "29168": "cén", + "29169": "lì", + "2916A": "shuāng", + "2916E": "cén", + "29170": "sī", + "29172": "duì", + "29174": "hūn", + "2917C": "jiān", + "2917D": "nóng", + "2917E": "dàn", + "2917F": "fù", + "29180": "huò", + "29181": "huì", + "29182": "cí", + "29184": "yǒng", + "29185": "sà", + "29186": "tíng", + "2918E": "liù", + "29191": "suān", + "29192": "líng", + "29193": "mán", + "29194": "diàn", + "29198": "pāo", + "2919A": "líng", + "2919D": "lì", + "2919F": "nóu", + "291A3": "liè", + "291A4": "shǎn", + "291A6": "fèi", + "291AB": "shǎn", + "291AE": "líng", + "291AF": "zhàn", + "291B1": "bīn", + "291B2": "lí", + "291B5": "sī", + "291B6": "ráng", + "291B7": "jiān", + "291B8": "zhuó", + "291BB": "líng", + "291BC": "líng", + "291BD": "mèng", + "291BF": "shuāng", + "291C4": "líng", + "291C7": "hùn", + "291CE": "líng", + "291CF": "jiān", + "291D0": "qú", + "291D4": "nóng", + "291D5": "jìng", + "291D6": "chēn", + "291DC": "zhēn", + "291DD": "qìng", + "291DF": "qìng", + "291E0": "è", + "291E3": "sè", + "291E9": "bèi", + "291EB": "fēi", + "291EE": "fèi", + "291EF": "féi", + "291F4": "fāng", + "291F5": "kǔ", + "291FA": "zá", + "291FB": "huì", + "291FD": "féi", + "29201": "duì", + "29206": "pā", + "29207": "niǔ", + "29208": "pàng", + "29209": "dàn", + "2920A": "dān", + "2920B": "ài", + "2920D": "tiǎn", + "2920E": "chǎo", + "2920F": "ǎo", + "29210": "mèi", + "29211": "nǎn", + "29214": "bò", + "29215": "yù", + "29216": "xiān", + "29217": "mài", + "2921A": "pīng", + "2921C": "duī", + "2921E": "dào", + "29221": "xìng", + "29222": "nì", + "29223": "hān", + "29224": "chù", + "29225": "shuǎ", + "29226": "mǎn", + "2922C": "wàn", + "2922D": "yì", + "2922E": "diào", + "2922F": "yān", + "29231": "wò", + "29232": "suàn", + "29234": "ǎn", + "29235": "lán", + "29236": "nǎn", + "29238": "qiǔ", + "29239": "miàn", + "2923A": "nuǒ", + "2923B": "cán", + "2923C": "cǎn", + "29240": "làn", + "29241": "tiǎn", + "29242": "yè", + "29244": "niǎn", + "29246": "shuǎ", + "2924B": "cí", + "2924D": "jiǎn", + "29250": "gàn", + "29254": "jiàn", + "29255": "guó", + "29257": "zhān", + "29259": "luǒ", + "2925C": "jī", + "2925D": "guì", + "29261": "jiá", + "29262": "jǐ", + "29265": "xuàn", + "29267": "fēng", + "2926B": "bì", + "2926C": "qí", + "2926F": "yuán", + "29270": "àng", + "29271": "dī", + "29274": "è", + "29275": "fén", + "29278": "jù", + "29279": "nǐ", + "2927A": "tuó", + "2927C": "shēn", + "2927D": "fú", + "2927E": "xiá", + "2927F": "qú", + "29280": "pò", + "29281": "wǎn", + "29282": "líng", + "29283": "mà", + "29284": "zhòu", + "29285": "bào", + "29287": "yù", + "2928C": "běng", + "2928D": "mài", + "2928F": "jiā", + "29291": "yǎng", + "29293": "kuǎ", + "29294": "jiào", + "29296": "bǐng", + "2929A": "luò", + "2929B": "guǐ", + "2929C": "duò", + "2929D": "zhì", + "292A1": "zhèn", + "292A2": "è", + "292A3": "zhū", + "292A4": "bá", + "292A8": "zhèn", + "292A9": "fēng", + "292AA": "dòu", + "292AB": "niǎn", + "292AC": "bù", + "292AD": "duì", + "292AE": "shā", + "292AF": "sè", + "292B0": "bì", + "292B4": "zhì", + "292B5": "zhé", + "292B6": "bù", + "292BA": "jué", + "292BB": "xùn", + "292BF": "xì", + "292C1": "zhuó", + "292C2": "bài", + "292C3": "yáo", + "292C4": "chǒu", + "292C5": "tà", + "292C6": "qiān", + "292C8": "nào", + "292C9": "yù", + "292CA": "è", + "292CB": "jiān", + "292CC": "yì", + "292CD": "xiāo", + "292CF": "niè", + "292D2": "bīng", + "292D7": "guǒ", + "292D8": "xié", + "292D9": "diào", + "292DC": "jū", + "292DD": "suǒ", + "292DE": "dié", + "292DF": "fú", + "292E0": "miǎn", + "292E1": "shì", + "292E2": "xuàn", + "292E3": "tí", + "292E4": "yù", + "292E7": "xié", + "292E8": "fú", + "292E9": "zhì", + "292EA": "nǐ", + "292EB": "xuàn", + "292EC": "yáng", + "292EE": "fěng", + "292EF": "zòng", + "292F0": "zhòu", + "292F1": "xuān", + "292F5": "zhū", + "292F7": "la", + "292F9": "yìng", + "292FA": "gào", + "292FB": "kuò", + "292FD": "é", + "292FE": "wéi", + "292FF": "méi", + "29303": "huái", + "29304": "chǒu", + "29306": "suǒ", + "29307": "tà", + "29308": "suǒ", + "29309": "tà", + "2930A": "xuè", + "2930C": "gǒng", + "2930D": "jiǎ", + "2930F": "bó", + "29310": "tà", + "29311": "yuǎn", + "29318": "tà", + "2931D": "chuí", + "29320": "xiōng", + "29321": "hé", + "29322": "suō", + "29327": "mò", + "29328": "chóng", + "29329": "suī", + "2932A": "zé", + "2932B": "lù", + "2932C": "zhāng", + "2932D": "luò", + "2932E": "xù", + "2932F": "jiān", + "29330": "shān", + "29332": "xù", + "2933E": "jiǎng", + "29342": "bào", + "29343": "mái", + "29345": "tóng", + "29346": "xì", + "29349": "róng", + "2934B": "shéng", + "2934C": "zhòu", + "2934E": "jiān", + "2934F": "fù", + "29350": "dèng", + "29353": "yōng", + "29354": "jū", + "29356": "yì", + "29357": "bāng", + "29359": "sè", + "2935A": "suì", + "2935C": "duó", + "2935D": "xiè", + "29361": "huán", + "29365": "rǔ", + "29366": "nǐ", + "29367": "zhòu", + "29368": "guì", + "2936A": "luò", + "29372": "zhī", + "29373": "xù", + "29375": "zhī", + "29377": "jué", + "29378": "jū", + "2937B": "yuán", + "2937C": "lú", + "2937F": "bó", + "29382": "róng", + "29383": "xiè", + "29389": "xǐ", + "2938A": "luó", + "2938E": "gé", + "29391": "zuān", + "29392": "hàn", + "29394": "jiāo", + "29395": "sǎ", + "29396": "qín", + "29397": "qūn", + "29398": "páo", + "29399": "yuè", + "2939A": "chè", + "2939B": "fú", + "2939C": "pēi", + "2939F": "mèi", + "293A2": "tāo", + "293A4": "kēn", + "293A5": "xì", + "293AB": "duò", + "293AD": "yì", + "293B0": "suì", + "293B2": "xiá", + "293B3": "juān", + "293B5": "wéi", + "293B7": "yì", + "293B9": "yù", + "293BB": "bài", + "293BC": "tuó", + "293BD": "tà", + "293BE": "páo", + "293C2": "bǐng", + "293C5": "yùn", + "293C6": "yùn", + "293C7": "duàn", + "293C8": "ruǎn", + "293C9": "wéi", + "293CF": "wěi", + "293D0": "guì", + "293D2": "dá", + "293D3": "xiá", + "293D6": "hùn", + "293D7": "juǎn", + "293D8": "suī", + "293DA": "suì", + "293DD": "lóu", + "293DE": "bài", + "293DF": "yù", + "293E0": "zhèng", + "293E1": "guì", + "293E3": "kuī", + "293E4": "gāo", + "293E5": "dān", + "293E9": "xiǎn", + "293EA": "zhái", + "293EB": "sè", + "293ED": "kē", + "293EE": "bǔ", + "293EF": "bó", + "293F2": "suì", + "293F4": "yù", + "293F5": "bǔ", + "293F6": "jiū", + "293F7": "jiū", + "293F9": "juàn", + "293FA": "jué", + "293FC": "nà", + "293FD": "zhái", + "293FE": "tāo", + "293FF": "wěi", + "29400": "xiá", + "29401": "xiè", + "29405": "sà", + "29406": "jī", + "29409": "xiè", + "2940C": "duì", + "2940D": "zǐ", + "29418": "yuǎn", + "29419": "qìn", + "2941A": "fú", + "2941B": "péng", + "2941C": "páo", + "2941E": "yìn", + "29420": "hōng", + "29421": "zú", + "29423": "gōng", + "29424": "dòng", + "29425": "hē", + "29426": "wò", + "29428": "pāng", + "2942B": "sù", + "2942C": "kǎn", + "2942D": "niè", + "2942E": "háo", + "2942F": "fèng", + "29430": "è", + "29431": "yè", + "29434": "tíng", + "29435": "dòng", + "29436": "zhé", + "29437": "sāng", + "2943B": "mò", + "2943C": "sù", + "2943E": "lè", + "29440": "pǔ", + "29441": "é", + "29442": "zhuó", + "29443": "yè", + "29447": "xiāng", + "29448": "guàng", + "29449": "rěn", + "2944A": "líng", + "2944D": "ào", + "29450": "chāi", + "29452": "duó", + "29453": "qióng", + "29454": "kū", + "29455": "xū", + "29456": "huán", + "29457": "yāo", + "29458": "zhèn", + "29459": "tǐng", + "2945A": "běng", + "2945D": "áng", + "2945F": "kān", + "29461": "kū", + "29462": "péi", + "29463": "yòu", + "29464": "ǎo", + "29465": "mén", + "29466": "mò", + "2946C": "fǔ", + "2946D": "qīng", + "2946E": "là", + "2946F": "dǒu", + "29470": "tǎn", + "29473": "qiǎn", + "29474": "yào", + "29475": "wèi", + "29476": "hú", + "29477": "mò", + "29478": "hē", + "29479": "xuàn", + "2947B": "bì", + "2947C": "pō", + "2947E": "dī", + "29480": "zhěn", + "29482": "shī", + "29483": "kǎn", + "29484": "cè", + "29487": "xū", + "29488": "zhěn", + "2948A": "zhǔ", + "2948F": "huì", + "29490": "chǐ", + "29493": "hǒng", + "29494": "nóu", + "29495": "niè", + "29496": "yàn", + "29498": "chǒng", + "29499": "fǔ", + "2949A": "guāng", + "2949B": "qī", + "2949D": "gěn", + "2949E": "tǐng", + "294A2": "tǎn", + "294A3": "qiǎn", + "294A6": "jiù", + "294A7": "xū", + "294A8": "qǐ", + "294AA": "zhèn", + "294AE": "qiú", + "294B0": "ě", + "294B3": "huì", + "294B4": "hòng", + "294B5": "qǐng", + "294B7": "chē", + "294BA": "fù", + "294BC": "hōng", + "294BD": "xī", + "294BE": "wú", + "294BF": "máng", + "294C2": "tī", + "294C5": "hōng", + "294D0": "bó", + "294D2": "qǐn", + "294D3": "gěn", + "294D6": "fú", + "294D7": "kuǐ", + "294DD": "bié", + "294DE": "jìng", + "294DF": "kǎn", + "294E0": "guī", + "294E2": "gǎo", + "294E3": "xū", + "294E4": "àn", + "294E5": "yuè", + "294E6": "wù", + "294E7": "yí", + "294E8": "jīng", + "294EA": "lù", + "294EB": "quán", + "294EC": "tuí", + "294EE": "jì", + "294FA": "jiǒng", + "294FB": "jué", + "294FC": "piē", + "294FD": "kūn", + "29500": "wài", + "29501": "huì", + "29502": "dùn", + "29503": "yuǎn", + "29504": "jié", + "29506": "guì", + "29507": "gǎo", + "29508": "pò", + "29509": "mén", + "2950A": "zhuàn", + "2950B": "hàng", + "29514": "yóng", + "29515": "qiú", + "29517": "lèi", + "29518": "áng", + "29519": "pǐ", + "2951A": "wēng", + "2951D": "qìn", + "2951F": "qǐn", + "29520": "miè", + "29521": "dōu", + "29522": "mí", + "29523": "zhān", + "29525": "qǐng", + "29526": "yí", + "2952E": "bān", + "29531": "juān", + "29533": "zé", + "29534": "xù", + "29535": "lán", + "29536": "má", + "29537": "má", + "29538": "ōu", + "29539": "bēi", + "2953B": "póu", + "2953C": "xù", + "29540": "ào", + "29546": "hǒng", + "29549": "hǒng", + "2954A": "zhǎn", + "2954C": "sěn", + "2954D": "gǎo", + "2954F": "pó", + "29550": "liào", + "29555": "wài", + "29556": "xuān", + "2955C": "kuí", + "2955F": "è", + "29560": "hàn", + "29561": "sè", + "29564": "dàn", + "2956A": "xuān", + "2956C": "è", + "2956D": "gài", + "2956F": "dāo", + "29571": "měng", + "29572": "yī", + "29573": "nǐng", + "29575": "pín", + "29579": "cāng", + "2957E": "yuàn", + "29580": "è", + "29581": "niè", + "29584": "yǐn", + "29587": "qiāo", + "29589": "hōng", + "2958A": "líng", + "2958C": "chān", + "2958D": "yǐng", + "29592": "guān", + "29594": "niǎo", + "29595": "xū", + "29596": "tán", + "29597": "jìn", + "2959B": "péng", + "2959D": "liáo", + "295A0": "bèi", + "295A3": "xín", + "295A4": "tún", + "295A5": "chāo", + "295A6": "gān", + "295A8": "hū", + "295A9": "wǎng", + "295AC": "fú", + "295AD": "pèi", + "295AF": "náo", + "295B0": "xún", + "295B1": "xuè", + "295B4": "liǔ", + "295B5": "líng", + "295B6": "xuè", + "295B7": "qū", + "295B8": "háo", + "295B9": "yí", + "295BA": "hàn", + "295BC": "fú", + "295BD": "bá", + "295BE": "yí", + "295C0": "bó", + "295C4": "hōng", + "295C5": "lì", + "295C9": "sà", + "295CA": "xī", + "295CE": "shì", + "295CF": "piāo", + "295D0": "huà", + "295D1": "yí", + "295D2": "bó", + "295D3": "bó", + "295D4": "něi", + "295D5": "qiú", + "295D8": "wěi", + "295D9": "chè", + "295DA": "yóu", + "295DC": "wèi", + "295DD": "huǐ", + "295DE": "sà", + "295E2": "hòng", + "295E3": "sōu", + "295E4": "hàn", + "295E5": "páo", + "295E7": "fáng", + "295E9": "liú", + "295EA": "zhòu", + "295EB": "pí", + "295ED": "lì", + "295F0": "chuí", + "295F1": "xī", + "295F2": "zhēng", + "295F4": "bèng", + "295F5": "zhěng", + "295F6": "suì", + "295F7": "yǎn", + "295FC": "qīng", + "295FD": "wù", + "295FE": "liǎng", + "29600": "zhào", + "29601": "liáng", + "29605": "jiē", + "29607": "hōng", + "29608": "yōu", + "2960A": "là", + "2960B": "hòu", + "2960D": "yuàn", + "2960E": "hóng", + "2960F": "yè", + "29611": "yǐng", + "29612": "xuǎn", + "29613": "yóu", + "29618": "quán", + "2961C": "táng", + "2961D": "suǒ", + "2961F": "lì", + "29620": "sōu", + "29621": "lì", + "29624": "yù", + "29627": "yì", + "2962D": "xiū", + "2962E": "áo", + "2962F": "tuán", + "29630": "sù", + "29631": "shuài", + "29633": "yù", + "29635": "fēng", + "29639": "sù", + "2963A": "tuí", + "2963B": "yù", + "2963C": "zhēng", + "2963D": "zhēng", + "2963F": "táo", + "29644": "liú", + "29646": "chéng", + "29647": "suí", + "29648": "sāo", + "2964F": "gǔ", + "29650": "fēng", + "29651": "liè", + "29652": "piāo", + "29656": "lì", + "29658": "lóng", + "29659": "chū", + "2965A": "xiāo", + "2965B": "hōng", + "2965C": "xiè", + "2965D": "shè", + "29660": "lóng", + "29661": "hōu", + "29662": "xuán", + "29663": "fēng", + "29665": "bá", + "29666": "bó", + "29667": "táo", + "29668": "sù", + "29669": "zhào", + "2966A": "biāo", + "2966B": "sōu", + "2966C": "tuí", + "2966D": "suǒ", + "2966E": "xiāo", + "2966F": "héng", + "29670": "sāo", + "29672": "fēi", + "29677": "niù", + "29678": "mǎng", + "2967D": "huán", + "2967E": "zhī", + "29682": "yì", + "29684": "yù", + "29687": "yí", + "29688": "yuē", + "29689": "chí", + "29695": "yǐn", + "29696": "niù", + "29697": "rǒng", + "2969B": "nà", + "296A3": "tián", + "296A5": "bā", + "296AA": "ěr", + "296AB": "zhēng", + "296AC": "è", + "296AD": "póu", + "296AE": "jī", + "296AF": "ní", + "296B1": "jiǒng", + "296B2": "jiá", + "296B5": "gān", + "296B9": "líng", + "296BB": "zuì", + "296BE": "bèi", + "296C5": "shū", + "296C6": "yǐ", + "296C7": "pāi", + "296CB": "nǎo", + "296CC": "shì", + "296CE": "mǎn", + "296CF": "shì", + "296D1": "tí", + "296D8": "gōng", + "296DD": "lèi", + "296DE": "bǎo", + "296DF": "yuān", + "296E0": "zuō", + "296E1": "láng", + "296E2": "xiū", + "296E5": "zài", + "296E6": "chèng", + "296E7": "jiān", + "296E8": "mào", + "296E9": "jiá", + "296EA": "yù", + "296ED": "yù", + "296EE": "yí", + "296F2": "māng", + "296F3": "zài", + "296F5": "zhuì", + "296F6": "tí", + "296F9": "xì", + "296FA": "jú", + "296FB": "zàn", + "296FC": "lù", + "296FD": "táo", + "29700": "zhuì", + "29701": "líng", + "29703": "jù", + "29706": "jī", + "29707": "juǎn", + "2970A": "zī", + "2970C": "yuē", + "2970D": "dōng", + "29712": "nǎng", + "29716": "chóng", + "2971F": "àng", + "29723": "gēng", + "29725": "bō", + "29726": "dìng", + "29727": "wěi", + "2972C": "quán", + "2972D": "kē", + "29730": "pì", + "29731": "kǎn", + "29732": "fú", + "29733": "yǒng", + "29735": "tuán", + "29736": "tǒu", + "29737": "yòu", + "29738": "yāo", + "2973A": "yē", + "2973D": "yàn", + "29748": "xián", + "2974A": "tí", + "2974C": "suì", + "29750": "cí", + "29754": "xǔ", + "29755": "wù", + "29756": "cān", + "29757": "yù", + "2975A": "chǎn", + "2975B": "xiá", + "2975D": "kào", + "2975E": "cāng", + "2975F": "chā", + "29760": "qiǔ", + "29763": "dā", + "29765": "sù", + "29768": "huā", + "29777": "wū", + "29778": "yuān", + "2977D": "jiàng", + "2977E": "xiǎng", + "2977F": "zhāi", + "29780": "sǎn", + "29781": "mó", + "29783": "shǎng", + "29784": "cáo", + "29785": "suī", + "29786": "chuáng", + "29787": "mí", + "29788": "zhú", + "29789": "chóng", + "2978A": "jì", + "2978B": "chóng", + "29799": "lián", + "2979E": "hài", + "297A4": "dūn", + "297A5": "xiǎng", + "297A6": "chēng", + "297A7": "shǎng", + "297A8": "lì", + "297A9": "huáng", + "297AC": "dèng", + "297AF": "liáng", + "297B6": "zā", + "297BA": "huò", + "297BB": "lín", + "297BE": "dú", + "297BF": "hàn", + "297C0": "yōng", + "297C1": "yuàn", + "297C2": "guò", + "297C3": "líng", + "297C5": "liǎn", + "297C7": "ào", + "297C8": "dāng", + "297C9": "yì", + "297CA": "nóng", + "297CB": "shàn", + "297CD": "xìn", + "297D0": "dá", + "297D1": "yù", + "297D2": "cān", + "297D3": "wò", + "297D4": "chá", + "297D5": "bó", + "297D7": "jiǎn", + "297DE": "méng", + "297DF": "wěi", + "297E0": "mó", + "297E5": "shuì", + "297E6": "jié", + "297E7": "shuò", + "297E8": "huò", + "297EB": "chuò", + "297ED": "lóng", + "297EE": "huài", + "297F0": "tuō", + "297F3": "yú", + "297F6": "chàn", + "297F7": "yōng", + "297F8": "huò", + "297FA": "lǎn", + "297FF": "nà", + "29800": "bā", + "29801": "gān", + "29802": "yǐ", + "29803": "jiá", + "29805": "dá", + "29806": "dìng", + "29807": "xùn", + "29808": "rěn", + "29809": "juǎn", + "2980A": "tuán", + "2980B": "xǔ", + "2980C": "sòng", + "2980E": "cáo", + "2980F": "chēng", + "29811": "dǐng", + "2981A": "hái", + "2981F": "wǔ", + "29826": "qǐ", + "29828": "jī", + "2982E": "kuí", + "2982F": "wéi", + "29836": "shǒu", + "29837": "fú", + "29839": "tuán", + "2983B": "bié", + "2983D": "tán", + "2983E": "hāng", + "2983F": "piē", + "29843": "yú", + "29844": "tán", + "2984C": "xiāng", + "2984E": "xiū", + "29853": "wěng", + "29854": "hài", + "29855": "péng", + "2985D": "tán", + "2985F": "bié", + "29860": "xiāng", + "29863": "yǐ", + "29866": "piáo", + "29867": "huán", + "29868": "mǔ", + "29869": "bā", + "2986B": "fàn", + "2986F": "dīng", + "29877": "fēn", + "2987A": "jiè", + "2987E": "suó", + "29884": "wàn", + "29885": "gē", + "29888": "fēn", + "2988A": "tuó", + "2988C": "wén", + "2988D": "guā", + "2988E": "duō", + "29890": "zhé", + "29891": "cǐ", + "29892": "yǎo", + "29894": "bàn", + "29895": "bù", + "29896": "mò", + "29898": "pǒ", + "2989B": "gé", + "2989E": "liú", + "298A1": "rǎn", + "298A8": "gān", + "298AA": "hú", + "298AB": "móu", + "298AE": "xiū", + "298AF": "huāng", + "298B0": "fú", + "298B1": "huí", + "298B3": "qú", + "298B4": "jié", + "298B5": "tuō", + "298B6": "yú", + "298B7": "mò", + "298B8": "zhōu", + "298B9": "jiù", + "298BB": "shú", + "298BC": "kuāng", + "298BD": "qióng", + "298BE": "liè", + "298BF": "fù", + "298CA": "xù", + "298D6": "lìn", + "298D8": "niè", + "298DA": "pī", + "298DC": "fù", + "298DD": "bù", + "298DE": "yì", + "298E1": "bó", + "298E3": "é", + "298E9": "zhé", + "298EB": "lì", + "298EE": "tù", + "298EF": "dá", + "298F1": "lù", + "298F2": "yān", + "298F3": "dōng", + "298F4": "qiè", + "298F5": "wǎn", + "298F6": "mǐng", + "298F7": "zuī", + "298F8": "fù", + "298F9": "qū", + "298FA": "bēn", + "298FB": "ǎo", + "298FC": "qiāng", + "29901": "qūn", + "29908": "què", + "29909": "huá", + "2990A": "xiàn", + "2990B": "kùn", + "2990F": "cuì", + "29912": "yí", + "29916": "chī", + "29917": "zòng", + "29918": "nǎo", + "29919": "chéng", + "2991A": "duān", + "2991B": "yóng", + "2991C": "zhě", + "2991E": "tàn", + "2991F": "yáng", + "29920": "xié", + "29921": "xuān", + "29923": "duàn", + "29924": "shuǎ", + "29925": "xián", + "29926": "xián", + "29929": "é", + "29932": "lā", + "29938": "wèi", + "29939": "yōu", + "2993A": "yú", + "2993D": "tī", + "2993F": "jīn", + "29941": "táng", + "29942": "qí", + "29944": "diān", + "29945": "tāo", + "29946": "lǜ", + "29947": "zhàn", + "29948": "wēn", + "29949": "jì", + "2994A": "āo", + "2994B": "òu", + "2994C": "qià", + "29950": "shī", + "29951": "tǎ", + "29954": "mò", + "29958": "yóu", + "29960": "zhá", + "29963": "yáo", + "2996B": "chōng", + "2996C": "lí", + "2996D": "yú", + "2996E": "chǎn", + "2996F": "yī", + "29972": "chì", + "29974": "lí", + "2997D": "tú", + "2997F": "zú", + "29982": "xián", + "29987": "xì", + "29989": "bié", + "2998A": "hán", + "2998B": "qí", + "2998C": "sāng", + "2998E": "fēi", + "29990": "shàn", + "29998": "huān", + "299A0": "bàng", + "299A1": "yú", + "299A2": "yú", + "299A4": "jí", + "299B1": "kuǎi", + "299B2": "zōng", + "299B9": "xiàn", + "299BA": "méng", + "299C3": "lì", + "299C4": "zhì", + "299C5": "fán", + "299C6": "liè", + "299C7": "cài", + "299C8": "dú", + "299C9": "guāng", + "299CA": "xiòng", + "299CB": "lí", + "299CC": "qì", + "299CF": "jué", + "299D0": "tuō", + "299D2": "jù", + "299D3": "xiāo", + "299D8": "qú", + "299DC": "zhuǎn", + "299E1": "jué", + "299E6": "jiè", + "299E8": "zhòu", + "299E9": "xiàn", + "299EA": "lóng", + "299EB": "yǎng", + "299EC": "rǎn", + "299ED": "yì", + "299EE": "liè", + "299EF": "bō", + "299F0": "hún", + "299F1": "jì", + "299F2": "dòng", + "299F3": "zhōu", + "299F4": "quān", + "299F5": "jié", + "299FA": "jú", + "299FC": "bēn", + "299FF": "bī", + "29A00": "gé", + "29A01": "chǔn", + "29A03": "qián", + "29A04": "sōu", + "29A05": "wèi", + "29A06": "chéng", + "29A07": "lóu", + "29A08": "yú", + "29A09": "lā", + "29A0A": "qián", + "29A0B": "diān", + "29A0C": "tǎ", + "29A0D": "zhàn", + "29A0F": "fán", + "29A10": "liè", + "29A11": "tīng", + "29A12": "jī", + "29A13": "qiān", + "29A14": "hú", + "29A17": "yú", + "29A18": "qì", + "29A19": "yú", + "29A1A": "wā", + "29A1C": "bà", + "29A1D": "qí", + "29A1E": "sǎ", + "29A1F": "qiāo", + "29A20": "yà", + "29A21": "xiǎn", + "29A28": "cī", + "29A29": "fàn", + "29A2B": "kǔn", + "29A2C": "gǔn", + "29A2D": "quē", + "29A2E": "è", + "29A2F": "qióng", + "29A32": "mà", + "29A33": "kū", + "29A34": "yǎo", + "29A37": "quē", + "29A38": "chū", + "29A39": "jiǎ", + "29A3B": "zhǔ", + "29A3D": "duī", + "29A3E": "wá", + "29A40": "nǎo", + "29A44": "yán", + "29A45": "tóng", + "29A4B": "xíng", + "29A4C": "gǔn", + "29A4D": "pīng", + "29A51": "yǔ", + "29A52": "hè", + "29A54": "zhuó", + "29A57": "shē", + "29A58": "yǔ", + "29A5B": "jì", + "29A5D": "qiāng", + "29A5E": "shuì", + "29A5F": "chuò", + "29A60": "zú", + "29A61": "léng", + "29A62": "ní", + "29A64": "wā", + "29A65": "zhá", + "29A67": "dàn", + "29A6E": "dù", + "29A6F": "biàn", + "29A70": "jiē", + "29A71": "qià", + "29A72": "hé", + "29A73": "chòng", + "29A74": "yán", + "29A76": "yàn", + "29A7A": "sóng", + "29A7B": "téng", + "29A7C": "yǎo", + "29A7E": "kāo", + "29A80": "zhuī", + "29A81": "guì", + "29A82": "ái", + "29A83": "hài", + "29A88": "suǒ", + "29A89": "xù", + "29A8A": "biāo", + "29A8C": "fèng", + "29A8D": "qū", + "29A8E": "mǎng", + "29A90": "guó", + "29A96": "bì", + "29A97": "jué", + "29A98": "chuáng", + "29A9B": "pú", + "29A9F": "yì", + "29AA2": "qiān", + "29AA3": "yì", + "29AA4": "è", + "29AA5": "líng", + "29AA7": "bì", + "29AAD": "huò", + "29AAE": "mǒ", + "29AB1": "xūn", + "29AB4": "yàn", + "29AB8": "lì", + "29ABA": "tán", + "29ABE": "luán", + "29AC0": "kài", + "29AC1": "mào", + "29AC2": "xiāo", + "29AC7": "ǎi", + "29ACA": "tǎ", + "29ACD": "mèi", + "29ACF": "guō", + "29AD3": "gǎo", + "29AD4": "náo", + "29AD5": "háo", + "29AE0": "quē", + "29AE5": "cáo", + "29AE6": "sào", + "29AEB": "pí", + "29AF2": "xiē", + "29AF3": "xiāo", + "29AF4": "jú", + "29AF9": "chéng", + "29AFA": "nǎo", + "29B00": "nèi", + "29B0D": "mǔ", + "29B0F": "shāo", + "29B11": "diān", + "29B14": "líng", + "29B16": "zhěn", + "29B17": "yǎo", + "29B19": "fù", + "29B1A": "qián", + "29B1B": "qióng", + "29B1C": "jú", + "29B1D": "bìng", + "29B1E": "máo", + "29B1F": "zhà", + "29B20": "tāi", + "29B24": "chōng", + "29B2B": "zhǎi", + "29B2D": "shī", + "29B2E": "yòng", + "29B30": "qióng", + "29B31": "dào", + "29B32": "tì", + "29B33": "zhuǐ", + "29B35": "yìn", + "29B37": "nǎo", + "29B38": "bō", + "29B39": "kuāng", + "29B3A": "zhǐ", + "29B3B": "duǒ", + "29B3C": "cōng", + "29B3D": "bǎo", + "29B47": "lí", + "29B4A": "jú", + "29B4B": "wén", + "29B4C": "liè", + "29B4F": "wǒ", + "29B50": "shǐ", + "29B51": "niǎo", + "29B52": "máng", + "29B53": "jiū", + "29B58": "xiū", + "29B5D": "wō", + "29B5F": "dào", + "29B61": "xī", + "29B62": "àn", + "29B63": "dá", + "29B64": "zǒng", + "29B65": "hàn", + "29B66": "chuí", + "29B67": "bī", + "29B69": "dòng", + "29B6B": "zhǎng", + "29B6F": "yā", + "29B72": "dí", + "29B73": "huō", + "29B77": "mín", + "29B7A": "fù", + "29B7C": "bǎo", + "29B7D": "kè", + "29B7E": "máo", + "29B7F": "rè", + "29B80": "zōng", + "29B81": "qià", + "29B82": "xiā", + "29B83": "sōu", + "29B84": "xiū", + "29B85": "nà", + "29B89": "mán", + "29B8E": "zhā", + "29B8F": "chán", + "29B90": "shè", + "29B91": "wǒ", + "29B96": "ái", + "29B97": "bàng", + "29B98": "hāo", + "29B9A": "sāo", + "29B9B": "suǒ", + "29B9C": "tì", + "29B9D": "yà", + "29B9F": "bìng", + "29BA0": "róng", + "29BAB": "shā", + "29BAC": "wěng", + "29BAF": "áo", + "29BB1": "zhuāng", + "29BB3": "piào", + "29BB4": "suī", + "29BB5": "yī", + "29BB6": "sōu", + "29BB7": "dōu", + "29BB8": "sōu", + "29BB9": "luó", + "29BC3": "fèi", + "29BC4": "zùn", + "29BC6": "nào", + "29BC7": "dēng", + "29BC8": "zhí", + "29BC9": "cuō", + "29BCA": "liáo", + "29BCB": "jǐ", + "29BCC": "bō", + "29BCD": "cóng", + "29BCE": "chéng", + "29BCF": "bǔ", + "29BD1": "sān", + "29BD2": "zàn", + "29BD8": "jiào", + "29BDB": "yào", + "29BDC": "lǔ", + "29BDE": "càn", + "29BE8": "nǐ", + "29BF0": "jié", + "29BF1": "pú", + "29BF2": "zhuàng", + "29BF3": "zàn", + "29BFA": "lì", + "29BFD": "là", + "29C00": "chōng", + "29C03": "zhàn", + "29C0D": "biàn", + "29C0E": "wēng", + "29C13": "hòng", + "29C17": "pīn", + "29C19": "sè", + "29C1E": "nǐ", + "29C1F": "fēn", + "29C20": "xǔ", + "29C22": "shǐ", + "29C24": "jù", + "29C28": "jué", + "29C2A": "yù", + "29C2C": "guō", + "29C2D": "guō", + "29C2F": "hú", + "29C32": "lì", + "29C33": "xié", + "29C34": "ér", + "29C35": "yuán", + "29C36": "hái", + "29C39": "jìng", + "29C3B": "kè", + "29C3D": "zōng", + "29C3E": "fèi", + "29C40": "pēng", + "29C41": "gēng", + "29C43": "jiān", + "29C44": "ní", + "29C46": "xián", + "29C47": "lì", + "29C48": "chǎo", + "29C4A": "ér", + "29C4B": "gēng", + "29C4C": "yù", + "29C4D": "hú", + "29C4E": "fèi", + "29C4F": "áo", + "29C53": "ěr", + "29C58": "kè", + "29C59": "kù", + "29C5A": "bó", + "29C5D": "yè", + "29C5E": "jiào", + "29C66": "chǎo", + "29C67": "gēng", + "29C68": "rù", + "29C6A": "yuè", + "29C6C": "lín", + "29C71": "yù", + "29C72": "yuè", + "29C73": "zhāi", + "29C74": "xiāo", + "29C77": "miè", + "29C7B": "guǐ", + "29C7C": "jiū", + "29C7E": "tuò", + "29C81": "xí", + "29C82": "wěi", + "29C83": "zhuó", + "29C84": "wèi", + "29C85": "kuí", + "29C88": "mèi", + "29C8A": "hào", + "29C8B": "hāng", + "29C8C": "fāng", + "29C8D": "niú", + "29C8E": "yòu", + "29C8F": "huà", + "29C92": "làng", + "29CA0": "zhú", + "29CA1": "guǐ", + "29CA2": "bì", + "29CA3": "jiǎ", + "29CA4": "tiáo", + "29CA6": "lǜ", + "29CA7": "kǒng", + "29CA8": "zuǐ", + "29CA9": "líng", + "29CAA": "qí", + "29CAC": "zhú", + "29CB1": "gǔ", + "29CB2": "zù", + "29CB4": "yāng", + "29CB5": "sū", + "29CB7": "kuí", + "29CB9": "chāng", + "29CBB": "yáo", + "29CBE": "yù", + "29CC5": "shū", + "29CC6": "lài", + "29CC7": "yì", + "29CC8": "dōu", + "29CCC": "wú", + "29CCD": "yǐng", + "29CCE": "fú", + "29CCF": "zhuàn", + "29CD0": "fǔ", + "29CD2": "sù", + "29CD3": "lǐ", + "29CD4": "yào", + "29CD5": "tuì", + "29CDD": "guì", + "29CE1": "lǜ", + "29CE2": "yàn", + "29CE3": "qí", + "29CE4": "làng", + "29CE5": "zhú", + "29CE7": "guǐ", + "29CE8": "hū", + "29CEF": "jīng", + "29CF2": "chǐ", + "29CF5": "jú", + "29CF6": "zhá", + "29CF8": "miáo", + "29D00": "zhū", + "29D01": "gān", + "29D02": "xiōng", + "29D03": "jí", + "29D07": "shài", + "29D08": "mèi", + "29D09": "yùn", + "29D0D": "shòu", + "29D10": "lǜ", + "29D11": "yòu", + "29D12": "jiàng", + "29D13": "nuó", + "29D18": "jù", + "29D19": "yòu", + "29D1C": "yì", + "29D1D": "téng", + "29D1E": "wéi", + "29D1F": "chě", + "29D20": "lìn", + "29D21": "gù", + "29D23": "lì", + "29D24": "liào", + "29D27": "jiāo", + "29D28": "yáng", + "29D29": "biāo", + "29D2A": "qí", + "29D2E": "yì", + "29D31": "bīn", + "29D32": "méng", + "29D33": "chà", + "29D35": "gān", + "29D39": "qú", + "29D3A": "dí", + "29D3B": "léi", + "29D40": "líng", + "29D44": "huān", + "29D45": "qú", + "29D47": "luó", + "29D49": "kuí", + "29D4D": "qiú", + "29D4E": "yǔ", + "29D4F": "huà", + "29D53": "lèi", + "29D55": "rèn", + "29D56": "xiǎo", + "29D57": "sì", + "29D5A": "dù", + "29D5B": "biē", + "29D60": "niú", + "29D62": "hè", + "29D63": "pēi", + "29D65": "fèi", + "29D66": "mù", + "29D69": "fū", + "29D6C": "hú", + "29D6D": "wáng", + "29D6E": "shā", + "29D70": "jiāo", + "29D71": "wǔ", + "29D79": "fù", + "29D81": "bǐng", + "29D82": "zhù", + "29D84": "zhú", + "29D85": "chī", + "29D87": "shěn", + "29D88": "hū", + "29D89": "bū", + "29D8E": "rǎn", + "29D96": "mù", + "29D98": "lì", + "29D9B": "jiā", + "29D9E": "mà", + "29DA1": "méng", + "29DA2": "móu", + "29DA3": "zhōu", + "29DA4": "xiǎn", + "29DA5": "huǐ", + "29DA6": "guài", + "29DA7": "jiù", + "29DA9": "mù", + "29DAB": "rù", + "29DAD": "wú", + "29DAF": "rú", + "29DB1": "zhà", + "29DC1": "nuǒ", + "29DC2": "xié", + "29DC4": "jiàng", + "29DCB": "lǐ", + "29DCC": "shū", + "29DCD": "yì", + "29DCE": "dí", + "29DCF": "qíng", + "29DD0": "jú", + "29DD3": "zhì", + "29DD5": "láng", + "29DD6": "bù", + "29DD7": "kuáng", + "29DD8": "yì", + "29DDA": "bó", + "29DE7": "chì", + "29DED": "jiàng", + "29DEF": "wò", + "29DF0": "xùn", + "29DF5": "tūn", + "29DF6": "máng", + "29DF8": "fáng", + "29DF9": "zhuó", + "29DFB": "qià", + "29DFD": "tǎ", + "29DFE": "qí", + "29E00": "pèng", + "29E01": "biē", + "29E02": "fèn", + "29E03": "tù", + "29E04": "huà", + "29E07": "è", + "29E0B": "è", + "29E0E": "dìng", + "29E10": "rú", + "29E16": "è", + "29E1E": "yàn", + "29E1F": "sì", + "29E25": "yíng", + "29E26": "ní", + "29E27": "ní", + "29E28": "yí", + "29E39": "mí", + "29E3E": "yé", + "29E3F": "pō", + "29E40": "còu", + "29E42": "wèi", + "29E44": "hài", + "29E45": "yīng", + "29E47": "tíng", + "29E48": "zhì", + "29E49": "fēi", + "29E4A": "yóu", + "29E4D": "kuí", + "29E4E": "àn", + "29E4F": "bà", + "29E51": "hàn", + "29E5E": "nán", + "29E5F": "nài", + "29E62": "jīng", + "29E65": "wēi", + "29E71": "chù", + "29E73": "suǒ", + "29E74": "tāo", + "29E75": "qí", + "29E76": "táng", + "29E77": "wěi", + "29E78": "gǎn", + "29E7A": "gé", + "29E7C": "hàn", + "29E7E": "nà", + "29E7F": "gé", + "29E84": "zhēng", + "29E97": "tǎ", + "29E9B": "sī", + "29E9D": "nì", + "29E9E": "sǎng", + "29EAB": "xié", + "29EAF": "zú", + "29EB0": "yú", + "29EB1": "nì", + "29EB2": "qī", + "29EB5": "shēn", + "29EBC": "bū", + "29ECB": "kūn", + "29ECC": "lí", + "29ECE": "guā", + "29ED6": "yǎn", + "29ED7": "bù", + "29ED8": "jiàn", + "29EDA": "wú", + "29EDB": "cén", + "29EDC": "lín", + "29EDD": "zhuàn", + "29EDF": "huī", + "29EE1": "tóng", + "29EE2": "zhǎ", + "29EE4": "hēi", + "29EE7": "guǒ", + "29EF1": "jǐng", + "29EF5": "dié", + "29EF7": "yíng", + "29EFC": "zhì", + "29F02": "wěi", + "29F04": "jì", + "29F05": "rǒng", + "29F08": "ào", + "29F09": "dāng", + "29F0A": "luó", + "29F0B": "yè", + "29F0C": "wēi", + "29F12": "qiáng", + "29F19": "gé", + "29F1A": "jì", + "29F26": "zòu", + "29F28": "yí", + "29F2B": "zhǎ", + "29F2D": "liè", + "29F34": "yè", + "29F3C": "zhān", + "29F40": "chóu", + "29F41": "biāo", + "29F46": "xù", + "29F47": "yōu", + "29F4D": "xiè", + "29F4E": "wéi", + "29F4F": "lì", + "29F5B": "bó", + "29F5C": "jiǎn", + "29F5D": "chán", + "29F5E": "kūn", + "29F61": "qíng", + "29F67": "shuāng", + "29F68": "xī", + "29F69": "qú", + "29F70": "luó", + "29F73": "dǎng", + "29F74": "nián", + "29F75": "lǐ", + "29F77": "bà", + "29F79": "è", + "29F7A": "fū", + "29F7B": "fù", + "29F7C": "hǔn", + "29F7D": "zhà", + "29F7E": "ān", + "29F81": "qiú", + "29F82": "chóu", + "29F83": "miǎn", + "29F84": "xùn", + "29F85": "tù", + "29F86": "ní", + "29F87": "hu", + "29F88": "shū", + "29F8A": "xū", + "29F8B": "zhòng", + "29F8C": "kāng", + "29F92": "xiāo", + "29F93": "xiāo", + "29F94": "cì", + "29F95": "chì", + "29F97": "diāo", + "29F98": "yì", + "29F9A": "dīng", + "29F9D": "hàn", + "29F9E": "wán", + "29FA0": "yǐ", + "29FA1": "bào", + "29FA2": "yì", + "29FA7": "xùn", + "29FAC": "xiáng", + "29FB3": "bí", + "29FB6": "jié", + "29FB7": "gē", + "29FB8": "zè", + "29FBA": "zhèn", + "29FBB": "hú", + "29FBC": "xī", + "29FBD": "xīn", + "29FBE": "xiāo", + "29FBF": "fù", + "29FC0": "zhòng", + "29FC2": "mào", + "29FC3": "xīn", + "29FC4": "qiāng", + "29FC8": "fén", + "29FC9": "bān", + "29FCA": "huān", + "29FD1": "jiāo", + "29FD3": "bào", + "29FD4": "yā", + "29FD5": "yáo", + "29FDB": "xì", + "29FDD": "jù", + "29FDF": "qù", + "29FE0": "yuè", + "29FE1": "tái", + "29FE2": "tǒu", + "29FE3": "mò", + "29FE4": "zhá", + "29FE5": "qú", + "29FE7": "fū", + "29FE9": "qú", + "29FEA": "chì", + "29FEC": "yóu", + "29FF7": "tí", + "29FFA": "wā", + "29FFD": "tuó", + "29FFF": "chú", + "2A001": "gē", + "2A008": "yuān", + "2A009": "gē", + "2A00A": "qú", + "2A00F": "jù", + "2A012": "dié", + "2A013": "yí", + "2A014": "shī", + "2A015": "yì", + "2A017": "guǐ", + "2A018": "jiàng", + "2A01A": "sōng", + "2A01B": "qióng", + "2A01D": "è", + "2A01E": "huāng", + "2A01F": "huí", + "2A020": "xún", + "2A023": "jú", + "2A025": "zhái", + "2A026": "chì", + "2A027": "lǎo", + "2A029": "qí", + "2A02A": "xiū", + "2A02C": "huī", + "2A02D": "tóng", + "2A03A": "fù", + "2A03D": "xún", + "2A03E": "jié", + "2A03F": "mǐ", + "2A040": "yù", + "2A048": "zhuàng", + "2A049": "jiāo", + "2A04A": "zhì", + "2A04B": "chéng", + "2A04D": "jié", + "2A04E": "xiāo", + "2A04F": "chén", + "2A050": "lí", + "2A051": "yuè", + "2A053": "zhì", + "2A054": "láo", + "2A055": "wò", + "2A056": "qú", + "2A058": "wāng", + "2A05A": "yī", + "2A05B": "yì", + "2A05C": "láng", + "2A05E": "tóu", + "2A05F": "ān", + "2A060": "jué", + "2A061": "yàn", + "2A065": "jù", + "2A067": "zhèn", + "2A069": "zhì", + "2A06A": "mǎng", + "2A06E": "xiù", + "2A071": "chuáng", + "2A072": "chū", + "2A078": "qiāng", + "2A079": "fēi", + "2A07A": "cháng", + "2A07C": "mián", + "2A07D": "sù", + "2A07E": "ǎo", + "2A080": "fǔ", + "2A084": "wèi", + "2A085": "zhī", + "2A086": "mín", + "2A087": "chāng", + "2A088": "yán", + "2A089": "yù", + "2A08B": "fù", + "2A08C": "tà", + "2A08D": "jǐ", + "2A08F": "fèi", + "2A092": "hú", + "2A093": "jū", + "2A095": "yǔ", + "2A09B": "qí", + "2A09C": "méi", + "2A09F": "biē", + "2A0A0": "guǒ", + "2A0A4": "mìng", + "2A0A6": "wǎn", + "2A0A7": "wǎn", + "2A0B4": "jīng", + "2A0B5": "yù", + "2A0B6": "xián", + "2A0B9": "chūn", + "2A0BA": "jí", + "2A0BC": "xiāng", + "2A0BD": "pén", + "2A0BE": "fù", + "2A0C2": "liú", + "2A0C4": "sāi", + "2A0C5": "xuē", + "2A0C6": "zòu", + "2A0C8": "jié", + "2A0CB": "zhān", + "2A0CD": "yú", + "2A0CE": "yú", + "2A0CF": "méi", + "2A0D0": "miǎo", + "2A0D1": "mào", + "2A0D2": "duó", + "2A0D3": "fù", + "2A0DB": "jiàn", + "2A0E6": "miáo", + "2A0E8": "āo", + "2A0ED": "kè", + "2A0F6": "hóu", + "2A0FA": "gòu", + "2A0FC": "xī", + "2A0FE": "róng", + "2A0FF": "gē", + "2A100": "pán", + "2A101": "yuán", + "2A102": "xià", + "2A105": "shā", + "2A106": "pī", + "2A108": "qíng", + "2A109": "yōng", + "2A10A": "qú", + "2A10C": "gòng", + "2A10E": "gé", + "2A10F": "xiān", + "2A111": "sù", + "2A115": "bān", + "2A116": "qí", + "2A117": "hòu", + "2A11B": "xī", + "2A11D": "wū", + "2A12D": "qī", + "2A12E": "hù", + "2A12F": "guī", + "2A131": "dí", + "2A132": "shāng", + "2A133": "mài", + "2A134": "mǐn", + "2A135": "jì", + "2A136": "xí", + "2A137": "xiān", + "2A138": "jí", + "2A139": "cháng", + "2A13A": "kòu", + "2A13B": "chōng", + "2A142": "zhāng", + "2A143": "piǎo", + "2A144": "sù", + "2A145": "lüè", + "2A146": "lí", + "2A147": "mèng", + "2A148": "chōng", + "2A149": "tiān", + "2A14B": "líng", + "2A14D": "chì", + "2A156": "chōng", + "2A159": "chì", + "2A15D": "niǎo", + "2A15F": "yóng", + "2A16E": "mì", + "2A170": "shū", + "2A172": "xì", + "2A174": "è", + "2A175": "zī", + "2A178": "jié", + "2A179": "jī", + "2A17A": "hōu", + "2A17B": "shèng", + "2A17C": "lì", + "2A17E": "qī", + "2A180": "zhōu", + "2A181": "sī", + "2A182": "qú", + "2A18B": "xié", + "2A197": "sī", + "2A19B": "xū", + "2A1A0": "fù", + "2A1AF": "nóng", + "2A1B0": "yà", + "2A1B1": "liú", + "2A1B2": "jiǎ", + "2A1B3": "guī", + "2A1B4": "kuí", + "2A1B5": "chì", + "2A1B6": "càn", + "2A1B7": "chú", + "2A1B9": "guō", + "2A1BB": "dǎn", + "2A1BF": "jiàn", + "2A1C1": "dāng", + "2A1C2": "hòu", + "2A1C4": "kòu", + "2A1C6": "chù", + "2A1C7": "qiān", + "2A1C8": "ài", + "2A1CA": "pì", + "2A1D1": "xùn", + "2A1D2": "jīng", + "2A1D3": "mèng", + "2A1D5": "bīn", + "2A1D6": "lán", + "2A1D7": "gǔ", + "2A1D8": "chóu", + "2A1DB": "yōng", + "2A1DC": "guá", + "2A1DD": "yú", + "2A1DE": "zhòu", + "2A1ED": "cài", + "2A1EF": "liú", + "2A1F0": "bǔ", + "2A1F1": "luò", + "2A1F2": "jié", + "2A1F3": "zhēn", + "2A1F4": "miè", + "2A1F5": "guǎng", + "2A1F7": "jiá", + "2A1F9": "là", + "2A200": "shòu", + "2A203": "guō", + "2A206": "mèng", + "2A207": "qián", + "2A208": "lài", + "2A20A": "hé", + "2A20B": "tuán", + "2A211": "huī", + "2A218": "hōng", + "2A21C": "lǚ", + "2A21F": "jiá", + "2A225": "guī", + "2A228": "yī", + "2A229": "huān", + "2A230": "luó", + "2A234": "jué", + "2A238": "guàn", + "2A23B": "quán", + "2A23C": "niǎo", + "2A23F": "mán", + "2A242": "yùn", + "2A243": "wén", + "2A244": "chì", + "2A245": "chì", + "2A246": "zhī", + "2A248": "cí", + "2A249": "zhuàng", + "2A24A": "huá", + "2A24B": "jié", + "2A24C": "qú", + "2A24D": "tū", + "2A24E": "mín", + "2A24F": "méi", + "2A250": "yú", + "2A251": "áo", + "2A252": "bān", + "2A254": "pī", + "2A255": "zhēn", + "2A256": "lǔ", + "2A257": "chì", + "2A258": "tóu", + "2A25A": "jiē", + "2A25C": "zhān", + "2A262": "jīn", + "2A263": "lǔ", + "2A266": "jiàn", + "2A267": "tàn", + "2A268": "chāng", + "2A26A": "cì", + "2A26D": "wāi", + "2A26E": "còu", + "2A26F": "kàn", + "2A271": "biàn", + "2A278": "wēn", + "2A27B": "qiān", + "2A27F": "gàn", + "2A282": "huì", + "2A284": "gǎn", + "2A286": "jì", + "2A287": "gàn", + "2A289": "huái", + "2A28D": "sì", + "2A290": "fū", + "2A295": "pí", + "2A297": "cā", + "2A29C": "bèn", + "2A2A2": "shǐ", + "2A2A5": "huán", + "2A2A7": "guī", + "2A2AA": "ǒu", + "2A2B3": "páo", + "2A2B5": "yǐng", + "2A2B6": "tǐng", + "2A2B7": "xiào", + "2A2B9": "zhù", + "2A2BB": "yú", + "2A2C1": "jiàn", + "2A2C4": "qǔ", + "2A2C5": "wǎn", + "2A2C6": "kūn", + "2A2C7": "zhuī", + "2A2C9": "yù", + "2A2CA": "guǒ", + "2A2CB": "píng", + "2A2CC": "zuǐ", + "2A2CD": "zú", + "2A2CF": "zhū", + "2A2D0": "nuàn", + "2A2D1": "zhū", + "2A2D6": "piāo", + "2A2D7": "mí", + "2A2DC": "bì", + "2A2DD": "sù", + "2A2E1": "pú", + "2A2E2": "mí", + "2A2EB": "yè", + "2A2EC": "yǔ", + "2A2EE": "yù", + "2A2F0": "zhǔ", + "2A2F3": "líng", + "2A2FA": "nòu", + "2A2FE": "líng", + "2A300": "liǎo", + "2A302": "tuō", + "2A304": "bǐ", + "2A305": "nà", + "2A306": "qú", + "2A308": "pí", + "2A309": "dǒu", + "2A30A": "niè", + "2A30B": "tún", + "2A30D": "jī", + "2A30F": "líng", + "2A313": "kù", + "2A314": "sù", + "2A318": "tǒu", + "2A31E": "nái", + "2A31F": "zé", + "2A322": "tǒng", + "2A323": "gé", + "2A324": "duī", + "2A327": "jié", + "2A329": "tián", + "2A32A": "tiào", + "2A32B": "chí", + "2A32C": "qū", + "2A32E": "shā", + "2A330": "bó", + "2A331": "lí", + "2A333": "luò", + "2A335": "liáo", + "2A336": "shù", + "2A337": "děng", + "2A339": "chī", + "2A33A": "miè", + "2A33C": "táo", + "2A33D": "hún", + "2A33F": "nié", + "2A341": "jùn", + "2A342": "hù", + "2A344": "lù", + "2A345": "yè", + "2A347": "mò", + "2A348": "chào", + "2A34C": "suò", + "2A34E": "kē", + "2A34F": "fù", + "2A351": "chǎo", + "2A354": "suǒ", + "2A357": "qiū", + "2A35B": "sù", + "2A35D": "yùn", + "2A35F": "suǒ", + "2A360": "kū", + "2A361": "bó", + "2A363": "lǒu", + "2A364": "mò", + "2A366": "liǎn", + "2A367": "xuàn", + "2A368": "suǒ", + "2A369": "mán", + "2A36A": "bì", + "2A372": "tì", + "2A374": "lián", + "2A375": "tán", + "2A376": "shàn", + "2A378": "qú", + "2A379": "dú", + "2A37A": "huán", + "2A37B": "sào", + "2A37F": "kuàng", + "2A383": "niè", + "2A385": "niè", + "2A386": "luó", + "2A387": "zuó", + "2A388": "yì", + "2A389": "xiàn", + "2A38A": "chǎo", + "2A38B": "tiè", + "2A392": "shuò", + "2A394": "mǐ", + "2A397": "mí", + "2A39B": "wǎn", + "2A39D": "bèn", + "2A39E": "qiāng", + "2A3A0": "mǒ", + "2A3A3": "liú", + "2A3A4": "wò", + "2A3A6": "měi", + "2A3A8": "tóu", + "2A3AB": "mǔ", + "2A3AD": "méi", + "2A3B2": "zuò", + "2A3B4": "tún", + "2A3B5": "kàng", + "2A3B6": "tún", + "2A3BA": "chè", + "2A3BB": "zhèng", + "2A3BD": "chōng", + "2A3BE": "tiān", + "2A3C0": "zhì", + "2A3C1": "chán", + "2A3C2": "chán", + "2A3C5": "qīng", + "2A3C6": "tūn", + "2A3C7": "huǐ", + "2A3C8": "què", + "2A3C9": "zhān", + "2A3CA": "jiān", + "2A3CB": "chán", + "2A3CD": "huáng", + "2A3CF": "huī", + "2A3D0": "chí", + "2A3D2": "huáng", + "2A3D3": "héng", + "2A3D4": "yǔn", + "2A3D6": "tuān", + "2A3D7": "biān", + "2A3D9": "huáng", + "2A3DA": "yǔn", + "2A3DF": "mò", + "2A3E0": "gōng", + "2A3E2": "gōng", + "2A3E4": "guì", + "2A3E6": "chán", + "2A3E8": "què", + "2A3E9": "ruì", + "2A3EA": "kuàng", + "2A3EB": "piào", + "2A3EE": "rǔ", + "2A3F2": "niǔ", + "2A3F3": "hù", + "2A3F4": "jǐn", + "2A3F5": "nì", + "2A3F6": "bào", + "2A3F8": "nǐ", + "2A3FA": "bì", + "2A3FB": "hú", + "2A3FC": "lí", + "2A3FF": "zhū", + "2A400": "nǎ", + "2A402": "quǎn", + "2A403": "fěng", + "2A404": "bǐ", + "2A405": "lí", + "2A406": "bié", + "2A407": "nián", + "2A408": "dǒng", + "2A40B": "lián", + "2A40C": "nì", + "2A40D": "lián", + "2A40E": "má", + "2A40F": "zhé", + "2A413": "jiā", + "2A414": "yí", + "2A416": "lǒng", + "2A418": "yì", + "2A41D": "dài", + "2A41E": "dù", + "2A423": "yǐ", + "2A425": "tài", + "2A426": "hāng", + "2A427": "shù", + "2A42C": "wán", + "2A42E": "sù", + "2A42F": "yǎo", + "2A430": "èr", + "2A432": "zhèn", + "2A43A": "dòu", + "2A43B": "jiān", + "2A43F": "pāng", + "2A440": "huī", + "2A442": "chà", + "2A443": "shān", + "2A444": "lú", + "2A446": "yù", + "2A448": "yàn", + "2A449": "wǎn", + "2A44A": "qiào", + "2A44B": "luō", + "2A44C": "yù", + "2A44F": "tú", + "2A450": "wèi", + "2A452": "tùn", + "2A455": "hǔn", + "2A456": "bēn", + "2A457": "qiè", + "2A459": "jīn", + "2A45A": "lái", + "2A45C": "zhǐ", + "2A45D": "yú", + "2A45F": "cì", + "2A466": "yè", + "2A467": "dié", + "2A468": "chà", + "2A469": "diàn", + "2A46A": "mán", + "2A46C": "dèng", + "2A46D": "wēi", + "2A46E": "niǎn", + "2A46F": "lèi", + "2A470": "bīng", + "2A471": "wū", + "2A473": "zhěn", + "2A476": "róu", + "2A477": "wài", + "2A478": "mì", + "2A479": "jiè", + "2A47B": "hóu", + "2A47D": "zhài", + "2A47E": "rǔ", + "2A47F": "zī", + "2A480": "pán", + "2A482": "mò", + "2A484": "mì", + "2A486": "qī", + "2A487": "mò", + "2A48A": "zhī", + "2A48B": "bān", + "2A48D": "miè", + "2A48F": "lù", + "2A491": "qī", + "2A492": "chōng", + "2A494": "lí", + "2A495": "yì", + "2A498": "dèng", + "2A499": "cuō", + "2A49B": "duì", + "2A49C": "mà", + "2A49D": "yǎn", + "2A49F": "zèng", + "2A4A0": "yǎn", + "2A4A1": "duì", + "2A4A2": "pū", + "2A4A5": "yuè", + "2A4A9": "huò", + "2A4AA": "mài", + "2A4AB": "jiǎn", + "2A4AC": "nóng", + "2A4AD": "qín", + "2A4AF": "qín", + "2A4B2": "yè", + "2A4B4": "tái", + "2A4B9": "jiān", + "2A4BC": "chá", + "2A4BE": "dàn", + "2A4BF": "téng", + "2A4C0": "lì", + "2A4C3": "niǎng", + "2A4C4": "chán", + "2A4C5": "zāng", + "2A4CA": "yù", + "2A4CC": "zuì", + "2A4CD": "biān", + "2A4D0": "chǔ", + "2A4D8": "rán", + "2A4DA": "rán", + "2A4DB": "yāng", + "2A4DC": "bǒ", + "2A4E1": "cù", + "2A4EC": "mí", + "2A4EE": "kě", + "2A4F0": "cù", + "2A4F7": "xí", + "2A4F9": "má", + "2A4FB": "shī", + "2A4FC": "diān", + "2A4FF": "shī", + "2A502": "dǐng", + "2A503": "jiōng", + "2A505": "yuán", + "2A506": "gān", + "2A50A": "huì", + "2A50B": "jī", + "2A50D": "péng", + "2A50F": "dēng", + "2A511": "bèng", + "2A514": "pāng", + "2A515": "tà", + "2A517": "yuān", + "2A518": "gāo", + "2A519": "yuān", + "2A51F": "jiā", + "2A523": "kōng", + "2A526": "dòng", + "2A529": "xián", + "2A52A": "qì", + "2A52C": "sāng", + "2A530": "yìn", + "2A533": "lóng", + "2A536": "tēng", + "2A537": "lóng", + "2A53A": "rèn", + "2A53D": "yìn", + "2A53E": "píng", + "2A53F": "pū", + "2A540": "yuán", + "2A541": "rǒng", + "2A543": "fāng", + "2A547": "hāng", + "2A548": "mí", + "2A549": "hú", + "2A54A": "zī", + "2A54C": "líng", + "2A54D": "jiōng", + "2A54E": "rǒng", + "2A552": "píng", + "2A553": "guāng", + "2A554": "ěr", + "2A55D": "cù", + "2A55E": "jùn", + "2A566": "xiǔ", + "2A568": "ér", + "2A569": "tì", + "2A56B": "yáng", + "2A56D": "ài", + "2A56E": "hú", + "2A56F": "xí", + "2A571": "hú", + "2A573": "sī", + "2A574": "lǐ", + "2A576": "yì", + "2A577": "gǔ", + "2A579": "táng", + "2A580": "què", + "2A581": "zōng", + "2A582": "lí", + "2A584": "jiào", + "2A587": "fán", + "2A588": "pú", + "2A589": "sī", + "2A58B": "jié", + "2A58C": "lú", + "2A58D": "lì", + "2A58E": "chán", + "2A590": "yào", + "2A595": "huī", + "2A599": "hōu", + "2A59A": "diān", + "2A59B": "qiù", + "2A59C": "jué", + "2A59E": "pì", + "2A5A2": "kuī", + "2A5A5": "xǐ", + "2A5A6": "tī", + "2A5A9": "xù", + "2A5AF": "biǎn", + "2A5B2": "hē", + "2A5B3": "lián", + "2A5B6": "sù", + "2A5B7": "liào", + "2A5BC": "jīn", + "2A5C1": "lì", + "2A5C2": "chán", + "2A5C5": "qí", + "2A5C6": "qí", + "2A5C9": "zī", + "2A5CB": "zī", + "2A5CD": "qí", + "2A5CF": "qí", + "2A5D0": "zī", + "2A5D2": "zhāi", + "2A5D3": "zhāi", + "2A5D4": "pà", + "2A5D6": "jū", + "2A5D9": "yǎn", + "2A5DC": "háng", + "2A5DD": "nà", + "2A5E4": "yǎn", + "2A5E6": "zhàn", + "2A5E7": "shǐ", + "2A5E8": "zhí", + "2A5ED": "zhā", + "2A5F4": "rǒng", + "2A5F5": "zhā", + "2A5F7": "yì", + "2A5F8": "míng", + "2A5F9": "yá", + "2A5FB": "zhì", + "2A5FD": "kuò", + "2A5FE": "xiá", + "2A600": "pián", + "2A601": "tà", + "2A603": "yǐ", + "2A606": "xiū", + "2A607": "zhāi", + "2A609": "duǒ", + "2A60A": "è", + "2A60E": "yín", + "2A610": "è", + "2A611": "suān", + "2A612": "ān", + "2A613": "cuó", + "2A615": "tuó", + "2A617": "tuó", + "2A618": "xiá", + "2A61B": "chuò", + "2A61D": "suān", + "2A625": "jì", + "2A626": "qiǎn", + "2A627": "zú", + "2A628": "zhāi", + "2A629": "yǔn", + "2A62A": "zhàn", + "2A62C": "yí", + "2A632": "yá", + "2A633": "yuē", + "2A639": "hé", + "2A63A": "qià", + "2A63E": "chā", + "2A643": "óu", + "2A648": "hú", + "2A64A": "yàn", + "2A64C": "qiè", + "2A64D": "bó", + "2A64E": "qiāng", + "2A64F": "jiè", + "2A65B": "nì", + "2A65E": "chǎn", + "2A65F": "qǐn", + "2A661": "zāo", + "2A664": "yǐn", + "2A665": "xiè", + "2A667": "qí", + "2A668": "jiàn", + "2A66B": "xū", + "2A66D": "zèng", + "2A66F": "è", + "2A673": "zū", + "2A674": "yǐ", + "2A679": "zhí", + "2A67A": "lì", + "2A67D": "lì", + "2A67E": "yín", + "2A681": "lián", + "2A683": "chán", + "2A685": "jué", + "2A687": "zá", + "2A68E": "zhāi", + "2A68F": "pián", + "2A691": "lóng", + "2A693": "lóng", + "2A698": "lóng", + "2A69D": "lóng", + "2A6A0": "lóng", + "2A6A2": "mǎng", + "2A6A5": "zhé", + "2A6AC": "gàn", + "2A6AD": "gōu", + "2A6AE": "rán", + "2A6AF": "cù", + "2A6B0": "jiāo", + "2A6B7": "bǒ", + "2A6B9": "zhù", + "2A6BA": "qiū", + "2A6BB": "yāng", + "2A6C0": "xiào", + "2A6C2": "huí", + "2A6C3": "qū", + "2A6C8": "líng", + "2A6CA": "yín", + "2A6CE": "pì", + "2A6D2": "lián", + "2A79D": "duó", + "2A848": "bái", + "2A84F": "zhān", + "2A8AE": "luán", + "2AA0A": "sóng", + "2AA17": "juē", + "2AA9D": "yōng", + "2AEB9": "nǔ", + "2AED0": "cōng", + "2AFA2": "xiàn", + "2B061": "lì", + "2B088": "fèi", + "2B099": "sù", + "2B0DC": "kòu", + "2B128": "chī", + "2B138": "xūn", + "2B230": "qià", + "2B2D0": "gǒng", + "2B300": "jī", + "2B328": "luó", + "2B359": "yì", + "2B35F": "yí", + "2B362": "náo", + "2B370": "xǐ", + "2B372": "xiǎo", + "2B3CB": "juē", + "2B404": "yuè", + "2B406": "kuài", + "2B409": "líng", + "2B410": "ní", + "2B413": "bù", + "2B4B6": "hán", + "2B4E7": "fū", + "2B4E9": "cōng", + "2B50E": "jué", + "2B5E0": "zhāng", + "2B5E6": "bù", + "2B5E7": "sù", + "2B5EE": "huáng", + "2B5F4": "zhān", + "2B61D": "jué", + "2B623": "hàn", + "2B624": "ái", + "2B628": "tí", + "2B688": "xù", + "2B689": "hóng", + "2B692": "fú", + "2B694": "huí", + "2B695": "shī", + "2B699": "pū", + "2B6DB": "zhī", + "2B6DE": "jué", + "2B6E2": "níng", + "2B6F6": "chì", + "2B6F8": "tí", +} \ No newline at end of file diff --git a/vendor/github.com/Shopify/sarama/.gitignore b/vendor/github.com/Shopify/sarama/.gitignore new file mode 100644 index 0000000..6e362e4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/.gitignore @@ -0,0 +1,27 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so +*.test + +# Folders +_obj +_test +.vagrant + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +coverage.txt +profile.out diff --git a/vendor/github.com/Shopify/sarama/.travis.yml b/vendor/github.com/Shopify/sarama/.travis.yml new file mode 100644 index 0000000..d609423 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/.travis.yml @@ -0,0 +1,38 @@ +dist: xenial +language: go +go: +- 1.11.x +- 1.12.x +- 1.13.x + +env: + global: + - KAFKA_PEERS=localhost:9091,localhost:9092,localhost:9093,localhost:9094,localhost:9095 + - TOXIPROXY_ADDR=http://localhost:8474 + - KAFKA_INSTALL_ROOT=/home/travis/kafka + - KAFKA_HOSTNAME=localhost + - DEBUG=true + matrix: + - KAFKA_VERSION=2.2.1 KAFKA_SCALA_VERSION=2.12 + - KAFKA_VERSION=2.3.0 KAFKA_SCALA_VERSION=2.12 + +before_install: +- export REPOSITORY_ROOT=${TRAVIS_BUILD_DIR} +- vagrant/install_cluster.sh +- vagrant/boot_cluster.sh +- vagrant/create_topics.sh +- vagrant/run_java_producer.sh + +install: make install_dependencies + +script: +- make test +- make vet +- make errcheck +- if [[ "$TRAVIS_GO_VERSION" == 1.13* ]]; then make fmt; fi + +after_success: +- go tool cover -func coverage.txt +- bash <(curl -s https://codecov.io/bash) + +after_script: vagrant/halt_cluster.sh diff --git a/vendor/github.com/Shopify/sarama/CHANGELOG.md b/vendor/github.com/Shopify/sarama/CHANGELOG.md new file mode 100644 index 0000000..dfa7e75 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/CHANGELOG.md @@ -0,0 +1,843 @@ +# Changelog + +#### Version 1.24.1 (2019-10-31) + +New Features: +- Add DescribeLogDirs Request/Response pair + ([1520](https://github.com/Shopify/sarama/pull/1520)). + +Bug Fixes: +- Fix ClusterAdmin returning invalid controller ID on DescribeCluster + ([1518](https://github.com/Shopify/sarama/pull/1518)). +- Fix issue with consumergroup not rebalancing when new partition is added + ([1525](https://github.com/Shopify/sarama/pull/1525)). +- Ensure consistent use of read/write deadlines + ([1529](https://github.com/Shopify/sarama/pull/1529)). + +#### Version 1.24.0 (2019-10-09) + +New Features: +- Add sticky partition assignor + ([1416](https://github.com/Shopify/sarama/pull/1416)). +- Switch from cgo zstd package to pure Go implementation + ([1477](https://github.com/Shopify/sarama/pull/1477)). + +Improvements: +- Allow creating ClusterAdmin from client + ([1415](https://github.com/Shopify/sarama/pull/1415)). +- Set KafkaVersion in ListAcls method + ([1452](https://github.com/Shopify/sarama/pull/1452)). +- Set request version in CreateACL ClusterAdmin method + ([1458](https://github.com/Shopify/sarama/pull/1458)). +- Set request version in DeleteACL ClusterAdmin method + ([1461](https://github.com/Shopify/sarama/pull/1461)). +- Handle missed error codes on TopicMetaDataRequest and GroupCoordinatorRequest + ([1464](https://github.com/Shopify/sarama/pull/1464)). +- Remove direct usage of gofork + ([1465](https://github.com/Shopify/sarama/pull/1465)). +- Add support for Go 1.13 + ([1478](https://github.com/Shopify/sarama/pull/1478)). +- Improve behavior of NewMockListAclsResponse + ([1481](https://github.com/Shopify/sarama/pull/1481)). + +Bug Fixes: +- Fix race condition in consumergroup example + ([1434](https://github.com/Shopify/sarama/pull/1434)). +- Fix brokerProducer goroutine leak + ([1442](https://github.com/Shopify/sarama/pull/1442)). +- Use released version of lz4 library + ([1469](https://github.com/Shopify/sarama/pull/1469)). +- Set correct version in MockDeleteTopicsResponse + ([1484](https://github.com/Shopify/sarama/pull/1484)). +- Fix CLI help message typo + ([1494](https://github.com/Shopify/sarama/pull/1494)). + +Known Issues: +- Please **don't** use Zstd, as it doesn't work right now. + See https://github.com/Shopify/sarama/issues/1252 + +#### Version 1.23.1 (2019-07-22) + +Bug Fixes: +- Fix fetch delete bug record + ([1425](https://github.com/Shopify/sarama/pull/1425)). +- Handle SASL/OAUTHBEARER token rejection + ([1428](https://github.com/Shopify/sarama/pull/1428)). + +#### Version 1.23.0 (2019-07-02) + +New Features: +- Add support for Kafka 2.3.0 + ([1418](https://github.com/Shopify/sarama/pull/1418)). +- Add support for ListConsumerGroupOffsets v2 + ([1374](https://github.com/Shopify/sarama/pull/1374)). +- Add support for DeleteConsumerGroup + ([1417](https://github.com/Shopify/sarama/pull/1417)). +- Add support for SASLVersion configuration + ([1410](https://github.com/Shopify/sarama/pull/1410)). +- Add kerberos support + ([1366](https://github.com/Shopify/sarama/pull/1366)). + +Improvements: +- Improve sasl_scram_client example + ([1406](https://github.com/Shopify/sarama/pull/1406)). +- Fix shutdown and race-condition in consumer-group example + ([1404](https://github.com/Shopify/sarama/pull/1404)). +- Add support for error codes 77—81 + ([1397](https://github.com/Shopify/sarama/pull/1397)). +- Pool internal objects allocated per message + ([1385](https://github.com/Shopify/sarama/pull/1385)). +- Reduce packet decoder allocations + ([1373](https://github.com/Shopify/sarama/pull/1373)). +- Support timeout when fetching metadata + ([1359](https://github.com/Shopify/sarama/pull/1359)). + +Bug Fixes: +- Fix fetch size integer overflow + ([1376](https://github.com/Shopify/sarama/pull/1376)). +- Handle and log throttled FetchResponses + ([1383](https://github.com/Shopify/sarama/pull/1383)). +- Refactor misspelled word Resouce to Resource + ([1368](https://github.com/Shopify/sarama/pull/1368)). + +#### Version 1.22.1 (2019-04-29) + +Improvements: +- Use zstd 1.3.8 + ([1350](https://github.com/Shopify/sarama/pull/1350)). +- Add support for SaslHandshakeRequest v1 + ([1354](https://github.com/Shopify/sarama/pull/1354)). + +Bug Fixes: +- Fix V5 MetadataRequest nullable topics array + ([1353](https://github.com/Shopify/sarama/pull/1353)). +- Use a different SCRAM client for each broker connection + ([1349](https://github.com/Shopify/sarama/pull/1349)). +- Fix AllowAutoTopicCreation for MetadataRequest greater than v3 + ([1344](https://github.com/Shopify/sarama/pull/1344)). + +#### Version 1.22.0 (2019-04-09) + +New Features: +- Add Offline Replicas Operation to Client + ([1318](https://github.com/Shopify/sarama/pull/1318)). +- Allow using proxy when connecting to broker + ([1326](https://github.com/Shopify/sarama/pull/1326)). +- Implement ReadCommitted + ([1307](https://github.com/Shopify/sarama/pull/1307)). +- Add support for Kafka 2.2.0 + ([1331](https://github.com/Shopify/sarama/pull/1331)). +- Add SASL SCRAM-SHA-512 and SCRAM-SHA-256 mechanismes + ([1331](https://github.com/Shopify/sarama/pull/1295)). + +Improvements: +- Unregister all broker metrics on broker stop + ([1232](https://github.com/Shopify/sarama/pull/1232)). +- Add SCRAM authentication example + ([1303](https://github.com/Shopify/sarama/pull/1303)). +- Add consumergroup examples + ([1304](https://github.com/Shopify/sarama/pull/1304)). +- Expose consumer batch size metric + ([1296](https://github.com/Shopify/sarama/pull/1296)). +- Add TLS options to console producer and consumer + ([1300](https://github.com/Shopify/sarama/pull/1300)). +- Reduce client close bookkeeping + ([1297](https://github.com/Shopify/sarama/pull/1297)). +- Satisfy error interface in create responses + ([1154](https://github.com/Shopify/sarama/pull/1154)). +- Please lint gods + ([1346](https://github.com/Shopify/sarama/pull/1346)). + +Bug Fixes: +- Fix multi consumer group instance crash + ([1338](https://github.com/Shopify/sarama/pull/1338)). +- Update lz4 to latest version + ([1347](https://github.com/Shopify/sarama/pull/1347)). +- Retry ErrNotCoordinatorForConsumer in new consumergroup session + ([1231](https://github.com/Shopify/sarama/pull/1231)). +- Fix cleanup error handler + ([1332](https://github.com/Shopify/sarama/pull/1332)). +- Fix rate condition in PartitionConsumer + ([1156](https://github.com/Shopify/sarama/pull/1156)). + +#### Version 1.21.0 (2019-02-24) + +New Features: +- Add CreateAclRequest, DescribeAclRequest, DeleteAclRequest + ([1236](https://github.com/Shopify/sarama/pull/1236)). +- Add DescribeTopic, DescribeConsumerGroup, ListConsumerGroups, ListConsumerGroupOffsets admin requests + ([1178](https://github.com/Shopify/sarama/pull/1178)). +- Implement SASL/OAUTHBEARER + ([1240](https://github.com/Shopify/sarama/pull/1240)). + +Improvements: +- Add Go mod support + ([1282](https://github.com/Shopify/sarama/pull/1282)). +- Add error codes 73—76 + ([1239](https://github.com/Shopify/sarama/pull/1239)). +- Add retry backoff function + ([1160](https://github.com/Shopify/sarama/pull/1160)). +- Maintain metadata in the producer even when retries are disabled + ([1189](https://github.com/Shopify/sarama/pull/1189)). +- Include ReplicaAssignment in ListTopics + ([1274](https://github.com/Shopify/sarama/pull/1274)). +- Add producer performance tool + ([1222](https://github.com/Shopify/sarama/pull/1222)). +- Add support LogAppend timestamps + ([1258](https://github.com/Shopify/sarama/pull/1258)). + +Bug Fixes: +- Fix potential deadlock when a heartbeat request fails + ([1286](https://github.com/Shopify/sarama/pull/1286)). +- Fix consuming compacted topic + ([1227](https://github.com/Shopify/sarama/pull/1227)). +- Set correct Kafka version for DescribeConfigsRequest v1 + ([1277](https://github.com/Shopify/sarama/pull/1277)). +- Update kafka test version + ([1273](https://github.com/Shopify/sarama/pull/1273)). + +#### Version 1.20.1 (2019-01-10) + +New Features: +- Add optional replica id in offset request + ([1100](https://github.com/Shopify/sarama/pull/1100)). + +Improvements: +- Implement DescribeConfigs Request + Response v1 & v2 + ([1230](https://github.com/Shopify/sarama/pull/1230)). +- Reuse compression objects + ([1185](https://github.com/Shopify/sarama/pull/1185)). +- Switch from png to svg for GoDoc link in README + ([1243](https://github.com/Shopify/sarama/pull/1243)). +- Fix typo in deprecation notice for FetchResponseBlock.Records + ([1242](https://github.com/Shopify/sarama/pull/1242)). +- Fix typos in consumer metadata response file + ([1244](https://github.com/Shopify/sarama/pull/1244)). + +Bug Fixes: +- Revert to individual msg retries for non-idempotent + ([1203](https://github.com/Shopify/sarama/pull/1203)). +- Respect MaxMessageBytes limit for uncompressed messages + ([1141](https://github.com/Shopify/sarama/pull/1141)). + +#### Version 1.20.0 (2018-12-10) + +New Features: + - Add support for zstd compression + ([#1170](https://github.com/Shopify/sarama/pull/1170)). + - Add support for Idempotent Producer + ([#1152](https://github.com/Shopify/sarama/pull/1152)). + - Add support support for Kafka 2.1.0 + ([#1229](https://github.com/Shopify/sarama/pull/1229)). + - Add support support for OffsetCommit request/response pairs versions v1 to v5 + ([#1201](https://github.com/Shopify/sarama/pull/1201)). + - Add support support for OffsetFetch request/response pair up to version v5 + ([#1198](https://github.com/Shopify/sarama/pull/1198)). + +Improvements: + - Export broker's Rack setting + ([#1173](https://github.com/Shopify/sarama/pull/1173)). + - Always use latest patch version of Go on CI + ([#1202](https://github.com/Shopify/sarama/pull/1202)). + - Add error codes 61 to 72 + ([#1195](https://github.com/Shopify/sarama/pull/1195)). + +Bug Fixes: + - Fix build without cgo + ([#1182](https://github.com/Shopify/sarama/pull/1182)). + - Fix go vet suggestion in consumer group file + ([#1209](https://github.com/Shopify/sarama/pull/1209)). + - Fix typos in code and comments + ([#1228](https://github.com/Shopify/sarama/pull/1228)). + +#### Version 1.19.0 (2018-09-27) + +New Features: + - Implement a higher-level consumer group + ([#1099](https://github.com/Shopify/sarama/pull/1099)). + +Improvements: + - Add support for Go 1.11 + ([#1176](https://github.com/Shopify/sarama/pull/1176)). + +Bug Fixes: + - Fix encoding of `MetadataResponse` with version 2 and higher + ([#1174](https://github.com/Shopify/sarama/pull/1174)). + - Fix race condition in mock async producer + ([#1174](https://github.com/Shopify/sarama/pull/1174)). + +#### Version 1.18.0 (2018-09-07) + +New Features: + - Make `Partitioner.RequiresConsistency` vary per-message + ([#1112](https://github.com/Shopify/sarama/pull/1112)). + - Add customizable partitioner + ([#1118](https://github.com/Shopify/sarama/pull/1118)). + - Add `ClusterAdmin` support for `CreateTopic`, `DeleteTopic`, `CreatePartitions`, + `DeleteRecords`, `DescribeConfig`, `AlterConfig`, `CreateACL`, `ListAcls`, `DeleteACL` + ([#1055](https://github.com/Shopify/sarama/pull/1055)). + +Improvements: + - Add support for Kafka 2.0.0 + ([#1149](https://github.com/Shopify/sarama/pull/1149)). + - Allow setting `LocalAddr` when dialing an address to support multi-homed hosts + ([#1123](https://github.com/Shopify/sarama/pull/1123)). + - Simpler offset management + ([#1127](https://github.com/Shopify/sarama/pull/1127)). + +Bug Fixes: + - Fix mutation of `ProducerMessage.MetaData` when producing to Kafka + ([#1110](https://github.com/Shopify/sarama/pull/1110)). + - Fix consumer block when response did not contain all the + expected topic/partition blocks + ([#1086](https://github.com/Shopify/sarama/pull/1086)). + - Fix consumer block when response contains only constrol messages + ([#1115](https://github.com/Shopify/sarama/pull/1115)). + - Add timeout config for ClusterAdmin requests + ([#1142](https://github.com/Shopify/sarama/pull/1142)). + - Add version check when producing message with headers + ([#1117](https://github.com/Shopify/sarama/pull/1117)). + - Fix `MetadataRequest` for empty list of topics + ([#1132](https://github.com/Shopify/sarama/pull/1132)). + - Fix producer topic metadata on-demand fetch when topic error happens in metadata response + ([#1125](https://github.com/Shopify/sarama/pull/1125)). + +#### Version 1.17.0 (2018-05-30) + +New Features: + - Add support for gzip compression levels + ([#1044](https://github.com/Shopify/sarama/pull/1044)). + - Add support for Metadata request/response pairs versions v1 to v5 + ([#1047](https://github.com/Shopify/sarama/pull/1047), + [#1069](https://github.com/Shopify/sarama/pull/1069)). + - Add versioning to JoinGroup request/response pairs + ([#1098](https://github.com/Shopify/sarama/pull/1098)) + - Add support for CreatePartitions, DeleteGroups, DeleteRecords request/response pairs + ([#1065](https://github.com/Shopify/sarama/pull/1065), + [#1096](https://github.com/Shopify/sarama/pull/1096), + [#1027](https://github.com/Shopify/sarama/pull/1027)). + - Add `Controller()` method to Client interface + ([#1063](https://github.com/Shopify/sarama/pull/1063)). + +Improvements: + - ConsumerMetadataReq/Resp has been migrated to FindCoordinatorReq/Resp + ([#1010](https://github.com/Shopify/sarama/pull/1010)). + - Expose missing protocol parts: `msgSet` and `recordBatch` + ([#1049](https://github.com/Shopify/sarama/pull/1049)). + - Add support for v1 DeleteTopics Request + ([#1052](https://github.com/Shopify/sarama/pull/1052)). + - Add support for Go 1.10 + ([#1064](https://github.com/Shopify/sarama/pull/1064)). + - Claim support for Kafka 1.1.0 + ([#1073](https://github.com/Shopify/sarama/pull/1073)). + +Bug Fixes: + - Fix FindCoordinatorResponse.encode to allow nil Coordinator + ([#1050](https://github.com/Shopify/sarama/pull/1050), + [#1051](https://github.com/Shopify/sarama/pull/1051)). + - Clear all metadata when we have the latest topic info + ([#1033](https://github.com/Shopify/sarama/pull/1033)). + - Make `PartitionConsumer.Close` idempotent + ([#1092](https://github.com/Shopify/sarama/pull/1092)). + +#### Version 1.16.0 (2018-02-12) + +New Features: + - Add support for the Create/Delete Topics request/response pairs + ([#1007](https://github.com/Shopify/sarama/pull/1007), + [#1008](https://github.com/Shopify/sarama/pull/1008)). + - Add support for the Describe/Create/Delete ACL request/response pairs + ([#1009](https://github.com/Shopify/sarama/pull/1009)). + - Add support for the five transaction-related request/response pairs + ([#1016](https://github.com/Shopify/sarama/pull/1016)). + +Improvements: + - Permit setting version on mock producer responses + ([#999](https://github.com/Shopify/sarama/pull/999)). + - Add `NewMockBrokerListener` helper for testing TLS connections + ([#1019](https://github.com/Shopify/sarama/pull/1019)). + - Changed the default value for `Consumer.Fetch.Default` from 32KiB to 1MiB + which results in much higher throughput in most cases + ([#1024](https://github.com/Shopify/sarama/pull/1024)). + - Reuse the `time.Ticker` across fetch requests in the PartitionConsumer to + reduce CPU and memory usage when processing many partitions + ([#1028](https://github.com/Shopify/sarama/pull/1028)). + - Assign relative offsets to messages in the producer to save the brokers a + recompression pass + ([#1002](https://github.com/Shopify/sarama/pull/1002), + [#1015](https://github.com/Shopify/sarama/pull/1015)). + +Bug Fixes: + - Fix producing uncompressed batches with the new protocol format + ([#1032](https://github.com/Shopify/sarama/issues/1032)). + - Fix consuming compacted topics with the new protocol format + ([#1005](https://github.com/Shopify/sarama/issues/1005)). + - Fix consuming topics with a mix of protocol formats + ([#1021](https://github.com/Shopify/sarama/issues/1021)). + - Fix consuming when the broker includes multiple batches in a single response + ([#1022](https://github.com/Shopify/sarama/issues/1022)). + - Fix detection of `PartialTrailingMessage` when the partial message was + truncated before the magic value indicating its version + ([#1030](https://github.com/Shopify/sarama/pull/1030)). + - Fix expectation-checking in the mock of `SyncProducer.SendMessages` + ([#1035](https://github.com/Shopify/sarama/pull/1035)). + +#### Version 1.15.0 (2017-12-08) + +New Features: + - Claim official support for Kafka 1.0, though it did already work + ([#984](https://github.com/Shopify/sarama/pull/984)). + - Helper methods for Kafka version numbers to/from strings + ([#989](https://github.com/Shopify/sarama/pull/989)). + - Implement CreatePartitions request/response + ([#985](https://github.com/Shopify/sarama/pull/985)). + +Improvements: + - Add error codes 45-60 + ([#986](https://github.com/Shopify/sarama/issues/986)). + +Bug Fixes: + - Fix slow consuming for certain Kafka 0.11/1.0 configurations + ([#982](https://github.com/Shopify/sarama/pull/982)). + - Correctly determine when a FetchResponse contains the new message format + ([#990](https://github.com/Shopify/sarama/pull/990)). + - Fix producing with multiple headers + ([#996](https://github.com/Shopify/sarama/pull/996)). + - Fix handling of truncated record batches + ([#998](https://github.com/Shopify/sarama/pull/998)). + - Fix leaking metrics when closing brokers + ([#991](https://github.com/Shopify/sarama/pull/991)). + +#### Version 1.14.0 (2017-11-13) + +New Features: + - Add support for the new Kafka 0.11 record-batch format, including the wire + protocol and the necessary behavioural changes in the producer and consumer. + Transactions and idempotency are not yet supported, but producing and + consuming should work with all the existing bells and whistles (batching, + compression, etc) as well as the new custom headers. Thanks to Vlad Hanciuta + of Arista Networks for this work. Part of + ([#901](https://github.com/Shopify/sarama/issues/901)). + +Bug Fixes: + - Fix encoding of ProduceResponse versions in test + ([#970](https://github.com/Shopify/sarama/pull/970)). + - Return partial replicas list when we have it + ([#975](https://github.com/Shopify/sarama/pull/975)). + +#### Version 1.13.0 (2017-10-04) + +New Features: + - Support for FetchRequest version 3 + ([#905](https://github.com/Shopify/sarama/pull/905)). + - Permit setting version on mock FetchResponses + ([#939](https://github.com/Shopify/sarama/pull/939)). + - Add a configuration option to support storing only minimal metadata for + extremely large clusters + ([#937](https://github.com/Shopify/sarama/pull/937)). + - Add `PartitionOffsetManager.ResetOffset` for backtracking tracked offsets + ([#932](https://github.com/Shopify/sarama/pull/932)). + +Improvements: + - Provide the block-level timestamp when consuming compressed messages + ([#885](https://github.com/Shopify/sarama/issues/885)). + - `Client.Replicas` and `Client.InSyncReplicas` now respect the order returned + by the broker, which can be meaningful + ([#930](https://github.com/Shopify/sarama/pull/930)). + - Use a `Ticker` to reduce consumer timer overhead at the cost of higher + variance in the actual timeout + ([#933](https://github.com/Shopify/sarama/pull/933)). + +Bug Fixes: + - Gracefully handle messages with negative timestamps + ([#907](https://github.com/Shopify/sarama/pull/907)). + - Raise a proper error when encountering an unknown message version + ([#940](https://github.com/Shopify/sarama/pull/940)). + +#### Version 1.12.0 (2017-05-08) + +New Features: + - Added support for the `ApiVersions` request and response pair, and Kafka + version 0.10.2 ([#867](https://github.com/Shopify/sarama/pull/867)). Note + that you still need to specify the Kafka version in the Sarama configuration + for the time being. + - Added a `Brokers` method to the Client which returns the complete set of + active brokers ([#813](https://github.com/Shopify/sarama/pull/813)). + - Added an `InSyncReplicas` method to the Client which returns the set of all + in-sync broker IDs for the given partition, now that the Kafka versions for + which this was misleading are no longer in our supported set + ([#872](https://github.com/Shopify/sarama/pull/872)). + - Added a `NewCustomHashPartitioner` method which allows constructing a hash + partitioner with a custom hash method in case the default (FNV-1a) is not + suitable + ([#837](https://github.com/Shopify/sarama/pull/837), + [#841](https://github.com/Shopify/sarama/pull/841)). + +Improvements: + - Recognize more Kafka error codes + ([#859](https://github.com/Shopify/sarama/pull/859)). + +Bug Fixes: + - Fix an issue where decoding a malformed FetchRequest would not return the + correct error ([#818](https://github.com/Shopify/sarama/pull/818)). + - Respect ordering of group protocols in JoinGroupRequests. This fix is + transparent if you're using the `AddGroupProtocol` or + `AddGroupProtocolMetadata` helpers; otherwise you will need to switch from + the `GroupProtocols` field (now deprecated) to use `OrderedGroupProtocols` + ([#812](https://github.com/Shopify/sarama/issues/812)). + - Fix an alignment-related issue with atomics on 32-bit architectures + ([#859](https://github.com/Shopify/sarama/pull/859)). + +#### Version 1.11.0 (2016-12-20) + +_Important:_ As of Sarama 1.11 it is necessary to set the config value of +`Producer.Return.Successes` to true in order to use the SyncProducer. Previous +versions would silently override this value when instantiating a SyncProducer +which led to unexpected values and data races. + +New Features: + - Metrics! Thanks to Sébastien Launay for all his work on this feature + ([#701](https://github.com/Shopify/sarama/pull/701), + [#746](https://github.com/Shopify/sarama/pull/746), + [#766](https://github.com/Shopify/sarama/pull/766)). + - Add support for LZ4 compression + ([#786](https://github.com/Shopify/sarama/pull/786)). + - Add support for ListOffsetRequest v1 and Kafka 0.10.1 + ([#775](https://github.com/Shopify/sarama/pull/775)). + - Added a `HighWaterMarks` method to the Consumer which aggregates the + `HighWaterMarkOffset` values of its child topic/partitions + ([#769](https://github.com/Shopify/sarama/pull/769)). + +Bug Fixes: + - Fixed producing when using timestamps, compression and Kafka 0.10 + ([#759](https://github.com/Shopify/sarama/pull/759)). + - Added missing decoder methods to DescribeGroups response + ([#756](https://github.com/Shopify/sarama/pull/756)). + - Fix producer shutdown when `Return.Errors` is disabled + ([#787](https://github.com/Shopify/sarama/pull/787)). + - Don't mutate configuration in SyncProducer + ([#790](https://github.com/Shopify/sarama/pull/790)). + - Fix crash on SASL initialization failure + ([#795](https://github.com/Shopify/sarama/pull/795)). + +#### Version 1.10.1 (2016-08-30) + +Bug Fixes: + - Fix the documentation for `HashPartitioner` which was incorrect + ([#717](https://github.com/Shopify/sarama/pull/717)). + - Permit client creation even when it is limited by ACLs + ([#722](https://github.com/Shopify/sarama/pull/722)). + - Several fixes to the consumer timer optimization code, regressions introduced + in v1.10.0. Go's timers are finicky + ([#730](https://github.com/Shopify/sarama/pull/730), + [#733](https://github.com/Shopify/sarama/pull/733), + [#734](https://github.com/Shopify/sarama/pull/734)). + - Handle consuming compressed relative offsets with Kafka 0.10 + ([#735](https://github.com/Shopify/sarama/pull/735)). + +#### Version 1.10.0 (2016-08-02) + +_Important:_ As of Sarama 1.10 it is necessary to tell Sarama the version of +Kafka you are running against (via the `config.Version` value) in order to use +features that may not be compatible with old Kafka versions. If you don't +specify this value it will default to 0.8.2 (the minimum supported), and trying +to use more recent features (like the offset manager) will fail with an error. + +_Also:_ The offset-manager's behaviour has been changed to match the upstream +java consumer (see [#705](https://github.com/Shopify/sarama/pull/705) and +[#713](https://github.com/Shopify/sarama/pull/713)). If you use the +offset-manager, please ensure that you are committing one *greater* than the +last consumed message offset or else you may end up consuming duplicate +messages. + +New Features: + - Support for Kafka 0.10 + ([#672](https://github.com/Shopify/sarama/pull/672), + [#678](https://github.com/Shopify/sarama/pull/678), + [#681](https://github.com/Shopify/sarama/pull/681), and others). + - Support for configuring the target Kafka version + ([#676](https://github.com/Shopify/sarama/pull/676)). + - Batch producing support in the SyncProducer + ([#677](https://github.com/Shopify/sarama/pull/677)). + - Extend producer mock to allow setting expectations on message contents + ([#667](https://github.com/Shopify/sarama/pull/667)). + +Improvements: + - Support `nil` compressed messages for deleting in compacted topics + ([#634](https://github.com/Shopify/sarama/pull/634)). + - Pre-allocate decoding errors, greatly reducing heap usage and GC time against + misbehaving brokers ([#690](https://github.com/Shopify/sarama/pull/690)). + - Re-use consumer expiry timers, removing one allocation per consumed message + ([#707](https://github.com/Shopify/sarama/pull/707)). + +Bug Fixes: + - Actually default the client ID to "sarama" like we say we do + ([#664](https://github.com/Shopify/sarama/pull/664)). + - Fix a rare issue where `Client.Leader` could return the wrong error + ([#685](https://github.com/Shopify/sarama/pull/685)). + - Fix a possible tight loop in the consumer + ([#693](https://github.com/Shopify/sarama/pull/693)). + - Match upstream's offset-tracking behaviour + ([#705](https://github.com/Shopify/sarama/pull/705)). + - Report UnknownTopicOrPartition errors from the offset manager + ([#706](https://github.com/Shopify/sarama/pull/706)). + - Fix possible negative partition value from the HashPartitioner + ([#709](https://github.com/Shopify/sarama/pull/709)). + +#### Version 1.9.0 (2016-05-16) + +New Features: + - Add support for custom offset manager retention durations + ([#602](https://github.com/Shopify/sarama/pull/602)). + - Publish low-level mocks to enable testing of third-party producer/consumer + implementations ([#570](https://github.com/Shopify/sarama/pull/570)). + - Declare support for Golang 1.6 + ([#611](https://github.com/Shopify/sarama/pull/611)). + - Support for SASL plain-text auth + ([#648](https://github.com/Shopify/sarama/pull/648)). + +Improvements: + - Simplified broker locking scheme slightly + ([#604](https://github.com/Shopify/sarama/pull/604)). + - Documentation cleanup + ([#605](https://github.com/Shopify/sarama/pull/605), + [#621](https://github.com/Shopify/sarama/pull/621), + [#654](https://github.com/Shopify/sarama/pull/654)). + +Bug Fixes: + - Fix race condition shutting down the OffsetManager + ([#658](https://github.com/Shopify/sarama/pull/658)). + +#### Version 1.8.0 (2016-02-01) + +New Features: + - Full support for Kafka 0.9: + - All protocol messages and fields + ([#586](https://github.com/Shopify/sarama/pull/586), + [#588](https://github.com/Shopify/sarama/pull/588), + [#590](https://github.com/Shopify/sarama/pull/590)). + - Verified that TLS support works + ([#581](https://github.com/Shopify/sarama/pull/581)). + - Fixed the OffsetManager compatibility + ([#585](https://github.com/Shopify/sarama/pull/585)). + +Improvements: + - Optimize for fewer system calls when reading from the network + ([#584](https://github.com/Shopify/sarama/pull/584)). + - Automatically retry `InvalidMessage` errors to match upstream behaviour + ([#589](https://github.com/Shopify/sarama/pull/589)). + +#### Version 1.7.0 (2015-12-11) + +New Features: + - Preliminary support for Kafka 0.9 + ([#572](https://github.com/Shopify/sarama/pull/572)). This comes with several + caveats: + - Protocol-layer support is mostly in place + ([#577](https://github.com/Shopify/sarama/pull/577)), however Kafka 0.9 + renamed some messages and fields, which we did not in order to preserve API + compatibility. + - The producer and consumer work against 0.9, but the offset manager does + not ([#573](https://github.com/Shopify/sarama/pull/573)). + - TLS support may or may not work + ([#581](https://github.com/Shopify/sarama/pull/581)). + +Improvements: + - Don't wait for request timeouts on dead brokers, greatly speeding recovery + when the TCP connection is left hanging + ([#548](https://github.com/Shopify/sarama/pull/548)). + - Refactored part of the producer. The new version provides a much more elegant + solution to [#449](https://github.com/Shopify/sarama/pull/449). It is also + slightly more efficient, and much more precise in calculating batch sizes + when compression is used + ([#549](https://github.com/Shopify/sarama/pull/549), + [#550](https://github.com/Shopify/sarama/pull/550), + [#551](https://github.com/Shopify/sarama/pull/551)). + +Bug Fixes: + - Fix race condition in consumer test mock + ([#553](https://github.com/Shopify/sarama/pull/553)). + +#### Version 1.6.1 (2015-09-25) + +Bug Fixes: + - Fix panic that could occur if a user-supplied message value failed to encode + ([#449](https://github.com/Shopify/sarama/pull/449)). + +#### Version 1.6.0 (2015-09-04) + +New Features: + - Implementation of a consumer offset manager using the APIs introduced in + Kafka 0.8.2. The API is designed mainly for integration into a future + high-level consumer, not for direct use, although it is *possible* to use it + directly. + ([#461](https://github.com/Shopify/sarama/pull/461)). + +Improvements: + - CRC32 calculation is much faster on machines with SSE4.2 instructions, + removing a major hotspot from most profiles + ([#255](https://github.com/Shopify/sarama/pull/255)). + +Bug Fixes: + - Make protocol decoding more robust against some malformed packets generated + by go-fuzz ([#523](https://github.com/Shopify/sarama/pull/523), + [#525](https://github.com/Shopify/sarama/pull/525)) or found in other ways + ([#528](https://github.com/Shopify/sarama/pull/528)). + - Fix a potential race condition panic in the consumer on shutdown + ([#529](https://github.com/Shopify/sarama/pull/529)). + +#### Version 1.5.0 (2015-08-17) + +New Features: + - TLS-encrypted network connections are now supported. This feature is subject + to change when Kafka releases built-in TLS support, but for now this is + enough to work with TLS-terminating proxies + ([#154](https://github.com/Shopify/sarama/pull/154)). + +Improvements: + - The consumer will not block if a single partition is not drained by the user; + all other partitions will continue to consume normally + ([#485](https://github.com/Shopify/sarama/pull/485)). + - Formatting of error strings has been much improved + ([#495](https://github.com/Shopify/sarama/pull/495)). + - Internal refactoring of the producer for code cleanliness and to enable + future work ([#300](https://github.com/Shopify/sarama/pull/300)). + +Bug Fixes: + - Fix a potential deadlock in the consumer on shutdown + ([#475](https://github.com/Shopify/sarama/pull/475)). + +#### Version 1.4.3 (2015-07-21) + +Bug Fixes: + - Don't include the partitioner in the producer's "fetch partitions" + circuit-breaker ([#466](https://github.com/Shopify/sarama/pull/466)). + - Don't retry messages until the broker is closed when abandoning a broker in + the producer ([#468](https://github.com/Shopify/sarama/pull/468)). + - Update the import path for snappy-go, it has moved again and the API has + changed slightly ([#486](https://github.com/Shopify/sarama/pull/486)). + +#### Version 1.4.2 (2015-05-27) + +Bug Fixes: + - Update the import path for snappy-go, it has moved from google code to github + ([#456](https://github.com/Shopify/sarama/pull/456)). + +#### Version 1.4.1 (2015-05-25) + +Improvements: + - Optimizations when decoding snappy messages, thanks to John Potocny + ([#446](https://github.com/Shopify/sarama/pull/446)). + +Bug Fixes: + - Fix hypothetical race conditions on producer shutdown + ([#450](https://github.com/Shopify/sarama/pull/450), + [#451](https://github.com/Shopify/sarama/pull/451)). + +#### Version 1.4.0 (2015-05-01) + +New Features: + - The consumer now implements `Topics()` and `Partitions()` methods to enable + users to dynamically choose what topics/partitions to consume without + instantiating a full client + ([#431](https://github.com/Shopify/sarama/pull/431)). + - The partition-consumer now exposes the high water mark offset value returned + by the broker via the `HighWaterMarkOffset()` method ([#339](https://github.com/Shopify/sarama/pull/339)). + - Added a `kafka-console-consumer` tool capable of handling multiple + partitions, and deprecated the now-obsolete `kafka-console-partitionConsumer` + ([#439](https://github.com/Shopify/sarama/pull/439), + [#442](https://github.com/Shopify/sarama/pull/442)). + +Improvements: + - The producer's logging during retry scenarios is more consistent, more + useful, and slightly less verbose + ([#429](https://github.com/Shopify/sarama/pull/429)). + - The client now shuffles its initial list of seed brokers in order to prevent + thundering herd on the first broker in the list + ([#441](https://github.com/Shopify/sarama/pull/441)). + +Bug Fixes: + - The producer now correctly manages its state if retries occur when it is + shutting down, fixing several instances of confusing behaviour and at least + one potential deadlock ([#419](https://github.com/Shopify/sarama/pull/419)). + - The consumer now handles messages for different partitions asynchronously, + making it much more resilient to specific user code ordering + ([#325](https://github.com/Shopify/sarama/pull/325)). + +#### Version 1.3.0 (2015-04-16) + +New Features: + - The client now tracks consumer group coordinators using + ConsumerMetadataRequests similar to how it tracks partition leadership using + regular MetadataRequests ([#411](https://github.com/Shopify/sarama/pull/411)). + This adds two methods to the client API: + - `Coordinator(consumerGroup string) (*Broker, error)` + - `RefreshCoordinator(consumerGroup string) error` + +Improvements: + - ConsumerMetadataResponses now automatically create a Broker object out of the + ID/address/port combination for the Coordinator; accessing the fields + individually has been deprecated + ([#413](https://github.com/Shopify/sarama/pull/413)). + - Much improved handling of `OffsetOutOfRange` errors in the consumer. + Consumers will fail to start if the provided offset is out of range + ([#418](https://github.com/Shopify/sarama/pull/418)) + and they will automatically shut down if the offset falls out of range + ([#424](https://github.com/Shopify/sarama/pull/424)). + - Small performance improvement in encoding and decoding protocol messages + ([#427](https://github.com/Shopify/sarama/pull/427)). + +Bug Fixes: + - Fix a rare race condition in the client's background metadata refresher if + it happens to be activated while the client is being closed + ([#422](https://github.com/Shopify/sarama/pull/422)). + +#### Version 1.2.0 (2015-04-07) + +Improvements: + - The producer's behaviour when `Flush.Frequency` is set is now more intuitive + ([#389](https://github.com/Shopify/sarama/pull/389)). + - The producer is now somewhat more memory-efficient during and after retrying + messages due to an improved queue implementation + ([#396](https://github.com/Shopify/sarama/pull/396)). + - The consumer produces much more useful logging output when leadership + changes ([#385](https://github.com/Shopify/sarama/pull/385)). + - The client's `GetOffset` method will now automatically refresh metadata and + retry once in the event of stale information or similar + ([#394](https://github.com/Shopify/sarama/pull/394)). + - Broker connections now have support for using TCP keepalives + ([#407](https://github.com/Shopify/sarama/issues/407)). + +Bug Fixes: + - The OffsetCommitRequest message now correctly implements all three possible + API versions ([#390](https://github.com/Shopify/sarama/pull/390), + [#400](https://github.com/Shopify/sarama/pull/400)). + +#### Version 1.1.0 (2015-03-20) + +Improvements: + - Wrap the producer's partitioner call in a circuit-breaker so that repeatedly + broken topics don't choke throughput + ([#373](https://github.com/Shopify/sarama/pull/373)). + +Bug Fixes: + - Fix the producer's internal reference counting in certain unusual scenarios + ([#367](https://github.com/Shopify/sarama/pull/367)). + - Fix the consumer's internal reference counting in certain unusual scenarios + ([#369](https://github.com/Shopify/sarama/pull/369)). + - Fix a condition where the producer's internal control messages could have + gotten stuck ([#368](https://github.com/Shopify/sarama/pull/368)). + - Fix an issue where invalid partition lists would be cached when asking for + metadata for a non-existant topic ([#372](https://github.com/Shopify/sarama/pull/372)). + + +#### Version 1.0.0 (2015-03-17) + +Version 1.0.0 is the first tagged version, and is almost a complete rewrite. The primary differences with previous untagged versions are: + +- The producer has been rewritten; there is now a `SyncProducer` with a blocking API, and an `AsyncProducer` that is non-blocking. +- The consumer has been rewritten to only open one connection per broker instead of one connection per partition. +- The main types of Sarama are now interfaces to make depedency injection easy; mock implementations for `Consumer`, `SyncProducer` and `AsyncProducer` are provided in the `github.com/Shopify/sarama/mocks` package. +- For most uses cases, it is no longer necessary to open a `Client`; this will be done for you. +- All the configuration values have been unified in the `Config` struct. +- Much improved test suite. diff --git a/vendor/github.com/Shopify/sarama/LICENSE b/vendor/github.com/Shopify/sarama/LICENSE new file mode 100644 index 0000000..d2bf435 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Shopify + +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. diff --git a/vendor/github.com/Shopify/sarama/Makefile b/vendor/github.com/Shopify/sarama/Makefile new file mode 100644 index 0000000..9c8329e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/Makefile @@ -0,0 +1,56 @@ +export GO111MODULE=on + +default: fmt vet errcheck test lint + +# Taken from https://github.com/codecov/example-go#caveat-multiple-files +.PHONY: test +test: + echo "mode: atomic" > coverage.txt + for d in `go list ./...`; do \ + go test -p 1 -v -timeout 6m -race -coverprofile=profile.out -covermode=atomic $$d || exit 1; \ + if [ -f profile.out ]; then \ + tail +2 profile.out >> coverage.txt; \ + rm profile.out; \ + fi \ + done + +GOLINT := $(shell command -v golint) + +.PHONY: lint +lint: +ifndef GOLINT + go get golang.org/x/lint/golint +endif + go list ./... | xargs golint + +.PHONY: vet +vet: + go vet ./... + +ERRCHECK := $(shell command -v errcheck) +# See https://github.com/kisielk/errcheck/pull/141 for details on ignorepkg +.PHONY: errcheck +errcheck: +ifndef ERRCHECK + go get github.com/kisielk/errcheck +endif + errcheck -ignorepkg fmt github.com/Shopify/sarama/... + +.PHONY: fmt +fmt: + @if [ -n "$$(go fmt ./...)" ]; then echo 'Please run go fmt on your code.' && exit 1; fi + +.PHONY : install_dependencies +install_dependencies: get + +.PHONY: get +get: + go get -v ./... + +.PHONY: clean +clean: + go clean ./... + +.PHONY: tidy +tidy: + go mod tidy -v diff --git a/vendor/github.com/Shopify/sarama/README.md b/vendor/github.com/Shopify/sarama/README.md new file mode 100644 index 0000000..0206fac --- /dev/null +++ b/vendor/github.com/Shopify/sarama/README.md @@ -0,0 +1,36 @@ +# sarama + +[![GoDoc](https://godoc.org/github.com/Shopify/sarama?status.svg)](https://godoc.org/github.com/Shopify/sarama) +[![Build Status](https://travis-ci.org/Shopify/sarama.svg?branch=master)](https://travis-ci.org/Shopify/sarama) +[![Coverage](https://codecov.io/gh/Shopify/sarama/branch/master/graph/badge.svg)](https://codecov.io/gh/Shopify/sarama) + +Sarama is an MIT-licensed Go client library for [Apache Kafka](https://kafka.apache.org/) version 0.8 (and later). + +## Getting started + +- API documentation and examples are available via [godoc](https://godoc.org/github.com/Shopify/sarama). +- Mocks for testing are available in the [mocks](./mocks) subpackage. +- The [examples](./examples) directory contains more elaborate example applications. +- The [tools](./tools) directory contains command line tools that can be useful for testing, diagnostics, and instrumentation. + +You might also want to look at the [Frequently Asked Questions](https://github.com/Shopify/sarama/wiki/Frequently-Asked-Questions). + +## Compatibility and API stability + +Sarama provides a "2 releases + 2 months" compatibility guarantee: we support +the two latest stable releases of Kafka and Go, and we provide a two month +grace period for older releases. This means we currently officially support +Go 1.11 through 1.13, and Kafka 2.1 through 2.3, although older releases are +still likely to work. + +Sarama follows semantic versioning and provides API stability via the gopkg.in service. +You can import a version with a guaranteed stable API via http://gopkg.in/Shopify/sarama.v1. +A changelog is available [here](CHANGELOG.md). + +## Contributing + +- Get started by checking our [contribution guidelines](https://github.com/Shopify/sarama/blob/master/.github/CONTRIBUTING.md). +- Read the [Sarama wiki](https://github.com/Shopify/sarama/wiki) for more technical and design details. +- The [Kafka Protocol Specification](https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol) contains a wealth of useful information. +- For more general issues, there is [a google group](https://groups.google.com/forum/#!forum/kafka-clients) for Kafka client developers. +- If you have any questions, just ask! diff --git a/vendor/github.com/Shopify/sarama/Vagrantfile b/vendor/github.com/Shopify/sarama/Vagrantfile new file mode 100644 index 0000000..f4b848a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/Vagrantfile @@ -0,0 +1,20 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +# We have 5 * 192MB ZK processes and 5 * 320MB Kafka processes => 2560MB +MEMORY = 3072 + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.box = "ubuntu/trusty64" + + config.vm.provision :shell, path: "vagrant/provision.sh" + + config.vm.network "private_network", ip: "192.168.100.67" + + config.vm.provider "virtualbox" do |v| + v.memory = MEMORY + end +end diff --git a/vendor/github.com/Shopify/sarama/acl_bindings.go b/vendor/github.com/Shopify/sarama/acl_bindings.go new file mode 100644 index 0000000..50b689d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_bindings.go @@ -0,0 +1,138 @@ +package sarama + +//Resource holds information about acl resource type +type Resource struct { + ResourceType AclResourceType + ResourceName string + ResourcePatternType AclResourcePatternType +} + +func (r *Resource) encode(pe packetEncoder, version int16) error { + pe.putInt8(int8(r.ResourceType)) + + if err := pe.putString(r.ResourceName); err != nil { + return err + } + + if version == 1 { + if r.ResourcePatternType == AclPatternUnknown { + Logger.Print("Cannot encode an unknown resource pattern type, using Literal instead") + r.ResourcePatternType = AclPatternLiteral + } + pe.putInt8(int8(r.ResourcePatternType)) + } + + return nil +} + +func (r *Resource) decode(pd packetDecoder, version int16) (err error) { + resourceType, err := pd.getInt8() + if err != nil { + return err + } + r.ResourceType = AclResourceType(resourceType) + + if r.ResourceName, err = pd.getString(); err != nil { + return err + } + if version == 1 { + pattern, err := pd.getInt8() + if err != nil { + return err + } + r.ResourcePatternType = AclResourcePatternType(pattern) + } + + return nil +} + +//Acl holds information about acl type +type Acl struct { + Principal string + Host string + Operation AclOperation + PermissionType AclPermissionType +} + +func (a *Acl) encode(pe packetEncoder) error { + if err := pe.putString(a.Principal); err != nil { + return err + } + + if err := pe.putString(a.Host); err != nil { + return err + } + + pe.putInt8(int8(a.Operation)) + pe.putInt8(int8(a.PermissionType)) + + return nil +} + +func (a *Acl) decode(pd packetDecoder, version int16) (err error) { + if a.Principal, err = pd.getString(); err != nil { + return err + } + + if a.Host, err = pd.getString(); err != nil { + return err + } + + operation, err := pd.getInt8() + if err != nil { + return err + } + a.Operation = AclOperation(operation) + + permissionType, err := pd.getInt8() + if err != nil { + return err + } + a.PermissionType = AclPermissionType(permissionType) + + return nil +} + +//ResourceAcls is an acl resource type +type ResourceAcls struct { + Resource + Acls []*Acl +} + +func (r *ResourceAcls) encode(pe packetEncoder, version int16) error { + if err := r.Resource.encode(pe, version); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.Acls)); err != nil { + return err + } + for _, acl := range r.Acls { + if err := acl.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *ResourceAcls) decode(pd packetDecoder, version int16) error { + if err := r.Resource.decode(pd, version); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Acls = make([]*Acl, n) + for i := 0; i < n; i++ { + r.Acls[i] = new(Acl) + if err := r.Acls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_create_request.go b/vendor/github.com/Shopify/sarama/acl_create_request.go new file mode 100644 index 0000000..da1cdef --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_create_request.go @@ -0,0 +1,85 @@ +package sarama + +//CreateAclsRequest is an acl creation request +type CreateAclsRequest struct { + Version int16 + AclCreations []*AclCreation +} + +func (c *CreateAclsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.AclCreations)); err != nil { + return err + } + + for _, aclCreation := range c.AclCreations { + if err := aclCreation.encode(pe, c.Version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsRequest) decode(pd packetDecoder, version int16) (err error) { + c.Version = version + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.AclCreations = make([]*AclCreation, n) + + for i := 0; i < n; i++ { + c.AclCreations[i] = new(AclCreation) + if err := c.AclCreations[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsRequest) key() int16 { + return 30 +} + +func (c *CreateAclsRequest) version() int16 { + return c.Version +} + +func (c *CreateAclsRequest) requiredVersion() KafkaVersion { + switch c.Version { + case 1: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} + +//AclCreation is a wrapper around Resource and Acl type +type AclCreation struct { + Resource + Acl +} + +func (a *AclCreation) encode(pe packetEncoder, version int16) error { + if err := a.Resource.encode(pe, version); err != nil { + return err + } + if err := a.Acl.encode(pe); err != nil { + return err + } + + return nil +} + +func (a *AclCreation) decode(pd packetDecoder, version int16) (err error) { + if err := a.Resource.decode(pd, version); err != nil { + return err + } + if err := a.Acl.decode(pd, version); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_create_response.go b/vendor/github.com/Shopify/sarama/acl_create_response.go new file mode 100644 index 0000000..f5a5e9a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_create_response.go @@ -0,0 +1,90 @@ +package sarama + +import "time" + +//CreateAclsResponse is a an acl reponse creation type +type CreateAclsResponse struct { + ThrottleTime time.Duration + AclCreationResponses []*AclCreationResponse +} + +func (c *CreateAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(c.AclCreationResponses)); err != nil { + return err + } + + for _, aclCreationResponse := range c.AclCreationResponses { + if err := aclCreationResponse.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.AclCreationResponses = make([]*AclCreationResponse, n) + for i := 0; i < n; i++ { + c.AclCreationResponses[i] = new(AclCreationResponse) + if err := c.AclCreationResponses[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsResponse) key() int16 { + return 30 +} + +func (c *CreateAclsResponse) version() int16 { + return 0 +} + +func (c *CreateAclsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +//AclCreationResponse is an acl creation response type +type AclCreationResponse struct { + Err KError + ErrMsg *string +} + +func (a *AclCreationResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(a.Err)) + + if err := pe.putNullableString(a.ErrMsg); err != nil { + return err + } + + return nil +} + +func (a *AclCreationResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + a.Err = KError(kerr) + + if a.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_request.go b/vendor/github.com/Shopify/sarama/acl_delete_request.go new file mode 100644 index 0000000..15908ea --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_delete_request.go @@ -0,0 +1,58 @@ +package sarama + +//DeleteAclsRequest is a delete acl request +type DeleteAclsRequest struct { + Version int + Filters []*AclFilter +} + +func (d *DeleteAclsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(d.Filters)); err != nil { + return err + } + + for _, filter := range d.Filters { + filter.Version = d.Version + if err := filter.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsRequest) decode(pd packetDecoder, version int16) (err error) { + d.Version = int(version) + n, err := pd.getArrayLength() + if err != nil { + return err + } + + d.Filters = make([]*AclFilter, n) + for i := 0; i < n; i++ { + d.Filters[i] = new(AclFilter) + d.Filters[i].Version = int(version) + if err := d.Filters[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsRequest) key() int16 { + return 31 +} + +func (d *DeleteAclsRequest) version() int16 { + return int16(d.Version) +} + +func (d *DeleteAclsRequest) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_response.go b/vendor/github.com/Shopify/sarama/acl_delete_response.go new file mode 100644 index 0000000..6529565 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_delete_response.go @@ -0,0 +1,159 @@ +package sarama + +import "time" + +//DeleteAclsResponse is a delete acl response +type DeleteAclsResponse struct { + Version int16 + ThrottleTime time.Duration + FilterResponses []*FilterResponse +} + +func (d *DeleteAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(d.FilterResponses)); err != nil { + return err + } + + for _, filterResponse := range d.FilterResponses { + if err := filterResponse.encode(pe, d.Version); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + d.FilterResponses = make([]*FilterResponse, n) + + for i := 0; i < n; i++ { + d.FilterResponses[i] = new(FilterResponse) + if err := d.FilterResponses[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsResponse) key() int16 { + return 31 +} + +func (d *DeleteAclsResponse) version() int16 { + return int16(d.Version) +} + +func (d *DeleteAclsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +//FilterResponse is a filter response type +type FilterResponse struct { + Err KError + ErrMsg *string + MatchingAcls []*MatchingAcl +} + +func (f *FilterResponse) encode(pe packetEncoder, version int16) error { + pe.putInt16(int16(f.Err)) + if err := pe.putNullableString(f.ErrMsg); err != nil { + return err + } + + if err := pe.putArrayLength(len(f.MatchingAcls)); err != nil { + return err + } + for _, matchingAcl := range f.MatchingAcls { + if err := matchingAcl.encode(pe, version); err != nil { + return err + } + } + + return nil +} + +func (f *FilterResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + f.Err = KError(kerr) + + if f.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + f.MatchingAcls = make([]*MatchingAcl, n) + for i := 0; i < n; i++ { + f.MatchingAcls[i] = new(MatchingAcl) + if err := f.MatchingAcls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +//MatchingAcl is a matching acl type +type MatchingAcl struct { + Err KError + ErrMsg *string + Resource + Acl +} + +func (m *MatchingAcl) encode(pe packetEncoder, version int16) error { + pe.putInt16(int16(m.Err)) + if err := pe.putNullableString(m.ErrMsg); err != nil { + return err + } + + if err := m.Resource.encode(pe, version); err != nil { + return err + } + + if err := m.Acl.encode(pe); err != nil { + return err + } + + return nil +} + +func (m *MatchingAcl) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + m.Err = KError(kerr) + + if m.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + if err := m.Resource.decode(pd, version); err != nil { + return err + } + + if err := m.Acl.decode(pd, version); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_request.go b/vendor/github.com/Shopify/sarama/acl_describe_request.go new file mode 100644 index 0000000..5222d46 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_describe_request.go @@ -0,0 +1,35 @@ +package sarama + +//DescribeAclsRequest is a secribe acl request type +type DescribeAclsRequest struct { + Version int + AclFilter +} + +func (d *DescribeAclsRequest) encode(pe packetEncoder) error { + d.AclFilter.Version = d.Version + return d.AclFilter.encode(pe) +} + +func (d *DescribeAclsRequest) decode(pd packetDecoder, version int16) (err error) { + d.Version = int(version) + d.AclFilter.Version = int(version) + return d.AclFilter.decode(pd, version) +} + +func (d *DescribeAclsRequest) key() int16 { + return 29 +} + +func (d *DescribeAclsRequest) version() int16 { + return int16(d.Version) +} + +func (d *DescribeAclsRequest) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_response.go b/vendor/github.com/Shopify/sarama/acl_describe_response.go new file mode 100644 index 0000000..12126e5 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_describe_response.go @@ -0,0 +1,87 @@ +package sarama + +import "time" + +//DescribeAclsResponse is a describe acl response type +type DescribeAclsResponse struct { + Version int16 + ThrottleTime time.Duration + Err KError + ErrMsg *string + ResourceAcls []*ResourceAcls +} + +func (d *DescribeAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(d.Err)) + + if err := pe.putNullableString(d.ErrMsg); err != nil { + return err + } + + if err := pe.putArrayLength(len(d.ResourceAcls)); err != nil { + return err + } + + for _, resourceAcl := range d.ResourceAcls { + if err := resourceAcl.encode(pe, d.Version); err != nil { + return err + } + } + + return nil +} + +func (d *DescribeAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + d.Err = KError(kerr) + + errmsg, err := pd.getString() + if err != nil { + return err + } + if errmsg != "" { + d.ErrMsg = &errmsg + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + d.ResourceAcls = make([]*ResourceAcls, n) + + for i := 0; i < n; i++ { + d.ResourceAcls[i] = new(ResourceAcls) + if err := d.ResourceAcls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DescribeAclsResponse) key() int16 { + return 29 +} + +func (d *DescribeAclsResponse) version() int16 { + return int16(d.Version) +} + +func (d *DescribeAclsResponse) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/acl_filter.go b/vendor/github.com/Shopify/sarama/acl_filter.go new file mode 100644 index 0000000..fad5558 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_filter.go @@ -0,0 +1,78 @@ +package sarama + +type AclFilter struct { + Version int + ResourceType AclResourceType + ResourceName *string + ResourcePatternTypeFilter AclResourcePatternType + Principal *string + Host *string + Operation AclOperation + PermissionType AclPermissionType +} + +func (a *AclFilter) encode(pe packetEncoder) error { + pe.putInt8(int8(a.ResourceType)) + if err := pe.putNullableString(a.ResourceName); err != nil { + return err + } + + if a.Version == 1 { + pe.putInt8(int8(a.ResourcePatternTypeFilter)) + } + + if err := pe.putNullableString(a.Principal); err != nil { + return err + } + if err := pe.putNullableString(a.Host); err != nil { + return err + } + pe.putInt8(int8(a.Operation)) + pe.putInt8(int8(a.PermissionType)) + + return nil +} + +func (a *AclFilter) decode(pd packetDecoder, version int16) (err error) { + resourceType, err := pd.getInt8() + if err != nil { + return err + } + a.ResourceType = AclResourceType(resourceType) + + if a.ResourceName, err = pd.getNullableString(); err != nil { + return err + } + + if a.Version == 1 { + pattern, err := pd.getInt8() + + if err != nil { + return err + } + + a.ResourcePatternTypeFilter = AclResourcePatternType(pattern) + } + + if a.Principal, err = pd.getNullableString(); err != nil { + return err + } + + if a.Host, err = pd.getNullableString(); err != nil { + return err + } + + operation, err := pd.getInt8() + if err != nil { + return err + } + a.Operation = AclOperation(operation) + + permissionType, err := pd.getInt8() + if err != nil { + return err + } + a.PermissionType = AclPermissionType(permissionType) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_types.go b/vendor/github.com/Shopify/sarama/acl_types.go new file mode 100644 index 0000000..c10ad7b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_types.go @@ -0,0 +1,55 @@ +package sarama + +type ( + AclOperation int + + AclPermissionType int + + AclResourceType int + + AclResourcePatternType int +) + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclOperation.java +const ( + AclOperationUnknown AclOperation = iota + AclOperationAny + AclOperationAll + AclOperationRead + AclOperationWrite + AclOperationCreate + AclOperationDelete + AclOperationAlter + AclOperationDescribe + AclOperationClusterAction + AclOperationDescribeConfigs + AclOperationAlterConfigs + AclOperationIdempotentWrite +) + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclPermissionType.java +const ( + AclPermissionUnknown AclPermissionType = iota + AclPermissionAny + AclPermissionDeny + AclPermissionAllow +) + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/ResourceType.java +const ( + AclResourceUnknown AclResourceType = iota + AclResourceAny + AclResourceTopic + AclResourceGroup + AclResourceCluster + AclResourceTransactionalID +) + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/PatternType.java +const ( + AclPatternUnknown AclResourcePatternType = iota + AclPatternAny + AclPatternMatch + AclPatternLiteral + AclPatternPrefixed +) diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go new file mode 100644 index 0000000..fc227ab --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go @@ -0,0 +1,53 @@ +package sarama + +//AddOffsetsToTxnRequest adds offsets to a transaction request +type AddOffsetsToTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + GroupID string +} + +func (a *AddOffsetsToTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + + pe.putInt64(a.ProducerID) + + pe.putInt16(a.ProducerEpoch) + + if err := pe.putString(a.GroupID); err != nil { + return err + } + + return nil +} + +func (a *AddOffsetsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + if a.GroupID, err = pd.getString(); err != nil { + return err + } + return nil +} + +func (a *AddOffsetsToTxnRequest) key() int16 { + return 25 +} + +func (a *AddOffsetsToTxnRequest) version() int16 { + return 0 +} + +func (a *AddOffsetsToTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go new file mode 100644 index 0000000..c88c1f8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go @@ -0,0 +1,45 @@ +package sarama + +import ( + "time" +) + +//AddOffsetsToTxnResponse is a response type for adding offsets to txns +type AddOffsetsToTxnResponse struct { + ThrottleTime time.Duration + Err KError +} + +func (a *AddOffsetsToTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(a.Err)) + return nil +} + +func (a *AddOffsetsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + a.Err = KError(kerr) + + return nil +} + +func (a *AddOffsetsToTxnResponse) key() int16 { + return 25 +} + +func (a *AddOffsetsToTxnResponse) version() int16 { + return 0 +} + +func (a *AddOffsetsToTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go new file mode 100644 index 0000000..8d4b42e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go @@ -0,0 +1,77 @@ +package sarama + +//AddPartitionsToTxnRequest is a add paartition request +type AddPartitionsToTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + TopicPartitions map[string][]int32 +} + +func (a *AddPartitionsToTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + pe.putInt64(a.ProducerID) + pe.putInt16(a.ProducerEpoch) + + if err := pe.putArrayLength(len(a.TopicPartitions)); err != nil { + return err + } + for topic, partitions := range a.TopicPartitions { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + + return nil +} + +func (a *AddPartitionsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + a.TopicPartitions = make(map[string][]int32) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + partitions, err := pd.getInt32Array() + if err != nil { + return err + } + + a.TopicPartitions[topic] = partitions + } + + return nil +} + +func (a *AddPartitionsToTxnRequest) key() int16 { + return 24 +} + +func (a *AddPartitionsToTxnRequest) version() int16 { + return 0 +} + +func (a *AddPartitionsToTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go new file mode 100644 index 0000000..eb4f23e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go @@ -0,0 +1,110 @@ +package sarama + +import ( + "time" +) + +//AddPartitionsToTxnResponse is a partition errors to transaction type +type AddPartitionsToTxnResponse struct { + ThrottleTime time.Duration + Errors map[string][]*PartitionError +} + +func (a *AddPartitionsToTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(a.Errors)); err != nil { + return err + } + + for topic, e := range a.Errors { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(e)); err != nil { + return err + } + for _, partitionError := range e { + if err := partitionError.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (a *AddPartitionsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Errors = make(map[string][]*PartitionError) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Errors[topic] = make([]*PartitionError, m) + + for j := 0; j < m; j++ { + a.Errors[topic][j] = new(PartitionError) + if err := a.Errors[topic][j].decode(pd, version); err != nil { + return err + } + } + } + + return nil +} + +func (a *AddPartitionsToTxnResponse) key() int16 { + return 24 +} + +func (a *AddPartitionsToTxnResponse) version() int16 { + return 0 +} + +func (a *AddPartitionsToTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +//PartitionError is a partition error type +type PartitionError struct { + Partition int32 + Err KError +} + +func (p *PartitionError) encode(pe packetEncoder) error { + pe.putInt32(p.Partition) + pe.putInt16(int16(p.Err)) + return nil +} + +func (p *PartitionError) decode(pd packetDecoder, version int16) (err error) { + if p.Partition, err = pd.getInt32(); err != nil { + return err + } + + kerr, err := pd.getInt16() + if err != nil { + return err + } + p.Err = KError(kerr) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/admin.go b/vendor/github.com/Shopify/sarama/admin.go new file mode 100644 index 0000000..6c9b1e9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/admin.go @@ -0,0 +1,690 @@ +package sarama + +import ( + "errors" + "math/rand" + "sync" +) + +// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics, +// brokers, configurations and ACLs. The minimum broker version required is 0.10.0.0. +// Methods with stricter requirements will specify the minimum broker version required. +// You MUST call Close() on a client to avoid leaks +type ClusterAdmin interface { + // Creates a new topic. This operation is supported by brokers with version 0.10.1.0 or higher. + // It may take several seconds after CreateTopic returns success for all the brokers + // to become aware that the topic has been created. During this time, listTopics + // may not return information about the new topic.The validateOnly option is supported from version 0.10.2.0. + CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error + + // List the topics available in the cluster with the default options. + ListTopics() (map[string]TopicDetail, error) + + // Describe some topics in the cluster. + DescribeTopics(topics []string) (metadata []*TopicMetadata, err error) + + // Delete a topic. It may take several seconds after the DeleteTopic to returns success + // and for all the brokers to become aware that the topics are gone. + // During this time, listTopics may continue to return information about the deleted topic. + // If delete.topic.enable is false on the brokers, deleteTopic will mark + // the topic for deletion, but not actually delete them. + // This operation is supported by brokers with version 0.10.1.0 or higher. + DeleteTopic(topic string) error + + // Increase the number of partitions of the topics according to the corresponding values. + // If partitions are increased for a topic that has a key, the partition logic or ordering of + // the messages will be affected. It may take several seconds after this method returns + // success for all the brokers to become aware that the partitions have been created. + // During this time, ClusterAdmin#describeTopics may not return information about the + // new partitions. This operation is supported by brokers with version 1.0.0 or higher. + CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error + + // Delete records whose offset is smaller than the given offset of the corresponding partition. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DeleteRecords(topic string, partitionOffsets map[int32]int64) error + + // Get the configuration for the specified resources. + // The returned configuration includes default values and the Default is true + // can be used to distinguish them from user supplied values. + // Config entries where ReadOnly is true cannot be updated. + // The value of config entries where Sensitive is true is always nil so + // sensitive information is not disclosed. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) + + // Update the configuration for the specified resources with the default options. + // This operation is supported by brokers with version 0.11.0.0 or higher. + // The resources with their configs (topic is the only resource type with configs + // that can be updated currently Updates are not transactional so they may succeed + // for some resources while fail for others. The configs for a particular resource are updated automatically. + AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error + + // Creates access control lists (ACLs) which are bound to specific resources. + // This operation is not transactional so it may succeed for some ACLs while fail for others. + // If you attempt to add an ACL that duplicates an existing ACL, no error will be raised, but + // no changes will be made. This operation is supported by brokers with version 0.11.0.0 or higher. + CreateACL(resource Resource, acl Acl) error + + // Lists access control lists (ACLs) according to the supplied filter. + // it may take some time for changes made by createAcls or deleteAcls to be reflected in the output of ListAcls + // This operation is supported by brokers with version 0.11.0.0 or higher. + ListAcls(filter AclFilter) ([]ResourceAcls, error) + + // Deletes access control lists (ACLs) according to the supplied filters. + // This operation is not transactional so it may succeed for some ACLs while fail for others. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) + + // List the consumer groups available in the cluster. + ListConsumerGroups() (map[string]string, error) + + // Describe the given consumer groups. + DescribeConsumerGroups(groups []string) ([]*GroupDescription, error) + + // List the consumer group offsets available in the cluster. + ListConsumerGroupOffsets(group string, topicPartitions map[string][]int32) (*OffsetFetchResponse, error) + + // Delete a consumer group. + DeleteConsumerGroup(group string) error + + // Get information about the nodes in the cluster + DescribeCluster() (brokers []*Broker, controllerID int32, err error) + + // Close shuts down the admin and closes underlying client. + Close() error +} + +type clusterAdmin struct { + client Client + conf *Config +} + +// NewClusterAdmin creates a new ClusterAdmin using the given broker addresses and configuration. +func NewClusterAdmin(addrs []string, conf *Config) (ClusterAdmin, error) { + client, err := NewClient(addrs, conf) + if err != nil { + return nil, err + } + return NewClusterAdminFromClient(client) +} + +// NewClusterAdminFromClient creates a new ClusterAdmin using the given client. +// Note that underlying client will also be closed on admin's Close() call. +func NewClusterAdminFromClient(client Client) (ClusterAdmin, error) { + //make sure we can retrieve the controller + _, err := client.Controller() + if err != nil { + return nil, err + } + + ca := &clusterAdmin{ + client: client, + conf: client.Config(), + } + return ca, nil +} + +func (ca *clusterAdmin) Close() error { + return ca.client.Close() +} + +func (ca *clusterAdmin) Controller() (*Broker, error) { + return ca.client.Controller() +} + +func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error { + + if topic == "" { + return ErrInvalidTopic + } + + if detail == nil { + return errors.New("you must specify topic details") + } + + topicDetails := make(map[string]*TopicDetail) + topicDetails[topic] = detail + + request := &CreateTopicsRequest{ + TopicDetails: topicDetails, + ValidateOnly: validateOnly, + Timeout: ca.conf.Admin.Timeout, + } + + if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 1 + } + if ca.conf.Version.IsAtLeast(V1_0_0_0) { + request.Version = 2 + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.CreateTopics(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicErrors[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr.Err != ErrNoError { + return topicErr + } + + return nil +} + +func (ca *clusterAdmin) DescribeTopics(topics []string) (metadata []*TopicMetadata, err error) { + controller, err := ca.Controller() + if err != nil { + return nil, err + } + + request := &MetadataRequest{ + Topics: topics, + AllowAutoTopicCreation: false, + } + + if ca.conf.Version.IsAtLeast(V1_0_0_0) { + request.Version = 5 + } else if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 4 + } + + response, err := controller.GetMetadata(request) + if err != nil { + return nil, err + } + return response.Topics, nil +} + +func (ca *clusterAdmin) DescribeCluster() (brokers []*Broker, controllerID int32, err error) { + controller, err := ca.Controller() + if err != nil { + return nil, int32(0), err + } + + request := &MetadataRequest{ + Topics: []string{}, + } + + if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 1 + } + + response, err := controller.GetMetadata(request) + if err != nil { + return nil, int32(0), err + } + + return response.Brokers, response.ControllerID, nil +} + +func (ca *clusterAdmin) findAnyBroker() (*Broker, error) { + brokers := ca.client.Brokers() + if len(brokers) > 0 { + index := rand.Intn(len(brokers)) + return brokers[index], nil + } + return nil, errors.New("no available broker") +} + +func (ca *clusterAdmin) ListTopics() (map[string]TopicDetail, error) { + // In order to build TopicDetails we need to first get the list of all + // topics using a MetadataRequest and then get their configs using a + // DescribeConfigsRequest request. To avoid sending many requests to the + // broker, we use a single DescribeConfigsRequest. + + // Send the all-topic MetadataRequest + b, err := ca.findAnyBroker() + if err != nil { + return nil, err + } + _ = b.Open(ca.client.Config()) + + metadataReq := &MetadataRequest{} + metadataResp, err := b.GetMetadata(metadataReq) + if err != nil { + return nil, err + } + + topicsDetailsMap := make(map[string]TopicDetail) + + var describeConfigsResources []*ConfigResource + + for _, topic := range metadataResp.Topics { + topicDetails := TopicDetail{ + NumPartitions: int32(len(topic.Partitions)), + } + if len(topic.Partitions) > 0 { + topicDetails.ReplicaAssignment = map[int32][]int32{} + for _, partition := range topic.Partitions { + topicDetails.ReplicaAssignment[partition.ID] = partition.Replicas + } + topicDetails.ReplicationFactor = int16(len(topic.Partitions[0].Replicas)) + } + topicsDetailsMap[topic.Name] = topicDetails + + // we populate the resources we want to describe from the MetadataResponse + topicResource := ConfigResource{ + Type: TopicResource, + Name: topic.Name, + } + describeConfigsResources = append(describeConfigsResources, &topicResource) + } + + // Send the DescribeConfigsRequest + describeConfigsReq := &DescribeConfigsRequest{ + Resources: describeConfigsResources, + } + describeConfigsResp, err := b.DescribeConfigs(describeConfigsReq) + if err != nil { + return nil, err + } + + for _, resource := range describeConfigsResp.Resources { + topicDetails := topicsDetailsMap[resource.Name] + topicDetails.ConfigEntries = make(map[string]*string) + + for _, entry := range resource.Configs { + // only include non-default non-sensitive config + // (don't actually think topic config will ever be sensitive) + if entry.Default || entry.Sensitive { + continue + } + topicDetails.ConfigEntries[entry.Name] = &entry.Value + } + + topicsDetailsMap[resource.Name] = topicDetails + } + + return topicsDetailsMap, nil +} + +func (ca *clusterAdmin) DeleteTopic(topic string) error { + + if topic == "" { + return ErrInvalidTopic + } + + request := &DeleteTopicsRequest{ + Topics: []string{topic}, + Timeout: ca.conf.Admin.Timeout, + } + + if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 1 + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.DeleteTopics(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicErrorCodes[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr != ErrNoError { + return topicErr + } + return nil +} + +func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error { + if topic == "" { + return ErrInvalidTopic + } + + topicPartitions := make(map[string]*TopicPartition) + topicPartitions[topic] = &TopicPartition{Count: count, Assignment: assignment} + + request := &CreatePartitionsRequest{ + TopicPartitions: topicPartitions, + Timeout: ca.conf.Admin.Timeout, + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.CreatePartitions(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicPartitionErrors[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr.Err != ErrNoError { + return topicErr + } + + return nil +} + +func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error { + + if topic == "" { + return ErrInvalidTopic + } + partitionPerBroker := make(map[*Broker][]int32) + for partition := range partitionOffsets { + broker, err := ca.client.Leader(topic, partition) + if err != nil { + return err + } + if _, ok := partitionPerBroker[broker]; ok { + partitionPerBroker[broker] = append(partitionPerBroker[broker], partition) + } else { + partitionPerBroker[broker] = []int32{partition} + } + } + errs := make([]error, 0) + for broker, partitions := range partitionPerBroker { + topics := make(map[string]*DeleteRecordsRequestTopic) + recordsToDelete := make(map[int32]int64) + for _, p := range partitions { + recordsToDelete[p] = partitionOffsets[p] + } + topics[topic] = &DeleteRecordsRequestTopic{PartitionOffsets: recordsToDelete} + request := &DeleteRecordsRequest{ + Topics: topics, + Timeout: ca.conf.Admin.Timeout, + } + + rsp, err := broker.DeleteRecords(request) + if err != nil { + errs = append(errs, err) + } else { + deleteRecordsResponseTopic, ok := rsp.Topics[topic] + if !ok { + errs = append(errs, ErrIncompleteResponse) + } else { + for _, deleteRecordsResponsePartition := range deleteRecordsResponseTopic.Partitions { + if deleteRecordsResponsePartition.Err != ErrNoError { + errs = append(errs, errors.New(deleteRecordsResponsePartition.Err.Error())) + } + } + } + } + } + if len(errs) > 0 { + return ErrDeleteRecords{MultiError{&errs}} + } + //todo since we are dealing with couple of partitions it would be good if we return slice of errors + //for each partition instead of one error + return nil +} + +func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) { + + var entries []ConfigEntry + var resources []*ConfigResource + resources = append(resources, &resource) + + request := &DescribeConfigsRequest{ + Resources: resources, + } + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DescribeConfigs(request) + if err != nil { + return nil, err + } + + for _, rspResource := range rsp.Resources { + if rspResource.Name == resource.Name { + if rspResource.ErrorMsg != "" { + return nil, errors.New(rspResource.ErrorMsg) + } + for _, cfgEntry := range rspResource.Configs { + entries = append(entries, *cfgEntry) + } + } + } + return entries, nil +} + +func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error { + + var resources []*AlterConfigsResource + resources = append(resources, &AlterConfigsResource{ + Type: resourceType, + Name: name, + ConfigEntries: entries, + }) + + request := &AlterConfigsRequest{ + Resources: resources, + ValidateOnly: validateOnly, + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.AlterConfigs(request) + if err != nil { + return err + } + + for _, rspResource := range rsp.Resources { + if rspResource.Name == name { + if rspResource.ErrorMsg != "" { + return errors.New(rspResource.ErrorMsg) + } + } + } + return nil +} + +func (ca *clusterAdmin) CreateACL(resource Resource, acl Acl) error { + var acls []*AclCreation + acls = append(acls, &AclCreation{resource, acl}) + request := &CreateAclsRequest{AclCreations: acls} + + if ca.conf.Version.IsAtLeast(V2_0_0_0) { + request.Version = 1 + } + + b, err := ca.Controller() + if err != nil { + return err + } + + _, err = b.CreateAcls(request) + return err +} + +func (ca *clusterAdmin) ListAcls(filter AclFilter) ([]ResourceAcls, error) { + + request := &DescribeAclsRequest{AclFilter: filter} + + if ca.conf.Version.IsAtLeast(V2_0_0_0) { + request.Version = 1 + } + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DescribeAcls(request) + if err != nil { + return nil, err + } + + var lAcls []ResourceAcls + for _, rAcl := range rsp.ResourceAcls { + lAcls = append(lAcls, *rAcl) + } + return lAcls, nil +} + +func (ca *clusterAdmin) DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) { + var filters []*AclFilter + filters = append(filters, &filter) + request := &DeleteAclsRequest{Filters: filters} + + if ca.conf.Version.IsAtLeast(V2_0_0_0) { + request.Version = 1 + } + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DeleteAcls(request) + if err != nil { + return nil, err + } + + var mAcls []MatchingAcl + for _, fr := range rsp.FilterResponses { + for _, mACL := range fr.MatchingAcls { + mAcls = append(mAcls, *mACL) + } + + } + return mAcls, nil +} + +func (ca *clusterAdmin) DescribeConsumerGroups(groups []string) (result []*GroupDescription, err error) { + groupsPerBroker := make(map[*Broker][]string) + + for _, group := range groups { + controller, err := ca.client.Coordinator(group) + if err != nil { + return nil, err + } + groupsPerBroker[controller] = append(groupsPerBroker[controller], group) + + } + + for broker, brokerGroups := range groupsPerBroker { + response, err := broker.DescribeGroups(&DescribeGroupsRequest{ + Groups: brokerGroups, + }) + if err != nil { + return nil, err + } + + result = append(result, response.Groups...) + } + return result, nil +} + +func (ca *clusterAdmin) ListConsumerGroups() (allGroups map[string]string, err error) { + allGroups = make(map[string]string) + + // Query brokers in parallel, since we have to query *all* brokers + brokers := ca.client.Brokers() + groupMaps := make(chan map[string]string, len(brokers)) + errors := make(chan error, len(brokers)) + wg := sync.WaitGroup{} + + for _, b := range brokers { + wg.Add(1) + go func(b *Broker, conf *Config) { + defer wg.Done() + _ = b.Open(conf) // Ensure that broker is opened + + response, err := b.ListGroups(&ListGroupsRequest{}) + if err != nil { + errors <- err + return + } + + groups := make(map[string]string) + for group, typ := range response.Groups { + groups[group] = typ + } + + groupMaps <- groups + + }(b, ca.conf) + } + + wg.Wait() + close(groupMaps) + close(errors) + + for groupMap := range groupMaps { + for group, protocolType := range groupMap { + allGroups[group] = protocolType + } + } + + // Intentionally return only the first error for simplicity + err = <-errors + return +} + +func (ca *clusterAdmin) ListConsumerGroupOffsets(group string, topicPartitions map[string][]int32) (*OffsetFetchResponse, error) { + coordinator, err := ca.client.Coordinator(group) + if err != nil { + return nil, err + } + + request := &OffsetFetchRequest{ + ConsumerGroup: group, + partitions: topicPartitions, + } + + if ca.conf.Version.IsAtLeast(V0_10_2_0) { + request.Version = 2 + } else if ca.conf.Version.IsAtLeast(V0_8_2_2) { + request.Version = 1 + } + + return coordinator.FetchOffset(request) +} + +func (ca *clusterAdmin) DeleteConsumerGroup(group string) error { + coordinator, err := ca.client.Coordinator(group) + if err != nil { + return err + } + + request := &DeleteGroupsRequest{ + Groups: []string{group}, + } + + resp, err := coordinator.DeleteGroups(request) + if err != nil { + return err + } + + groupErr, ok := resp.GroupErrorCodes[group] + if !ok { + return ErrIncompleteResponse + } + + if groupErr != ErrNoError { + return groupErr + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_request.go b/vendor/github.com/Shopify/sarama/alter_configs_request.go new file mode 100644 index 0000000..26c275b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/alter_configs_request.go @@ -0,0 +1,122 @@ +package sarama + +//AlterConfigsRequest is an alter config request type +type AlterConfigsRequest struct { + Resources []*AlterConfigsResource + ValidateOnly bool +} + +//AlterConfigsResource is an alter config resource type +type AlterConfigsResource struct { + Type ConfigResourceType + Name string + ConfigEntries map[string]*string +} + +func (a *AlterConfigsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(a.Resources)); err != nil { + return err + } + + for _, r := range a.Resources { + if err := r.encode(pe); err != nil { + return err + } + } + + pe.putBool(a.ValidateOnly) + return nil +} + +func (a *AlterConfigsRequest) decode(pd packetDecoder, version int16) error { + resourceCount, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Resources = make([]*AlterConfigsResource, resourceCount) + for i := range a.Resources { + r := &AlterConfigsResource{} + err = r.decode(pd, version) + if err != nil { + return err + } + a.Resources[i] = r + } + + validateOnly, err := pd.getBool() + if err != nil { + return err + } + + a.ValidateOnly = validateOnly + + return nil +} + +func (a *AlterConfigsResource) encode(pe packetEncoder) error { + pe.putInt8(int8(a.Type)) + + if err := pe.putString(a.Name); err != nil { + return err + } + + if err := pe.putArrayLength(len(a.ConfigEntries)); err != nil { + return err + } + for configKey, configValue := range a.ConfigEntries { + if err := pe.putString(configKey); err != nil { + return err + } + if err := pe.putNullableString(configValue); err != nil { + return err + } + } + + return nil +} + +func (a *AlterConfigsResource) decode(pd packetDecoder, version int16) error { + t, err := pd.getInt8() + if err != nil { + return err + } + a.Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + a.Name = name + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + a.ConfigEntries = make(map[string]*string, n) + for i := 0; i < n; i++ { + configKey, err := pd.getString() + if err != nil { + return err + } + if a.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { + return err + } + } + } + return err +} + +func (a *AlterConfigsRequest) key() int16 { + return 33 +} + +func (a *AlterConfigsRequest) version() int16 { + return 0 +} + +func (a *AlterConfigsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_response.go b/vendor/github.com/Shopify/sarama/alter_configs_response.go new file mode 100644 index 0000000..3893663 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/alter_configs_response.go @@ -0,0 +1,97 @@ +package sarama + +import "time" + +//AlterConfigsResponse is a reponse type for alter config +type AlterConfigsResponse struct { + ThrottleTime time.Duration + Resources []*AlterConfigsResourceResponse +} + +//AlterConfigsResourceResponse is a reponse type for alter config resource +type AlterConfigsResourceResponse struct { + ErrorCode int16 + ErrorMsg string + Type ConfigResourceType + Name string +} + +func (a *AlterConfigsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(a.Resources)); err != nil { + return err + } + + for i := range a.Resources { + pe.putInt16(a.Resources[i].ErrorCode) + err := pe.putString(a.Resources[i].ErrorMsg) + if err != nil { + return nil + } + pe.putInt8(int8(a.Resources[i].Type)) + err = pe.putString(a.Resources[i].Name) + if err != nil { + return nil + } + } + + return nil +} + +func (a *AlterConfigsResponse) decode(pd packetDecoder, version int16) error { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + responseCount, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Resources = make([]*AlterConfigsResourceResponse, responseCount) + + for i := range a.Resources { + a.Resources[i] = new(AlterConfigsResourceResponse) + + errCode, err := pd.getInt16() + if err != nil { + return err + } + a.Resources[i].ErrorCode = errCode + + e, err := pd.getString() + if err != nil { + return err + } + a.Resources[i].ErrorMsg = e + + t, err := pd.getInt8() + if err != nil { + return err + } + a.Resources[i].Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + a.Resources[i].Name = name + } + + return nil +} + +func (a *AlterConfigsResponse) key() int16 { + return 32 +} + +func (a *AlterConfigsResponse) version() int16 { + return 0 +} + +func (a *AlterConfigsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/api_versions_request.go b/vendor/github.com/Shopify/sarama/api_versions_request.go new file mode 100644 index 0000000..b33167c --- /dev/null +++ b/vendor/github.com/Shopify/sarama/api_versions_request.go @@ -0,0 +1,25 @@ +package sarama + +//ApiVersionsRequest ... +type ApiVersionsRequest struct { +} + +func (a *ApiVersionsRequest) encode(pe packetEncoder) error { + return nil +} + +func (a *ApiVersionsRequest) decode(pd packetDecoder, version int16) (err error) { + return nil +} + +func (a *ApiVersionsRequest) key() int16 { + return 18 +} + +func (a *ApiVersionsRequest) version() int16 { + return 0 +} + +func (a *ApiVersionsRequest) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/api_versions_response.go b/vendor/github.com/Shopify/sarama/api_versions_response.go new file mode 100644 index 0000000..bb1f0b3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/api_versions_response.go @@ -0,0 +1,89 @@ +package sarama + +//ApiVersionsResponseBlock is an api version reponse block type +type ApiVersionsResponseBlock struct { + ApiKey int16 + MinVersion int16 + MaxVersion int16 +} + +func (b *ApiVersionsResponseBlock) encode(pe packetEncoder) error { + pe.putInt16(b.ApiKey) + pe.putInt16(b.MinVersion) + pe.putInt16(b.MaxVersion) + return nil +} + +func (b *ApiVersionsResponseBlock) decode(pd packetDecoder) error { + var err error + + if b.ApiKey, err = pd.getInt16(); err != nil { + return err + } + + if b.MinVersion, err = pd.getInt16(); err != nil { + return err + } + + if b.MaxVersion, err = pd.getInt16(); err != nil { + return err + } + + return nil +} + +//ApiVersionsResponse is an api version response type +type ApiVersionsResponse struct { + Err KError + ApiVersions []*ApiVersionsResponseBlock +} + +func (r *ApiVersionsResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + if err := pe.putArrayLength(len(r.ApiVersions)); err != nil { + return err + } + for _, apiVersion := range r.ApiVersions { + if err := apiVersion.encode(pe); err != nil { + return err + } + } + return nil +} + +func (r *ApiVersionsResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.ApiVersions = make([]*ApiVersionsResponseBlock, numBlocks) + for i := 0; i < numBlocks; i++ { + block := new(ApiVersionsResponseBlock) + if err := block.decode(pd); err != nil { + return err + } + r.ApiVersions[i] = block + } + + return nil +} + +func (r *ApiVersionsResponse) key() int16 { + return 18 +} + +func (r *ApiVersionsResponse) version() int16 { + return 0 +} + +func (r *ApiVersionsResponse) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/async_producer.go b/vendor/github.com/Shopify/sarama/async_producer.go new file mode 100644 index 0000000..9b15cd1 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/async_producer.go @@ -0,0 +1,1116 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "sync" + "time" + + "github.com/eapache/go-resiliency/breaker" + "github.com/eapache/queue" +) + +// AsyncProducer publishes Kafka messages using a non-blocking API. It routes messages +// to the correct broker for the provided topic-partition, refreshing metadata as appropriate, +// and parses responses for errors. You must read from the Errors() channel or the +// producer will deadlock. You must call Close() or AsyncClose() on a producer to avoid +// leaks: it will not be garbage-collected automatically when it passes out of +// scope. +type AsyncProducer interface { + + // AsyncClose triggers a shutdown of the producer. The shutdown has completed + // when both the Errors and Successes channels have been closed. When calling + // AsyncClose, you *must* continue to read from those channels in order to + // drain the results of any messages in flight. + AsyncClose() + + // Close shuts down the producer and waits for any buffered messages to be + // flushed. You must call this function before a producer object passes out of + // scope, as it may otherwise leak memory. You must call this before calling + // Close on the underlying client. + Close() error + + // Input is the input channel for the user to write messages to that they + // wish to send. + Input() chan<- *ProducerMessage + + // Successes is the success output channel back to the user when Return.Successes is + // enabled. If Return.Successes is true, you MUST read from this channel or the + // Producer will deadlock. It is suggested that you send and read messages + // together in a single select statement. + Successes() <-chan *ProducerMessage + + // Errors is the error output channel back to the user. You MUST read from this + // channel or the Producer will deadlock when the channel is full. Alternatively, + // you can set Producer.Return.Errors in your config to false, which prevents + // errors to be returned. + Errors() <-chan *ProducerError +} + +// transactionManager keeps the state necessary to ensure idempotent production +type transactionManager struct { + producerID int64 + producerEpoch int16 + sequenceNumbers map[string]int32 + mutex sync.Mutex +} + +const ( + noProducerID = -1 + noProducerEpoch = -1 +) + +func (t *transactionManager) getAndIncrementSequenceNumber(topic string, partition int32) int32 { + key := fmt.Sprintf("%s-%d", topic, partition) + t.mutex.Lock() + defer t.mutex.Unlock() + sequence := t.sequenceNumbers[key] + t.sequenceNumbers[key] = sequence + 1 + return sequence +} + +func newTransactionManager(conf *Config, client Client) (*transactionManager, error) { + txnmgr := &transactionManager{ + producerID: noProducerID, + producerEpoch: noProducerEpoch, + } + + if conf.Producer.Idempotent { + initProducerIDResponse, err := client.InitProducerID() + if err != nil { + return nil, err + } + txnmgr.producerID = initProducerIDResponse.ProducerID + txnmgr.producerEpoch = initProducerIDResponse.ProducerEpoch + txnmgr.sequenceNumbers = make(map[string]int32) + txnmgr.mutex = sync.Mutex{} + + Logger.Printf("Obtained a ProducerId: %d and ProducerEpoch: %d\n", txnmgr.producerID, txnmgr.producerEpoch) + } + + return txnmgr, nil +} + +type asyncProducer struct { + client Client + conf *Config + + errors chan *ProducerError + input, successes, retries chan *ProducerMessage + inFlight sync.WaitGroup + + brokers map[*Broker]*brokerProducer + brokerRefs map[*brokerProducer]int + brokerLock sync.Mutex + + txnmgr *transactionManager +} + +// NewAsyncProducer creates a new AsyncProducer using the given broker addresses and configuration. +func NewAsyncProducer(addrs []string, conf *Config) (AsyncProducer, error) { + client, err := NewClient(addrs, conf) + if err != nil { + return nil, err + } + return newAsyncProducer(client) +} + +// NewAsyncProducerFromClient creates a new Producer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this producer. +func NewAsyncProducerFromClient(client Client) (AsyncProducer, error) { + // For clients passed in by the client, ensure we don't + // call Close() on it. + cli := &nopCloserClient{client} + return newAsyncProducer(cli) +} + +func newAsyncProducer(client Client) (AsyncProducer, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + txnmgr, err := newTransactionManager(client.Config(), client) + if err != nil { + return nil, err + } + + p := &asyncProducer{ + client: client, + conf: client.Config(), + errors: make(chan *ProducerError), + input: make(chan *ProducerMessage), + successes: make(chan *ProducerMessage), + retries: make(chan *ProducerMessage), + brokers: make(map[*Broker]*brokerProducer), + brokerRefs: make(map[*brokerProducer]int), + txnmgr: txnmgr, + } + + // launch our singleton dispatchers + go withRecover(p.dispatcher) + go withRecover(p.retryHandler) + + return p, nil +} + +type flagSet int8 + +const ( + syn flagSet = 1 << iota // first message from partitionProducer to brokerProducer + fin // final message from partitionProducer to brokerProducer and back + shutdown // start the shutdown process +) + +// ProducerMessage is the collection of elements passed to the Producer in order to send a message. +type ProducerMessage struct { + Topic string // The Kafka topic for this message. + // The partitioning key for this message. Pre-existing Encoders include + // StringEncoder and ByteEncoder. + Key Encoder + // The actual message to store in Kafka. Pre-existing Encoders include + // StringEncoder and ByteEncoder. + Value Encoder + + // The headers are key-value pairs that are transparently passed + // by Kafka between producers and consumers. + Headers []RecordHeader + + // This field is used to hold arbitrary data you wish to include so it + // will be available when receiving on the Successes and Errors channels. + // Sarama completely ignores this field and is only to be used for + // pass-through data. + Metadata interface{} + + // Below this point are filled in by the producer as the message is processed + + // Offset is the offset of the message stored on the broker. This is only + // guaranteed to be defined if the message was successfully delivered and + // RequiredAcks is not NoResponse. + Offset int64 + // Partition is the partition that the message was sent to. This is only + // guaranteed to be defined if the message was successfully delivered. + Partition int32 + // Timestamp can vary in behaviour depending on broker configuration, being + // in either one of the CreateTime or LogAppendTime modes (default CreateTime), + // and requiring version at least 0.10.0. + // + // When configured to CreateTime, the timestamp is specified by the producer + // either by explicitly setting this field, or when the message is added + // to a produce set. + // + // When configured to LogAppendTime, the timestamp assigned to the message + // by the broker. This is only guaranteed to be defined if the message was + // successfully delivered and RequiredAcks is not NoResponse. + Timestamp time.Time + + retries int + flags flagSet + expectation chan *ProducerError + sequenceNumber int32 +} + +const producerMessageOverhead = 26 // the metadata overhead of CRC, flags, etc. + +func (m *ProducerMessage) byteSize(version int) int { + var size int + if version >= 2 { + size = maximumRecordOverhead + for _, h := range m.Headers { + size += len(h.Key) + len(h.Value) + 2*binary.MaxVarintLen32 + } + } else { + size = producerMessageOverhead + } + if m.Key != nil { + size += m.Key.Length() + } + if m.Value != nil { + size += m.Value.Length() + } + return size +} + +func (m *ProducerMessage) clear() { + m.flags = 0 + m.retries = 0 +} + +// ProducerError is the type of error generated when the producer fails to deliver a message. +// It contains the original ProducerMessage as well as the actual error value. +type ProducerError struct { + Msg *ProducerMessage + Err error +} + +func (pe ProducerError) Error() string { + return fmt.Sprintf("kafka: Failed to produce message to topic %s: %s", pe.Msg.Topic, pe.Err) +} + +// ProducerErrors is a type that wraps a batch of "ProducerError"s and implements the Error interface. +// It can be returned from the Producer's Close method to avoid the need to manually drain the Errors channel +// when closing a producer. +type ProducerErrors []*ProducerError + +func (pe ProducerErrors) Error() string { + return fmt.Sprintf("kafka: Failed to deliver %d messages.", len(pe)) +} + +func (p *asyncProducer) Errors() <-chan *ProducerError { + return p.errors +} + +func (p *asyncProducer) Successes() <-chan *ProducerMessage { + return p.successes +} + +func (p *asyncProducer) Input() chan<- *ProducerMessage { + return p.input +} + +func (p *asyncProducer) Close() error { + p.AsyncClose() + + if p.conf.Producer.Return.Successes { + go withRecover(func() { + for range p.successes { + } + }) + } + + var errors ProducerErrors + if p.conf.Producer.Return.Errors { + for event := range p.errors { + errors = append(errors, event) + } + } else { + <-p.errors + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (p *asyncProducer) AsyncClose() { + go withRecover(p.shutdown) +} + +// singleton +// dispatches messages by topic +func (p *asyncProducer) dispatcher() { + handlers := make(map[string]chan<- *ProducerMessage) + shuttingDown := false + + for msg := range p.input { + if msg == nil { + Logger.Println("Something tried to send a nil message, it was ignored.") + continue + } + + if msg.flags&shutdown != 0 { + shuttingDown = true + p.inFlight.Done() + continue + } else if msg.retries == 0 { + if shuttingDown { + // we can't just call returnError here because that decrements the wait group, + // which hasn't been incremented yet for this message, and shouldn't be + pErr := &ProducerError{Msg: msg, Err: ErrShuttingDown} + if p.conf.Producer.Return.Errors { + p.errors <- pErr + } else { + Logger.Println(pErr) + } + continue + } + p.inFlight.Add(1) + } + + version := 1 + if p.conf.Version.IsAtLeast(V0_11_0_0) { + version = 2 + } else if msg.Headers != nil { + p.returnError(msg, ConfigurationError("Producing headers requires Kafka at least v0.11")) + continue + } + if msg.byteSize(version) > p.conf.Producer.MaxMessageBytes { + p.returnError(msg, ErrMessageSizeTooLarge) + continue + } + + handler := handlers[msg.Topic] + if handler == nil { + handler = p.newTopicProducer(msg.Topic) + handlers[msg.Topic] = handler + } + + handler <- msg + } + + for _, handler := range handlers { + close(handler) + } +} + +// one per topic +// partitions messages, then dispatches them by partition +type topicProducer struct { + parent *asyncProducer + topic string + input <-chan *ProducerMessage + + breaker *breaker.Breaker + handlers map[int32]chan<- *ProducerMessage + partitioner Partitioner +} + +func (p *asyncProducer) newTopicProducer(topic string) chan<- *ProducerMessage { + input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) + tp := &topicProducer{ + parent: p, + topic: topic, + input: input, + breaker: breaker.New(3, 1, 10*time.Second), + handlers: make(map[int32]chan<- *ProducerMessage), + partitioner: p.conf.Producer.Partitioner(topic), + } + go withRecover(tp.dispatch) + return input +} + +func (tp *topicProducer) dispatch() { + for msg := range tp.input { + if msg.retries == 0 { + if err := tp.partitionMessage(msg); err != nil { + tp.parent.returnError(msg, err) + continue + } + } + // All messages being retried (sent or not) have already had their retry count updated + if tp.parent.conf.Producer.Idempotent && msg.retries == 0 { + msg.sequenceNumber = tp.parent.txnmgr.getAndIncrementSequenceNumber(msg.Topic, msg.Partition) + } + + handler := tp.handlers[msg.Partition] + if handler == nil { + handler = tp.parent.newPartitionProducer(msg.Topic, msg.Partition) + tp.handlers[msg.Partition] = handler + } + + handler <- msg + } + + for _, handler := range tp.handlers { + close(handler) + } +} + +func (tp *topicProducer) partitionMessage(msg *ProducerMessage) error { + var partitions []int32 + + err := tp.breaker.Run(func() (err error) { + var requiresConsistency = false + if ep, ok := tp.partitioner.(DynamicConsistencyPartitioner); ok { + requiresConsistency = ep.MessageRequiresConsistency(msg) + } else { + requiresConsistency = tp.partitioner.RequiresConsistency() + } + + if requiresConsistency { + partitions, err = tp.parent.client.Partitions(msg.Topic) + } else { + partitions, err = tp.parent.client.WritablePartitions(msg.Topic) + } + return + }) + + if err != nil { + return err + } + + numPartitions := int32(len(partitions)) + + if numPartitions == 0 { + return ErrLeaderNotAvailable + } + + choice, err := tp.partitioner.Partition(msg, numPartitions) + + if err != nil { + return err + } else if choice < 0 || choice >= numPartitions { + return ErrInvalidPartition + } + + msg.Partition = partitions[choice] + + return nil +} + +// one per partition per topic +// dispatches messages to the appropriate broker +// also responsible for maintaining message order during retries +type partitionProducer struct { + parent *asyncProducer + topic string + partition int32 + input <-chan *ProducerMessage + + leader *Broker + breaker *breaker.Breaker + brokerProducer *brokerProducer + + // highWatermark tracks the "current" retry level, which is the only one where we actually let messages through, + // all other messages get buffered in retryState[msg.retries].buf to preserve ordering + // retryState[msg.retries].expectChaser simply tracks whether we've seen a fin message for a given level (and + // therefore whether our buffer is complete and safe to flush) + highWatermark int + retryState []partitionRetryState +} + +type partitionRetryState struct { + buf []*ProducerMessage + expectChaser bool +} + +func (p *asyncProducer) newPartitionProducer(topic string, partition int32) chan<- *ProducerMessage { + input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) + pp := &partitionProducer{ + parent: p, + topic: topic, + partition: partition, + input: input, + + breaker: breaker.New(3, 1, 10*time.Second), + retryState: make([]partitionRetryState, p.conf.Producer.Retry.Max+1), + } + go withRecover(pp.dispatch) + return input +} + +func (pp *partitionProducer) backoff(retries int) { + var backoff time.Duration + if pp.parent.conf.Producer.Retry.BackoffFunc != nil { + maxRetries := pp.parent.conf.Producer.Retry.Max + backoff = pp.parent.conf.Producer.Retry.BackoffFunc(retries, maxRetries) + } else { + backoff = pp.parent.conf.Producer.Retry.Backoff + } + if backoff > 0 { + time.Sleep(backoff) + } +} + +func (pp *partitionProducer) dispatch() { + // try to prefetch the leader; if this doesn't work, we'll do a proper call to `updateLeader` + // on the first message + pp.leader, _ = pp.parent.client.Leader(pp.topic, pp.partition) + if pp.leader != nil { + pp.brokerProducer = pp.parent.getBrokerProducer(pp.leader) + pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight + pp.brokerProducer.input <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} + } + + defer func() { + if pp.brokerProducer != nil { + pp.parent.unrefBrokerProducer(pp.leader, pp.brokerProducer) + } + }() + + for msg := range pp.input { + if pp.brokerProducer != nil && pp.brokerProducer.abandoned != nil { + select { + case <-pp.brokerProducer.abandoned: + // a message on the abandoned channel means that our current broker selection is out of date + Logger.Printf("producer/leader/%s/%d abandoning broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + pp.parent.unrefBrokerProducer(pp.leader, pp.brokerProducer) + pp.brokerProducer = nil + time.Sleep(pp.parent.conf.Producer.Retry.Backoff) + default: + // producer connection is still open. + } + } + + if msg.retries > pp.highWatermark { + // a new, higher, retry level; handle it and then back off + pp.newHighWatermark(msg.retries) + pp.backoff(msg.retries) + } else if pp.highWatermark > 0 { + // we are retrying something (else highWatermark would be 0) but this message is not a *new* retry level + if msg.retries < pp.highWatermark { + // in fact this message is not even the current retry level, so buffer it for now (unless it's a just a fin) + if msg.flags&fin == fin { + pp.retryState[msg.retries].expectChaser = false + pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected + } else { + pp.retryState[msg.retries].buf = append(pp.retryState[msg.retries].buf, msg) + } + continue + } else if msg.flags&fin == fin { + // this message is of the current retry level (msg.retries == highWatermark) and the fin flag is set, + // meaning this retry level is done and we can go down (at least) one level and flush that + pp.retryState[pp.highWatermark].expectChaser = false + pp.flushRetryBuffers() + pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected + continue + } + } + + // if we made it this far then the current msg contains real data, and can be sent to the next goroutine + // without breaking any of our ordering guarantees + + if pp.brokerProducer == nil { + if err := pp.updateLeader(); err != nil { + pp.parent.returnError(msg, err) + pp.backoff(msg.retries) + continue + } + Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + } + + pp.brokerProducer.input <- msg + } +} + +func (pp *partitionProducer) newHighWatermark(hwm int) { + Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, hwm) + pp.highWatermark = hwm + + // send off a fin so that we know when everything "in between" has made it + // back to us and we can safely flush the backlog (otherwise we risk re-ordering messages) + pp.retryState[pp.highWatermark].expectChaser = true + pp.parent.inFlight.Add(1) // we're generating a fin message; track it so we don't shut down while it's still inflight + pp.brokerProducer.input <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: fin, retries: pp.highWatermark - 1} + + // a new HWM means that our current broker selection is out of date + Logger.Printf("producer/leader/%s/%d abandoning broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + pp.parent.unrefBrokerProducer(pp.leader, pp.brokerProducer) + pp.brokerProducer = nil +} + +func (pp *partitionProducer) flushRetryBuffers() { + Logger.Printf("producer/leader/%s/%d state change to [flushing-%d]\n", pp.topic, pp.partition, pp.highWatermark) + for { + pp.highWatermark-- + + if pp.brokerProducer == nil { + if err := pp.updateLeader(); err != nil { + pp.parent.returnErrors(pp.retryState[pp.highWatermark].buf, err) + goto flushDone + } + Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + } + + for _, msg := range pp.retryState[pp.highWatermark].buf { + pp.brokerProducer.input <- msg + } + + flushDone: + pp.retryState[pp.highWatermark].buf = nil + if pp.retryState[pp.highWatermark].expectChaser { + Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, pp.highWatermark) + break + } else if pp.highWatermark == 0 { + Logger.Printf("producer/leader/%s/%d state change to [normal]\n", pp.topic, pp.partition) + break + } + } +} + +func (pp *partitionProducer) updateLeader() error { + return pp.breaker.Run(func() (err error) { + if err = pp.parent.client.RefreshMetadata(pp.topic); err != nil { + return err + } + + if pp.leader, err = pp.parent.client.Leader(pp.topic, pp.partition); err != nil { + return err + } + + pp.brokerProducer = pp.parent.getBrokerProducer(pp.leader) + pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight + pp.brokerProducer.input <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} + + return nil + }) +} + +// one per broker; also constructs an associated flusher +func (p *asyncProducer) newBrokerProducer(broker *Broker) *brokerProducer { + var ( + input = make(chan *ProducerMessage) + bridge = make(chan *produceSet) + responses = make(chan *brokerProducerResponse) + ) + + bp := &brokerProducer{ + parent: p, + broker: broker, + input: input, + output: bridge, + responses: responses, + stopchan: make(chan struct{}), + buffer: newProduceSet(p), + currentRetries: make(map[string]map[int32]error), + } + go withRecover(bp.run) + + // minimal bridge to make the network response `select`able + go withRecover(func() { + for set := range bridge { + request := set.buildRequest() + + response, err := broker.Produce(request) + + responses <- &brokerProducerResponse{ + set: set, + err: err, + res: response, + } + } + close(responses) + }) + + if p.conf.Producer.Retry.Max <= 0 { + bp.abandoned = make(chan struct{}) + } + + return bp +} + +type brokerProducerResponse struct { + set *produceSet + err error + res *ProduceResponse +} + +// groups messages together into appropriately-sized batches for sending to the broker +// handles state related to retries etc +type brokerProducer struct { + parent *asyncProducer + broker *Broker + + input chan *ProducerMessage + output chan<- *produceSet + responses <-chan *brokerProducerResponse + abandoned chan struct{} + stopchan chan struct{} + + buffer *produceSet + timer <-chan time.Time + timerFired bool + + closing error + currentRetries map[string]map[int32]error +} + +func (bp *brokerProducer) run() { + var output chan<- *produceSet + Logger.Printf("producer/broker/%d starting up\n", bp.broker.ID()) + + for { + select { + case msg, ok := <-bp.input: + if !ok { + Logger.Printf("producer/broker/%d input chan closed\n", bp.broker.ID()) + bp.shutdown() + return + } + + if msg == nil { + continue + } + + if msg.flags&syn == syn { + Logger.Printf("producer/broker/%d state change to [open] on %s/%d\n", + bp.broker.ID(), msg.Topic, msg.Partition) + if bp.currentRetries[msg.Topic] == nil { + bp.currentRetries[msg.Topic] = make(map[int32]error) + } + bp.currentRetries[msg.Topic][msg.Partition] = nil + bp.parent.inFlight.Done() + continue + } + + if reason := bp.needsRetry(msg); reason != nil { + bp.parent.retryMessage(msg, reason) + + if bp.closing == nil && msg.flags&fin == fin { + // we were retrying this partition but we can start processing again + delete(bp.currentRetries[msg.Topic], msg.Partition) + Logger.Printf("producer/broker/%d state change to [closed] on %s/%d\n", + bp.broker.ID(), msg.Topic, msg.Partition) + } + + continue + } + + if bp.buffer.wouldOverflow(msg) { + if err := bp.waitForSpace(msg); err != nil { + bp.parent.retryMessage(msg, err) + continue + } + } + + if err := bp.buffer.add(msg); err != nil { + bp.parent.returnError(msg, err) + continue + } + + if bp.parent.conf.Producer.Flush.Frequency > 0 && bp.timer == nil { + bp.timer = time.After(bp.parent.conf.Producer.Flush.Frequency) + } + case <-bp.timer: + bp.timerFired = true + case output <- bp.buffer: + bp.rollOver() + case response, ok := <-bp.responses: + if ok { + bp.handleResponse(response) + } + case <-bp.stopchan: + Logger.Printf( + "producer/broker/%d run loop asked to stop\n", bp.broker.ID()) + return + } + + if bp.timerFired || bp.buffer.readyToFlush() { + output = bp.output + } else { + output = nil + } + } +} + +func (bp *brokerProducer) shutdown() { + for !bp.buffer.empty() { + select { + case response := <-bp.responses: + bp.handleResponse(response) + case bp.output <- bp.buffer: + bp.rollOver() + } + } + close(bp.output) + for response := range bp.responses { + bp.handleResponse(response) + } + close(bp.stopchan) + Logger.Printf("producer/broker/%d shut down\n", bp.broker.ID()) +} + +func (bp *brokerProducer) needsRetry(msg *ProducerMessage) error { + if bp.closing != nil { + return bp.closing + } + + return bp.currentRetries[msg.Topic][msg.Partition] +} + +func (bp *brokerProducer) waitForSpace(msg *ProducerMessage) error { + Logger.Printf("producer/broker/%d maximum request accumulated, waiting for space\n", bp.broker.ID()) + + for { + select { + case response := <-bp.responses: + bp.handleResponse(response) + // handling a response can change our state, so re-check some things + if reason := bp.needsRetry(msg); reason != nil { + return reason + } else if !bp.buffer.wouldOverflow(msg) { + return nil + } + case bp.output <- bp.buffer: + bp.rollOver() + return nil + } + } +} + +func (bp *brokerProducer) rollOver() { + bp.timer = nil + bp.timerFired = false + bp.buffer = newProduceSet(bp.parent) +} + +func (bp *brokerProducer) handleResponse(response *brokerProducerResponse) { + if response.err != nil { + bp.handleError(response.set, response.err) + } else { + bp.handleSuccess(response.set, response.res) + } + + if bp.buffer.empty() { + bp.rollOver() // this can happen if the response invalidated our buffer + } +} + +func (bp *brokerProducer) handleSuccess(sent *produceSet, response *ProduceResponse) { + // we iterate through the blocks in the request set, not the response, so that we notice + // if the response is missing a block completely + var retryTopics []string + sent.eachPartition(func(topic string, partition int32, pSet *partitionSet) { + if response == nil { + // this only happens when RequiredAcks is NoResponse, so we have to assume success + bp.parent.returnSuccesses(pSet.msgs) + return + } + + block := response.GetBlock(topic, partition) + if block == nil { + bp.parent.returnErrors(pSet.msgs, ErrIncompleteResponse) + return + } + + switch block.Err { + // Success + case ErrNoError: + if bp.parent.conf.Version.IsAtLeast(V0_10_0_0) && !block.Timestamp.IsZero() { + for _, msg := range pSet.msgs { + msg.Timestamp = block.Timestamp + } + } + for i, msg := range pSet.msgs { + msg.Offset = block.Offset + int64(i) + } + bp.parent.returnSuccesses(pSet.msgs) + // Duplicate + case ErrDuplicateSequenceNumber: + bp.parent.returnSuccesses(pSet.msgs) + // Retriable errors + case ErrInvalidMessage, ErrUnknownTopicOrPartition, ErrLeaderNotAvailable, ErrNotLeaderForPartition, + ErrRequestTimedOut, ErrNotEnoughReplicas, ErrNotEnoughReplicasAfterAppend: + if bp.parent.conf.Producer.Retry.Max <= 0 { + bp.parent.abandonBrokerConnection(bp.broker) + bp.parent.returnErrors(pSet.msgs, block.Err) + } else { + retryTopics = append(retryTopics, topic) + } + // Other non-retriable errors + default: + if bp.parent.conf.Producer.Retry.Max <= 0 { + bp.parent.abandonBrokerConnection(bp.broker) + } + bp.parent.returnErrors(pSet.msgs, block.Err) + } + }) + + if len(retryTopics) > 0 { + if bp.parent.conf.Producer.Idempotent { + err := bp.parent.client.RefreshMetadata(retryTopics...) + if err != nil { + Logger.Printf("Failed refreshing metadata because of %v\n", err) + } + } + + sent.eachPartition(func(topic string, partition int32, pSet *partitionSet) { + block := response.GetBlock(topic, partition) + if block == nil { + // handled in the previous "eachPartition" loop + return + } + + switch block.Err { + case ErrInvalidMessage, ErrUnknownTopicOrPartition, ErrLeaderNotAvailable, ErrNotLeaderForPartition, + ErrRequestTimedOut, ErrNotEnoughReplicas, ErrNotEnoughReplicasAfterAppend: + Logger.Printf("producer/broker/%d state change to [retrying] on %s/%d because %v\n", + bp.broker.ID(), topic, partition, block.Err) + if bp.currentRetries[topic] == nil { + bp.currentRetries[topic] = make(map[int32]error) + } + bp.currentRetries[topic][partition] = block.Err + if bp.parent.conf.Producer.Idempotent { + go bp.parent.retryBatch(topic, partition, pSet, block.Err) + } else { + bp.parent.retryMessages(pSet.msgs, block.Err) + } + // dropping the following messages has the side effect of incrementing their retry count + bp.parent.retryMessages(bp.buffer.dropPartition(topic, partition), block.Err) + } + }) + } +} + +func (p *asyncProducer) retryBatch(topic string, partition int32, pSet *partitionSet, kerr KError) { + Logger.Printf("Retrying batch for %v-%d because of %s\n", topic, partition, kerr) + produceSet := newProduceSet(p) + produceSet.msgs[topic] = make(map[int32]*partitionSet) + produceSet.msgs[topic][partition] = pSet + produceSet.bufferBytes += pSet.bufferBytes + produceSet.bufferCount += len(pSet.msgs) + for _, msg := range pSet.msgs { + if msg.retries >= p.conf.Producer.Retry.Max { + p.returnError(msg, kerr) + return + } + msg.retries++ + } + + // it's expected that a metadata refresh has been requested prior to calling retryBatch + leader, err := p.client.Leader(topic, partition) + if err != nil { + Logger.Printf("Failed retrying batch for %v-%d because of %v while looking up for new leader\n", topic, partition, err) + for _, msg := range pSet.msgs { + p.returnError(msg, kerr) + } + return + } + bp := p.getBrokerProducer(leader) + bp.output <- produceSet +} + +func (bp *brokerProducer) handleError(sent *produceSet, err error) { + switch err.(type) { + case PacketEncodingError: + sent.eachPartition(func(topic string, partition int32, pSet *partitionSet) { + bp.parent.returnErrors(pSet.msgs, err) + }) + default: + Logger.Printf("producer/broker/%d state change to [closing] because %s\n", bp.broker.ID(), err) + bp.parent.abandonBrokerConnection(bp.broker) + _ = bp.broker.Close() + bp.closing = err + sent.eachPartition(func(topic string, partition int32, pSet *partitionSet) { + bp.parent.retryMessages(pSet.msgs, err) + }) + bp.buffer.eachPartition(func(topic string, partition int32, pSet *partitionSet) { + bp.parent.retryMessages(pSet.msgs, err) + }) + bp.rollOver() + } +} + +// singleton +// effectively a "bridge" between the flushers and the dispatcher in order to avoid deadlock +// based on https://godoc.org/github.com/eapache/channels#InfiniteChannel +func (p *asyncProducer) retryHandler() { + var msg *ProducerMessage + buf := queue.New() + + for { + if buf.Length() == 0 { + msg = <-p.retries + } else { + select { + case msg = <-p.retries: + case p.input <- buf.Peek().(*ProducerMessage): + buf.Remove() + continue + } + } + + if msg == nil { + return + } + + buf.Add(msg) + } +} + +// utility functions + +func (p *asyncProducer) shutdown() { + Logger.Println("Producer shutting down.") + p.inFlight.Add(1) + p.input <- &ProducerMessage{flags: shutdown} + + p.inFlight.Wait() + + err := p.client.Close() + if err != nil { + Logger.Println("producer/shutdown failed to close the embedded client:", err) + } + + close(p.input) + close(p.retries) + close(p.errors) + close(p.successes) +} + +func (p *asyncProducer) returnError(msg *ProducerMessage, err error) { + msg.clear() + pErr := &ProducerError{Msg: msg, Err: err} + if p.conf.Producer.Return.Errors { + p.errors <- pErr + } else { + Logger.Println(pErr) + } + p.inFlight.Done() +} + +func (p *asyncProducer) returnErrors(batch []*ProducerMessage, err error) { + for _, msg := range batch { + p.returnError(msg, err) + } +} + +func (p *asyncProducer) returnSuccesses(batch []*ProducerMessage) { + for _, msg := range batch { + if p.conf.Producer.Return.Successes { + msg.clear() + p.successes <- msg + } + p.inFlight.Done() + } +} + +func (p *asyncProducer) retryMessage(msg *ProducerMessage, err error) { + if msg.retries >= p.conf.Producer.Retry.Max { + p.returnError(msg, err) + } else { + msg.retries++ + p.retries <- msg + } +} + +func (p *asyncProducer) retryMessages(batch []*ProducerMessage, err error) { + for _, msg := range batch { + p.retryMessage(msg, err) + } +} + +func (p *asyncProducer) getBrokerProducer(broker *Broker) *brokerProducer { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + bp := p.brokers[broker] + + if bp == nil { + bp = p.newBrokerProducer(broker) + p.brokers[broker] = bp + p.brokerRefs[bp] = 0 + } + + p.brokerRefs[bp]++ + + return bp +} + +func (p *asyncProducer) unrefBrokerProducer(broker *Broker, bp *brokerProducer) { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + p.brokerRefs[bp]-- + if p.brokerRefs[bp] == 0 { + close(bp.input) + delete(p.brokerRefs, bp) + + if p.brokers[broker] == bp { + delete(p.brokers, broker) + } + } +} + +func (p *asyncProducer) abandonBrokerConnection(broker *Broker) { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + bc, ok := p.brokers[broker] + if ok && bc.abandoned != nil { + close(bc.abandoned) + } + + delete(p.brokers, broker) +} diff --git a/vendor/github.com/Shopify/sarama/balance_strategy.go b/vendor/github.com/Shopify/sarama/balance_strategy.go new file mode 100644 index 0000000..67c4d96 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/balance_strategy.go @@ -0,0 +1,1053 @@ +package sarama + +import ( + "container/heap" + "math" + "sort" + "strings" +) + +const ( + // RangeBalanceStrategyName identifies strategies that use the range partition assignment strategy + RangeBalanceStrategyName = "range" + + // RoundRobinBalanceStrategyName identifies strategies that use the round-robin partition assignment strategy + RoundRobinBalanceStrategyName = "roundrobin" + + // StickyBalanceStrategyName identifies strategies that use the sticky-partition assignment strategy + StickyBalanceStrategyName = "sticky" + + defaultGeneration = -1 +) + +// BalanceStrategyPlan is the results of any BalanceStrategy.Plan attempt. +// It contains an allocation of topic/partitions by memberID in the form of +// a `memberID -> topic -> partitions` map. +type BalanceStrategyPlan map[string]map[string][]int32 + +// Add assigns a topic with a number partitions to a member. +func (p BalanceStrategyPlan) Add(memberID, topic string, partitions ...int32) { + if len(partitions) == 0 { + return + } + if _, ok := p[memberID]; !ok { + p[memberID] = make(map[string][]int32, 1) + } + p[memberID][topic] = append(p[memberID][topic], partitions...) +} + +// -------------------------------------------------------------------- + +// BalanceStrategy is used to balance topics and partitions +// across members of a consumer group +type BalanceStrategy interface { + // Name uniquely identifies the strategy. + Name() string + + // Plan accepts a map of `memberID -> metadata` and a map of `topic -> partitions` + // and returns a distribution plan. + Plan(members map[string]ConsumerGroupMemberMetadata, topics map[string][]int32) (BalanceStrategyPlan, error) +} + +// -------------------------------------------------------------------- + +// BalanceStrategyRange is the default and assigns partitions as ranges to consumer group members. +// Example with one topic T with six partitions (0..5) and two members (M1, M2): +// M1: {T: [0, 1, 2]} +// M2: {T: [3, 4, 5]} +var BalanceStrategyRange = &balanceStrategy{ + name: RangeBalanceStrategyName, + coreFn: func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) { + step := float64(len(partitions)) / float64(len(memberIDs)) + + for i, memberID := range memberIDs { + pos := float64(i) + min := int(math.Floor(pos*step + 0.5)) + max := int(math.Floor((pos+1)*step + 0.5)) + plan.Add(memberID, topic, partitions[min:max]...) + } + }, +} + +// BalanceStrategyRoundRobin assigns partitions to members in alternating order. +// Example with topic T with six partitions (0..5) and two members (M1, M2): +// M1: {T: [0, 2, 4]} +// M2: {T: [1, 3, 5]} +var BalanceStrategyRoundRobin = &balanceStrategy{ + name: RoundRobinBalanceStrategyName, + coreFn: func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) { + for i, part := range partitions { + memberID := memberIDs[i%len(memberIDs)] + plan.Add(memberID, topic, part) + } + }, +} + +// BalanceStrategySticky assigns partitions to members with an attempt to preserve earlier assignments +// while maintain a balanced partition distribution. +// Example with topic T with six partitions (0..5) and two members (M1, M2): +// M1: {T: [0, 2, 4]} +// M2: {T: [1, 3, 5]} +// +// On reassignment with an additional consumer, you might get an assignment plan like: +// M1: {T: [0, 2]} +// M2: {T: [1, 3]} +// M3: {T: [4, 5]} +// +var BalanceStrategySticky = &stickyBalanceStrategy{} + +// -------------------------------------------------------------------- + +type balanceStrategy struct { + name string + coreFn func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) +} + +// Name implements BalanceStrategy. +func (s *balanceStrategy) Name() string { return s.name } + +// Plan implements BalanceStrategy. +func (s *balanceStrategy) Plan(members map[string]ConsumerGroupMemberMetadata, topics map[string][]int32) (BalanceStrategyPlan, error) { + // Build members by topic map + mbt := make(map[string][]string) + for memberID, meta := range members { + for _, topic := range meta.Topics { + mbt[topic] = append(mbt[topic], memberID) + } + } + + // Sort members for each topic + for topic, memberIDs := range mbt { + sort.Sort(&balanceStrategySortable{ + topic: topic, + memberIDs: memberIDs, + }) + } + + // Assemble plan + plan := make(BalanceStrategyPlan, len(members)) + for topic, memberIDs := range mbt { + s.coreFn(plan, memberIDs, topic, topics[topic]) + } + return plan, nil +} + +type balanceStrategySortable struct { + topic string + memberIDs []string +} + +func (p balanceStrategySortable) Len() int { return len(p.memberIDs) } +func (p balanceStrategySortable) Swap(i, j int) { + p.memberIDs[i], p.memberIDs[j] = p.memberIDs[j], p.memberIDs[i] +} +func (p balanceStrategySortable) Less(i, j int) bool { + return balanceStrategyHashValue(p.topic, p.memberIDs[i]) < balanceStrategyHashValue(p.topic, p.memberIDs[j]) +} + +func balanceStrategyHashValue(vv ...string) uint32 { + h := uint32(2166136261) + for _, s := range vv { + for _, c := range s { + h ^= uint32(c) + h *= 16777619 + } + } + return h +} + +type stickyBalanceStrategy struct { + movements partitionMovements +} + +// Name implements BalanceStrategy. +func (s *stickyBalanceStrategy) Name() string { return StickyBalanceStrategyName } + +// Plan implements BalanceStrategy. +func (s *stickyBalanceStrategy) Plan(members map[string]ConsumerGroupMemberMetadata, topics map[string][]int32) (BalanceStrategyPlan, error) { + // track partition movements during generation of the partition assignment plan + s.movements = partitionMovements{ + Movements: make(map[topicPartitionAssignment]consumerPair), + PartitionMovementsByTopic: make(map[string]map[consumerPair]map[topicPartitionAssignment]bool), + } + + // prepopulate the current assignment state from userdata on the consumer group members + currentAssignment, prevAssignment, err := prepopulateCurrentAssignments(members) + if err != nil { + return nil, err + } + + // determine if we're dealing with a completely fresh assignment, or if there's existing assignment state + isFreshAssignment := false + if len(currentAssignment) == 0 { + isFreshAssignment = true + } + + // create a mapping of all current topic partitions and the consumers that can be assigned to them + partition2AllPotentialConsumers := make(map[topicPartitionAssignment][]string) + for topic, partitions := range topics { + for _, partition := range partitions { + partition2AllPotentialConsumers[topicPartitionAssignment{Topic: topic, Partition: partition}] = []string{} + } + } + + // create a mapping of all consumers to all potential topic partitions that can be assigned to them + // also, populate the mapping of partitions to potential consumers + consumer2AllPotentialPartitions := make(map[string][]topicPartitionAssignment, len(members)) + for memberID, meta := range members { + consumer2AllPotentialPartitions[memberID] = make([]topicPartitionAssignment, 0) + for _, topicSubscription := range meta.Topics { + // only evaluate topic subscriptions that are present in the supplied topics map + if _, found := topics[topicSubscription]; found { + for _, partition := range topics[topicSubscription] { + topicPartition := topicPartitionAssignment{Topic: topicSubscription, Partition: partition} + consumer2AllPotentialPartitions[memberID] = append(consumer2AllPotentialPartitions[memberID], topicPartition) + partition2AllPotentialConsumers[topicPartition] = append(partition2AllPotentialConsumers[topicPartition], memberID) + } + } + } + + // add this consumer to currentAssignment (with an empty topic partition assignment) if it does not already exist + if _, exists := currentAssignment[memberID]; !exists { + currentAssignment[memberID] = make([]topicPartitionAssignment, 0) + } + } + + // create a mapping of each partition to its current consumer, where possible + currentPartitionConsumers := make(map[topicPartitionAssignment]string, len(currentAssignment)) + unvisitedPartitions := make(map[topicPartitionAssignment]bool, len(partition2AllPotentialConsumers)) + for partition := range partition2AllPotentialConsumers { + unvisitedPartitions[partition] = true + } + var unassignedPartitions []topicPartitionAssignment + for memberID, partitions := range currentAssignment { + var keepPartitions []topicPartitionAssignment + for _, partition := range partitions { + // If this partition no longer exists at all, likely due to the + // topic being deleted, we remove the partition from the member. + if _, exists := partition2AllPotentialConsumers[partition]; !exists { + continue + } + delete(unvisitedPartitions, partition) + currentPartitionConsumers[partition] = memberID + + if !strsContains(members[memberID].Topics, partition.Topic) { + unassignedPartitions = append(unassignedPartitions, partition) + continue + } + keepPartitions = append(keepPartitions, partition) + } + currentAssignment[memberID] = keepPartitions + } + for unvisited := range unvisitedPartitions { + unassignedPartitions = append(unassignedPartitions, unvisited) + } + + // sort the topic partitions in order of priority for reassignment + sortedPartitions := sortPartitions(currentAssignment, prevAssignment, isFreshAssignment, partition2AllPotentialConsumers, consumer2AllPotentialPartitions) + + // at this point we have preserved all valid topic partition to consumer assignments and removed + // all invalid topic partitions and invalid consumers. Now we need to assign unassignedPartitions + // to consumers so that the topic partition assignments are as balanced as possible. + + // an ascending sorted set of consumers based on how many topic partitions are already assigned to them + sortedCurrentSubscriptions := sortMemberIDsByPartitionAssignments(currentAssignment) + s.balance(currentAssignment, prevAssignment, sortedPartitions, unassignedPartitions, sortedCurrentSubscriptions, consumer2AllPotentialPartitions, partition2AllPotentialConsumers, currentPartitionConsumers) + + // Assemble plan + plan := make(BalanceStrategyPlan, len(currentAssignment)) + for memberID, assignments := range currentAssignment { + if len(assignments) == 0 { + plan[memberID] = make(map[string][]int32, 0) + } else { + for _, assignment := range assignments { + plan.Add(memberID, assignment.Topic, assignment.Partition) + } + } + } + return plan, nil +} + +func strsContains(s []string, value string) bool { + for _, entry := range s { + if entry == value { + return true + } + } + return false +} + +// Balance assignments across consumers for maximum fairness and stickiness. +func (s *stickyBalanceStrategy) balance(currentAssignment map[string][]topicPartitionAssignment, prevAssignment map[topicPartitionAssignment]consumerGenerationPair, sortedPartitions []topicPartitionAssignment, unassignedPartitions []topicPartitionAssignment, sortedCurrentSubscriptions []string, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment, partition2AllPotentialConsumers map[topicPartitionAssignment][]string, currentPartitionConsumer map[topicPartitionAssignment]string) { + initializing := false + if len(sortedCurrentSubscriptions) == 0 || len(currentAssignment[sortedCurrentSubscriptions[0]]) == 0 { + initializing = true + } + + // assign all unassigned partitions + for _, partition := range unassignedPartitions { + // skip if there is no potential consumer for the partition + if len(partition2AllPotentialConsumers[partition]) == 0 { + continue + } + sortedCurrentSubscriptions = assignPartition(partition, sortedCurrentSubscriptions, currentAssignment, consumer2AllPotentialPartitions, currentPartitionConsumer) + } + + // narrow down the reassignment scope to only those partitions that can actually be reassigned + for partition := range partition2AllPotentialConsumers { + if !canTopicPartitionParticipateInReassignment(partition, partition2AllPotentialConsumers) { + sortedPartitions = removeTopicPartitionFromMemberAssignments(sortedPartitions, partition) + } + } + + // narrow down the reassignment scope to only those consumers that are subject to reassignment + fixedAssignments := make(map[string][]topicPartitionAssignment) + for memberID := range consumer2AllPotentialPartitions { + if !canConsumerParticipateInReassignment(memberID, currentAssignment, consumer2AllPotentialPartitions, partition2AllPotentialConsumers) { + fixedAssignments[memberID] = currentAssignment[memberID] + delete(currentAssignment, memberID) + sortedCurrentSubscriptions = sortMemberIDsByPartitionAssignments(currentAssignment) + } + } + + // create a deep copy of the current assignment so we can revert to it if we do not get a more balanced assignment later + preBalanceAssignment := deepCopyAssignment(currentAssignment) + preBalancePartitionConsumers := make(map[topicPartitionAssignment]string, len(currentPartitionConsumer)) + for k, v := range currentPartitionConsumer { + preBalancePartitionConsumers[k] = v + } + + reassignmentPerformed := s.performReassignments(sortedPartitions, currentAssignment, prevAssignment, sortedCurrentSubscriptions, consumer2AllPotentialPartitions, partition2AllPotentialConsumers, currentPartitionConsumer) + + // if we are not preserving existing assignments and we have made changes to the current assignment + // make sure we are getting a more balanced assignment; otherwise, revert to previous assignment + if !initializing && reassignmentPerformed && getBalanceScore(currentAssignment) >= getBalanceScore(preBalanceAssignment) { + currentAssignment = deepCopyAssignment(preBalanceAssignment) + currentPartitionConsumer = make(map[topicPartitionAssignment]string, len(preBalancePartitionConsumers)) + for k, v := range preBalancePartitionConsumers { + currentPartitionConsumer[k] = v + } + } + + // add the fixed assignments (those that could not change) back + for consumer, assignments := range fixedAssignments { + currentAssignment[consumer] = assignments + } +} + +// Calculate the balance score of the given assignment, as the sum of assigned partitions size difference of all consumer pairs. +// A perfectly balanced assignment (with all consumers getting the same number of partitions) has a balance score of 0. +// Lower balance score indicates a more balanced assignment. +func getBalanceScore(assignment map[string][]topicPartitionAssignment) int { + consumer2AssignmentSize := make(map[string]int, len(assignment)) + for memberID, partitions := range assignment { + consumer2AssignmentSize[memberID] = len(partitions) + } + + var score float64 + for memberID, consumerAssignmentSize := range consumer2AssignmentSize { + delete(consumer2AssignmentSize, memberID) + for _, otherConsumerAssignmentSize := range consumer2AssignmentSize { + score += math.Abs(float64(consumerAssignmentSize - otherConsumerAssignmentSize)) + } + } + return int(score) +} + +// Determine whether the current assignment plan is balanced. +func isBalanced(currentAssignment map[string][]topicPartitionAssignment, sortedCurrentSubscriptions []string, allSubscriptions map[string][]topicPartitionAssignment) bool { + sortedCurrentSubscriptions = sortMemberIDsByPartitionAssignments(currentAssignment) + min := len(currentAssignment[sortedCurrentSubscriptions[0]]) + max := len(currentAssignment[sortedCurrentSubscriptions[len(sortedCurrentSubscriptions)-1]]) + if min >= max-1 { + // if minimum and maximum numbers of partitions assigned to consumers differ by at most one return true + return true + } + + // create a mapping from partitions to the consumer assigned to them + allPartitions := make(map[topicPartitionAssignment]string) + for memberID, partitions := range currentAssignment { + for _, partition := range partitions { + if _, exists := allPartitions[partition]; exists { + Logger.Printf("Topic %s Partition %d is assigned more than one consumer", partition.Topic, partition.Partition) + } + allPartitions[partition] = memberID + } + } + + // for each consumer that does not have all the topic partitions it can get make sure none of the topic partitions it + // could but did not get cannot be moved to it (because that would break the balance) + for _, memberID := range sortedCurrentSubscriptions { + consumerPartitions := currentAssignment[memberID] + consumerPartitionCount := len(consumerPartitions) + + // skip if this consumer already has all the topic partitions it can get + if consumerPartitionCount == len(allSubscriptions[memberID]) { + continue + } + + // otherwise make sure it cannot get any more + potentialTopicPartitions := allSubscriptions[memberID] + for _, partition := range potentialTopicPartitions { + if !memberAssignmentsIncludeTopicPartition(currentAssignment[memberID], partition) { + otherConsumer := allPartitions[partition] + otherConsumerPartitionCount := len(currentAssignment[otherConsumer]) + if consumerPartitionCount < otherConsumerPartitionCount { + return false + } + } + } + } + return true +} + +// Reassign all topic partitions that need reassignment until balanced. +func (s *stickyBalanceStrategy) performReassignments(reassignablePartitions []topicPartitionAssignment, currentAssignment map[string][]topicPartitionAssignment, prevAssignment map[topicPartitionAssignment]consumerGenerationPair, sortedCurrentSubscriptions []string, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment, partition2AllPotentialConsumers map[topicPartitionAssignment][]string, currentPartitionConsumer map[topicPartitionAssignment]string) bool { + reassignmentPerformed := false + modified := false + + // repeat reassignment until no partition can be moved to improve the balance + for { + modified = false + // reassign all reassignable partitions (starting from the partition with least potential consumers and if needed) + // until the full list is processed or a balance is achieved + for _, partition := range reassignablePartitions { + if isBalanced(currentAssignment, sortedCurrentSubscriptions, consumer2AllPotentialPartitions) { + break + } + + // the partition must have at least two consumers + if len(partition2AllPotentialConsumers[partition]) <= 1 { + Logger.Printf("Expected more than one potential consumer for partition %s topic %d", partition.Topic, partition.Partition) + } + + // the partition must have a consumer + consumer := currentPartitionConsumer[partition] + if consumer == "" { + Logger.Printf("Expected topic %s partition %d to be assigned to a consumer", partition.Topic, partition.Partition) + } + + if _, exists := prevAssignment[partition]; exists { + if len(currentAssignment[consumer]) > (len(currentAssignment[prevAssignment[partition].MemberID]) + 1) { + sortedCurrentSubscriptions = s.reassignPartition(partition, currentAssignment, sortedCurrentSubscriptions, currentPartitionConsumer, prevAssignment[partition].MemberID) + reassignmentPerformed = true + modified = true + continue + } + } + + // check if a better-suited consumer exists for the partition; if so, reassign it + for _, otherConsumer := range partition2AllPotentialConsumers[partition] { + if len(currentAssignment[consumer]) > (len(currentAssignment[otherConsumer]) + 1) { + sortedCurrentSubscriptions = s.reassignPartitionToNewConsumer(partition, currentAssignment, sortedCurrentSubscriptions, currentPartitionConsumer, consumer2AllPotentialPartitions) + reassignmentPerformed = true + modified = true + break + } + } + } + if !modified { + return reassignmentPerformed + } + } +} + +// Identify a new consumer for a topic partition and reassign it. +func (s *stickyBalanceStrategy) reassignPartitionToNewConsumer(partition topicPartitionAssignment, currentAssignment map[string][]topicPartitionAssignment, sortedCurrentSubscriptions []string, currentPartitionConsumer map[topicPartitionAssignment]string, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment) []string { + for _, anotherConsumer := range sortedCurrentSubscriptions { + if memberAssignmentsIncludeTopicPartition(consumer2AllPotentialPartitions[anotherConsumer], partition) { + return s.reassignPartition(partition, currentAssignment, sortedCurrentSubscriptions, currentPartitionConsumer, anotherConsumer) + } + } + return sortedCurrentSubscriptions +} + +// Reassign a specific partition to a new consumer +func (s *stickyBalanceStrategy) reassignPartition(partition topicPartitionAssignment, currentAssignment map[string][]topicPartitionAssignment, sortedCurrentSubscriptions []string, currentPartitionConsumer map[topicPartitionAssignment]string, newConsumer string) []string { + consumer := currentPartitionConsumer[partition] + // find the correct partition movement considering the stickiness requirement + partitionToBeMoved := s.movements.getTheActualPartitionToBeMoved(partition, consumer, newConsumer) + return s.processPartitionMovement(partitionToBeMoved, newConsumer, currentAssignment, sortedCurrentSubscriptions, currentPartitionConsumer) +} + +// Track the movement of a topic partition after assignment +func (s *stickyBalanceStrategy) processPartitionMovement(partition topicPartitionAssignment, newConsumer string, currentAssignment map[string][]topicPartitionAssignment, sortedCurrentSubscriptions []string, currentPartitionConsumer map[topicPartitionAssignment]string) []string { + oldConsumer := currentPartitionConsumer[partition] + s.movements.movePartition(partition, oldConsumer, newConsumer) + + currentAssignment[oldConsumer] = removeTopicPartitionFromMemberAssignments(currentAssignment[oldConsumer], partition) + currentAssignment[newConsumer] = append(currentAssignment[newConsumer], partition) + currentPartitionConsumer[partition] = newConsumer + return sortMemberIDsByPartitionAssignments(currentAssignment) +} + +// Determine whether a specific consumer should be considered for topic partition assignment. +func canConsumerParticipateInReassignment(memberID string, currentAssignment map[string][]topicPartitionAssignment, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment, partition2AllPotentialConsumers map[topicPartitionAssignment][]string) bool { + currentPartitions := currentAssignment[memberID] + currentAssignmentSize := len(currentPartitions) + maxAssignmentSize := len(consumer2AllPotentialPartitions[memberID]) + if currentAssignmentSize > maxAssignmentSize { + Logger.Printf("The consumer %s is assigned more partitions than the maximum possible", memberID) + } + if currentAssignmentSize < maxAssignmentSize { + // if a consumer is not assigned all its potential partitions it is subject to reassignment + return true + } + for _, partition := range currentPartitions { + if canTopicPartitionParticipateInReassignment(partition, partition2AllPotentialConsumers) { + return true + } + } + return false +} + +// Only consider reassigning those topic partitions that have two or more potential consumers. +func canTopicPartitionParticipateInReassignment(partition topicPartitionAssignment, partition2AllPotentialConsumers map[topicPartitionAssignment][]string) bool { + return len(partition2AllPotentialConsumers[partition]) >= 2 +} + +// The assignment should improve the overall balance of the partition assignments to consumers. +func assignPartition(partition topicPartitionAssignment, sortedCurrentSubscriptions []string, currentAssignment map[string][]topicPartitionAssignment, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment, currentPartitionConsumer map[topicPartitionAssignment]string) []string { + for _, memberID := range sortedCurrentSubscriptions { + if memberAssignmentsIncludeTopicPartition(consumer2AllPotentialPartitions[memberID], partition) { + currentAssignment[memberID] = append(currentAssignment[memberID], partition) + currentPartitionConsumer[partition] = memberID + break + } + } + return sortMemberIDsByPartitionAssignments(currentAssignment) +} + +// Deserialize topic partition assignment data to aid with creation of a sticky assignment. +func deserializeTopicPartitionAssignment(userDataBytes []byte) (StickyAssignorUserData, error) { + userDataV1 := &StickyAssignorUserDataV1{} + if err := decode(userDataBytes, userDataV1); err != nil { + userDataV0 := &StickyAssignorUserDataV0{} + if err := decode(userDataBytes, userDataV0); err != nil { + return nil, err + } + return userDataV0, nil + } + return userDataV1, nil +} + +// filterAssignedPartitions returns a map of consumer group members to their list of previously-assigned topic partitions, limited +// to those topic partitions currently reported by the Kafka cluster. +func filterAssignedPartitions(currentAssignment map[string][]topicPartitionAssignment, partition2AllPotentialConsumers map[topicPartitionAssignment][]string) map[string][]topicPartitionAssignment { + assignments := deepCopyAssignment(currentAssignment) + for memberID, partitions := range assignments { + // perform in-place filtering + i := 0 + for _, partition := range partitions { + if _, exists := partition2AllPotentialConsumers[partition]; exists { + partitions[i] = partition + i++ + } + } + assignments[memberID] = partitions[:i] + } + return assignments +} + +func removeTopicPartitionFromMemberAssignments(assignments []topicPartitionAssignment, topic topicPartitionAssignment) []topicPartitionAssignment { + for i, assignment := range assignments { + if assignment == topic { + return append(assignments[:i], assignments[i+1:]...) + } + } + return assignments +} + +func memberAssignmentsIncludeTopicPartition(assignments []topicPartitionAssignment, topic topicPartitionAssignment) bool { + for _, assignment := range assignments { + if assignment == topic { + return true + } + } + return false +} + +func sortPartitions(currentAssignment map[string][]topicPartitionAssignment, partitionsWithADifferentPreviousAssignment map[topicPartitionAssignment]consumerGenerationPair, isFreshAssignment bool, partition2AllPotentialConsumers map[topicPartitionAssignment][]string, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment) []topicPartitionAssignment { + unassignedPartitions := make(map[topicPartitionAssignment]bool, len(partition2AllPotentialConsumers)) + for partition := range partition2AllPotentialConsumers { + unassignedPartitions[partition] = true + } + + sortedPartitions := make([]topicPartitionAssignment, 0) + if !isFreshAssignment && areSubscriptionsIdentical(partition2AllPotentialConsumers, consumer2AllPotentialPartitions) { + // if this is a reassignment and the subscriptions are identical (all consumers can consumer from all topics) + // then we just need to simply list partitions in a round robin fashion (from consumers with + // most assigned partitions to those with least) + assignments := filterAssignedPartitions(currentAssignment, partition2AllPotentialConsumers) + + // use priority-queue to evaluate consumer group members in descending-order based on + // the number of topic partition assignments (i.e. consumers with most assignments first) + pq := make(assignmentPriorityQueue, len(assignments)) + i := 0 + for consumerID, consumerAssignments := range assignments { + pq[i] = &consumerGroupMember{ + id: consumerID, + assignments: consumerAssignments, + } + i++ + } + heap.Init(&pq) + + for { + // loop until no consumer-group members remain + if pq.Len() == 0 { + break + } + member := pq[0] + + // partitions that were assigned to a different consumer last time + var prevPartitionIndex int + for i, partition := range member.assignments { + if _, exists := partitionsWithADifferentPreviousAssignment[partition]; exists { + prevPartitionIndex = i + break + } + } + + if len(member.assignments) > 0 { + partition := member.assignments[prevPartitionIndex] + sortedPartitions = append(sortedPartitions, partition) + delete(unassignedPartitions, partition) + if prevPartitionIndex == 0 { + member.assignments = member.assignments[1:] + } else { + member.assignments = append(member.assignments[:prevPartitionIndex], member.assignments[prevPartitionIndex+1:]...) + } + heap.Fix(&pq, 0) + } else { + heap.Pop(&pq) + } + } + + for partition := range unassignedPartitions { + sortedPartitions = append(sortedPartitions, partition) + } + } else { + // an ascending sorted set of topic partitions based on how many consumers can potentially use them + sortedPartitions = sortPartitionsByPotentialConsumerAssignments(partition2AllPotentialConsumers) + } + return sortedPartitions +} + +func sortMemberIDsByPartitionAssignments(assignments map[string][]topicPartitionAssignment) []string { + // sort the members by the number of partition assignments in ascending order + sortedMemberIDs := make([]string, 0, len(assignments)) + for memberID := range assignments { + sortedMemberIDs = append(sortedMemberIDs, memberID) + } + sort.SliceStable(sortedMemberIDs, func(i, j int) bool { + ret := len(assignments[sortedMemberIDs[i]]) - len(assignments[sortedMemberIDs[j]]) + if ret == 0 { + return sortedMemberIDs[i] < sortedMemberIDs[j] + } + return len(assignments[sortedMemberIDs[i]]) < len(assignments[sortedMemberIDs[j]]) + }) + return sortedMemberIDs +} + +func sortPartitionsByPotentialConsumerAssignments(partition2AllPotentialConsumers map[topicPartitionAssignment][]string) []topicPartitionAssignment { + // sort the members by the number of partition assignments in descending order + sortedPartionIDs := make([]topicPartitionAssignment, len(partition2AllPotentialConsumers)) + i := 0 + for partition := range partition2AllPotentialConsumers { + sortedPartionIDs[i] = partition + i++ + } + sort.Slice(sortedPartionIDs, func(i, j int) bool { + if len(partition2AllPotentialConsumers[sortedPartionIDs[i]]) == len(partition2AllPotentialConsumers[sortedPartionIDs[j]]) { + ret := strings.Compare(sortedPartionIDs[i].Topic, sortedPartionIDs[j].Topic) + if ret == 0 { + return sortedPartionIDs[i].Partition < sortedPartionIDs[j].Partition + } + return ret < 0 + } + return len(partition2AllPotentialConsumers[sortedPartionIDs[i]]) < len(partition2AllPotentialConsumers[sortedPartionIDs[j]]) + }) + return sortedPartionIDs +} + +func deepCopyPartitions(src []topicPartitionAssignment) []topicPartitionAssignment { + dst := make([]topicPartitionAssignment, len(src)) + for i, partition := range src { + dst[i] = partition + } + return dst +} + +func deepCopyAssignment(assignment map[string][]topicPartitionAssignment) map[string][]topicPartitionAssignment { + copy := make(map[string][]topicPartitionAssignment, len(assignment)) + for memberID, subscriptions := range assignment { + copy[memberID] = append(subscriptions[:0:0], subscriptions...) + } + return copy +} + +func areSubscriptionsIdentical(partition2AllPotentialConsumers map[topicPartitionAssignment][]string, consumer2AllPotentialPartitions map[string][]topicPartitionAssignment) bool { + curMembers := make(map[string]int) + for _, cur := range partition2AllPotentialConsumers { + if len(curMembers) == 0 { + for _, curMembersElem := range cur { + curMembers[curMembersElem]++ + } + continue + } + + if len(curMembers) != len(cur) { + return false + } + + yMap := make(map[string]int) + for _, yElem := range cur { + yMap[yElem]++ + } + + for curMembersMapKey, curMembersMapVal := range curMembers { + if yMap[curMembersMapKey] != curMembersMapVal { + return false + } + } + } + + curPartitions := make(map[topicPartitionAssignment]int) + for _, cur := range consumer2AllPotentialPartitions { + if len(curPartitions) == 0 { + for _, curPartitionElem := range cur { + curPartitions[curPartitionElem]++ + } + continue + } + + if len(curPartitions) != len(cur) { + return false + } + + yMap := make(map[topicPartitionAssignment]int) + for _, yElem := range cur { + yMap[yElem]++ + } + + for curMembersMapKey, curMembersMapVal := range curPartitions { + if yMap[curMembersMapKey] != curMembersMapVal { + return false + } + } + } + return true +} + +// We need to process subscriptions' user data with each consumer's reported generation in mind +// higher generations overwrite lower generations in case of a conflict +// note that a conflict could exist only if user data is for different generations +func prepopulateCurrentAssignments(members map[string]ConsumerGroupMemberMetadata) (map[string][]topicPartitionAssignment, map[topicPartitionAssignment]consumerGenerationPair, error) { + currentAssignment := make(map[string][]topicPartitionAssignment) + prevAssignment := make(map[topicPartitionAssignment]consumerGenerationPair) + + // for each partition we create a sorted map of its consumers by generation + sortedPartitionConsumersByGeneration := make(map[topicPartitionAssignment]map[int]string) + for memberID, meta := range members { + consumerUserData, err := deserializeTopicPartitionAssignment(meta.UserData) + if err != nil { + return nil, nil, err + } + for _, partition := range consumerUserData.partitions() { + if consumers, exists := sortedPartitionConsumersByGeneration[partition]; exists { + if consumerUserData.hasGeneration() { + if _, generationExists := consumers[consumerUserData.generation()]; generationExists { + // same partition is assigned to two consumers during the same rebalance. + // log a warning and skip this record + Logger.Printf("Topic %s Partition %d is assigned to multiple consumers following sticky assignment generation %d", partition.Topic, partition.Partition, consumerUserData.generation()) + continue + } else { + consumers[consumerUserData.generation()] = memberID + } + } else { + consumers[defaultGeneration] = memberID + } + } else { + generation := defaultGeneration + if consumerUserData.hasGeneration() { + generation = consumerUserData.generation() + } + sortedPartitionConsumersByGeneration[partition] = map[int]string{generation: memberID} + } + } + } + + // prevAssignment holds the prior ConsumerGenerationPair (before current) of each partition + // current and previous consumers are the last two consumers of each partition in the above sorted map + for partition, consumers := range sortedPartitionConsumersByGeneration { + // sort consumers by generation in decreasing order + var generations []int + for generation := range consumers { + generations = append(generations, generation) + } + sort.Sort(sort.Reverse(sort.IntSlice(generations))) + + consumer := consumers[generations[0]] + if _, exists := currentAssignment[consumer]; !exists { + currentAssignment[consumer] = []topicPartitionAssignment{partition} + } else { + currentAssignment[consumer] = append(currentAssignment[consumer], partition) + } + + // check for previous assignment, if any + if len(generations) > 1 { + prevAssignment[partition] = consumerGenerationPair{ + MemberID: consumers[generations[1]], + Generation: generations[1], + } + } + } + return currentAssignment, prevAssignment, nil +} + +type consumerGenerationPair struct { + MemberID string + Generation int +} + +// consumerPair represents a pair of Kafka consumer ids involved in a partition reassignment. +type consumerPair struct { + SrcMemberID string + DstMemberID string +} + +// partitionMovements maintains some data structures to simplify lookup of partition movements among consumers. +type partitionMovements struct { + PartitionMovementsByTopic map[string]map[consumerPair]map[topicPartitionAssignment]bool + Movements map[topicPartitionAssignment]consumerPair +} + +func (p *partitionMovements) removeMovementRecordOfPartition(partition topicPartitionAssignment) consumerPair { + pair := p.Movements[partition] + delete(p.Movements, partition) + + partitionMovementsForThisTopic := p.PartitionMovementsByTopic[partition.Topic] + delete(partitionMovementsForThisTopic[pair], partition) + if len(partitionMovementsForThisTopic[pair]) == 0 { + delete(partitionMovementsForThisTopic, pair) + } + if len(p.PartitionMovementsByTopic[partition.Topic]) == 0 { + delete(p.PartitionMovementsByTopic, partition.Topic) + } + return pair +} + +func (p *partitionMovements) addPartitionMovementRecord(partition topicPartitionAssignment, pair consumerPair) { + p.Movements[partition] = pair + if _, exists := p.PartitionMovementsByTopic[partition.Topic]; !exists { + p.PartitionMovementsByTopic[partition.Topic] = make(map[consumerPair]map[topicPartitionAssignment]bool) + } + partitionMovementsForThisTopic := p.PartitionMovementsByTopic[partition.Topic] + if _, exists := partitionMovementsForThisTopic[pair]; !exists { + partitionMovementsForThisTopic[pair] = make(map[topicPartitionAssignment]bool) + } + partitionMovementsForThisTopic[pair][partition] = true +} + +func (p *partitionMovements) movePartition(partition topicPartitionAssignment, oldConsumer, newConsumer string) { + pair := consumerPair{ + SrcMemberID: oldConsumer, + DstMemberID: newConsumer, + } + if _, exists := p.Movements[partition]; exists { + // this partition has previously moved + existingPair := p.removeMovementRecordOfPartition(partition) + if existingPair.DstMemberID != oldConsumer { + Logger.Printf("Existing pair DstMemberID %s was not equal to the oldConsumer ID %s", existingPair.DstMemberID, oldConsumer) + } + if existingPair.SrcMemberID != newConsumer { + // the partition is not moving back to its previous consumer + p.addPartitionMovementRecord(partition, consumerPair{ + SrcMemberID: existingPair.SrcMemberID, + DstMemberID: newConsumer, + }) + } + } else { + p.addPartitionMovementRecord(partition, pair) + } +} + +func (p *partitionMovements) getTheActualPartitionToBeMoved(partition topicPartitionAssignment, oldConsumer, newConsumer string) topicPartitionAssignment { + if _, exists := p.PartitionMovementsByTopic[partition.Topic]; !exists { + return partition + } + if _, exists := p.Movements[partition]; exists { + // this partition has previously moved + if oldConsumer != p.Movements[partition].DstMemberID { + Logger.Printf("Partition movement DstMemberID %s was not equal to the oldConsumer ID %s", p.Movements[partition].DstMemberID, oldConsumer) + } + oldConsumer = p.Movements[partition].SrcMemberID + } + + partitionMovementsForThisTopic := p.PartitionMovementsByTopic[partition.Topic] + reversePair := consumerPair{ + SrcMemberID: newConsumer, + DstMemberID: oldConsumer, + } + if _, exists := partitionMovementsForThisTopic[reversePair]; !exists { + return partition + } + var reversePairPartition topicPartitionAssignment + for otherPartition := range partitionMovementsForThisTopic[reversePair] { + reversePairPartition = otherPartition + } + return reversePairPartition +} + +func (p *partitionMovements) isLinked(src, dst string, pairs []consumerPair, currentPath []string) ([]string, bool) { + if src == dst { + return currentPath, false + } + if len(pairs) == 0 { + return currentPath, false + } + for _, pair := range pairs { + if src == pair.SrcMemberID && dst == pair.DstMemberID { + currentPath = append(currentPath, src, dst) + return currentPath, true + } + } + + for _, pair := range pairs { + if pair.SrcMemberID == src { + // create a deep copy of the pairs, excluding the current pair + reducedSet := make([]consumerPair, len(pairs)-1) + i := 0 + for _, p := range pairs { + if p != pair { + reducedSet[i] = pair + i++ + } + } + + currentPath = append(currentPath, pair.SrcMemberID) + return p.isLinked(pair.DstMemberID, dst, reducedSet, currentPath) + } + } + return currentPath, false +} + +func (p *partitionMovements) in(cycle []string, cycles [][]string) bool { + superCycle := make([]string, len(cycle)-1) + for i := 0; i < len(cycle)-1; i++ { + superCycle[i] = cycle[i] + } + for _, c := range cycle { + superCycle = append(superCycle, c) + } + for _, foundCycle := range cycles { + if len(foundCycle) == len(cycle) && indexOfSubList(superCycle, foundCycle) != -1 { + return true + } + } + return false +} + +func (p *partitionMovements) hasCycles(pairs []consumerPair) bool { + cycles := make([][]string, 0) + for _, pair := range pairs { + // create a deep copy of the pairs, excluding the current pair + reducedPairs := make([]consumerPair, len(pairs)-1) + i := 0 + for _, p := range pairs { + if p != pair { + reducedPairs[i] = pair + i++ + } + } + if path, linked := p.isLinked(pair.DstMemberID, pair.SrcMemberID, reducedPairs, []string{pair.SrcMemberID}); linked { + if !p.in(path, cycles) { + cycles = append(cycles, path) + Logger.Printf("A cycle of length %d was found: %v", len(path)-1, path) + } + } + } + + // for now we want to make sure there is no partition movements of the same topic between a pair of consumers. + // the odds of finding a cycle among more than two consumers seem to be very low (according to various randomized + // tests with the given sticky algorithm) that it should not worth the added complexity of handling those cases. + for _, cycle := range cycles { + if len(cycle) == 3 { + return true + } + } + return false +} + +func (p *partitionMovements) isSticky() bool { + for topic, movements := range p.PartitionMovementsByTopic { + movementPairs := make([]consumerPair, len(movements)) + i := 0 + for pair := range movements { + movementPairs[i] = pair + i++ + } + if p.hasCycles(movementPairs) { + Logger.Printf("Stickiness is violated for topic %s", topic) + Logger.Printf("Partition movements for this topic occurred among the following consumer pairs: %v", movements) + return false + } + } + return true +} + +func indexOfSubList(source []string, target []string) int { + targetSize := len(target) + maxCandidate := len(source) - targetSize +nextCand: + for candidate := 0; candidate <= maxCandidate; candidate++ { + j := candidate + for i := 0; i < targetSize; i++ { + if target[i] != source[j] { + // Element mismatch, try next cand + continue nextCand + } + j++ + } + // All elements of candidate matched target + return candidate + } + return -1 +} + +type consumerGroupMember struct { + id string + assignments []topicPartitionAssignment +} + +// assignmentPriorityQueue is a priority-queue of consumer group members that is sorted +// in descending order (most assignments to least assignments). +type assignmentPriorityQueue []*consumerGroupMember + +func (pq assignmentPriorityQueue) Len() int { return len(pq) } + +func (pq assignmentPriorityQueue) Less(i, j int) bool { + // order asssignment priority queue in descending order using assignment-count/member-id + if len(pq[i].assignments) == len(pq[j].assignments) { + return strings.Compare(pq[i].id, pq[j].id) > 0 + } + return len(pq[i].assignments) > len(pq[j].assignments) +} + +func (pq assignmentPriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] +} + +func (pq *assignmentPriorityQueue) Push(x interface{}) { + member := x.(*consumerGroupMember) + *pq = append(*pq, member) +} + +func (pq *assignmentPriorityQueue) Pop() interface{} { + old := *pq + n := len(old) + member := old[n-1] + *pq = old[0 : n-1] + return member +} diff --git a/vendor/github.com/Shopify/sarama/broker.go b/vendor/github.com/Shopify/sarama/broker.go new file mode 100644 index 0000000..8146749 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/broker.go @@ -0,0 +1,1354 @@ +package sarama + +import ( + "crypto/tls" + "encoding/binary" + "fmt" + "io" + "net" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + metrics "github.com/rcrowley/go-metrics" +) + +// Broker represents a single Kafka broker connection. All operations on this object are entirely concurrency-safe. +type Broker struct { + conf *Config + rack *string + + id int32 + addr string + correlationID int32 + conn net.Conn + connErr error + lock sync.Mutex + opened int32 + responses chan responsePromise + done chan bool + + registeredMetrics []string + + incomingByteRate metrics.Meter + requestRate metrics.Meter + requestSize metrics.Histogram + requestLatency metrics.Histogram + outgoingByteRate metrics.Meter + responseRate metrics.Meter + responseSize metrics.Histogram + brokerIncomingByteRate metrics.Meter + brokerRequestRate metrics.Meter + brokerRequestSize metrics.Histogram + brokerRequestLatency metrics.Histogram + brokerOutgoingByteRate metrics.Meter + brokerResponseRate metrics.Meter + brokerResponseSize metrics.Histogram + + kerberosAuthenticator GSSAPIKerberosAuth +} + +// SASLMechanism specifies the SASL mechanism the client uses to authenticate with the broker +type SASLMechanism string + +const ( + // SASLTypeOAuth represents the SASL/OAUTHBEARER mechanism (Kafka 2.0.0+) + SASLTypeOAuth = "OAUTHBEARER" + // SASLTypePlaintext represents the SASL/PLAIN mechanism + SASLTypePlaintext = "PLAIN" + // SASLTypeSCRAMSHA256 represents the SCRAM-SHA-256 mechanism. + SASLTypeSCRAMSHA256 = "SCRAM-SHA-256" + // SASLTypeSCRAMSHA512 represents the SCRAM-SHA-512 mechanism. + SASLTypeSCRAMSHA512 = "SCRAM-SHA-512" + SASLTypeGSSAPI = "GSSAPI" + // SASLHandshakeV0 is v0 of the Kafka SASL handshake protocol. Client and + // server negotiate SASL auth using opaque packets. + SASLHandshakeV0 = int16(0) + // SASLHandshakeV1 is v1 of the Kafka SASL handshake protocol. Client and + // server negotiate SASL by wrapping tokens with Kafka protocol headers. + SASLHandshakeV1 = int16(1) + // SASLExtKeyAuth is the reserved extension key name sent as part of the + // SASL/OAUTHBEARER intial client response + SASLExtKeyAuth = "auth" +) + +// AccessToken contains an access token used to authenticate a +// SASL/OAUTHBEARER client along with associated metadata. +type AccessToken struct { + // Token is the access token payload. + Token string + // Extensions is a optional map of arbitrary key-value pairs that can be + // sent with the SASL/OAUTHBEARER initial client response. These values are + // ignored by the SASL server if they are unexpected. This feature is only + // supported by Kafka >= 2.1.0. + Extensions map[string]string +} + +// AccessTokenProvider is the interface that encapsulates how implementors +// can generate access tokens for Kafka broker authentication. +type AccessTokenProvider interface { + // Token returns an access token. The implementation should ensure token + // reuse so that multiple calls at connect time do not create multiple + // tokens. The implementation should also periodically refresh the token in + // order to guarantee that each call returns an unexpired token. This + // method should not block indefinitely--a timeout error should be returned + // after a short period of inactivity so that the broker connection logic + // can log debugging information and retry. + Token() (*AccessToken, error) +} + +// SCRAMClient is a an interface to a SCRAM +// client implementation. +type SCRAMClient interface { + // Begin prepares the client for the SCRAM exchange + // with the server with a user name and a password + Begin(userName, password, authzID string) error + // Step steps client through the SCRAM exchange. It is + // called repeatedly until it errors or `Done` returns true. + Step(challenge string) (response string, err error) + // Done should return true when the SCRAM conversation + // is over. + Done() bool +} + +type responsePromise struct { + requestTime time.Time + correlationID int32 + packets chan []byte + errors chan error +} + +// NewBroker creates and returns a Broker targeting the given host:port address. +// This does not attempt to actually connect, you have to call Open() for that. +func NewBroker(addr string) *Broker { + return &Broker{id: -1, addr: addr} +} + +// Open tries to connect to the Broker if it is not already connected or connecting, but does not block +// waiting for the connection to complete. This means that any subsequent operations on the broker will +// block waiting for the connection to succeed or fail. To get the effect of a fully synchronous Open call, +// follow it by a call to Connected(). The only errors Open will return directly are ConfigurationError or +// AlreadyConnected. If conf is nil, the result of NewConfig() is used. +func (b *Broker) Open(conf *Config) error { + if !atomic.CompareAndSwapInt32(&b.opened, 0, 1) { + return ErrAlreadyConnected + } + + if conf == nil { + conf = NewConfig() + } + + err := conf.Validate() + if err != nil { + return err + } + + b.lock.Lock() + + go withRecover(func() { + defer b.lock.Unlock() + + dialer := net.Dialer{ + Timeout: conf.Net.DialTimeout, + KeepAlive: conf.Net.KeepAlive, + LocalAddr: conf.Net.LocalAddr, + } + + if conf.Net.TLS.Enable { + b.conn, b.connErr = tls.DialWithDialer(&dialer, "tcp", b.addr, conf.Net.TLS.Config) + } else if conf.Net.Proxy.Enable { + b.conn, b.connErr = conf.Net.Proxy.Dialer.Dial("tcp", b.addr) + } else { + b.conn, b.connErr = dialer.Dial("tcp", b.addr) + } + if b.connErr != nil { + Logger.Printf("Failed to connect to broker %s: %s\n", b.addr, b.connErr) + b.conn = nil + atomic.StoreInt32(&b.opened, 0) + return + } + b.conn = newBufConn(b.conn) + + b.conf = conf + + // Create or reuse the global metrics shared between brokers + b.incomingByteRate = metrics.GetOrRegisterMeter("incoming-byte-rate", conf.MetricRegistry) + b.requestRate = metrics.GetOrRegisterMeter("request-rate", conf.MetricRegistry) + b.requestSize = getOrRegisterHistogram("request-size", conf.MetricRegistry) + b.requestLatency = getOrRegisterHistogram("request-latency-in-ms", conf.MetricRegistry) + b.outgoingByteRate = metrics.GetOrRegisterMeter("outgoing-byte-rate", conf.MetricRegistry) + b.responseRate = metrics.GetOrRegisterMeter("response-rate", conf.MetricRegistry) + b.responseSize = getOrRegisterHistogram("response-size", conf.MetricRegistry) + // Do not gather metrics for seeded broker (only used during bootstrap) because they share + // the same id (-1) and are already exposed through the global metrics above + if b.id >= 0 { + b.registerMetrics() + } + + if conf.Net.SASL.Enable { + + b.connErr = b.authenticateViaSASL() + + if b.connErr != nil { + err = b.conn.Close() + if err == nil { + Logger.Printf("Closed connection to broker %s\n", b.addr) + } else { + Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) + } + b.conn = nil + atomic.StoreInt32(&b.opened, 0) + return + } + } + + b.done = make(chan bool) + b.responses = make(chan responsePromise, b.conf.Net.MaxOpenRequests-1) + + if b.id >= 0 { + Logger.Printf("Connected to broker at %s (registered as #%d)\n", b.addr, b.id) + } else { + Logger.Printf("Connected to broker at %s (unregistered)\n", b.addr) + } + go withRecover(b.responseReceiver) + }) + + return nil +} + +// Connected returns true if the broker is connected and false otherwise. If the broker is not +// connected but it had tried to connect, the error from that connection attempt is also returned. +func (b *Broker) Connected() (bool, error) { + b.lock.Lock() + defer b.lock.Unlock() + + return b.conn != nil, b.connErr +} + +//Close closes the broker resources +func (b *Broker) Close() error { + b.lock.Lock() + defer b.lock.Unlock() + + if b.conn == nil { + return ErrNotConnected + } + + close(b.responses) + <-b.done + + err := b.conn.Close() + + b.conn = nil + b.connErr = nil + b.done = nil + b.responses = nil + + b.unregisterMetrics() + + if err == nil { + Logger.Printf("Closed connection to broker %s\n", b.addr) + } else { + Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) + } + + atomic.StoreInt32(&b.opened, 0) + + return err +} + +// ID returns the broker ID retrieved from Kafka's metadata, or -1 if that is not known. +func (b *Broker) ID() int32 { + return b.id +} + +// Addr returns the broker address as either retrieved from Kafka's metadata or passed to NewBroker. +func (b *Broker) Addr() string { + return b.addr +} + +// Rack returns the broker's rack as retrieved from Kafka's metadata or the +// empty string if it is not known. The returned value corresponds to the +// broker's broker.rack configuration setting. Requires protocol version to be +// at least v0.10.0.0. +func (b *Broker) Rack() string { + if b.rack == nil { + return "" + } + return *b.rack +} + +//GetMetadata send a metadata request and returns a metadata response or error +func (b *Broker) GetMetadata(request *MetadataRequest) (*MetadataResponse, error) { + response := new(MetadataResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +//GetConsumerMetadata send a consumer metadata request and returns a consumer metadata response or error +func (b *Broker) GetConsumerMetadata(request *ConsumerMetadataRequest) (*ConsumerMetadataResponse, error) { + response := new(ConsumerMetadataResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +//FindCoordinator sends a find coordinate request and returns a response or error +func (b *Broker) FindCoordinator(request *FindCoordinatorRequest) (*FindCoordinatorResponse, error) { + response := new(FindCoordinatorResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +//GetAvailableOffsets return an offset response or error +func (b *Broker) GetAvailableOffsets(request *OffsetRequest) (*OffsetResponse, error) { + response := new(OffsetResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +//Produce returns a produce response or error +func (b *Broker) Produce(request *ProduceRequest) (*ProduceResponse, error) { + var ( + response *ProduceResponse + err error + ) + + if request.RequiredAcks == NoResponse { + err = b.sendAndReceive(request, nil) + } else { + response = new(ProduceResponse) + err = b.sendAndReceive(request, response) + } + + if err != nil { + return nil, err + } + + return response, nil +} + +//Fetch returns a FetchResponse or error +func (b *Broker) Fetch(request *FetchRequest) (*FetchResponse, error) { + response := new(FetchResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//CommitOffset return an Offset commit reponse or error +func (b *Broker) CommitOffset(request *OffsetCommitRequest) (*OffsetCommitResponse, error) { + response := new(OffsetCommitResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//FetchOffset returns an offset fetch response or error +func (b *Broker) FetchOffset(request *OffsetFetchRequest) (*OffsetFetchResponse, error) { + response := new(OffsetFetchResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//JoinGroup returns a join group response or error +func (b *Broker) JoinGroup(request *JoinGroupRequest) (*JoinGroupResponse, error) { + response := new(JoinGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//SyncGroup returns a sync group response or error +func (b *Broker) SyncGroup(request *SyncGroupRequest) (*SyncGroupResponse, error) { + response := new(SyncGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//LeaveGroup return a leave group response or error +func (b *Broker) LeaveGroup(request *LeaveGroupRequest) (*LeaveGroupResponse, error) { + response := new(LeaveGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//Heartbeat returns a heartbeat response or error +func (b *Broker) Heartbeat(request *HeartbeatRequest) (*HeartbeatResponse, error) { + response := new(HeartbeatResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//ListGroups return a list group response or error +func (b *Broker) ListGroups(request *ListGroupsRequest) (*ListGroupsResponse, error) { + response := new(ListGroupsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DescribeGroups return describe group response or error +func (b *Broker) DescribeGroups(request *DescribeGroupsRequest) (*DescribeGroupsResponse, error) { + response := new(DescribeGroupsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//ApiVersions return api version response or error +func (b *Broker) ApiVersions(request *ApiVersionsRequest) (*ApiVersionsResponse, error) { + response := new(ApiVersionsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//CreateTopics send a create topic request and returns create topic response +func (b *Broker) CreateTopics(request *CreateTopicsRequest) (*CreateTopicsResponse, error) { + response := new(CreateTopicsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DeleteTopics sends a delete topic request and returns delete topic response +func (b *Broker) DeleteTopics(request *DeleteTopicsRequest) (*DeleteTopicsResponse, error) { + response := new(DeleteTopicsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//CreatePartitions sends a create partition request and returns create +//partitions response or error +func (b *Broker) CreatePartitions(request *CreatePartitionsRequest) (*CreatePartitionsResponse, error) { + response := new(CreatePartitionsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DeleteRecords send a request to delete records and return delete record +//response or error +func (b *Broker) DeleteRecords(request *DeleteRecordsRequest) (*DeleteRecordsResponse, error) { + response := new(DeleteRecordsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DescribeAcls sends a describe acl request and returns a response or error +func (b *Broker) DescribeAcls(request *DescribeAclsRequest) (*DescribeAclsResponse, error) { + response := new(DescribeAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//CreateAcls sends a create acl request and returns a response or error +func (b *Broker) CreateAcls(request *CreateAclsRequest) (*CreateAclsResponse, error) { + response := new(CreateAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DeleteAcls sends a delete acl request and returns a response or error +func (b *Broker) DeleteAcls(request *DeleteAclsRequest) (*DeleteAclsResponse, error) { + response := new(DeleteAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//InitProducerID sends an init producer request and returns a response or error +func (b *Broker) InitProducerID(request *InitProducerIDRequest) (*InitProducerIDResponse, error) { + response := new(InitProducerIDResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//AddPartitionsToTxn send a request to add partition to txn and returns +//a response or error +func (b *Broker) AddPartitionsToTxn(request *AddPartitionsToTxnRequest) (*AddPartitionsToTxnResponse, error) { + response := new(AddPartitionsToTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//AddOffsetsToTxn sends a request to add offsets to txn and returns a response +//or error +func (b *Broker) AddOffsetsToTxn(request *AddOffsetsToTxnRequest) (*AddOffsetsToTxnResponse, error) { + response := new(AddOffsetsToTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//EndTxn sends a request to end txn and returns a response or error +func (b *Broker) EndTxn(request *EndTxnRequest) (*EndTxnResponse, error) { + response := new(EndTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//TxnOffsetCommit sends a request to commit transaction offsets and returns +//a response or error +func (b *Broker) TxnOffsetCommit(request *TxnOffsetCommitRequest) (*TxnOffsetCommitResponse, error) { + response := new(TxnOffsetCommitResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DescribeConfigs sends a request to describe config and returns a response or +//error +func (b *Broker) DescribeConfigs(request *DescribeConfigsRequest) (*DescribeConfigsResponse, error) { + response := new(DescribeConfigsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//AlterConfigs sends a request to alter config and return a response or error +func (b *Broker) AlterConfigs(request *AlterConfigsRequest) (*AlterConfigsResponse, error) { + response := new(AlterConfigsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +//DeleteGroups sends a request to delete groups and returns a response or error +func (b *Broker) DeleteGroups(request *DeleteGroupsRequest) (*DeleteGroupsResponse, error) { + response := new(DeleteGroupsResponse) + + if err := b.sendAndReceive(request, response); err != nil { + return nil, err + } + + return response, nil +} + +//DescribeLogDirs sends a request to get the broker's log dir paths and sizes +func (b *Broker) DescribeLogDirs(request *DescribeLogDirsRequest) (*DescribeLogDirsResponse, error) { + response := new(DescribeLogDirsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +// readFull ensures the conn ReadDeadline has been setup before making a +// call to io.ReadFull +func (b *Broker) readFull(buf []byte) (n int, err error) { + if err := b.conn.SetReadDeadline(time.Now().Add(b.conf.Net.ReadTimeout)); err != nil { + return 0, err + } + + return io.ReadFull(b.conn, buf) +} + +// write ensures the conn WriteDeadline has been setup before making a +// call to conn.Write +func (b *Broker) write(buf []byte) (n int, err error) { + if err := b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)); err != nil { + return 0, err + } + + return b.conn.Write(buf) +} + +func (b *Broker) send(rb protocolBody, promiseResponse bool) (*responsePromise, error) { + b.lock.Lock() + defer b.lock.Unlock() + + if b.conn == nil { + if b.connErr != nil { + return nil, b.connErr + } + return nil, ErrNotConnected + } + + if !b.conf.Version.IsAtLeast(rb.requiredVersion()) { + return nil, ErrUnsupportedVersion + } + + req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return nil, err + } + + requestTime := time.Now() + bytes, err := b.write(buf) + b.updateOutgoingCommunicationMetrics(bytes) + if err != nil { + return nil, err + } + b.correlationID++ + + if !promiseResponse { + // Record request latency without the response + b.updateRequestLatencyMetrics(time.Since(requestTime)) + return nil, nil + } + + promise := responsePromise{requestTime, req.correlationID, make(chan []byte), make(chan error)} + b.responses <- promise + + return &promise, nil +} + +func (b *Broker) sendAndReceive(req protocolBody, res versionedDecoder) error { + promise, err := b.send(req, res != nil) + if err != nil { + return err + } + + if promise == nil { + return nil + } + + select { + case buf := <-promise.packets: + return versionedDecode(buf, res, req.version()) + case err = <-promise.errors: + return err + } +} + +func (b *Broker) decode(pd packetDecoder, version int16) (err error) { + b.id, err = pd.getInt32() + if err != nil { + return err + } + + host, err := pd.getString() + if err != nil { + return err + } + + port, err := pd.getInt32() + if err != nil { + return err + } + + if version >= 1 { + b.rack, err = pd.getNullableString() + if err != nil { + return err + } + } + + b.addr = net.JoinHostPort(host, fmt.Sprint(port)) + if _, _, err := net.SplitHostPort(b.addr); err != nil { + return err + } + + return nil +} + +func (b *Broker) encode(pe packetEncoder, version int16) (err error) { + host, portstr, err := net.SplitHostPort(b.addr) + if err != nil { + return err + } + + port, err := strconv.Atoi(portstr) + if err != nil { + return err + } + + pe.putInt32(b.id) + + err = pe.putString(host) + if err != nil { + return err + } + + pe.putInt32(int32(port)) + + if version >= 1 { + err = pe.putNullableString(b.rack) + if err != nil { + return err + } + } + + return nil +} + +func (b *Broker) responseReceiver() { + var dead error + header := make([]byte, 8) + + for response := range b.responses { + if dead != nil { + response.errors <- dead + continue + } + + bytesReadHeader, err := b.readFull(header) + requestLatency := time.Since(response.requestTime) + if err != nil { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + dead = err + response.errors <- err + continue + } + + decodedHeader := responseHeader{} + err = decode(header, &decodedHeader) + if err != nil { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + dead = err + response.errors <- err + continue + } + if decodedHeader.correlationID != response.correlationID { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + // TODO if decoded ID < cur ID, discard until we catch up + // TODO if decoded ID > cur ID, save it so when cur ID catches up we have a response + dead = PacketDecodingError{fmt.Sprintf("correlation ID didn't match, wanted %d, got %d", response.correlationID, decodedHeader.correlationID)} + response.errors <- dead + continue + } + + buf := make([]byte, decodedHeader.length-4) + bytesReadBody, err := b.readFull(buf) + b.updateIncomingCommunicationMetrics(bytesReadHeader+bytesReadBody, requestLatency) + if err != nil { + dead = err + response.errors <- err + continue + } + + response.packets <- buf + } + close(b.done) +} + +func (b *Broker) authenticateViaSASL() error { + switch b.conf.Net.SASL.Mechanism { + case SASLTypeOAuth: + return b.sendAndReceiveSASLOAuth(b.conf.Net.SASL.TokenProvider) + case SASLTypeSCRAMSHA256, SASLTypeSCRAMSHA512: + return b.sendAndReceiveSASLSCRAMv1() + case SASLTypeGSSAPI: + return b.sendAndReceiveKerberos() + default: + return b.sendAndReceiveSASLPlainAuth() + } +} + +func (b *Broker) sendAndReceiveKerberos() error { + b.kerberosAuthenticator.Config = &b.conf.Net.SASL.GSSAPI + if b.kerberosAuthenticator.NewKerberosClientFunc == nil { + b.kerberosAuthenticator.NewKerberosClientFunc = NewKerberosClient + } + return b.kerberosAuthenticator.Authorize(b) +} + +func (b *Broker) sendAndReceiveSASLHandshake(saslType SASLMechanism, version int16) error { + rb := &SaslHandshakeRequest{Mechanism: string(saslType), Version: version} + + req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return err + } + + requestTime := time.Now() + bytes, err := b.write(buf) + b.updateOutgoingCommunicationMetrics(bytes) + if err != nil { + Logger.Printf("Failed to send SASL handshake %s: %s\n", b.addr, err.Error()) + return err + } + b.correlationID++ + + header := make([]byte, 8) // response header + _, err = b.readFull(header) + if err != nil { + Logger.Printf("Failed to read SASL handshake header : %s\n", err.Error()) + return err + } + + length := binary.BigEndian.Uint32(header[:4]) + payload := make([]byte, length-4) + n, err := b.readFull(payload) + if err != nil { + Logger.Printf("Failed to read SASL handshake payload : %s\n", err.Error()) + return err + } + + b.updateIncomingCommunicationMetrics(n+8, time.Since(requestTime)) + res := &SaslHandshakeResponse{} + + err = versionedDecode(payload, res, 0) + if err != nil { + Logger.Printf("Failed to parse SASL handshake : %s\n", err.Error()) + return err + } + + if res.Err != ErrNoError { + Logger.Printf("Invalid SASL Mechanism : %s\n", res.Err.Error()) + return res.Err + } + + Logger.Print("Successful SASL handshake. Available mechanisms: ", res.EnabledMechanisms) + return nil +} + +// Kafka 0.10.x supported SASL PLAIN/Kerberos via KAFKA-3149 (KIP-43). +// Kafka 1.x.x onward added a SaslAuthenticate request/response message which +// wraps the SASL flow in the Kafka protocol, which allows for returning +// meaningful errors on authentication failure. +// +// In SASL Plain, Kafka expects the auth header to be in the following format +// Message format (from https://tools.ietf.org/html/rfc4616): +// +// message = [authzid] UTF8NUL authcid UTF8NUL passwd +// authcid = 1*SAFE ; MUST accept up to 255 octets +// authzid = 1*SAFE ; MUST accept up to 255 octets +// passwd = 1*SAFE ; MUST accept up to 255 octets +// UTF8NUL = %x00 ; UTF-8 encoded NUL character +// +// SAFE = UTF1 / UTF2 / UTF3 / UTF4 +// ;; any UTF-8 encoded Unicode character except NUL +// +// With SASL v0 handshake and auth then: +// When credentials are valid, Kafka returns a 4 byte array of null characters. +// When credentials are invalid, Kafka closes the connection. +// +// With SASL v1 handshake and auth then: +// When credentials are invalid, Kafka replies with a SaslAuthenticate response +// containing an error code and message detailing the authentication failure. +func (b *Broker) sendAndReceiveSASLPlainAuth() error { + // default to V0 to allow for backward compatability when SASL is enabled + // but not the handshake + if b.conf.Net.SASL.Handshake { + + handshakeErr := b.sendAndReceiveSASLHandshake(SASLTypePlaintext, b.conf.Net.SASL.Version) + if handshakeErr != nil { + Logger.Printf("Error while performing SASL handshake %s\n", b.addr) + return handshakeErr + } + } + + if b.conf.Net.SASL.Version == SASLHandshakeV1 { + return b.sendAndReceiveV1SASLPlainAuth() + } + return b.sendAndReceiveV0SASLPlainAuth() +} + +// sendAndReceiveV0SASLPlainAuth flows the v0 sasl auth NOT wrapped in the kafka protocol +func (b *Broker) sendAndReceiveV0SASLPlainAuth() error { + + length := 1 + len(b.conf.Net.SASL.User) + 1 + len(b.conf.Net.SASL.Password) + authBytes := make([]byte, length+4) //4 byte length header + auth data + binary.BigEndian.PutUint32(authBytes, uint32(length)) + copy(authBytes[4:], []byte("\x00"+b.conf.Net.SASL.User+"\x00"+b.conf.Net.SASL.Password)) + + requestTime := time.Now() + bytesWritten, err := b.write(authBytes) + b.updateOutgoingCommunicationMetrics(bytesWritten) + if err != nil { + Logger.Printf("Failed to write SASL auth header to broker %s: %s\n", b.addr, err.Error()) + return err + } + + header := make([]byte, 4) + n, err := b.readFull(header) + b.updateIncomingCommunicationMetrics(n, time.Since(requestTime)) + // If the credentials are valid, we would get a 4 byte response filled with null characters. + // Otherwise, the broker closes the connection and we get an EOF + if err != nil { + Logger.Printf("Failed to read response while authenticating with SASL to broker %s: %s\n", b.addr, err.Error()) + return err + } + + Logger.Printf("SASL authentication successful with broker %s:%v - %v\n", b.addr, n, header) + return nil +} + +// sendAndReceiveV1SASLPlainAuth flows the v1 sasl authentication using the kafka protocol +func (b *Broker) sendAndReceiveV1SASLPlainAuth() error { + correlationID := b.correlationID + + requestTime := time.Now() + + bytesWritten, err := b.sendSASLPlainAuthClientResponse(correlationID) + + b.updateOutgoingCommunicationMetrics(bytesWritten) + + if err != nil { + Logger.Printf("Failed to write SASL auth header to broker %s: %s\n", b.addr, err.Error()) + return err + } + + b.correlationID++ + + bytesRead, err := b.receiveSASLServerResponse(&SaslAuthenticateResponse{}, correlationID) + b.updateIncomingCommunicationMetrics(bytesRead, time.Since(requestTime)) + + // With v1 sasl we get an error message set in the response we can return + if err != nil { + Logger.Printf("Error returned from broker during SASL flow %s: %s\n", b.addr, err.Error()) + return err + } + + return nil +} + +// sendAndReceiveSASLOAuth performs the authentication flow as described by KIP-255 +// https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=75968876 +func (b *Broker) sendAndReceiveSASLOAuth(provider AccessTokenProvider) error { + if err := b.sendAndReceiveSASLHandshake(SASLTypeOAuth, SASLHandshakeV1); err != nil { + return err + } + + token, err := provider.Token() + if err != nil { + return err + } + + message, err := buildClientFirstMessage(token) + if err != nil { + return err + } + + challenged, err := b.sendClientMessage(message) + if err != nil { + return err + } + + if challenged { + // Abort the token exchange. The broker returns the failure code. + _, err = b.sendClientMessage([]byte(`\x01`)) + } + + return err +} + +// sendClientMessage sends a SASL/OAUTHBEARER client message and returns true +// if the broker responds with a challenge, in which case the token is +// rejected. +func (b *Broker) sendClientMessage(message []byte) (bool, error) { + + requestTime := time.Now() + correlationID := b.correlationID + + bytesWritten, err := b.sendSASLOAuthBearerClientMessage(message, correlationID) + if err != nil { + return false, err + } + + b.updateOutgoingCommunicationMetrics(bytesWritten) + b.correlationID++ + + res := &SaslAuthenticateResponse{} + bytesRead, err := b.receiveSASLServerResponse(res, correlationID) + + requestLatency := time.Since(requestTime) + b.updateIncomingCommunicationMetrics(bytesRead, requestLatency) + + isChallenge := len(res.SaslAuthBytes) > 0 + + if isChallenge && err != nil { + Logger.Printf("Broker rejected authentication token: %s", res.SaslAuthBytes) + } + + return isChallenge, err +} + +func (b *Broker) sendAndReceiveSASLSCRAMv1() error { + if err := b.sendAndReceiveSASLHandshake(b.conf.Net.SASL.Mechanism, SASLHandshakeV1); err != nil { + return err + } + + scramClient := b.conf.Net.SASL.SCRAMClientGeneratorFunc() + if err := scramClient.Begin(b.conf.Net.SASL.User, b.conf.Net.SASL.Password, b.conf.Net.SASL.SCRAMAuthzID); err != nil { + return fmt.Errorf("failed to start SCRAM exchange with the server: %s", err.Error()) + } + + msg, err := scramClient.Step("") + if err != nil { + return fmt.Errorf("failed to advance the SCRAM exchange: %s", err.Error()) + + } + + for !scramClient.Done() { + requestTime := time.Now() + correlationID := b.correlationID + bytesWritten, err := b.sendSaslAuthenticateRequest(correlationID, []byte(msg)) + if err != nil { + Logger.Printf("Failed to write SASL auth header to broker %s: %s\n", b.addr, err.Error()) + return err + } + + b.updateOutgoingCommunicationMetrics(bytesWritten) + b.correlationID++ + challenge, err := b.receiveSaslAuthenticateResponse(correlationID) + if err != nil { + Logger.Printf("Failed to read response while authenticating with SASL to broker %s: %s\n", b.addr, err.Error()) + return err + } + + b.updateIncomingCommunicationMetrics(len(challenge), time.Since(requestTime)) + msg, err = scramClient.Step(string(challenge)) + if err != nil { + Logger.Println("SASL authentication failed", err) + return err + } + } + + Logger.Println("SASL authentication succeeded") + return nil +} + +func (b *Broker) sendSaslAuthenticateRequest(correlationID int32, msg []byte) (int, error) { + rb := &SaslAuthenticateRequest{msg} + req := &request{correlationID: correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return 0, err + } + + return b.write(buf) +} + +func (b *Broker) receiveSaslAuthenticateResponse(correlationID int32) ([]byte, error) { + buf := make([]byte, responseLengthSize+correlationIDSize) + _, err := b.readFull(buf) + if err != nil { + return nil, err + } + + header := responseHeader{} + err = decode(buf, &header) + if err != nil { + return nil, err + } + + if header.correlationID != correlationID { + return nil, fmt.Errorf("correlation ID didn't match, wanted %d, got %d", b.correlationID, header.correlationID) + } + + buf = make([]byte, header.length-correlationIDSize) + _, err = b.readFull(buf) + if err != nil { + return nil, err + } + + res := &SaslAuthenticateResponse{} + if err := versionedDecode(buf, res, 0); err != nil { + return nil, err + } + if res.Err != ErrNoError { + return nil, res.Err + } + return res.SaslAuthBytes, nil +} + +// Build SASL/OAUTHBEARER initial client response as described by RFC-7628 +// https://tools.ietf.org/html/rfc7628 +func buildClientFirstMessage(token *AccessToken) ([]byte, error) { + var ext string + + if token.Extensions != nil && len(token.Extensions) > 0 { + if _, ok := token.Extensions[SASLExtKeyAuth]; ok { + return []byte{}, fmt.Errorf("the extension `%s` is invalid", SASLExtKeyAuth) + } + ext = "\x01" + mapToString(token.Extensions, "=", "\x01") + } + + resp := []byte(fmt.Sprintf("n,,\x01auth=Bearer %s%s\x01\x01", token.Token, ext)) + + return resp, nil +} + +// mapToString returns a list of key-value pairs ordered by key. +// keyValSep separates the key from the value. elemSep separates each pair. +func mapToString(extensions map[string]string, keyValSep string, elemSep string) string { + buf := make([]string, 0, len(extensions)) + + for k, v := range extensions { + buf = append(buf, k+keyValSep+v) + } + + sort.Strings(buf) + + return strings.Join(buf, elemSep) +} + +func (b *Broker) sendSASLPlainAuthClientResponse(correlationID int32) (int, error) { + authBytes := []byte("\x00" + b.conf.Net.SASL.User + "\x00" + b.conf.Net.SASL.Password) + rb := &SaslAuthenticateRequest{authBytes} + req := &request{correlationID: correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return 0, err + } + + return b.write(buf) +} + +func (b *Broker) sendSASLOAuthBearerClientMessage(initialResp []byte, correlationID int32) (int, error) { + + rb := &SaslAuthenticateRequest{initialResp} + + req := &request{correlationID: correlationID, clientID: b.conf.ClientID, body: rb} + + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return 0, err + } + + return b.write(buf) +} + +func (b *Broker) receiveSASLServerResponse(res *SaslAuthenticateResponse, correlationID int32) (int, error) { + buf := make([]byte, responseLengthSize+correlationIDSize) + bytesRead, err := b.readFull(buf) + if err != nil { + return bytesRead, err + } + + header := responseHeader{} + err = decode(buf, &header) + if err != nil { + return bytesRead, err + } + + if header.correlationID != correlationID { + return bytesRead, fmt.Errorf("correlation ID didn't match, wanted %d, got %d", b.correlationID, header.correlationID) + } + + buf = make([]byte, header.length-correlationIDSize) + c, err := b.readFull(buf) + bytesRead += c + if err != nil { + return bytesRead, err + } + + if err := versionedDecode(buf, res, 0); err != nil { + return bytesRead, err + } + + if res.Err != ErrNoError { + return bytesRead, res.Err + } + + return bytesRead, nil +} + +func (b *Broker) updateIncomingCommunicationMetrics(bytes int, requestLatency time.Duration) { + b.updateRequestLatencyMetrics(requestLatency) + b.responseRate.Mark(1) + + if b.brokerResponseRate != nil { + b.brokerResponseRate.Mark(1) + } + + responseSize := int64(bytes) + b.incomingByteRate.Mark(responseSize) + if b.brokerIncomingByteRate != nil { + b.brokerIncomingByteRate.Mark(responseSize) + } + + b.responseSize.Update(responseSize) + if b.brokerResponseSize != nil { + b.brokerResponseSize.Update(responseSize) + } +} + +func (b *Broker) updateRequestLatencyMetrics(requestLatency time.Duration) { + requestLatencyInMs := int64(requestLatency / time.Millisecond) + b.requestLatency.Update(requestLatencyInMs) + + if b.brokerRequestLatency != nil { + b.brokerRequestLatency.Update(requestLatencyInMs) + } + +} + +func (b *Broker) updateOutgoingCommunicationMetrics(bytes int) { + b.requestRate.Mark(1) + if b.brokerRequestRate != nil { + b.brokerRequestRate.Mark(1) + } + + requestSize := int64(bytes) + b.outgoingByteRate.Mark(requestSize) + if b.brokerOutgoingByteRate != nil { + b.brokerOutgoingByteRate.Mark(requestSize) + } + + b.requestSize.Update(requestSize) + if b.brokerRequestSize != nil { + b.brokerRequestSize.Update(requestSize) + } + +} + +func (b *Broker) registerMetrics() { + b.brokerIncomingByteRate = b.registerMeter("incoming-byte-rate") + b.brokerRequestRate = b.registerMeter("request-rate") + b.brokerRequestSize = b.registerHistogram("request-size") + b.brokerRequestLatency = b.registerHistogram("request-latency-in-ms") + b.brokerOutgoingByteRate = b.registerMeter("outgoing-byte-rate") + b.brokerResponseRate = b.registerMeter("response-rate") + b.brokerResponseSize = b.registerHistogram("response-size") +} + +func (b *Broker) unregisterMetrics() { + for _, name := range b.registeredMetrics { + b.conf.MetricRegistry.Unregister(name) + } +} + +func (b *Broker) registerMeter(name string) metrics.Meter { + nameForBroker := getMetricNameForBroker(name, b) + b.registeredMetrics = append(b.registeredMetrics, nameForBroker) + return metrics.GetOrRegisterMeter(nameForBroker, b.conf.MetricRegistry) +} + +func (b *Broker) registerHistogram(name string) metrics.Histogram { + nameForBroker := getMetricNameForBroker(name, b) + b.registeredMetrics = append(b.registeredMetrics, nameForBroker) + return getOrRegisterHistogram(nameForBroker, b.conf.MetricRegistry) +} diff --git a/vendor/github.com/Shopify/sarama/client.go b/vendor/github.com/Shopify/sarama/client.go new file mode 100644 index 0000000..040cfe9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/client.go @@ -0,0 +1,1001 @@ +package sarama + +import ( + "math/rand" + "sort" + "sync" + "time" +) + +// Client is a generic Kafka client. It manages connections to one or more Kafka brokers. +// You MUST call Close() on a client to avoid leaks, it will not be garbage-collected +// automatically when it passes out of scope. It is safe to share a client amongst many +// users, however Kafka will process requests from a single client strictly in serial, +// so it is generally more efficient to use the default one client per producer/consumer. +type Client interface { + // Config returns the Config struct of the client. This struct should not be + // altered after it has been created. + Config() *Config + + // Controller returns the cluster controller broker. Requires Kafka 0.10 or higher. + Controller() (*Broker, error) + + // Brokers returns the current set of active brokers as retrieved from cluster metadata. + Brokers() []*Broker + + // Topics returns the set of available topics as retrieved from cluster metadata. + Topics() ([]string, error) + + // Partitions returns the sorted list of all partition IDs for the given topic. + Partitions(topic string) ([]int32, error) + + // WritablePartitions returns the sorted list of all writable partition IDs for + // the given topic, where "writable" means "having a valid leader accepting + // writes". + WritablePartitions(topic string) ([]int32, error) + + // Leader returns the broker object that is the leader of the current + // topic/partition, as determined by querying the cluster metadata. + Leader(topic string, partitionID int32) (*Broker, error) + + // Replicas returns the set of all replica IDs for the given partition. + Replicas(topic string, partitionID int32) ([]int32, error) + + // InSyncReplicas returns the set of all in-sync replica IDs for the given + // partition. In-sync replicas are replicas which are fully caught up with + // the partition leader. + InSyncReplicas(topic string, partitionID int32) ([]int32, error) + + // OfflineReplicas returns the set of all offline replica IDs for the given + // partition. Offline replicas are replicas which are offline + OfflineReplicas(topic string, partitionID int32) ([]int32, error) + + // RefreshMetadata takes a list of topics and queries the cluster to refresh the + // available metadata for those topics. If no topics are provided, it will refresh + // metadata for all topics. + RefreshMetadata(topics ...string) error + + // GetOffset queries the cluster to get the most recent available offset at the + // given time (in milliseconds) on the topic/partition combination. + // Time should be OffsetOldest for the earliest available offset, + // OffsetNewest for the offset of the message that will be produced next, or a time. + GetOffset(topic string, partitionID int32, time int64) (int64, error) + + // Coordinator returns the coordinating broker for a consumer group. It will + // return a locally cached value if it's available. You can call + // RefreshCoordinator to update the cached value. This function only works on + // Kafka 0.8.2 and higher. + Coordinator(consumerGroup string) (*Broker, error) + + // RefreshCoordinator retrieves the coordinator for a consumer group and stores it + // in local cache. This function only works on Kafka 0.8.2 and higher. + RefreshCoordinator(consumerGroup string) error + + // InitProducerID retrieves information required for Idempotent Producer + InitProducerID() (*InitProducerIDResponse, error) + + // Close shuts down all broker connections managed by this client. It is required + // to call this function before a client object passes out of scope, as it will + // otherwise leak memory. You must close any Producers or Consumers using a client + // before you close the client. + Close() error + + // Closed returns true if the client has already had Close called on it + Closed() bool +} + +const ( + // OffsetNewest stands for the log head offset, i.e. the offset that will be + // assigned to the next message that will be produced to the partition. You + // can send this to a client's GetOffset method to get this offset, or when + // calling ConsumePartition to start consuming new messages. + OffsetNewest int64 = -1 + // OffsetOldest stands for the oldest offset available on the broker for a + // partition. You can send this to a client's GetOffset method to get this + // offset, or when calling ConsumePartition to start consuming from the + // oldest offset that is still available on the broker. + OffsetOldest int64 = -2 +) + +type client struct { + conf *Config + closer, closed chan none // for shutting down background metadata updater + + // the broker addresses given to us through the constructor are not guaranteed to be returned in + // the cluster metadata (I *think* it only returns brokers who are currently leading partitions?) + // so we store them separately + seedBrokers []*Broker + deadSeeds []*Broker + + controllerID int32 // cluster controller broker id + brokers map[int32]*Broker // maps broker ids to brokers + metadata map[string]map[int32]*PartitionMetadata // maps topics to partition ids to metadata + metadataTopics map[string]none // topics that need to collect metadata + coordinators map[string]int32 // Maps consumer group names to coordinating broker IDs + + // If the number of partitions is large, we can get some churn calling cachedPartitions, + // so the result is cached. It is important to update this value whenever metadata is changed + cachedPartitionsResults map[string][maxPartitionIndex][]int32 + + lock sync.RWMutex // protects access to the maps that hold cluster state. +} + +// NewClient creates a new Client. It connects to one of the given broker addresses +// and uses that broker to automatically fetch metadata on the rest of the kafka cluster. If metadata cannot +// be retrieved from any of the given broker addresses, the client is not created. +func NewClient(addrs []string, conf *Config) (Client, error) { + Logger.Println("Initializing new client") + + if conf == nil { + conf = NewConfig() + } + + if err := conf.Validate(); err != nil { + return nil, err + } + + if len(addrs) < 1 { + return nil, ConfigurationError("You must provide at least one broker address") + } + + client := &client{ + conf: conf, + closer: make(chan none), + closed: make(chan none), + brokers: make(map[int32]*Broker), + metadata: make(map[string]map[int32]*PartitionMetadata), + metadataTopics: make(map[string]none), + cachedPartitionsResults: make(map[string][maxPartitionIndex][]int32), + coordinators: make(map[string]int32), + } + + random := rand.New(rand.NewSource(time.Now().UnixNano())) + for _, index := range random.Perm(len(addrs)) { + client.seedBrokers = append(client.seedBrokers, NewBroker(addrs[index])) + } + + if conf.Metadata.Full { + // do an initial fetch of all cluster metadata by specifying an empty list of topics + err := client.RefreshMetadata() + switch err { + case nil: + break + case ErrLeaderNotAvailable, ErrReplicaNotAvailable, ErrTopicAuthorizationFailed, ErrClusterAuthorizationFailed: + // indicates that maybe part of the cluster is down, but is not fatal to creating the client + Logger.Println(err) + default: + close(client.closed) // we haven't started the background updater yet, so we have to do this manually + _ = client.Close() + return nil, err + } + } + go withRecover(client.backgroundMetadataUpdater) + + Logger.Println("Successfully initialized new client") + + return client, nil +} + +func (client *client) Config() *Config { + return client.conf +} + +func (client *client) Brokers() []*Broker { + client.lock.RLock() + defer client.lock.RUnlock() + brokers := make([]*Broker, 0, len(client.brokers)) + for _, broker := range client.brokers { + brokers = append(brokers, broker) + } + return brokers +} + +func (client *client) InitProducerID() (*InitProducerIDResponse, error) { + var err error + for broker := client.any(); broker != nil; broker = client.any() { + + req := &InitProducerIDRequest{} + + response, err := broker.InitProducerID(req) + switch err.(type) { + case nil: + return response, nil + default: + // some error, remove that broker and try again + Logger.Printf("Client got error from broker %d when issuing InitProducerID : %v\n", broker.ID(), err) + _ = broker.Close() + client.deregisterBroker(broker) + } + } + return nil, err +} + +func (client *client) Close() error { + if client.Closed() { + // Chances are this is being called from a defer() and the error will go unobserved + // so we go ahead and log the event in this case. + Logger.Printf("Close() called on already closed client") + return ErrClosedClient + } + + // shutdown and wait for the background thread before we take the lock, to avoid races + close(client.closer) + <-client.closed + + client.lock.Lock() + defer client.lock.Unlock() + Logger.Println("Closing Client") + + for _, broker := range client.brokers { + safeAsyncClose(broker) + } + + for _, broker := range client.seedBrokers { + safeAsyncClose(broker) + } + + client.brokers = nil + client.metadata = nil + client.metadataTopics = nil + + return nil +} + +func (client *client) Closed() bool { + return client.brokers == nil +} + +func (client *client) Topics() ([]string, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + client.lock.RLock() + defer client.lock.RUnlock() + + ret := make([]string, 0, len(client.metadata)) + for topic := range client.metadata { + ret = append(ret, topic) + } + + return ret, nil +} + +func (client *client) MetadataTopics() ([]string, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + client.lock.RLock() + defer client.lock.RUnlock() + + ret := make([]string, 0, len(client.metadataTopics)) + for topic := range client.metadataTopics { + ret = append(ret, topic) + } + + return ret, nil +} + +func (client *client) Partitions(topic string) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + partitions := client.cachedPartitions(topic, allPartitions) + + if len(partitions) == 0 { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + partitions = client.cachedPartitions(topic, allPartitions) + } + + // no partitions found after refresh metadata + if len(partitions) == 0 { + return nil, ErrUnknownTopicOrPartition + } + + return partitions, nil +} + +func (client *client) WritablePartitions(topic string) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + partitions := client.cachedPartitions(topic, writablePartitions) + + // len==0 catches when it's nil (no such topic) and the odd case when every single + // partition is undergoing leader election simultaneously. Callers have to be able to handle + // this function returning an empty slice (which is a valid return value) but catching it + // here the first time (note we *don't* catch it below where we return ErrUnknownTopicOrPartition) triggers + // a metadata refresh as a nicety so callers can just try again and don't have to manually + // trigger a refresh (otherwise they'd just keep getting a stale cached copy). + if len(partitions) == 0 { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + partitions = client.cachedPartitions(topic, writablePartitions) + } + + if partitions == nil { + return nil, ErrUnknownTopicOrPartition + } + + return partitions, nil +} + +func (client *client) Replicas(topic string, partitionID int32) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + metadata := client.cachedMetadata(topic, partitionID) + + if metadata == nil { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + metadata = client.cachedMetadata(topic, partitionID) + } + + if metadata == nil { + return nil, ErrUnknownTopicOrPartition + } + + if metadata.Err == ErrReplicaNotAvailable { + return dupInt32Slice(metadata.Replicas), metadata.Err + } + return dupInt32Slice(metadata.Replicas), nil +} + +func (client *client) InSyncReplicas(topic string, partitionID int32) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + metadata := client.cachedMetadata(topic, partitionID) + + if metadata == nil { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + metadata = client.cachedMetadata(topic, partitionID) + } + + if metadata == nil { + return nil, ErrUnknownTopicOrPartition + } + + if metadata.Err == ErrReplicaNotAvailable { + return dupInt32Slice(metadata.Isr), metadata.Err + } + return dupInt32Slice(metadata.Isr), nil +} + +func (client *client) OfflineReplicas(topic string, partitionID int32) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + metadata := client.cachedMetadata(topic, partitionID) + + if metadata == nil { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + metadata = client.cachedMetadata(topic, partitionID) + } + + if metadata == nil { + return nil, ErrUnknownTopicOrPartition + } + + if metadata.Err == ErrReplicaNotAvailable { + return dupInt32Slice(metadata.OfflineReplicas), metadata.Err + } + return dupInt32Slice(metadata.OfflineReplicas), nil +} + +func (client *client) Leader(topic string, partitionID int32) (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + leader, err := client.cachedLeader(topic, partitionID) + + if leader == nil { + err = client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + leader, err = client.cachedLeader(topic, partitionID) + } + + return leader, err +} + +func (client *client) RefreshMetadata(topics ...string) error { + if client.Closed() { + return ErrClosedClient + } + + // Prior to 0.8.2, Kafka will throw exceptions on an empty topic and not return a proper + // error. This handles the case by returning an error instead of sending it + // off to Kafka. See: https://github.com/Shopify/sarama/pull/38#issuecomment-26362310 + for _, topic := range topics { + if len(topic) == 0 { + return ErrInvalidTopic // this is the error that 0.8.2 and later correctly return + } + } + + deadline := time.Time{} + if client.conf.Metadata.Timeout > 0 { + deadline = time.Now().Add(client.conf.Metadata.Timeout) + } + return client.tryRefreshMetadata(topics, client.conf.Metadata.Retry.Max, deadline) +} + +func (client *client) GetOffset(topic string, partitionID int32, time int64) (int64, error) { + if client.Closed() { + return -1, ErrClosedClient + } + + offset, err := client.getOffset(topic, partitionID, time) + + if err != nil { + if err := client.RefreshMetadata(topic); err != nil { + return -1, err + } + return client.getOffset(topic, partitionID, time) + } + + return offset, err +} + +func (client *client) Controller() (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + if !client.conf.Version.IsAtLeast(V0_10_0_0) { + return nil, ErrUnsupportedVersion + } + + controller := client.cachedController() + if controller == nil { + if err := client.refreshMetadata(); err != nil { + return nil, err + } + controller = client.cachedController() + } + + if controller == nil { + return nil, ErrControllerNotAvailable + } + + _ = controller.Open(client.conf) + return controller, nil +} + +func (client *client) Coordinator(consumerGroup string) (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + coordinator := client.cachedCoordinator(consumerGroup) + + if coordinator == nil { + if err := client.RefreshCoordinator(consumerGroup); err != nil { + return nil, err + } + coordinator = client.cachedCoordinator(consumerGroup) + } + + if coordinator == nil { + return nil, ErrConsumerCoordinatorNotAvailable + } + + _ = coordinator.Open(client.conf) + return coordinator, nil +} + +func (client *client) RefreshCoordinator(consumerGroup string) error { + if client.Closed() { + return ErrClosedClient + } + + response, err := client.getConsumerMetadata(consumerGroup, client.conf.Metadata.Retry.Max) + if err != nil { + return err + } + + client.lock.Lock() + defer client.lock.Unlock() + client.registerBroker(response.Coordinator) + client.coordinators[consumerGroup] = response.Coordinator.ID() + return nil +} + +// private broker management helpers + +// registerBroker makes sure a broker received by a Metadata or Coordinator request is registered +// in the brokers map. It returns the broker that is registered, which may be the provided broker, +// or a previously registered Broker instance. You must hold the write lock before calling this function. +func (client *client) registerBroker(broker *Broker) { + if client.brokers[broker.ID()] == nil { + client.brokers[broker.ID()] = broker + Logger.Printf("client/brokers registered new broker #%d at %s", broker.ID(), broker.Addr()) + } else if broker.Addr() != client.brokers[broker.ID()].Addr() { + safeAsyncClose(client.brokers[broker.ID()]) + client.brokers[broker.ID()] = broker + Logger.Printf("client/brokers replaced registered broker #%d with %s", broker.ID(), broker.Addr()) + } +} + +// deregisterBroker removes a broker from the seedsBroker list, and if it's +// not the seedbroker, removes it from brokers map completely. +func (client *client) deregisterBroker(broker *Broker) { + client.lock.Lock() + defer client.lock.Unlock() + + if len(client.seedBrokers) > 0 && broker == client.seedBrokers[0] { + client.deadSeeds = append(client.deadSeeds, broker) + client.seedBrokers = client.seedBrokers[1:] + } else { + // we do this so that our loop in `tryRefreshMetadata` doesn't go on forever, + // but we really shouldn't have to; once that loop is made better this case can be + // removed, and the function generally can be renamed from `deregisterBroker` to + // `nextSeedBroker` or something + Logger.Printf("client/brokers deregistered broker #%d at %s", broker.ID(), broker.Addr()) + delete(client.brokers, broker.ID()) + } +} + +func (client *client) resurrectDeadBrokers() { + client.lock.Lock() + defer client.lock.Unlock() + + Logger.Printf("client/brokers resurrecting %d dead seed brokers", len(client.deadSeeds)) + client.seedBrokers = append(client.seedBrokers, client.deadSeeds...) + client.deadSeeds = nil +} + +func (client *client) any() *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + + if len(client.seedBrokers) > 0 { + _ = client.seedBrokers[0].Open(client.conf) + return client.seedBrokers[0] + } + + // not guaranteed to be random *or* deterministic + for _, broker := range client.brokers { + _ = broker.Open(client.conf) + return broker + } + + return nil +} + +// private caching/lazy metadata helpers + +type partitionType int + +const ( + allPartitions partitionType = iota + writablePartitions + // If you add any more types, update the partition cache in update() + + // Ensure this is the last partition type value + maxPartitionIndex +) + +func (client *client) cachedMetadata(topic string, partitionID int32) *PartitionMetadata { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions := client.metadata[topic] + if partitions != nil { + return partitions[partitionID] + } + + return nil +} + +func (client *client) cachedPartitions(topic string, partitionSet partitionType) []int32 { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions, exists := client.cachedPartitionsResults[topic] + + if !exists { + return nil + } + return partitions[partitionSet] +} + +func (client *client) setPartitionCache(topic string, partitionSet partitionType) []int32 { + partitions := client.metadata[topic] + + if partitions == nil { + return nil + } + + ret := make([]int32, 0, len(partitions)) + for _, partition := range partitions { + if partitionSet == writablePartitions && partition.Err == ErrLeaderNotAvailable { + continue + } + ret = append(ret, partition.ID) + } + + sort.Sort(int32Slice(ret)) + return ret +} + +func (client *client) cachedLeader(topic string, partitionID int32) (*Broker, error) { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions := client.metadata[topic] + if partitions != nil { + metadata, ok := partitions[partitionID] + if ok { + if metadata.Err == ErrLeaderNotAvailable { + return nil, ErrLeaderNotAvailable + } + b := client.brokers[metadata.Leader] + if b == nil { + return nil, ErrLeaderNotAvailable + } + _ = b.Open(client.conf) + return b, nil + } + } + + return nil, ErrUnknownTopicOrPartition +} + +func (client *client) getOffset(topic string, partitionID int32, time int64) (int64, error) { + broker, err := client.Leader(topic, partitionID) + if err != nil { + return -1, err + } + + request := &OffsetRequest{} + if client.conf.Version.IsAtLeast(V0_10_1_0) { + request.Version = 1 + } + request.AddBlock(topic, partitionID, time, 1) + + response, err := broker.GetAvailableOffsets(request) + if err != nil { + _ = broker.Close() + return -1, err + } + + block := response.GetBlock(topic, partitionID) + if block == nil { + _ = broker.Close() + return -1, ErrIncompleteResponse + } + if block.Err != ErrNoError { + return -1, block.Err + } + if len(block.Offsets) != 1 { + return -1, ErrOffsetOutOfRange + } + + return block.Offsets[0], nil +} + +// core metadata update logic + +func (client *client) backgroundMetadataUpdater() { + defer close(client.closed) + + if client.conf.Metadata.RefreshFrequency == time.Duration(0) { + return + } + + ticker := time.NewTicker(client.conf.Metadata.RefreshFrequency) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := client.refreshMetadata(); err != nil { + Logger.Println("Client background metadata update:", err) + } + case <-client.closer: + return + } + } +} + +func (client *client) refreshMetadata() error { + topics := []string{} + + if !client.conf.Metadata.Full { + if specificTopics, err := client.MetadataTopics(); err != nil { + return err + } else if len(specificTopics) == 0 { + return ErrNoTopicsToUpdateMetadata + } else { + topics = specificTopics + } + } + + if err := client.RefreshMetadata(topics...); err != nil { + return err + } + + return nil +} + +func (client *client) tryRefreshMetadata(topics []string, attemptsRemaining int, deadline time.Time) error { + pastDeadline := func(backoff time.Duration) bool { + if !deadline.IsZero() && time.Now().Add(backoff).After(deadline) { + // we are past the deadline + return true + } + return false + } + retry := func(err error) error { + if attemptsRemaining > 0 { + backoff := client.computeBackoff(attemptsRemaining) + if pastDeadline(backoff) { + Logger.Println("client/metadata skipping last retries as we would go past the metadata timeout") + return err + } + Logger.Printf("client/metadata retrying after %dms... (%d attempts remaining)\n", client.conf.Metadata.Retry.Backoff/time.Millisecond, attemptsRemaining) + if backoff > 0 { + time.Sleep(backoff) + } + return client.tryRefreshMetadata(topics, attemptsRemaining-1, deadline) + } + return err + } + + broker := client.any() + for ; broker != nil && !pastDeadline(0); broker = client.any() { + allowAutoTopicCreation := true + if len(topics) > 0 { + Logger.Printf("client/metadata fetching metadata for %v from broker %s\n", topics, broker.addr) + } else { + allowAutoTopicCreation = false + Logger.Printf("client/metadata fetching metadata for all topics from broker %s\n", broker.addr) + } + + req := &MetadataRequest{Topics: topics, AllowAutoTopicCreation: allowAutoTopicCreation} + if client.conf.Version.IsAtLeast(V1_0_0_0) { + req.Version = 5 + } else if client.conf.Version.IsAtLeast(V0_10_0_0) { + req.Version = 1 + } + response, err := broker.GetMetadata(req) + switch err.(type) { + case nil: + allKnownMetaData := len(topics) == 0 + // valid response, use it + shouldRetry, err := client.updateMetadata(response, allKnownMetaData) + if shouldRetry { + Logger.Println("client/metadata found some partitions to be leaderless") + return retry(err) // note: err can be nil + } + return err + + case PacketEncodingError: + // didn't even send, return the error + return err + + case KError: + // if SASL auth error return as this _should_ be a non retryable err for all brokers + if err.(KError) == ErrSASLAuthenticationFailed { + Logger.Println("client/metadata failed SASL authentication") + return err + } + + if err.(KError) == ErrTopicAuthorizationFailed { + Logger.Println("client is not authorized to access this topic. The topics were: ", topics) + return err + } + // else remove that broker and try again + Logger.Printf("client/metadata got error from broker %d while fetching metadata: %v\n", broker.ID(), err) + _ = broker.Close() + client.deregisterBroker(broker) + + default: + // some other error, remove that broker and try again + Logger.Printf("client/metadata got error from broker %d while fetching metadata: %v\n", broker.ID(), err) + _ = broker.Close() + client.deregisterBroker(broker) + } + } + + if broker != nil { + Logger.Println("client/metadata not fetching metadata from broker %s as we would go past the metadata timeout\n", broker.addr) + return retry(ErrOutOfBrokers) + } + + Logger.Println("client/metadata no available broker to send metadata request to") + client.resurrectDeadBrokers() + return retry(ErrOutOfBrokers) +} + +// if no fatal error, returns a list of topics that need retrying due to ErrLeaderNotAvailable +func (client *client) updateMetadata(data *MetadataResponse, allKnownMetaData bool) (retry bool, err error) { + client.lock.Lock() + defer client.lock.Unlock() + + // For all the brokers we received: + // - if it is a new ID, save it + // - if it is an existing ID, but the address we have is stale, discard the old one and save it + // - otherwise ignore it, replacing our existing one would just bounce the connection + for _, broker := range data.Brokers { + client.registerBroker(broker) + } + + client.controllerID = data.ControllerID + + if allKnownMetaData { + client.metadata = make(map[string]map[int32]*PartitionMetadata) + client.metadataTopics = make(map[string]none) + client.cachedPartitionsResults = make(map[string][maxPartitionIndex][]int32) + } + for _, topic := range data.Topics { + // topics must be added firstly to `metadataTopics` to guarantee that all + // requested topics must be recorded to keep them trackable for periodically + // metadata refresh. + if _, exists := client.metadataTopics[topic.Name]; !exists { + client.metadataTopics[topic.Name] = none{} + } + delete(client.metadata, topic.Name) + delete(client.cachedPartitionsResults, topic.Name) + + switch topic.Err { + case ErrNoError: + // no-op + case ErrInvalidTopic, ErrTopicAuthorizationFailed: // don't retry, don't store partial results + err = topic.Err + continue + case ErrUnknownTopicOrPartition: // retry, do not store partial partition results + err = topic.Err + retry = true + continue + case ErrLeaderNotAvailable: // retry, but store partial partition results + retry = true + default: // don't retry, don't store partial results + Logger.Printf("Unexpected topic-level metadata error: %s", topic.Err) + err = topic.Err + continue + } + + client.metadata[topic.Name] = make(map[int32]*PartitionMetadata, len(topic.Partitions)) + for _, partition := range topic.Partitions { + client.metadata[topic.Name][partition.ID] = partition + if partition.Err == ErrLeaderNotAvailable { + retry = true + } + } + + var partitionCache [maxPartitionIndex][]int32 + partitionCache[allPartitions] = client.setPartitionCache(topic.Name, allPartitions) + partitionCache[writablePartitions] = client.setPartitionCache(topic.Name, writablePartitions) + client.cachedPartitionsResults[topic.Name] = partitionCache + } + + return +} + +func (client *client) cachedCoordinator(consumerGroup string) *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + if coordinatorID, ok := client.coordinators[consumerGroup]; ok { + return client.brokers[coordinatorID] + } + return nil +} + +func (client *client) cachedController() *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + + return client.brokers[client.controllerID] +} + +func (client *client) computeBackoff(attemptsRemaining int) time.Duration { + if client.conf.Metadata.Retry.BackoffFunc != nil { + maxRetries := client.conf.Metadata.Retry.Max + retries := maxRetries - attemptsRemaining + return client.conf.Metadata.Retry.BackoffFunc(retries, maxRetries) + } + return client.conf.Metadata.Retry.Backoff +} + +func (client *client) getConsumerMetadata(consumerGroup string, attemptsRemaining int) (*FindCoordinatorResponse, error) { + retry := func(err error) (*FindCoordinatorResponse, error) { + if attemptsRemaining > 0 { + backoff := client.computeBackoff(attemptsRemaining) + Logger.Printf("client/coordinator retrying after %dms... (%d attempts remaining)\n", backoff/time.Millisecond, attemptsRemaining) + time.Sleep(backoff) + return client.getConsumerMetadata(consumerGroup, attemptsRemaining-1) + } + return nil, err + } + + for broker := client.any(); broker != nil; broker = client.any() { + Logger.Printf("client/coordinator requesting coordinator for consumergroup %s from %s\n", consumerGroup, broker.Addr()) + + request := new(FindCoordinatorRequest) + request.CoordinatorKey = consumerGroup + request.CoordinatorType = CoordinatorGroup + + response, err := broker.FindCoordinator(request) + + if err != nil { + Logger.Printf("client/coordinator request to broker %s failed: %s\n", broker.Addr(), err) + + switch err.(type) { + case PacketEncodingError: + return nil, err + default: + _ = broker.Close() + client.deregisterBroker(broker) + continue + } + } + + switch response.Err { + case ErrNoError: + Logger.Printf("client/coordinator coordinator for consumergroup %s is #%d (%s)\n", consumerGroup, response.Coordinator.ID(), response.Coordinator.Addr()) + return response, nil + + case ErrConsumerCoordinatorNotAvailable: + Logger.Printf("client/coordinator coordinator for consumer group %s is not available\n", consumerGroup) + + // This is very ugly, but this scenario will only happen once per cluster. + // The __consumer_offsets topic only has to be created one time. + // The number of partitions not configurable, but partition 0 should always exist. + if _, err := client.Leader("__consumer_offsets", 0); err != nil { + Logger.Printf("client/coordinator the __consumer_offsets topic is not initialized completely yet. Waiting 2 seconds...\n") + time.Sleep(2 * time.Second) + } + + return retry(ErrConsumerCoordinatorNotAvailable) + case ErrGroupAuthorizationFailed: + Logger.Printf("client was not authorized to access group %s while attempting to find coordinator", consumerGroup) + return retry(ErrGroupAuthorizationFailed) + + default: + return nil, response.Err + } + } + + Logger.Println("client/coordinator no available broker to send consumer metadata request to") + client.resurrectDeadBrokers() + return retry(ErrOutOfBrokers) +} + +// nopCloserClient embeds an existing Client, but disables +// the Close method (yet all other methods pass +// through unchanged). This is for use in larger structs +// where it is undesirable to close the client that was +// passed in by the caller. +type nopCloserClient struct { + Client +} + +// Close intercepts and purposely does not call the underlying +// client's Close() method. +func (ncc *nopCloserClient) Close() error { + return nil +} diff --git a/vendor/github.com/Shopify/sarama/compress.go b/vendor/github.com/Shopify/sarama/compress.go new file mode 100644 index 0000000..9247c35 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/compress.go @@ -0,0 +1,75 @@ +package sarama + +import ( + "bytes" + "compress/gzip" + "fmt" + "sync" + + "github.com/eapache/go-xerial-snappy" + "github.com/pierrec/lz4" +) + +var ( + lz4WriterPool = sync.Pool{ + New: func() interface{} { + return lz4.NewWriter(nil) + }, + } + + gzipWriterPool = sync.Pool{ + New: func() interface{} { + return gzip.NewWriter(nil) + }, + } +) + +func compress(cc CompressionCodec, level int, data []byte) ([]byte, error) { + switch cc { + case CompressionNone: + return data, nil + case CompressionGZIP: + var ( + err error + buf bytes.Buffer + writer *gzip.Writer + ) + if level != CompressionLevelDefault { + writer, err = gzip.NewWriterLevel(&buf, level) + if err != nil { + return nil, err + } + } else { + writer = gzipWriterPool.Get().(*gzip.Writer) + defer gzipWriterPool.Put(writer) + writer.Reset(&buf) + } + if _, err := writer.Write(data); err != nil { + return nil, err + } + if err := writer.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil + case CompressionSnappy: + return snappy.Encode(data), nil + case CompressionLZ4: + writer := lz4WriterPool.Get().(*lz4.Writer) + defer lz4WriterPool.Put(writer) + + var buf bytes.Buffer + writer.Reset(&buf) + + if _, err := writer.Write(data); err != nil { + return nil, err + } + if err := writer.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil + case CompressionZSTD: + return zstdCompress(nil, data) + default: + return nil, PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", cc)} + } +} diff --git a/vendor/github.com/Shopify/sarama/config.go b/vendor/github.com/Shopify/sarama/config.go new file mode 100644 index 0000000..e515e04 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/config.go @@ -0,0 +1,695 @@ +package sarama + +import ( + "compress/gzip" + "crypto/tls" + "fmt" + "io/ioutil" + "net" + "regexp" + "time" + + "github.com/rcrowley/go-metrics" + "golang.org/x/net/proxy" +) + +const defaultClientID = "sarama" + +var validID = regexp.MustCompile(`\A[A-Za-z0-9._-]+\z`) + +// Config is used to pass multiple configuration options to Sarama's constructors. +type Config struct { + // Admin is the namespace for ClusterAdmin properties used by the administrative Kafka client. + Admin struct { + // The maximum duration the administrative Kafka client will wait for ClusterAdmin operations, + // including topics, brokers, configurations and ACLs (defaults to 3 seconds). + Timeout time.Duration + } + + // Net is the namespace for network-level properties used by the Broker, and + // shared by the Client/Producer/Consumer. + Net struct { + // How many outstanding requests a connection is allowed to have before + // sending on it blocks (default 5). + MaxOpenRequests int + + // All three of the below configurations are similar to the + // `socket.timeout.ms` setting in JVM kafka. All of them default + // to 30 seconds. + DialTimeout time.Duration // How long to wait for the initial connection. + ReadTimeout time.Duration // How long to wait for a response. + WriteTimeout time.Duration // How long to wait for a transmit. + + TLS struct { + // Whether or not to use TLS when connecting to the broker + // (defaults to false). + Enable bool + // The TLS configuration to use for secure connections if + // enabled (defaults to nil). + Config *tls.Config + } + + // SASL based authentication with broker. While there are multiple SASL authentication methods + // the current implementation is limited to plaintext (SASL/PLAIN) authentication + SASL struct { + // Whether or not to use SASL authentication when connecting to the broker + // (defaults to false). + Enable bool + // SASLMechanism is the name of the enabled SASL mechanism. + // Possible values: OAUTHBEARER, PLAIN (defaults to PLAIN). + Mechanism SASLMechanism + // Version is the SASL Protocol Version to use + // Kafka > 1.x should use V1, except on Azure EventHub which use V0 + Version int16 + // Whether or not to send the Kafka SASL handshake first if enabled + // (defaults to true). You should only set this to false if you're using + // a non-Kafka SASL proxy. + Handshake bool + //username and password for SASL/PLAIN or SASL/SCRAM authentication + User string + Password string + // authz id used for SASL/SCRAM authentication + SCRAMAuthzID string + // SCRAMClientGeneratorFunc is a generator of a user provided implementation of a SCRAM + // client used to perform the SCRAM exchange with the server. + SCRAMClientGeneratorFunc func() SCRAMClient + // TokenProvider is a user-defined callback for generating + // access tokens for SASL/OAUTHBEARER auth. See the + // AccessTokenProvider interface docs for proper implementation + // guidelines. + TokenProvider AccessTokenProvider + + GSSAPI GSSAPIConfig + } + + // KeepAlive specifies the keep-alive period for an active network connection. + // If zero, keep-alives are disabled. (default is 0: disabled). + KeepAlive time.Duration + + // LocalAddr is the local address to use when dialing an + // address. The address must be of a compatible type for the + // network being dialed. + // If nil, a local address is automatically chosen. + LocalAddr net.Addr + + Proxy struct { + // Whether or not to use proxy when connecting to the broker + // (defaults to false). + Enable bool + // The proxy dialer to use enabled (defaults to nil). + Dialer proxy.Dialer + } + } + + // Metadata is the namespace for metadata management properties used by the + // Client, and shared by the Producer/Consumer. + Metadata struct { + Retry struct { + // The total number of times to retry a metadata request when the + // cluster is in the middle of a leader election (default 3). + Max int + // How long to wait for leader election to occur before retrying + // (default 250ms). Similar to the JVM's `retry.backoff.ms`. + Backoff time.Duration + // Called to compute backoff time dynamically. Useful for implementing + // more sophisticated backoff strategies. This takes precedence over + // `Backoff` if set. + BackoffFunc func(retries, maxRetries int) time.Duration + } + // How frequently to refresh the cluster metadata in the background. + // Defaults to 10 minutes. Set to 0 to disable. Similar to + // `topic.metadata.refresh.interval.ms` in the JVM version. + RefreshFrequency time.Duration + + // Whether to maintain a full set of metadata for all topics, or just + // the minimal set that has been necessary so far. The full set is simpler + // and usually more convenient, but can take up a substantial amount of + // memory if you have many topics and partitions. Defaults to true. + Full bool + + // How long to wait for a successful metadata response. + // Disabled by default which means a metadata request against an unreachable + // cluster (all brokers are unreachable or unresponsive) can take up to + // `Net.[Dial|Read]Timeout * BrokerCount * (Metadata.Retry.Max + 1) + Metadata.Retry.Backoff * Metadata.Retry.Max` + // to fail. + Timeout time.Duration + } + + // Producer is the namespace for configuration related to producing messages, + // used by the Producer. + Producer struct { + // The maximum permitted size of a message (defaults to 1000000). Should be + // set equal to or smaller than the broker's `message.max.bytes`. + MaxMessageBytes int + // The level of acknowledgement reliability needed from the broker (defaults + // to WaitForLocal). Equivalent to the `request.required.acks` setting of the + // JVM producer. + RequiredAcks RequiredAcks + // The maximum duration the broker will wait the receipt of the number of + // RequiredAcks (defaults to 10 seconds). This is only relevant when + // RequiredAcks is set to WaitForAll or a number > 1. Only supports + // millisecond resolution, nanoseconds will be truncated. Equivalent to + // the JVM producer's `request.timeout.ms` setting. + Timeout time.Duration + // The type of compression to use on messages (defaults to no compression). + // Similar to `compression.codec` setting of the JVM producer. + Compression CompressionCodec + // The level of compression to use on messages. The meaning depends + // on the actual compression type used and defaults to default compression + // level for the codec. + CompressionLevel int + // Generates partitioners for choosing the partition to send messages to + // (defaults to hashing the message key). Similar to the `partitioner.class` + // setting for the JVM producer. + Partitioner PartitionerConstructor + // If enabled, the producer will ensure that exactly one copy of each message is + // written. + Idempotent bool + + // Return specifies what channels will be populated. If they are set to true, + // you must read from the respective channels to prevent deadlock. If, + // however, this config is used to create a `SyncProducer`, both must be set + // to true and you shall not read from the channels since the producer does + // this internally. + Return struct { + // If enabled, successfully delivered messages will be returned on the + // Successes channel (default disabled). + Successes bool + + // If enabled, messages that failed to deliver will be returned on the + // Errors channel, including error (default enabled). + Errors bool + } + + // The following config options control how often messages are batched up and + // sent to the broker. By default, messages are sent as fast as possible, and + // all messages received while the current batch is in-flight are placed + // into the subsequent batch. + Flush struct { + // The best-effort number of bytes needed to trigger a flush. Use the + // global sarama.MaxRequestSize to set a hard upper limit. + Bytes int + // The best-effort number of messages needed to trigger a flush. Use + // `MaxMessages` to set a hard upper limit. + Messages int + // The best-effort frequency of flushes. Equivalent to + // `queue.buffering.max.ms` setting of JVM producer. + Frequency time.Duration + // The maximum number of messages the producer will send in a single + // broker request. Defaults to 0 for unlimited. Similar to + // `queue.buffering.max.messages` in the JVM producer. + MaxMessages int + } + + Retry struct { + // The total number of times to retry sending a message (default 3). + // Similar to the `message.send.max.retries` setting of the JVM producer. + Max int + // How long to wait for the cluster to settle between retries + // (default 100ms). Similar to the `retry.backoff.ms` setting of the + // JVM producer. + Backoff time.Duration + // Called to compute backoff time dynamically. Useful for implementing + // more sophisticated backoff strategies. This takes precedence over + // `Backoff` if set. + BackoffFunc func(retries, maxRetries int) time.Duration + } + } + + // Consumer is the namespace for configuration related to consuming messages, + // used by the Consumer. + Consumer struct { + + // Group is the namespace for configuring consumer group. + Group struct { + Session struct { + // The timeout used to detect consumer failures when using Kafka's group management facility. + // The consumer sends periodic heartbeats to indicate its liveness to the broker. + // If no heartbeats are received by the broker before the expiration of this session timeout, + // then the broker will remove this consumer from the group and initiate a rebalance. + // Note that the value must be in the allowable range as configured in the broker configuration + // by `group.min.session.timeout.ms` and `group.max.session.timeout.ms` (default 10s) + Timeout time.Duration + } + Heartbeat struct { + // The expected time between heartbeats to the consumer coordinator when using Kafka's group + // management facilities. Heartbeats are used to ensure that the consumer's session stays active and + // to facilitate rebalancing when new consumers join or leave the group. + // The value must be set lower than Consumer.Group.Session.Timeout, but typically should be set no + // higher than 1/3 of that value. + // It can be adjusted even lower to control the expected time for normal rebalances (default 3s) + Interval time.Duration + } + Rebalance struct { + // Strategy for allocating topic partitions to members (default BalanceStrategyRange) + Strategy BalanceStrategy + // The maximum allowed time for each worker to join the group once a rebalance has begun. + // This is basically a limit on the amount of time needed for all tasks to flush any pending + // data and commit offsets. If the timeout is exceeded, then the worker will be removed from + // the group, which will cause offset commit failures (default 60s). + Timeout time.Duration + + Retry struct { + // When a new consumer joins a consumer group the set of consumers attempt to "rebalance" + // the load to assign partitions to each consumer. If the set of consumers changes while + // this assignment is taking place the rebalance will fail and retry. This setting controls + // the maximum number of attempts before giving up (default 4). + Max int + // Backoff time between retries during rebalance (default 2s) + Backoff time.Duration + } + } + Member struct { + // Custom metadata to include when joining the group. The user data for all joined members + // can be retrieved by sending a DescribeGroupRequest to the broker that is the + // coordinator for the group. + UserData []byte + } + } + + Retry struct { + // How long to wait after a failing to read from a partition before + // trying again (default 2s). + Backoff time.Duration + // Called to compute backoff time dynamically. Useful for implementing + // more sophisticated backoff strategies. This takes precedence over + // `Backoff` if set. + BackoffFunc func(retries int) time.Duration + } + + // Fetch is the namespace for controlling how many bytes are retrieved by any + // given request. + Fetch struct { + // The minimum number of message bytes to fetch in a request - the broker + // will wait until at least this many are available. The default is 1, + // as 0 causes the consumer to spin when no messages are available. + // Equivalent to the JVM's `fetch.min.bytes`. + Min int32 + // The default number of message bytes to fetch from the broker in each + // request (default 1MB). This should be larger than the majority of + // your messages, or else the consumer will spend a lot of time + // negotiating sizes and not actually consuming. Similar to the JVM's + // `fetch.message.max.bytes`. + Default int32 + // The maximum number of message bytes to fetch from the broker in a + // single request. Messages larger than this will return + // ErrMessageTooLarge and will not be consumable, so you must be sure + // this is at least as large as your largest message. Defaults to 0 + // (no limit). Similar to the JVM's `fetch.message.max.bytes`. The + // global `sarama.MaxResponseSize` still applies. + Max int32 + } + // The maximum amount of time the broker will wait for Consumer.Fetch.Min + // bytes to become available before it returns fewer than that anyways. The + // default is 250ms, since 0 causes the consumer to spin when no events are + // available. 100-500ms is a reasonable range for most cases. Kafka only + // supports precision up to milliseconds; nanoseconds will be truncated. + // Equivalent to the JVM's `fetch.wait.max.ms`. + MaxWaitTime time.Duration + + // The maximum amount of time the consumer expects a message takes to + // process for the user. If writing to the Messages channel takes longer + // than this, that partition will stop fetching more messages until it + // can proceed again. + // Note that, since the Messages channel is buffered, the actual grace time is + // (MaxProcessingTime * ChannelBufferSize). Defaults to 100ms. + // If a message is not written to the Messages channel between two ticks + // of the expiryTicker then a timeout is detected. + // Using a ticker instead of a timer to detect timeouts should typically + // result in many fewer calls to Timer functions which may result in a + // significant performance improvement if many messages are being sent + // and timeouts are infrequent. + // The disadvantage of using a ticker instead of a timer is that + // timeouts will be less accurate. That is, the effective timeout could + // be between `MaxProcessingTime` and `2 * MaxProcessingTime`. For + // example, if `MaxProcessingTime` is 100ms then a delay of 180ms + // between two messages being sent may not be recognized as a timeout. + MaxProcessingTime time.Duration + + // Return specifies what channels will be populated. If they are set to true, + // you must read from them to prevent deadlock. + Return struct { + // If enabled, any errors that occurred while consuming are returned on + // the Errors channel (default disabled). + Errors bool + } + + // Offsets specifies configuration for how and when to commit consumed + // offsets. This currently requires the manual use of an OffsetManager + // but will eventually be automated. + Offsets struct { + // How frequently to commit updated offsets. Defaults to 1s. + CommitInterval time.Duration + + // The initial offset to use if no offset was previously committed. + // Should be OffsetNewest or OffsetOldest. Defaults to OffsetNewest. + Initial int64 + + // The retention duration for committed offsets. If zero, disabled + // (in which case the `offsets.retention.minutes` option on the + // broker will be used). Kafka only supports precision up to + // milliseconds; nanoseconds will be truncated. Requires Kafka + // broker version 0.9.0 or later. + // (default is 0: disabled). + Retention time.Duration + + Retry struct { + // The total number of times to retry failing commit + // requests during OffsetManager shutdown (default 3). + Max int + } + } + + // IsolationLevel support 2 mode: + // - use `ReadUncommitted` (default) to consume and return all messages in message channel + // - use `ReadCommitted` to hide messages that are part of an aborted transaction + IsolationLevel IsolationLevel + } + + // A user-provided string sent with every request to the brokers for logging, + // debugging, and auditing purposes. Defaults to "sarama", but you should + // probably set it to something specific to your application. + ClientID string + // The number of events to buffer in internal and external channels. This + // permits the producer and consumer to continue processing some messages + // in the background while user code is working, greatly improving throughput. + // Defaults to 256. + ChannelBufferSize int + // The version of Kafka that Sarama will assume it is running against. + // Defaults to the oldest supported stable version. Since Kafka provides + // backwards-compatibility, setting it to a version older than you have + // will not break anything, although it may prevent you from using the + // latest features. Setting it to a version greater than you are actually + // running may lead to random breakage. + Version KafkaVersion + // The registry to define metrics into. + // Defaults to a local registry. + // If you want to disable metrics gathering, set "metrics.UseNilMetrics" to "true" + // prior to starting Sarama. + // See Examples on how to use the metrics registry + MetricRegistry metrics.Registry +} + +// NewConfig returns a new configuration instance with sane defaults. +func NewConfig() *Config { + c := &Config{} + + c.Admin.Timeout = 3 * time.Second + + c.Net.MaxOpenRequests = 5 + c.Net.DialTimeout = 30 * time.Second + c.Net.ReadTimeout = 30 * time.Second + c.Net.WriteTimeout = 30 * time.Second + c.Net.SASL.Handshake = true + c.Net.SASL.Version = SASLHandshakeV0 + + c.Metadata.Retry.Max = 3 + c.Metadata.Retry.Backoff = 250 * time.Millisecond + c.Metadata.RefreshFrequency = 10 * time.Minute + c.Metadata.Full = true + + c.Producer.MaxMessageBytes = 1000000 + c.Producer.RequiredAcks = WaitForLocal + c.Producer.Timeout = 10 * time.Second + c.Producer.Partitioner = NewHashPartitioner + c.Producer.Retry.Max = 3 + c.Producer.Retry.Backoff = 100 * time.Millisecond + c.Producer.Return.Errors = true + c.Producer.CompressionLevel = CompressionLevelDefault + + c.Consumer.Fetch.Min = 1 + c.Consumer.Fetch.Default = 1024 * 1024 + c.Consumer.Retry.Backoff = 2 * time.Second + c.Consumer.MaxWaitTime = 250 * time.Millisecond + c.Consumer.MaxProcessingTime = 100 * time.Millisecond + c.Consumer.Return.Errors = false + c.Consumer.Offsets.CommitInterval = 1 * time.Second + c.Consumer.Offsets.Initial = OffsetNewest + c.Consumer.Offsets.Retry.Max = 3 + + c.Consumer.Group.Session.Timeout = 10 * time.Second + c.Consumer.Group.Heartbeat.Interval = 3 * time.Second + c.Consumer.Group.Rebalance.Strategy = BalanceStrategyRange + c.Consumer.Group.Rebalance.Timeout = 60 * time.Second + c.Consumer.Group.Rebalance.Retry.Max = 4 + c.Consumer.Group.Rebalance.Retry.Backoff = 2 * time.Second + + c.ClientID = defaultClientID + c.ChannelBufferSize = 256 + c.Version = MinVersion + c.MetricRegistry = metrics.NewRegistry() + + return c +} + +// Validate checks a Config instance. It will return a +// ConfigurationError if the specified values don't make sense. +func (c *Config) Validate() error { + // some configuration values should be warned on but not fail completely, do those first + if !c.Net.TLS.Enable && c.Net.TLS.Config != nil { + Logger.Println("Net.TLS is disabled but a non-nil configuration was provided.") + } + if !c.Net.SASL.Enable { + if c.Net.SASL.User != "" { + Logger.Println("Net.SASL is disabled but a non-empty username was provided.") + } + if c.Net.SASL.Password != "" { + Logger.Println("Net.SASL is disabled but a non-empty password was provided.") + } + } + if c.Producer.RequiredAcks > 1 { + Logger.Println("Producer.RequiredAcks > 1 is deprecated and will raise an exception with kafka >= 0.8.2.0.") + } + if c.Producer.MaxMessageBytes >= int(MaxRequestSize) { + Logger.Println("Producer.MaxMessageBytes must be smaller than MaxRequestSize; it will be ignored.") + } + if c.Producer.Flush.Bytes >= int(MaxRequestSize) { + Logger.Println("Producer.Flush.Bytes must be smaller than MaxRequestSize; it will be ignored.") + } + if (c.Producer.Flush.Bytes > 0 || c.Producer.Flush.Messages > 0) && c.Producer.Flush.Frequency == 0 { + Logger.Println("Producer.Flush: Bytes or Messages are set, but Frequency is not; messages may not get flushed.") + } + if c.Producer.Timeout%time.Millisecond != 0 { + Logger.Println("Producer.Timeout only supports millisecond resolution; nanoseconds will be truncated.") + } + if c.Consumer.MaxWaitTime < 100*time.Millisecond { + Logger.Println("Consumer.MaxWaitTime is very low, which can cause high CPU and network usage. See documentation for details.") + } + if c.Consumer.MaxWaitTime%time.Millisecond != 0 { + Logger.Println("Consumer.MaxWaitTime only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Offsets.Retention%time.Millisecond != 0 { + Logger.Println("Consumer.Offsets.Retention only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Session.Timeout%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Session.Timeout only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Heartbeat.Interval%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Heartbeat.Interval only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Rebalance.Timeout%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Rebalance.Timeout only supports millisecond precision; nanoseconds will be truncated.") + } + if c.ClientID == defaultClientID { + Logger.Println("ClientID is the default of 'sarama', you should consider setting it to something application-specific.") + } + + // validate Net values + switch { + case c.Net.MaxOpenRequests <= 0: + return ConfigurationError("Net.MaxOpenRequests must be > 0") + case c.Net.DialTimeout <= 0: + return ConfigurationError("Net.DialTimeout must be > 0") + case c.Net.ReadTimeout <= 0: + return ConfigurationError("Net.ReadTimeout must be > 0") + case c.Net.WriteTimeout <= 0: + return ConfigurationError("Net.WriteTimeout must be > 0") + case c.Net.KeepAlive < 0: + return ConfigurationError("Net.KeepAlive must be >= 0") + case c.Net.SASL.Enable: + if c.Net.SASL.Mechanism == "" { + c.Net.SASL.Mechanism = SASLTypePlaintext + } + + switch c.Net.SASL.Mechanism { + case SASLTypePlaintext: + if c.Net.SASL.User == "" { + return ConfigurationError("Net.SASL.User must not be empty when SASL is enabled") + } + if c.Net.SASL.Password == "" { + return ConfigurationError("Net.SASL.Password must not be empty when SASL is enabled") + } + case SASLTypeOAuth: + if c.Net.SASL.TokenProvider == nil { + return ConfigurationError("An AccessTokenProvider instance must be provided to Net.SASL.TokenProvider") + } + case SASLTypeSCRAMSHA256, SASLTypeSCRAMSHA512: + if c.Net.SASL.User == "" { + return ConfigurationError("Net.SASL.User must not be empty when SASL is enabled") + } + if c.Net.SASL.Password == "" { + return ConfigurationError("Net.SASL.Password must not be empty when SASL is enabled") + } + if c.Net.SASL.SCRAMClientGeneratorFunc == nil { + return ConfigurationError("A SCRAMClientGeneratorFunc function must be provided to Net.SASL.SCRAMClientGeneratorFunc") + } + case SASLTypeGSSAPI: + if c.Net.SASL.GSSAPI.ServiceName == "" { + return ConfigurationError("Net.SASL.GSSAPI.ServiceName must not be empty when GSS-API mechanism is used") + } + + if c.Net.SASL.GSSAPI.AuthType == KRB5_USER_AUTH { + if c.Net.SASL.GSSAPI.Password == "" { + return ConfigurationError("Net.SASL.GSSAPI.Password must not be empty when GSS-API " + + "mechanism is used and Net.SASL.GSSAPI.AuthType = KRB5_USER_AUTH") + } + } else if c.Net.SASL.GSSAPI.AuthType == KRB5_KEYTAB_AUTH { + if c.Net.SASL.GSSAPI.KeyTabPath == "" { + return ConfigurationError("Net.SASL.GSSAPI.KeyTabPath must not be empty when GSS-API mechanism is used" + + " and Net.SASL.GSSAPI.AuthType = KRB5_KEYTAB_AUTH") + } + } else { + return ConfigurationError("Net.SASL.GSSAPI.AuthType is invalid. Possible values are KRB5_USER_AUTH and KRB5_KEYTAB_AUTH") + } + if c.Net.SASL.GSSAPI.KerberosConfigPath == "" { + return ConfigurationError("Net.SASL.GSSAPI.KerberosConfigPath must not be empty when GSS-API mechanism is used") + } + if c.Net.SASL.GSSAPI.Username == "" { + return ConfigurationError("Net.SASL.GSSAPI.Username must not be empty when GSS-API mechanism is used") + } + if c.Net.SASL.GSSAPI.Realm == "" { + return ConfigurationError("Net.SASL.GSSAPI.Realm must not be empty when GSS-API mechanism is used") + } + default: + msg := fmt.Sprintf("The SASL mechanism configuration is invalid. Possible values are `%s`, `%s`, `%s`, `%s` and `%s`", + SASLTypeOAuth, SASLTypePlaintext, SASLTypeSCRAMSHA256, SASLTypeSCRAMSHA512, SASLTypeGSSAPI) + return ConfigurationError(msg) + } + } + + // validate the Admin values + switch { + case c.Admin.Timeout <= 0: + return ConfigurationError("Admin.Timeout must be > 0") + } + + // validate the Metadata values + switch { + case c.Metadata.Retry.Max < 0: + return ConfigurationError("Metadata.Retry.Max must be >= 0") + case c.Metadata.Retry.Backoff < 0: + return ConfigurationError("Metadata.Retry.Backoff must be >= 0") + case c.Metadata.RefreshFrequency < 0: + return ConfigurationError("Metadata.RefreshFrequency must be >= 0") + } + + // validate the Producer values + switch { + case c.Producer.MaxMessageBytes <= 0: + return ConfigurationError("Producer.MaxMessageBytes must be > 0") + case c.Producer.RequiredAcks < -1: + return ConfigurationError("Producer.RequiredAcks must be >= -1") + case c.Producer.Timeout <= 0: + return ConfigurationError("Producer.Timeout must be > 0") + case c.Producer.Partitioner == nil: + return ConfigurationError("Producer.Partitioner must not be nil") + case c.Producer.Flush.Bytes < 0: + return ConfigurationError("Producer.Flush.Bytes must be >= 0") + case c.Producer.Flush.Messages < 0: + return ConfigurationError("Producer.Flush.Messages must be >= 0") + case c.Producer.Flush.Frequency < 0: + return ConfigurationError("Producer.Flush.Frequency must be >= 0") + case c.Producer.Flush.MaxMessages < 0: + return ConfigurationError("Producer.Flush.MaxMessages must be >= 0") + case c.Producer.Flush.MaxMessages > 0 && c.Producer.Flush.MaxMessages < c.Producer.Flush.Messages: + return ConfigurationError("Producer.Flush.MaxMessages must be >= Producer.Flush.Messages when set") + case c.Producer.Retry.Max < 0: + return ConfigurationError("Producer.Retry.Max must be >= 0") + case c.Producer.Retry.Backoff < 0: + return ConfigurationError("Producer.Retry.Backoff must be >= 0") + } + + if c.Producer.Compression == CompressionLZ4 && !c.Version.IsAtLeast(V0_10_0_0) { + return ConfigurationError("lz4 compression requires Version >= V0_10_0_0") + } + + if c.Producer.Compression == CompressionGZIP { + if c.Producer.CompressionLevel != CompressionLevelDefault { + if _, err := gzip.NewWriterLevel(ioutil.Discard, c.Producer.CompressionLevel); err != nil { + return ConfigurationError(fmt.Sprintf("gzip compression does not work with level %d: %v", c.Producer.CompressionLevel, err)) + } + } + } + + if c.Producer.Idempotent { + if !c.Version.IsAtLeast(V0_11_0_0) { + return ConfigurationError("Idempotent producer requires Version >= V0_11_0_0") + } + if c.Producer.Retry.Max == 0 { + return ConfigurationError("Idempotent producer requires Producer.Retry.Max >= 1") + } + if c.Producer.RequiredAcks != WaitForAll { + return ConfigurationError("Idempotent producer requires Producer.RequiredAcks to be WaitForAll") + } + if c.Net.MaxOpenRequests > 1 { + return ConfigurationError("Idempotent producer requires Net.MaxOpenRequests to be 1") + } + } + + // validate the Consumer values + switch { + case c.Consumer.Fetch.Min <= 0: + return ConfigurationError("Consumer.Fetch.Min must be > 0") + case c.Consumer.Fetch.Default <= 0: + return ConfigurationError("Consumer.Fetch.Default must be > 0") + case c.Consumer.Fetch.Max < 0: + return ConfigurationError("Consumer.Fetch.Max must be >= 0") + case c.Consumer.MaxWaitTime < 1*time.Millisecond: + return ConfigurationError("Consumer.MaxWaitTime must be >= 1ms") + case c.Consumer.MaxProcessingTime <= 0: + return ConfigurationError("Consumer.MaxProcessingTime must be > 0") + case c.Consumer.Retry.Backoff < 0: + return ConfigurationError("Consumer.Retry.Backoff must be >= 0") + case c.Consumer.Offsets.CommitInterval <= 0: + return ConfigurationError("Consumer.Offsets.CommitInterval must be > 0") + case c.Consumer.Offsets.Initial != OffsetOldest && c.Consumer.Offsets.Initial != OffsetNewest: + return ConfigurationError("Consumer.Offsets.Initial must be OffsetOldest or OffsetNewest") + case c.Consumer.Offsets.Retry.Max < 0: + return ConfigurationError("Consumer.Offsets.Retry.Max must be >= 0") + case c.Consumer.IsolationLevel != ReadUncommitted && c.Consumer.IsolationLevel != ReadCommitted: + return ConfigurationError("Consumer.IsolationLevel must be ReadUncommitted or ReadCommitted") + } + + // validate IsolationLevel + if c.Consumer.IsolationLevel == ReadCommitted && !c.Version.IsAtLeast(V0_11_0_0) { + return ConfigurationError("ReadCommitted requires Version >= V0_11_0_0") + } + + // validate the Consumer Group values + switch { + case c.Consumer.Group.Session.Timeout <= 2*time.Millisecond: + return ConfigurationError("Consumer.Group.Session.Timeout must be >= 2ms") + case c.Consumer.Group.Heartbeat.Interval < 1*time.Millisecond: + return ConfigurationError("Consumer.Group.Heartbeat.Interval must be >= 1ms") + case c.Consumer.Group.Heartbeat.Interval >= c.Consumer.Group.Session.Timeout: + return ConfigurationError("Consumer.Group.Heartbeat.Interval must be < Consumer.Group.Session.Timeout") + case c.Consumer.Group.Rebalance.Strategy == nil: + return ConfigurationError("Consumer.Group.Rebalance.Strategy must not be empty") + case c.Consumer.Group.Rebalance.Timeout <= time.Millisecond: + return ConfigurationError("Consumer.Group.Rebalance.Timeout must be >= 1ms") + case c.Consumer.Group.Rebalance.Retry.Max < 0: + return ConfigurationError("Consumer.Group.Rebalance.Retry.Max must be >= 0") + case c.Consumer.Group.Rebalance.Retry.Backoff < 0: + return ConfigurationError("Consumer.Group.Rebalance.Retry.Backoff must be >= 0") + } + + // validate misc shared values + switch { + case c.ChannelBufferSize < 0: + return ConfigurationError("ChannelBufferSize must be >= 0") + case !validID.MatchString(c.ClientID): + return ConfigurationError("ClientID is invalid") + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/config_resource_type.go b/vendor/github.com/Shopify/sarama/config_resource_type.go new file mode 100644 index 0000000..5399d75 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/config_resource_type.go @@ -0,0 +1,22 @@ +package sarama + +//ConfigResourceType is a type for config resource +type ConfigResourceType int8 + +// Taken from : +// https://cwiki.apache.org/confluence/display/KAFKA/KIP-133%3A+Describe+and+Alter+Configs+Admin+APIs#KIP-133:DescribeandAlterConfigsAdminAPIs-WireFormattypes + +const ( + //UnknownResource constant type + UnknownResource ConfigResourceType = iota + //AnyResource constant type + AnyResource + //TopicResource constant type + TopicResource + //GroupResource constant type + GroupResource + //ClusterResource constant type + ClusterResource + //BrokerResource constant type + BrokerResource +) diff --git a/vendor/github.com/Shopify/sarama/consumer.go b/vendor/github.com/Shopify/sarama/consumer.go new file mode 100644 index 0000000..72c4d7c --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer.go @@ -0,0 +1,896 @@ +package sarama + +import ( + "errors" + "fmt" + "math" + "sync" + "sync/atomic" + "time" + + "github.com/rcrowley/go-metrics" +) + +// ConsumerMessage encapsulates a Kafka message returned by the consumer. +type ConsumerMessage struct { + Headers []*RecordHeader // only set if kafka is version 0.11+ + Timestamp time.Time // only set if kafka is version 0.10+, inner message timestamp + BlockTimestamp time.Time // only set if kafka is version 0.10+, outer (compressed) block timestamp + + Key, Value []byte + Topic string + Partition int32 + Offset int64 +} + +// ConsumerError is what is provided to the user when an error occurs. +// It wraps an error and includes the topic and partition. +type ConsumerError struct { + Topic string + Partition int32 + Err error +} + +func (ce ConsumerError) Error() string { + return fmt.Sprintf("kafka: error while consuming %s/%d: %s", ce.Topic, ce.Partition, ce.Err) +} + +// ConsumerErrors is a type that wraps a batch of errors and implements the Error interface. +// It can be returned from the PartitionConsumer's Close methods to avoid the need to manually drain errors +// when stopping. +type ConsumerErrors []*ConsumerError + +func (ce ConsumerErrors) Error() string { + return fmt.Sprintf("kafka: %d errors while consuming", len(ce)) +} + +// Consumer manages PartitionConsumers which process Kafka messages from brokers. You MUST call Close() +// on a consumer to avoid leaks, it will not be garbage-collected automatically when it passes out of +// scope. +type Consumer interface { + // Topics returns the set of available topics as retrieved from the cluster + // metadata. This method is the same as Client.Topics(), and is provided for + // convenience. + Topics() ([]string, error) + + // Partitions returns the sorted list of all partition IDs for the given topic. + // This method is the same as Client.Partitions(), and is provided for convenience. + Partitions(topic string) ([]int32, error) + + // ConsumePartition creates a PartitionConsumer on the given topic/partition with + // the given offset. It will return an error if this Consumer is already consuming + // on the given topic/partition. Offset can be a literal offset, or OffsetNewest + // or OffsetOldest + ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) + + // HighWaterMarks returns the current high water marks for each topic and partition. + // Consistency between partitions is not guaranteed since high water marks are updated separately. + HighWaterMarks() map[string]map[int32]int64 + + // Close shuts down the consumer. It must be called after all child + // PartitionConsumers have already been closed. + Close() error +} + +type consumer struct { + conf *Config + children map[string]map[int32]*partitionConsumer + brokerConsumers map[*Broker]*brokerConsumer + client Client + lock sync.Mutex +} + +// NewConsumer creates a new consumer using the given broker addresses and configuration. +func NewConsumer(addrs []string, config *Config) (Consumer, error) { + client, err := NewClient(addrs, config) + if err != nil { + return nil, err + } + return newConsumer(client) +} + +// NewConsumerFromClient creates a new consumer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this consumer. +func NewConsumerFromClient(client Client) (Consumer, error) { + // For clients passed in by the client, ensure we don't + // call Close() on it. + cli := &nopCloserClient{client} + return newConsumer(cli) +} + +func newConsumer(client Client) (Consumer, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + c := &consumer{ + client: client, + conf: client.Config(), + children: make(map[string]map[int32]*partitionConsumer), + brokerConsumers: make(map[*Broker]*brokerConsumer), + } + + return c, nil +} + +func (c *consumer) Close() error { + return c.client.Close() +} + +func (c *consumer) Topics() ([]string, error) { + return c.client.Topics() +} + +func (c *consumer) Partitions(topic string) ([]int32, error) { + return c.client.Partitions(topic) +} + +func (c *consumer) ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) { + child := &partitionConsumer{ + consumer: c, + conf: c.conf, + topic: topic, + partition: partition, + messages: make(chan *ConsumerMessage, c.conf.ChannelBufferSize), + errors: make(chan *ConsumerError, c.conf.ChannelBufferSize), + feeder: make(chan *FetchResponse, 1), + trigger: make(chan none, 1), + dying: make(chan none), + fetchSize: c.conf.Consumer.Fetch.Default, + } + + if err := child.chooseStartingOffset(offset); err != nil { + return nil, err + } + + var leader *Broker + var err error + if leader, err = c.client.Leader(child.topic, child.partition); err != nil { + return nil, err + } + + if err := c.addChild(child); err != nil { + return nil, err + } + + go withRecover(child.dispatcher) + go withRecover(child.responseFeeder) + + child.broker = c.refBrokerConsumer(leader) + child.broker.input <- child + + return child, nil +} + +func (c *consumer) HighWaterMarks() map[string]map[int32]int64 { + c.lock.Lock() + defer c.lock.Unlock() + + hwms := make(map[string]map[int32]int64) + for topic, p := range c.children { + hwm := make(map[int32]int64, len(p)) + for partition, pc := range p { + hwm[partition] = pc.HighWaterMarkOffset() + } + hwms[topic] = hwm + } + + return hwms +} + +func (c *consumer) addChild(child *partitionConsumer) error { + c.lock.Lock() + defer c.lock.Unlock() + + topicChildren := c.children[child.topic] + if topicChildren == nil { + topicChildren = make(map[int32]*partitionConsumer) + c.children[child.topic] = topicChildren + } + + if topicChildren[child.partition] != nil { + return ConfigurationError("That topic/partition is already being consumed") + } + + topicChildren[child.partition] = child + return nil +} + +func (c *consumer) removeChild(child *partitionConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + delete(c.children[child.topic], child.partition) +} + +func (c *consumer) refBrokerConsumer(broker *Broker) *brokerConsumer { + c.lock.Lock() + defer c.lock.Unlock() + + bc := c.brokerConsumers[broker] + if bc == nil { + bc = c.newBrokerConsumer(broker) + c.brokerConsumers[broker] = bc + } + + bc.refs++ + + return bc +} + +func (c *consumer) unrefBrokerConsumer(brokerWorker *brokerConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + brokerWorker.refs-- + + if brokerWorker.refs == 0 { + close(brokerWorker.input) + if c.brokerConsumers[brokerWorker.broker] == brokerWorker { + delete(c.brokerConsumers, brokerWorker.broker) + } + } +} + +func (c *consumer) abandonBrokerConsumer(brokerWorker *brokerConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + delete(c.brokerConsumers, brokerWorker.broker) +} + +// PartitionConsumer + +// PartitionConsumer processes Kafka messages from a given topic and partition. You MUST call one of Close() or +// AsyncClose() on a PartitionConsumer to avoid leaks; it will not be garbage-collected automatically when it passes out +// of scope. +// +// The simplest way of using a PartitionConsumer is to loop over its Messages channel using a for/range +// loop. The PartitionConsumer will only stop itself in one case: when the offset being consumed is reported +// as out of range by the brokers. In this case you should decide what you want to do (try a different offset, +// notify a human, etc) and handle it appropriately. For all other error cases, it will just keep retrying. +// By default, it logs these errors to sarama.Logger; if you want to be notified directly of all errors, set +// your config's Consumer.Return.Errors to true and read from the Errors channel, using a select statement +// or a separate goroutine. Check out the Consumer examples to see implementations of these different approaches. +// +// To terminate such a for/range loop while the loop is executing, call AsyncClose. This will kick off the process of +// consumer tear-down & return immediately. Continue to loop, servicing the Messages channel until the teardown process +// AsyncClose initiated closes it (thus terminating the for/range loop). If you've already ceased reading Messages, call +// Close; this will signal the PartitionConsumer's goroutines to begin shutting down (just like AsyncClose), but will +// also drain the Messages channel, harvest all errors & return them once cleanup has completed. +type PartitionConsumer interface { + // AsyncClose initiates a shutdown of the PartitionConsumer. This method will return immediately, after which you + // should continue to service the 'Messages' and 'Errors' channels until they are empty. It is required to call this + // function, or Close before a consumer object passes out of scope, as it will otherwise leak memory. You must call + // this before calling Close on the underlying client. + AsyncClose() + + // Close stops the PartitionConsumer from fetching messages. It will initiate a shutdown just like AsyncClose, drain + // the Messages channel, harvest any errors & return them to the caller. Note that if you are continuing to service + // the Messages channel when this function is called, you will be competing with Close for messages; consider + // calling AsyncClose, instead. It is required to call this function (or AsyncClose) before a consumer object passes + // out of scope, as it will otherwise leak memory. You must call this before calling Close on the underlying client. + Close() error + + // Messages returns the read channel for the messages that are returned by + // the broker. + Messages() <-chan *ConsumerMessage + + // Errors returns a read channel of errors that occurred during consuming, if + // enabled. By default, errors are logged and not returned over this channel. + // If you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan *ConsumerError + + // HighWaterMarkOffset returns the high water mark offset of the partition, + // i.e. the offset that will be used for the next message that will be produced. + // You can use this to determine how far behind the processing is. + HighWaterMarkOffset() int64 +} + +type partitionConsumer struct { + highWaterMarkOffset int64 // must be at the top of the struct because https://golang.org/pkg/sync/atomic/#pkg-note-BUG + + consumer *consumer + conf *Config + broker *brokerConsumer + messages chan *ConsumerMessage + errors chan *ConsumerError + feeder chan *FetchResponse + + trigger, dying chan none + closeOnce sync.Once + topic string + partition int32 + responseResult error + fetchSize int32 + offset int64 + retries int32 +} + +var errTimedOut = errors.New("timed out feeding messages to the user") // not user-facing + +func (child *partitionConsumer) sendError(err error) { + cErr := &ConsumerError{ + Topic: child.topic, + Partition: child.partition, + Err: err, + } + + if child.conf.Consumer.Return.Errors { + child.errors <- cErr + } else { + Logger.Println(cErr) + } +} + +func (child *partitionConsumer) computeBackoff() time.Duration { + if child.conf.Consumer.Retry.BackoffFunc != nil { + retries := atomic.AddInt32(&child.retries, 1) + return child.conf.Consumer.Retry.BackoffFunc(int(retries)) + } + return child.conf.Consumer.Retry.Backoff +} + +func (child *partitionConsumer) dispatcher() { + for range child.trigger { + select { + case <-child.dying: + close(child.trigger) + case <-time.After(child.computeBackoff()): + if child.broker != nil { + child.consumer.unrefBrokerConsumer(child.broker) + child.broker = nil + } + + Logger.Printf("consumer/%s/%d finding new broker\n", child.topic, child.partition) + if err := child.dispatch(); err != nil { + child.sendError(err) + child.trigger <- none{} + } + } + } + + if child.broker != nil { + child.consumer.unrefBrokerConsumer(child.broker) + } + child.consumer.removeChild(child) + close(child.feeder) +} + +func (child *partitionConsumer) dispatch() error { + if err := child.consumer.client.RefreshMetadata(child.topic); err != nil { + return err + } + + var leader *Broker + var err error + if leader, err = child.consumer.client.Leader(child.topic, child.partition); err != nil { + return err + } + + child.broker = child.consumer.refBrokerConsumer(leader) + + child.broker.input <- child + + return nil +} + +func (child *partitionConsumer) chooseStartingOffset(offset int64) error { + newestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetNewest) + if err != nil { + return err + } + oldestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetOldest) + if err != nil { + return err + } + + switch { + case offset == OffsetNewest: + child.offset = newestOffset + case offset == OffsetOldest: + child.offset = oldestOffset + case offset >= oldestOffset && offset <= newestOffset: + child.offset = offset + default: + return ErrOffsetOutOfRange + } + + return nil +} + +func (child *partitionConsumer) Messages() <-chan *ConsumerMessage { + return child.messages +} + +func (child *partitionConsumer) Errors() <-chan *ConsumerError { + return child.errors +} + +func (child *partitionConsumer) AsyncClose() { + // this triggers whatever broker owns this child to abandon it and close its trigger channel, which causes + // the dispatcher to exit its loop, which removes it from the consumer then closes its 'messages' and + // 'errors' channel (alternatively, if the child is already at the dispatcher for some reason, that will + // also just close itself) + child.closeOnce.Do(func() { + close(child.dying) + }) +} + +func (child *partitionConsumer) Close() error { + child.AsyncClose() + + var errors ConsumerErrors + for err := range child.errors { + errors = append(errors, err) + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (child *partitionConsumer) HighWaterMarkOffset() int64 { + return atomic.LoadInt64(&child.highWaterMarkOffset) +} + +func (child *partitionConsumer) responseFeeder() { + var msgs []*ConsumerMessage + expiryTicker := time.NewTicker(child.conf.Consumer.MaxProcessingTime) + firstAttempt := true + +feederLoop: + for response := range child.feeder { + msgs, child.responseResult = child.parseResponse(response) + + if child.responseResult == nil { + atomic.StoreInt32(&child.retries, 0) + } + + for i, msg := range msgs { + messageSelect: + select { + case <-child.dying: + child.broker.acks.Done() + continue feederLoop + case child.messages <- msg: + firstAttempt = true + case <-expiryTicker.C: + if !firstAttempt { + child.responseResult = errTimedOut + child.broker.acks.Done() + remainingLoop: + for _, msg = range msgs[i:] { + select { + case child.messages <- msg: + case <-child.dying: + break remainingLoop + } + } + child.broker.input <- child + continue feederLoop + } else { + // current message has not been sent, return to select + // statement + firstAttempt = false + goto messageSelect + } + } + } + + child.broker.acks.Done() + } + + expiryTicker.Stop() + close(child.messages) + close(child.errors) +} + +func (child *partitionConsumer) parseMessages(msgSet *MessageSet) ([]*ConsumerMessage, error) { + var messages []*ConsumerMessage + for _, msgBlock := range msgSet.Messages { + for _, msg := range msgBlock.Messages() { + offset := msg.Offset + timestamp := msg.Msg.Timestamp + if msg.Msg.Version >= 1 { + baseOffset := msgBlock.Offset - msgBlock.Messages()[len(msgBlock.Messages())-1].Offset + offset += baseOffset + if msg.Msg.LogAppendTime { + timestamp = msgBlock.Msg.Timestamp + } + } + if offset < child.offset { + continue + } + messages = append(messages, &ConsumerMessage{ + Topic: child.topic, + Partition: child.partition, + Key: msg.Msg.Key, + Value: msg.Msg.Value, + Offset: offset, + Timestamp: timestamp, + BlockTimestamp: msgBlock.Msg.Timestamp, + }) + child.offset = offset + 1 + } + } + if len(messages) == 0 { + child.offset++ + } + return messages, nil +} + +func (child *partitionConsumer) parseRecords(batch *RecordBatch) ([]*ConsumerMessage, error) { + messages := make([]*ConsumerMessage, 0, len(batch.Records)) + + for _, rec := range batch.Records { + offset := batch.FirstOffset + rec.OffsetDelta + if offset < child.offset { + continue + } + timestamp := batch.FirstTimestamp.Add(rec.TimestampDelta) + if batch.LogAppendTime { + timestamp = batch.MaxTimestamp + } + messages = append(messages, &ConsumerMessage{ + Topic: child.topic, + Partition: child.partition, + Key: rec.Key, + Value: rec.Value, + Offset: offset, + Timestamp: timestamp, + Headers: rec.Headers, + }) + child.offset = offset + 1 + } + if len(messages) == 0 { + child.offset++ + } + return messages, nil +} + +func (child *partitionConsumer) parseResponse(response *FetchResponse) ([]*ConsumerMessage, error) { + var ( + metricRegistry = child.conf.MetricRegistry + consumerBatchSizeMetric metrics.Histogram + ) + + if metricRegistry != nil { + consumerBatchSizeMetric = getOrRegisterHistogram("consumer-batch-size", metricRegistry) + } + + // If request was throttled and empty we log and return without error + if response.ThrottleTime != time.Duration(0) && len(response.Blocks) == 0 { + Logger.Printf( + "consumer/broker/%d FetchResponse throttled %v\n", + child.broker.broker.ID(), response.ThrottleTime) + return nil, nil + } + + block := response.GetBlock(child.topic, child.partition) + if block == nil { + return nil, ErrIncompleteResponse + } + + if block.Err != ErrNoError { + return nil, block.Err + } + + nRecs, err := block.numRecords() + if err != nil { + return nil, err + } + + consumerBatchSizeMetric.Update(int64(nRecs)) + + if nRecs == 0 { + partialTrailingMessage, err := block.isPartial() + if err != nil { + return nil, err + } + // We got no messages. If we got a trailing one then we need to ask for more data. + // Otherwise we just poll again and wait for one to be produced... + if partialTrailingMessage { + if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize == child.conf.Consumer.Fetch.Max { + // we can't ask for more data, we've hit the configured limit + child.sendError(ErrMessageTooLarge) + child.offset++ // skip this one so we can keep processing future messages + } else { + child.fetchSize *= 2 + // check int32 overflow + if child.fetchSize < 0 { + child.fetchSize = math.MaxInt32 + } + if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize > child.conf.Consumer.Fetch.Max { + child.fetchSize = child.conf.Consumer.Fetch.Max + } + } + } + + return nil, nil + } + + // we got messages, reset our fetch size in case it was increased for a previous request + child.fetchSize = child.conf.Consumer.Fetch.Default + atomic.StoreInt64(&child.highWaterMarkOffset, block.HighWaterMarkOffset) + + // abortedProducerIDs contains producerID which message should be ignored as uncommitted + // - producerID are added when the partitionConsumer iterate over the offset at which an aborted transaction begins (abortedTransaction.FirstOffset) + // - producerID are removed when partitionConsumer iterate over an aborted controlRecord, meaning the aborted transaction for this producer is over + abortedProducerIDs := make(map[int64]struct{}, len(block.AbortedTransactions)) + abortedTransactions := block.getAbortedTransactions() + + messages := []*ConsumerMessage{} + for _, records := range block.RecordsSet { + switch records.recordsType { + case legacyRecords: + messageSetMessages, err := child.parseMessages(records.MsgSet) + if err != nil { + return nil, err + } + + messages = append(messages, messageSetMessages...) + case defaultRecords: + // Consume remaining abortedTransaction up to last offset of current batch + for _, txn := range abortedTransactions { + if txn.FirstOffset > records.RecordBatch.LastOffset() { + break + } + abortedProducerIDs[txn.ProducerID] = struct{}{} + // Pop abortedTransactions so that we never add it again + abortedTransactions = abortedTransactions[1:] + } + + recordBatchMessages, err := child.parseRecords(records.RecordBatch) + if err != nil { + return nil, err + } + + // Parse and commit offset but do not expose messages that are: + // - control records + // - part of an aborted transaction when set to `ReadCommitted` + + // control record + isControl, err := records.isControl() + if err != nil { + // I don't know why there is this continue in case of error to begin with + // Safe bet is to ignore control messages if ReadUncommitted + // and block on them in case of error and ReadCommitted + if child.conf.Consumer.IsolationLevel == ReadCommitted { + return nil, err + } + continue + } + if isControl { + controlRecord, err := records.getControlRecord() + if err != nil { + return nil, err + } + + if controlRecord.Type == ControlRecordAbort { + delete(abortedProducerIDs, records.RecordBatch.ProducerID) + } + continue + } + + // filter aborted transactions + if child.conf.Consumer.IsolationLevel == ReadCommitted { + _, isAborted := abortedProducerIDs[records.RecordBatch.ProducerID] + if records.RecordBatch.IsTransactional && isAborted { + continue + } + } + + messages = append(messages, recordBatchMessages...) + default: + return nil, fmt.Errorf("unknown records type: %v", records.recordsType) + } + } + + return messages, nil +} + +type brokerConsumer struct { + consumer *consumer + broker *Broker + input chan *partitionConsumer + newSubscriptions chan []*partitionConsumer + subscriptions map[*partitionConsumer]none + wait chan none + acks sync.WaitGroup + refs int +} + +func (c *consumer) newBrokerConsumer(broker *Broker) *brokerConsumer { + bc := &brokerConsumer{ + consumer: c, + broker: broker, + input: make(chan *partitionConsumer), + newSubscriptions: make(chan []*partitionConsumer), + wait: make(chan none), + subscriptions: make(map[*partitionConsumer]none), + refs: 0, + } + + go withRecover(bc.subscriptionManager) + go withRecover(bc.subscriptionConsumer) + + return bc +} + +// The subscriptionManager constantly accepts new subscriptions on `input` (even when the main subscriptionConsumer +// goroutine is in the middle of a network request) and batches it up. The main worker goroutine picks +// up a batch of new subscriptions between every network request by reading from `newSubscriptions`, so we give +// it nil if no new subscriptions are available. We also write to `wait` only when new subscriptions is available, +// so the main goroutine can block waiting for work if it has none. +func (bc *brokerConsumer) subscriptionManager() { + var buffer []*partitionConsumer + + for { + if len(buffer) > 0 { + select { + case event, ok := <-bc.input: + if !ok { + goto done + } + buffer = append(buffer, event) + case bc.newSubscriptions <- buffer: + buffer = nil + case bc.wait <- none{}: + } + } else { + select { + case event, ok := <-bc.input: + if !ok { + goto done + } + buffer = append(buffer, event) + case bc.newSubscriptions <- nil: + } + } + } + +done: + close(bc.wait) + if len(buffer) > 0 { + bc.newSubscriptions <- buffer + } + close(bc.newSubscriptions) +} + +//subscriptionConsumer ensures we will get nil right away if no new subscriptions is available +func (bc *brokerConsumer) subscriptionConsumer() { + <-bc.wait // wait for our first piece of work + + for newSubscriptions := range bc.newSubscriptions { + bc.updateSubscriptions(newSubscriptions) + + if len(bc.subscriptions) == 0 { + // We're about to be shut down or we're about to receive more subscriptions. + // Either way, the signal just hasn't propagated to our goroutine yet. + <-bc.wait + continue + } + + response, err := bc.fetchNewMessages() + + if err != nil { + Logger.Printf("consumer/broker/%d disconnecting due to error processing FetchRequest: %s\n", bc.broker.ID(), err) + bc.abort(err) + return + } + + bc.acks.Add(len(bc.subscriptions)) + for child := range bc.subscriptions { + child.feeder <- response + } + bc.acks.Wait() + bc.handleResponses() + } +} + +func (bc *brokerConsumer) updateSubscriptions(newSubscriptions []*partitionConsumer) { + for _, child := range newSubscriptions { + bc.subscriptions[child] = none{} + Logger.Printf("consumer/broker/%d added subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) + } + + for child := range bc.subscriptions { + select { + case <-child.dying: + Logger.Printf("consumer/broker/%d closed dead subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) + close(child.trigger) + delete(bc.subscriptions, child) + default: + // no-op + } + } +} + +//handleResponses handles the response codes left for us by our subscriptions, and abandons ones that have been closed +func (bc *brokerConsumer) handleResponses() { + for child := range bc.subscriptions { + result := child.responseResult + child.responseResult = nil + + switch result { + case nil: + // no-op + case errTimedOut: + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because consuming was taking too long\n", + bc.broker.ID(), child.topic, child.partition) + delete(bc.subscriptions, child) + case ErrOffsetOutOfRange: + // there's no point in retrying this it will just fail the same way again + // shut it down and force the user to choose what to do + child.sendError(result) + Logger.Printf("consumer/%s/%d shutting down because %s\n", child.topic, child.partition, result) + close(child.trigger) + delete(bc.subscriptions, child) + case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable, ErrReplicaNotAvailable: + // not an error, but does need redispatching + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", + bc.broker.ID(), child.topic, child.partition, result) + child.trigger <- none{} + delete(bc.subscriptions, child) + default: + // dunno, tell the user and try redispatching + child.sendError(result) + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", + bc.broker.ID(), child.topic, child.partition, result) + child.trigger <- none{} + delete(bc.subscriptions, child) + } + } +} + +func (bc *brokerConsumer) abort(err error) { + bc.consumer.abandonBrokerConsumer(bc) + _ = bc.broker.Close() // we don't care about the error this might return, we already have one + + for child := range bc.subscriptions { + child.sendError(err) + child.trigger <- none{} + } + + for newSubscriptions := range bc.newSubscriptions { + if len(newSubscriptions) == 0 { + <-bc.wait + continue + } + for _, child := range newSubscriptions { + child.sendError(err) + child.trigger <- none{} + } + } +} + +func (bc *brokerConsumer) fetchNewMessages() (*FetchResponse, error) { + request := &FetchRequest{ + MinBytes: bc.consumer.conf.Consumer.Fetch.Min, + MaxWaitTime: int32(bc.consumer.conf.Consumer.MaxWaitTime / time.Millisecond), + } + if bc.consumer.conf.Version.IsAtLeast(V0_9_0_0) { + request.Version = 1 + } + if bc.consumer.conf.Version.IsAtLeast(V0_10_0_0) { + request.Version = 2 + } + if bc.consumer.conf.Version.IsAtLeast(V0_10_1_0) { + request.Version = 3 + request.MaxBytes = MaxResponseSize + } + if bc.consumer.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 4 + request.Isolation = bc.consumer.conf.Consumer.IsolationLevel + } + + for child := range bc.subscriptions { + request.AddBlock(child.topic, child.partition, child.offset, child.fetchSize) + } + + return bc.broker.Fetch(request) +} diff --git a/vendor/github.com/Shopify/sarama/consumer_group.go b/vendor/github.com/Shopify/sarama/consumer_group.go new file mode 100644 index 0000000..da99e88 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_group.go @@ -0,0 +1,867 @@ +package sarama + +import ( + "context" + "errors" + "fmt" + "sort" + "sync" + "time" +) + +// ErrClosedConsumerGroup is the error returned when a method is called on a consumer group that has been closed. +var ErrClosedConsumerGroup = errors.New("kafka: tried to use a consumer group that was closed") + +// ConsumerGroup is responsible for dividing up processing of topics and partitions +// over a collection of processes (the members of the consumer group). +type ConsumerGroup interface { + // Consume joins a cluster of consumers for a given list of topics and + // starts a blocking ConsumerGroupSession through the ConsumerGroupHandler. + // + // The life-cycle of a session is represented by the following steps: + // + // 1. The consumers join the group (as explained in https://kafka.apache.org/documentation/#intro_consumers) + // and is assigned their "fair share" of partitions, aka 'claims'. + // 2. Before processing starts, the handler's Setup() hook is called to notify the user + // of the claims and allow any necessary preparation or alteration of state. + // 3. For each of the assigned claims the handler's ConsumeClaim() function is then called + // in a separate goroutine which requires it to be thread-safe. Any state must be carefully protected + // from concurrent reads/writes. + // 4. The session will persist until one of the ConsumeClaim() functions exits. This can be either when the + // parent context is cancelled or when a server-side rebalance cycle is initiated. + // 5. Once all the ConsumeClaim() loops have exited, the handler's Cleanup() hook is called + // to allow the user to perform any final tasks before a rebalance. + // 6. Finally, marked offsets are committed one last time before claims are released. + // + // Please note, that once a rebalance is triggered, sessions must be completed within + // Config.Consumer.Group.Rebalance.Timeout. This means that ConsumeClaim() functions must exit + // as quickly as possible to allow time for Cleanup() and the final offset commit. If the timeout + // is exceeded, the consumer will be removed from the group by Kafka, which will cause offset + // commit failures. + Consume(ctx context.Context, topics []string, handler ConsumerGroupHandler) error + + // Errors returns a read channel of errors that occurred during the consumer life-cycle. + // By default, errors are logged and not returned over this channel. + // If you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan error + + // Close stops the ConsumerGroup and detaches any running sessions. It is required to call + // this function before the object passes out of scope, as it will otherwise leak memory. + Close() error +} + +type consumerGroup struct { + client Client + + config *Config + consumer Consumer + groupID string + memberID string + errors chan error + + lock sync.Mutex + closed chan none + closeOnce sync.Once + + userData []byte +} + +// NewConsumerGroup creates a new consumer group the given broker addresses and configuration. +func NewConsumerGroup(addrs []string, groupID string, config *Config) (ConsumerGroup, error) { + client, err := NewClient(addrs, config) + if err != nil { + return nil, err + } + + c, err := newConsumerGroup(groupID, client) + if err != nil { + _ = client.Close() + } + return c, err +} + +// NewConsumerGroupFromClient creates a new consumer group using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this consumer. +// PLEASE NOTE: consumer groups can only re-use but not share clients. +func NewConsumerGroupFromClient(groupID string, client Client) (ConsumerGroup, error) { + // For clients passed in by the client, ensure we don't + // call Close() on it. + cli := &nopCloserClient{client} + return newConsumerGroup(groupID, cli) +} + +func newConsumerGroup(groupID string, client Client) (ConsumerGroup, error) { + config := client.Config() + if !config.Version.IsAtLeast(V0_10_2_0) { + return nil, ConfigurationError("consumer groups require Version to be >= V0_10_2_0") + } + + consumer, err := NewConsumerFromClient(client) + if err != nil { + return nil, err + } + + return &consumerGroup{ + client: client, + consumer: consumer, + config: config, + groupID: groupID, + errors: make(chan error, config.ChannelBufferSize), + closed: make(chan none), + }, nil +} + +// Errors implements ConsumerGroup. +func (c *consumerGroup) Errors() <-chan error { return c.errors } + +// Close implements ConsumerGroup. +func (c *consumerGroup) Close() (err error) { + c.closeOnce.Do(func() { + close(c.closed) + + c.lock.Lock() + defer c.lock.Unlock() + + // leave group + if e := c.leave(); e != nil { + err = e + } + + // drain errors + go func() { + close(c.errors) + }() + for e := range c.errors { + err = e + } + + if e := c.client.Close(); e != nil { + err = e + } + }) + return +} + +// Consume implements ConsumerGroup. +func (c *consumerGroup) Consume(ctx context.Context, topics []string, handler ConsumerGroupHandler) error { + // Ensure group is not closed + select { + case <-c.closed: + return ErrClosedConsumerGroup + default: + } + + c.lock.Lock() + defer c.lock.Unlock() + + // Quick exit when no topics are provided + if len(topics) == 0 { + return fmt.Errorf("no topics provided") + } + + // Refresh metadata for requested topics + if err := c.client.RefreshMetadata(topics...); err != nil { + return err + } + + // Init session + sess, err := c.newSession(ctx, topics, handler, c.config.Consumer.Group.Rebalance.Retry.Max) + if err == ErrClosedClient { + return ErrClosedConsumerGroup + } else if err != nil { + return err + } + + // loop check topic partition numbers changed + // will trigger rebalance when any topic partitions number had changed + go c.loopCheckPartitionNumbers(topics, sess) + + // Wait for session exit signal + <-sess.ctx.Done() + + // Gracefully release session claims + return sess.release(true) +} + +func (c *consumerGroup) retryNewSession(ctx context.Context, topics []string, handler ConsumerGroupHandler, retries int, refreshCoordinator bool) (*consumerGroupSession, error) { + select { + case <-c.closed: + return nil, ErrClosedConsumerGroup + case <-time.After(c.config.Consumer.Group.Rebalance.Retry.Backoff): + } + + if refreshCoordinator { + err := c.client.RefreshCoordinator(c.groupID) + if err != nil { + return c.retryNewSession(ctx, topics, handler, retries, true) + } + } + + return c.newSession(ctx, topics, handler, retries-1) +} + +func (c *consumerGroup) newSession(ctx context.Context, topics []string, handler ConsumerGroupHandler, retries int) (*consumerGroupSession, error) { + coordinator, err := c.client.Coordinator(c.groupID) + if err != nil { + if retries <= 0 { + return nil, err + } + + return c.retryNewSession(ctx, topics, handler, retries, true) + } + + // Join consumer group + join, err := c.joinGroupRequest(coordinator, topics) + if err != nil { + _ = coordinator.Close() + return nil, err + } + switch join.Err { + case ErrNoError: + c.memberID = join.MemberId + case ErrUnknownMemberId, ErrIllegalGeneration: // reset member ID and retry immediately + c.memberID = "" + return c.newSession(ctx, topics, handler, retries) + case ErrNotCoordinatorForConsumer: // retry after backoff with coordinator refresh + if retries <= 0 { + return nil, join.Err + } + + return c.retryNewSession(ctx, topics, handler, retries, true) + case ErrRebalanceInProgress: // retry after backoff + if retries <= 0 { + return nil, join.Err + } + + return c.retryNewSession(ctx, topics, handler, retries, false) + default: + return nil, join.Err + } + + // Prepare distribution plan if we joined as the leader + var plan BalanceStrategyPlan + if join.LeaderId == join.MemberId { + members, err := join.GetMembers() + if err != nil { + return nil, err + } + + plan, err = c.balance(members) + if err != nil { + return nil, err + } + } + + // Sync consumer group + sync, err := c.syncGroupRequest(coordinator, plan, join.GenerationId) + if err != nil { + _ = coordinator.Close() + return nil, err + } + switch sync.Err { + case ErrNoError: + case ErrUnknownMemberId, ErrIllegalGeneration: // reset member ID and retry immediately + c.memberID = "" + return c.newSession(ctx, topics, handler, retries) + case ErrNotCoordinatorForConsumer: // retry after backoff with coordinator refresh + if retries <= 0 { + return nil, sync.Err + } + + return c.retryNewSession(ctx, topics, handler, retries, true) + case ErrRebalanceInProgress: // retry after backoff + if retries <= 0 { + return nil, sync.Err + } + + return c.retryNewSession(ctx, topics, handler, retries, false) + default: + return nil, sync.Err + } + + // Retrieve and sort claims + var claims map[string][]int32 + if len(sync.MemberAssignment) > 0 { + members, err := sync.GetMemberAssignment() + if err != nil { + return nil, err + } + claims = members.Topics + c.userData = members.UserData + + for _, partitions := range claims { + sort.Sort(int32Slice(partitions)) + } + } + + return newConsumerGroupSession(ctx, c, claims, join.MemberId, join.GenerationId, handler) +} + +func (c *consumerGroup) joinGroupRequest(coordinator *Broker, topics []string) (*JoinGroupResponse, error) { + req := &JoinGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + SessionTimeout: int32(c.config.Consumer.Group.Session.Timeout / time.Millisecond), + ProtocolType: "consumer", + } + if c.config.Version.IsAtLeast(V0_10_1_0) { + req.Version = 1 + req.RebalanceTimeout = int32(c.config.Consumer.Group.Rebalance.Timeout / time.Millisecond) + } + + // use static user-data if configured, otherwise use consumer-group userdata from the last sync + userData := c.config.Consumer.Group.Member.UserData + if len(userData) == 0 { + userData = c.userData + } + meta := &ConsumerGroupMemberMetadata{ + Topics: topics, + UserData: userData, + } + strategy := c.config.Consumer.Group.Rebalance.Strategy + if err := req.AddGroupProtocolMetadata(strategy.Name(), meta); err != nil { + return nil, err + } + + return coordinator.JoinGroup(req) +} + +func (c *consumerGroup) syncGroupRequest(coordinator *Broker, plan BalanceStrategyPlan, generationID int32) (*SyncGroupResponse, error) { + req := &SyncGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + GenerationId: generationID, + } + for memberID, topics := range plan { + assignment := &ConsumerGroupMemberAssignment{Topics: topics} + + // Include topic assignments in group-assignment userdata for each consumer-group member + if c.config.Consumer.Group.Rebalance.Strategy.Name() == StickyBalanceStrategyName { + userDataBytes, err := encode(&StickyAssignorUserDataV1{ + Topics: topics, + Generation: generationID, + }, nil) + if err != nil { + return nil, err + } + assignment.UserData = userDataBytes + } + if err := req.AddGroupAssignmentMember(memberID, assignment); err != nil { + return nil, err + } + } + return coordinator.SyncGroup(req) +} + +func (c *consumerGroup) heartbeatRequest(coordinator *Broker, memberID string, generationID int32) (*HeartbeatResponse, error) { + req := &HeartbeatRequest{ + GroupId: c.groupID, + MemberId: memberID, + GenerationId: generationID, + } + + return coordinator.Heartbeat(req) +} + +func (c *consumerGroup) balance(members map[string]ConsumerGroupMemberMetadata) (BalanceStrategyPlan, error) { + topics := make(map[string][]int32) + for _, meta := range members { + for _, topic := range meta.Topics { + topics[topic] = nil + } + } + + for topic := range topics { + partitions, err := c.client.Partitions(topic) + if err != nil { + return nil, err + } + topics[topic] = partitions + } + + strategy := c.config.Consumer.Group.Rebalance.Strategy + return strategy.Plan(members, topics) +} + +// Leaves the cluster, called by Close, protected by lock. +func (c *consumerGroup) leave() error { + if c.memberID == "" { + return nil + } + + coordinator, err := c.client.Coordinator(c.groupID) + if err != nil { + return err + } + + resp, err := coordinator.LeaveGroup(&LeaveGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + }) + if err != nil { + _ = coordinator.Close() + return err + } + + // Unset memberID + c.memberID = "" + + // Check response + switch resp.Err { + case ErrRebalanceInProgress, ErrUnknownMemberId, ErrNoError: + return nil + default: + return resp.Err + } +} + +func (c *consumerGroup) handleError(err error, topic string, partition int32) { + select { + case <-c.closed: + return + default: + } + + if _, ok := err.(*ConsumerError); !ok && topic != "" && partition > -1 { + err = &ConsumerError{ + Topic: topic, + Partition: partition, + Err: err, + } + } + + if c.config.Consumer.Return.Errors { + select { + case c.errors <- err: + default: + } + } else { + Logger.Println(err) + } +} + +func (c *consumerGroup) loopCheckPartitionNumbers(topics []string, session *consumerGroupSession) { + pause := time.NewTicker(c.config.Consumer.Group.Heartbeat.Interval * 2) + defer session.cancel() + defer pause.Stop() + var oldTopicToPartitionNum map[string]int + var err error + if oldTopicToPartitionNum, err = c.topicToPartitionNumbers(topics); err != nil { + return + } + for { + if newTopicToPartitionNum, err := c.topicToPartitionNumbers(topics); err != nil { + return + } else { + for topic, num := range oldTopicToPartitionNum { + if newTopicToPartitionNum[topic] != num { + return // trigger the end of the session on exit + } + } + } + select { + case <-pause.C: + case <-c.closed: + return + } + } +} + +func (c *consumerGroup) topicToPartitionNumbers(topics []string) (map[string]int, error) { + if err := c.client.RefreshMetadata(topics...); err != nil { + Logger.Printf("Consumer Group refresh metadata failed %v", err) + return nil, err + } + topicToPartitionNum := make(map[string]int, len(topics)) + for _, topic := range topics { + if partitionNum, err := c.client.Partitions(topic); err != nil { + Logger.Printf("Consumer Group topic %s get partition number failed %v", topic, err) + return nil, err + } else { + topicToPartitionNum[topic] = len(partitionNum) + } + } + return topicToPartitionNum, nil +} + +// -------------------------------------------------------------------- + +// ConsumerGroupSession represents a consumer group member session. +type ConsumerGroupSession interface { + // Claims returns information about the claimed partitions by topic. + Claims() map[string][]int32 + + // MemberID returns the cluster member ID. + MemberID() string + + // GenerationID returns the current generation ID. + GenerationID() int32 + + // MarkOffset marks the provided offset, alongside a metadata string + // that represents the state of the partition consumer at that point in time. The + // metadata string can be used by another consumer to restore that state, so it + // can resume consumption. + // + // To follow upstream conventions, you are expected to mark the offset of the + // next message to read, not the last message read. Thus, when calling `MarkOffset` + // you should typically add one to the offset of the last consumed message. + // + // Note: calling MarkOffset does not necessarily commit the offset to the backend + // store immediately for efficiency reasons, and it may never be committed if + // your application crashes. This means that you may end up processing the same + // message twice, and your processing should ideally be idempotent. + MarkOffset(topic string, partition int32, offset int64, metadata string) + + // ResetOffset resets to the provided offset, alongside a metadata string that + // represents the state of the partition consumer at that point in time. Reset + // acts as a counterpart to MarkOffset, the difference being that it allows to + // reset an offset to an earlier or smaller value, where MarkOffset only + // allows incrementing the offset. cf MarkOffset for more details. + ResetOffset(topic string, partition int32, offset int64, metadata string) + + // MarkMessage marks a message as consumed. + MarkMessage(msg *ConsumerMessage, metadata string) + + // Context returns the session context. + Context() context.Context +} + +type consumerGroupSession struct { + parent *consumerGroup + memberID string + generationID int32 + handler ConsumerGroupHandler + + claims map[string][]int32 + offsets *offsetManager + ctx context.Context + cancel func() + + waitGroup sync.WaitGroup + releaseOnce sync.Once + hbDying, hbDead chan none +} + +func newConsumerGroupSession(ctx context.Context, parent *consumerGroup, claims map[string][]int32, memberID string, generationID int32, handler ConsumerGroupHandler) (*consumerGroupSession, error) { + // init offset manager + offsets, err := newOffsetManagerFromClient(parent.groupID, memberID, generationID, parent.client) + if err != nil { + return nil, err + } + + // init context + ctx, cancel := context.WithCancel(ctx) + + // init session + sess := &consumerGroupSession{ + parent: parent, + memberID: memberID, + generationID: generationID, + handler: handler, + offsets: offsets, + claims: claims, + ctx: ctx, + cancel: cancel, + hbDying: make(chan none), + hbDead: make(chan none), + } + + // start heartbeat loop + go sess.heartbeatLoop() + + // create a POM for each claim + for topic, partitions := range claims { + for _, partition := range partitions { + pom, err := offsets.ManagePartition(topic, partition) + if err != nil { + _ = sess.release(false) + return nil, err + } + + // handle POM errors + go func(topic string, partition int32) { + for err := range pom.Errors() { + sess.parent.handleError(err, topic, partition) + } + }(topic, partition) + } + } + + // perform setup + if err := handler.Setup(sess); err != nil { + _ = sess.release(true) + return nil, err + } + + // start consuming + for topic, partitions := range claims { + for _, partition := range partitions { + sess.waitGroup.Add(1) + + go func(topic string, partition int32) { + defer sess.waitGroup.Done() + + // cancel the as session as soon as the first + // goroutine exits + defer sess.cancel() + + // consume a single topic/partition, blocking + sess.consume(topic, partition) + }(topic, partition) + } + } + return sess, nil +} + +func (s *consumerGroupSession) Claims() map[string][]int32 { return s.claims } +func (s *consumerGroupSession) MemberID() string { return s.memberID } +func (s *consumerGroupSession) GenerationID() int32 { return s.generationID } + +func (s *consumerGroupSession) MarkOffset(topic string, partition int32, offset int64, metadata string) { + if pom := s.offsets.findPOM(topic, partition); pom != nil { + pom.MarkOffset(offset, metadata) + } +} + +func (s *consumerGroupSession) ResetOffset(topic string, partition int32, offset int64, metadata string) { + if pom := s.offsets.findPOM(topic, partition); pom != nil { + pom.ResetOffset(offset, metadata) + } +} + +func (s *consumerGroupSession) MarkMessage(msg *ConsumerMessage, metadata string) { + s.MarkOffset(msg.Topic, msg.Partition, msg.Offset+1, metadata) +} + +func (s *consumerGroupSession) Context() context.Context { + return s.ctx +} + +func (s *consumerGroupSession) consume(topic string, partition int32) { + // quick exit if rebalance is due + select { + case <-s.ctx.Done(): + return + case <-s.parent.closed: + return + default: + } + + // get next offset + offset := s.parent.config.Consumer.Offsets.Initial + if pom := s.offsets.findPOM(topic, partition); pom != nil { + offset, _ = pom.NextOffset() + } + + // create new claim + claim, err := newConsumerGroupClaim(s, topic, partition, offset) + if err != nil { + s.parent.handleError(err, topic, partition) + return + } + + // handle errors + go func() { + for err := range claim.Errors() { + s.parent.handleError(err, topic, partition) + } + }() + + // trigger close when session is done + go func() { + select { + case <-s.ctx.Done(): + case <-s.parent.closed: + } + claim.AsyncClose() + }() + + // start processing + if err := s.handler.ConsumeClaim(s, claim); err != nil { + s.parent.handleError(err, topic, partition) + } + + // ensure consumer is closed & drained + claim.AsyncClose() + for _, err := range claim.waitClosed() { + s.parent.handleError(err, topic, partition) + } +} + +func (s *consumerGroupSession) release(withCleanup bool) (err error) { + // signal release, stop heartbeat + s.cancel() + + // wait for consumers to exit + s.waitGroup.Wait() + + // perform release + s.releaseOnce.Do(func() { + if withCleanup { + if e := s.handler.Cleanup(s); e != nil { + s.parent.handleError(e, "", -1) + err = e + } + } + + if e := s.offsets.Close(); e != nil { + err = e + } + + close(s.hbDying) + <-s.hbDead + }) + + return +} + +func (s *consumerGroupSession) heartbeatLoop() { + defer close(s.hbDead) + defer s.cancel() // trigger the end of the session on exit + + pause := time.NewTicker(s.parent.config.Consumer.Group.Heartbeat.Interval) + defer pause.Stop() + + retries := s.parent.config.Metadata.Retry.Max + for { + coordinator, err := s.parent.client.Coordinator(s.parent.groupID) + if err != nil { + if retries <= 0 { + s.parent.handleError(err, "", -1) + return + } + + select { + case <-s.hbDying: + return + case <-time.After(s.parent.config.Metadata.Retry.Backoff): + retries-- + } + continue + } + + resp, err := s.parent.heartbeatRequest(coordinator, s.memberID, s.generationID) + if err != nil { + _ = coordinator.Close() + + if retries <= 0 { + s.parent.handleError(err, "", -1) + return + } + + retries-- + continue + } + + switch resp.Err { + case ErrNoError: + retries = s.parent.config.Metadata.Retry.Max + case ErrRebalanceInProgress, ErrUnknownMemberId, ErrIllegalGeneration: + return + default: + s.parent.handleError(err, "", -1) + return + } + + select { + case <-pause.C: + case <-s.hbDying: + return + } + } +} + +// -------------------------------------------------------------------- + +// ConsumerGroupHandler instances are used to handle individual topic/partition claims. +// It also provides hooks for your consumer group session life-cycle and allow you to +// trigger logic before or after the consume loop(s). +// +// PLEASE NOTE that handlers are likely be called from several goroutines concurrently, +// ensure that all state is safely protected against race conditions. +type ConsumerGroupHandler interface { + // Setup is run at the beginning of a new session, before ConsumeClaim. + Setup(ConsumerGroupSession) error + + // Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exited + // but before the offsets are committed for the very last time. + Cleanup(ConsumerGroupSession) error + + // ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages(). + // Once the Messages() channel is closed, the Handler must finish its processing + // loop and exit. + ConsumeClaim(ConsumerGroupSession, ConsumerGroupClaim) error +} + +// ConsumerGroupClaim processes Kafka messages from a given topic and partition within a consumer group. +type ConsumerGroupClaim interface { + // Topic returns the consumed topic name. + Topic() string + + // Partition returns the consumed partition. + Partition() int32 + + // InitialOffset returns the initial offset that was used as a starting point for this claim. + InitialOffset() int64 + + // HighWaterMarkOffset returns the high water mark offset of the partition, + // i.e. the offset that will be used for the next message that will be produced. + // You can use this to determine how far behind the processing is. + HighWaterMarkOffset() int64 + + // Messages returns the read channel for the messages that are returned by + // the broker. The messages channel will be closed when a new rebalance cycle + // is due. You must finish processing and mark offsets within + // Config.Consumer.Group.Session.Timeout before the topic/partition is eventually + // re-assigned to another group member. + Messages() <-chan *ConsumerMessage +} + +type consumerGroupClaim struct { + topic string + partition int32 + offset int64 + PartitionConsumer +} + +func newConsumerGroupClaim(sess *consumerGroupSession, topic string, partition int32, offset int64) (*consumerGroupClaim, error) { + pcm, err := sess.parent.consumer.ConsumePartition(topic, partition, offset) + if err == ErrOffsetOutOfRange { + offset = sess.parent.config.Consumer.Offsets.Initial + pcm, err = sess.parent.consumer.ConsumePartition(topic, partition, offset) + } + if err != nil { + return nil, err + } + + go func() { + for err := range pcm.Errors() { + sess.parent.handleError(err, topic, partition) + } + }() + + return &consumerGroupClaim{ + topic: topic, + partition: partition, + offset: offset, + PartitionConsumer: pcm, + }, nil +} + +func (c *consumerGroupClaim) Topic() string { return c.topic } +func (c *consumerGroupClaim) Partition() int32 { return c.partition } +func (c *consumerGroupClaim) InitialOffset() int64 { return c.offset } + +// Drains messages and errors, ensures the claim is fully closed. +func (c *consumerGroupClaim) waitClosed() (errs ConsumerErrors) { + go func() { + for range c.Messages() { + } + }() + + for err := range c.Errors() { + errs = append(errs, err) + } + return +} diff --git a/vendor/github.com/Shopify/sarama/consumer_group_members.go b/vendor/github.com/Shopify/sarama/consumer_group_members.go new file mode 100644 index 0000000..2d02cc3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_group_members.go @@ -0,0 +1,96 @@ +package sarama + +//ConsumerGroupMemberMetadata holds the metadata for consumer group +type ConsumerGroupMemberMetadata struct { + Version int16 + Topics []string + UserData []byte +} + +func (m *ConsumerGroupMemberMetadata) encode(pe packetEncoder) error { + pe.putInt16(m.Version) + + if err := pe.putStringArray(m.Topics); err != nil { + return err + } + + if err := pe.putBytes(m.UserData); err != nil { + return err + } + + return nil +} + +func (m *ConsumerGroupMemberMetadata) decode(pd packetDecoder) (err error) { + if m.Version, err = pd.getInt16(); err != nil { + return + } + + if m.Topics, err = pd.getStringArray(); err != nil { + return + } + + if m.UserData, err = pd.getBytes(); err != nil { + return + } + + return nil +} + +//ConsumerGroupMemberAssignment holds the member assignment for a consume group +type ConsumerGroupMemberAssignment struct { + Version int16 + Topics map[string][]int32 + UserData []byte +} + +func (m *ConsumerGroupMemberAssignment) encode(pe packetEncoder) error { + pe.putInt16(m.Version) + + if err := pe.putArrayLength(len(m.Topics)); err != nil { + return err + } + + for topic, partitions := range m.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + + if err := pe.putBytes(m.UserData); err != nil { + return err + } + + return nil +} + +func (m *ConsumerGroupMemberAssignment) decode(pd packetDecoder) (err error) { + if m.Version, err = pd.getInt16(); err != nil { + return + } + + var topicLen int + if topicLen, err = pd.getArrayLength(); err != nil { + return + } + + m.Topics = make(map[string][]int32, topicLen) + for i := 0; i < topicLen; i++ { + var topic string + if topic, err = pd.getString(); err != nil { + return + } + if m.Topics[topic], err = pd.getInt32Array(); err != nil { + return + } + } + + if m.UserData, err = pd.getBytes(); err != nil { + return + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_request.go b/vendor/github.com/Shopify/sarama/consumer_metadata_request.go new file mode 100644 index 0000000..a8dcaef --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_metadata_request.go @@ -0,0 +1,34 @@ +package sarama + +//ConsumerMetadataRequest is used for metadata requests +type ConsumerMetadataRequest struct { + ConsumerGroup string +} + +func (r *ConsumerMetadataRequest) encode(pe packetEncoder) error { + tmp := new(FindCoordinatorRequest) + tmp.CoordinatorKey = r.ConsumerGroup + tmp.CoordinatorType = CoordinatorGroup + return tmp.encode(pe) +} + +func (r *ConsumerMetadataRequest) decode(pd packetDecoder, version int16) (err error) { + tmp := new(FindCoordinatorRequest) + if err := tmp.decode(pd, version); err != nil { + return err + } + r.ConsumerGroup = tmp.CoordinatorKey + return nil +} + +func (r *ConsumerMetadataRequest) key() int16 { + return 10 +} + +func (r *ConsumerMetadataRequest) version() int16 { + return 0 +} + +func (r *ConsumerMetadataRequest) requiredVersion() KafkaVersion { + return V0_8_2_0 +} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_response.go b/vendor/github.com/Shopify/sarama/consumer_metadata_response.go new file mode 100644 index 0000000..f39a871 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_metadata_response.go @@ -0,0 +1,78 @@ +package sarama + +import ( + "net" + "strconv" +) + +//ConsumerMetadataResponse holds the response for a consumer group meta data requests +type ConsumerMetadataResponse struct { + Err KError + Coordinator *Broker + CoordinatorID int32 // deprecated: use Coordinator.ID() + CoordinatorHost string // deprecated: use Coordinator.Addr() + CoordinatorPort int32 // deprecated: use Coordinator.Addr() +} + +func (r *ConsumerMetadataResponse) decode(pd packetDecoder, version int16) (err error) { + tmp := new(FindCoordinatorResponse) + + if err := tmp.decode(pd, version); err != nil { + return err + } + + r.Err = tmp.Err + + r.Coordinator = tmp.Coordinator + if tmp.Coordinator == nil { + return nil + } + + // this can all go away in 2.0, but we have to fill in deprecated fields to maintain + // backwards compatibility + host, portstr, err := net.SplitHostPort(r.Coordinator.Addr()) + if err != nil { + return err + } + port, err := strconv.ParseInt(portstr, 10, 32) + if err != nil { + return err + } + r.CoordinatorID = r.Coordinator.ID() + r.CoordinatorHost = host + r.CoordinatorPort = int32(port) + + return nil +} + +func (r *ConsumerMetadataResponse) encode(pe packetEncoder) error { + if r.Coordinator == nil { + r.Coordinator = new(Broker) + r.Coordinator.id = r.CoordinatorID + r.Coordinator.addr = net.JoinHostPort(r.CoordinatorHost, strconv.Itoa(int(r.CoordinatorPort))) + } + + tmp := &FindCoordinatorResponse{ + Version: 0, + Err: r.Err, + Coordinator: r.Coordinator, + } + + if err := tmp.encode(pe); err != nil { + return err + } + + return nil +} + +func (r *ConsumerMetadataResponse) key() int16 { + return 10 +} + +func (r *ConsumerMetadataResponse) version() int16 { + return 0 +} + +func (r *ConsumerMetadataResponse) requiredVersion() KafkaVersion { + return V0_8_2_0 +} diff --git a/vendor/github.com/Shopify/sarama/control_record.go b/vendor/github.com/Shopify/sarama/control_record.go new file mode 100644 index 0000000..9b75ab5 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/control_record.go @@ -0,0 +1,72 @@ +package sarama + +//ControlRecordType ... +type ControlRecordType int + +const ( + //ControlRecordAbort is a control record for abort + ControlRecordAbort ControlRecordType = iota + //ControlRecordCommit is a control record for commit + ControlRecordCommit + //ControlRecordUnknown is a control record of unknown type + ControlRecordUnknown +) + +// Control records are returned as a record by fetchRequest +// However unlike "normal" records, they mean nothing application wise. +// They only serve internal logic for supporting transactions. +type ControlRecord struct { + Version int16 + CoordinatorEpoch int32 + Type ControlRecordType +} + +func (cr *ControlRecord) decode(key, value packetDecoder) error { + var err error + cr.Version, err = value.getInt16() + if err != nil { + return err + } + + cr.CoordinatorEpoch, err = value.getInt32() + if err != nil { + return err + } + + // There a version for the value part AND the key part. And I have no idea if they are supposed to match or not + // Either way, all these version can only be 0 for now + cr.Version, err = key.getInt16() + if err != nil { + return err + } + + recordType, err := key.getInt16() + if err != nil { + return err + } + + switch recordType { + case 0: + cr.Type = ControlRecordAbort + case 1: + cr.Type = ControlRecordCommit + default: + // from JAVA implementation: + // UNKNOWN is used to indicate a control type which the client is not aware of and should be ignored + cr.Type = ControlRecordUnknown + } + return nil +} + +func (cr *ControlRecord) encode(key, value packetEncoder) { + value.putInt16(cr.Version) + value.putInt32(cr.CoordinatorEpoch) + key.putInt16(cr.Version) + + switch cr.Type { + case ControlRecordAbort: + key.putInt16(0) + case ControlRecordCommit: + key.putInt16(1) + } +} diff --git a/vendor/github.com/Shopify/sarama/crc32_field.go b/vendor/github.com/Shopify/sarama/crc32_field.go new file mode 100644 index 0000000..38189a3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/crc32_field.go @@ -0,0 +1,86 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "hash/crc32" + "sync" +) + +type crcPolynomial int8 + +const ( + crcIEEE crcPolynomial = iota + crcCastagnoli +) + +var crc32FieldPool = sync.Pool{} + +func acquireCrc32Field(polynomial crcPolynomial) *crc32Field { + val := crc32FieldPool.Get() + if val != nil { + c := val.(*crc32Field) + c.polynomial = polynomial + return c + } + return newCRC32Field(polynomial) +} + +func releaseCrc32Field(c *crc32Field) { + crc32FieldPool.Put(c) +} + +var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) + +// crc32Field implements the pushEncoder and pushDecoder interfaces for calculating CRC32s. +type crc32Field struct { + startOffset int + polynomial crcPolynomial +} + +func (c *crc32Field) saveOffset(in int) { + c.startOffset = in +} + +func (c *crc32Field) reserveLength() int { + return 4 +} + +func newCRC32Field(polynomial crcPolynomial) *crc32Field { + return &crc32Field{polynomial: polynomial} +} + +func (c *crc32Field) run(curOffset int, buf []byte) error { + crc, err := c.crc(curOffset, buf) + if err != nil { + return err + } + binary.BigEndian.PutUint32(buf[c.startOffset:], crc) + return nil +} + +func (c *crc32Field) check(curOffset int, buf []byte) error { + crc, err := c.crc(curOffset, buf) + if err != nil { + return err + } + + expected := binary.BigEndian.Uint32(buf[c.startOffset:]) + if crc != expected { + return PacketDecodingError{fmt.Sprintf("CRC didn't match expected %#x got %#x", expected, crc)} + } + + return nil +} +func (c *crc32Field) crc(curOffset int, buf []byte) (uint32, error) { + var tab *crc32.Table + switch c.polynomial { + case crcIEEE: + tab = crc32.IEEETable + case crcCastagnoli: + tab = castagnoliTable + default: + return 0, PacketDecodingError{"invalid CRC type"} + } + return crc32.Checksum(buf[c.startOffset+4:curOffset], tab), nil +} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_request.go b/vendor/github.com/Shopify/sarama/create_partitions_request.go new file mode 100644 index 0000000..af321e9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_partitions_request.go @@ -0,0 +1,121 @@ +package sarama + +import "time" + +type CreatePartitionsRequest struct { + TopicPartitions map[string]*TopicPartition + Timeout time.Duration + ValidateOnly bool +} + +func (c *CreatePartitionsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.TopicPartitions)); err != nil { + return err + } + + for topic, partition := range c.TopicPartitions { + if err := pe.putString(topic); err != nil { + return err + } + if err := partition.encode(pe); err != nil { + return err + } + } + + pe.putInt32(int32(c.Timeout / time.Millisecond)) + + pe.putBool(c.ValidateOnly) + + return nil +} + +func (c *CreatePartitionsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + c.TopicPartitions = make(map[string]*TopicPartition, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicPartitions[topic] = new(TopicPartition) + if err := c.TopicPartitions[topic].decode(pd, version); err != nil { + return err + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + c.Timeout = time.Duration(timeout) * time.Millisecond + + if c.ValidateOnly, err = pd.getBool(); err != nil { + return err + } + + return nil +} + +func (r *CreatePartitionsRequest) key() int16 { + return 37 +} + +func (r *CreatePartitionsRequest) version() int16 { + return 0 +} + +func (r *CreatePartitionsRequest) requiredVersion() KafkaVersion { + return V1_0_0_0 +} + +type TopicPartition struct { + Count int32 + Assignment [][]int32 +} + +func (t *TopicPartition) encode(pe packetEncoder) error { + pe.putInt32(t.Count) + + if len(t.Assignment) == 0 { + pe.putInt32(-1) + return nil + } + + if err := pe.putArrayLength(len(t.Assignment)); err != nil { + return err + } + + for _, assign := range t.Assignment { + if err := pe.putInt32Array(assign); err != nil { + return err + } + } + + return nil +} + +func (t *TopicPartition) decode(pd packetDecoder, version int16) (err error) { + if t.Count, err = pd.getInt32(); err != nil { + return err + } + + n, err := pd.getInt32() + if err != nil { + return err + } + if n <= 0 { + return nil + } + t.Assignment = make([][]int32, n) + + for i := 0; i < int(n); i++ { + if t.Assignment[i], err = pd.getInt32Array(); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_response.go b/vendor/github.com/Shopify/sarama/create_partitions_response.go new file mode 100644 index 0000000..bb18204 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_partitions_response.go @@ -0,0 +1,105 @@ +package sarama + +import ( + "fmt" + "time" +) + +type CreatePartitionsResponse struct { + ThrottleTime time.Duration + TopicPartitionErrors map[string]*TopicPartitionError +} + +func (c *CreatePartitionsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(c.TopicPartitionErrors)); err != nil { + return err + } + + for topic, partitionError := range c.TopicPartitionErrors { + if err := pe.putString(topic); err != nil { + return err + } + if err := partitionError.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (c *CreatePartitionsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicPartitionErrors = make(map[string]*TopicPartitionError, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicPartitionErrors[topic] = new(TopicPartitionError) + if err := c.TopicPartitionErrors[topic].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (r *CreatePartitionsResponse) key() int16 { + return 37 +} + +func (r *CreatePartitionsResponse) version() int16 { + return 0 +} + +func (r *CreatePartitionsResponse) requiredVersion() KafkaVersion { + return V1_0_0_0 +} + +type TopicPartitionError struct { + Err KError + ErrMsg *string +} + +func (t *TopicPartitionError) Error() string { + text := t.Err.Error() + if t.ErrMsg != nil { + text = fmt.Sprintf("%s - %s", text, *t.ErrMsg) + } + return text +} + +func (t *TopicPartitionError) encode(pe packetEncoder) error { + pe.putInt16(int16(t.Err)) + + if err := pe.putNullableString(t.ErrMsg); err != nil { + return err + } + + return nil +} + +func (t *TopicPartitionError) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kerr) + + if t.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_topics_request.go b/vendor/github.com/Shopify/sarama/create_topics_request.go new file mode 100644 index 0000000..709c0a4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_topics_request.go @@ -0,0 +1,174 @@ +package sarama + +import ( + "time" +) + +type CreateTopicsRequest struct { + Version int16 + + TopicDetails map[string]*TopicDetail + Timeout time.Duration + ValidateOnly bool +} + +func (c *CreateTopicsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.TopicDetails)); err != nil { + return err + } + for topic, detail := range c.TopicDetails { + if err := pe.putString(topic); err != nil { + return err + } + if err := detail.encode(pe); err != nil { + return err + } + } + + pe.putInt32(int32(c.Timeout / time.Millisecond)) + + if c.Version >= 1 { + pe.putBool(c.ValidateOnly) + } + + return nil +} + +func (c *CreateTopicsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicDetails = make(map[string]*TopicDetail, n) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicDetails[topic] = new(TopicDetail) + if err = c.TopicDetails[topic].decode(pd, version); err != nil { + return err + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + c.Timeout = time.Duration(timeout) * time.Millisecond + + if version >= 1 { + c.ValidateOnly, err = pd.getBool() + if err != nil { + return err + } + + c.Version = version + } + + return nil +} + +func (c *CreateTopicsRequest) key() int16 { + return 19 +} + +func (c *CreateTopicsRequest) version() int16 { + return c.Version +} + +func (c *CreateTopicsRequest) requiredVersion() KafkaVersion { + switch c.Version { + case 2: + return V1_0_0_0 + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} + +type TopicDetail struct { + NumPartitions int32 + ReplicationFactor int16 + ReplicaAssignment map[int32][]int32 + ConfigEntries map[string]*string +} + +func (t *TopicDetail) encode(pe packetEncoder) error { + pe.putInt32(t.NumPartitions) + pe.putInt16(t.ReplicationFactor) + + if err := pe.putArrayLength(len(t.ReplicaAssignment)); err != nil { + return err + } + for partition, assignment := range t.ReplicaAssignment { + pe.putInt32(partition) + if err := pe.putInt32Array(assignment); err != nil { + return err + } + } + + if err := pe.putArrayLength(len(t.ConfigEntries)); err != nil { + return err + } + for configKey, configValue := range t.ConfigEntries { + if err := pe.putString(configKey); err != nil { + return err + } + if err := pe.putNullableString(configValue); err != nil { + return err + } + } + + return nil +} + +func (t *TopicDetail) decode(pd packetDecoder, version int16) (err error) { + if t.NumPartitions, err = pd.getInt32(); err != nil { + return err + } + if t.ReplicationFactor, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.ReplicaAssignment = make(map[int32][]int32, n) + for i := 0; i < n; i++ { + replica, err := pd.getInt32() + if err != nil { + return err + } + if t.ReplicaAssignment[replica], err = pd.getInt32Array(); err != nil { + return err + } + } + } + + n, err = pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.ConfigEntries = make(map[string]*string, n) + for i := 0; i < n; i++ { + configKey, err := pd.getString() + if err != nil { + return err + } + if t.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { + return err + } + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_topics_response.go b/vendor/github.com/Shopify/sarama/create_topics_response.go new file mode 100644 index 0000000..a493e02 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_topics_response.go @@ -0,0 +1,123 @@ +package sarama + +import ( + "fmt" + "time" +) + +type CreateTopicsResponse struct { + Version int16 + ThrottleTime time.Duration + TopicErrors map[string]*TopicError +} + +func (c *CreateTopicsResponse) encode(pe packetEncoder) error { + if c.Version >= 2 { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + } + + if err := pe.putArrayLength(len(c.TopicErrors)); err != nil { + return err + } + for topic, topicError := range c.TopicErrors { + if err := pe.putString(topic); err != nil { + return err + } + if err := topicError.encode(pe, c.Version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateTopicsResponse) decode(pd packetDecoder, version int16) (err error) { + c.Version = version + + if version >= 2 { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicErrors = make(map[string]*TopicError, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicErrors[topic] = new(TopicError) + if err := c.TopicErrors[topic].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateTopicsResponse) key() int16 { + return 19 +} + +func (c *CreateTopicsResponse) version() int16 { + return c.Version +} + +func (c *CreateTopicsResponse) requiredVersion() KafkaVersion { + switch c.Version { + case 2: + return V1_0_0_0 + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} + +type TopicError struct { + Err KError + ErrMsg *string +} + +func (t *TopicError) Error() string { + text := t.Err.Error() + if t.ErrMsg != nil { + text = fmt.Sprintf("%s - %s", text, *t.ErrMsg) + } + return text +} + +func (t *TopicError) encode(pe packetEncoder, version int16) error { + pe.putInt16(int16(t.Err)) + + if version >= 1 { + if err := pe.putNullableString(t.ErrMsg); err != nil { + return err + } + } + + return nil +} + +func (t *TopicError) decode(pd packetDecoder, version int16) (err error) { + kErr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kErr) + + if version >= 1 { + if t.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/decompress.go b/vendor/github.com/Shopify/sarama/decompress.go new file mode 100644 index 0000000..eaccbfc --- /dev/null +++ b/vendor/github.com/Shopify/sarama/decompress.go @@ -0,0 +1,63 @@ +package sarama + +import ( + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + "sync" + + "github.com/eapache/go-xerial-snappy" + "github.com/pierrec/lz4" +) + +var ( + lz4ReaderPool = sync.Pool{ + New: func() interface{} { + return lz4.NewReader(nil) + }, + } + + gzipReaderPool sync.Pool +) + +func decompress(cc CompressionCodec, data []byte) ([]byte, error) { + switch cc { + case CompressionNone: + return data, nil + case CompressionGZIP: + var ( + err error + reader *gzip.Reader + readerIntf = gzipReaderPool.Get() + ) + if readerIntf != nil { + reader = readerIntf.(*gzip.Reader) + } else { + reader, err = gzip.NewReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + } + + defer gzipReaderPool.Put(reader) + + if err := reader.Reset(bytes.NewReader(data)); err != nil { + return nil, err + } + + return ioutil.ReadAll(reader) + case CompressionSnappy: + return snappy.Decode(data) + case CompressionLZ4: + reader := lz4ReaderPool.Get().(*lz4.Reader) + defer lz4ReaderPool.Put(reader) + + reader.Reset(bytes.NewReader(data)) + return ioutil.ReadAll(reader) + case CompressionZSTD: + return zstdDecompress(nil, data) + default: + return nil, PacketDecodingError{fmt.Sprintf("invalid compression specified (%d)", cc)} + } +} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_request.go b/vendor/github.com/Shopify/sarama/delete_groups_request.go new file mode 100644 index 0000000..305a324 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_groups_request.go @@ -0,0 +1,30 @@ +package sarama + +type DeleteGroupsRequest struct { + Groups []string +} + +func (r *DeleteGroupsRequest) encode(pe packetEncoder) error { + return pe.putStringArray(r.Groups) +} + +func (r *DeleteGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + r.Groups, err = pd.getStringArray() + return +} + +func (r *DeleteGroupsRequest) key() int16 { + return 42 +} + +func (r *DeleteGroupsRequest) version() int16 { + return 0 +} + +func (r *DeleteGroupsRequest) requiredVersion() KafkaVersion { + return V1_1_0_0 +} + +func (r *DeleteGroupsRequest) AddGroup(group string) { + r.Groups = append(r.Groups, group) +} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_response.go b/vendor/github.com/Shopify/sarama/delete_groups_response.go new file mode 100644 index 0000000..c067ebb --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_groups_response.go @@ -0,0 +1,70 @@ +package sarama + +import ( + "time" +) + +type DeleteGroupsResponse struct { + ThrottleTime time.Duration + GroupErrorCodes map[string]KError +} + +func (r *DeleteGroupsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(r.GroupErrorCodes)); err != nil { + return err + } + for groupID, errorCode := range r.GroupErrorCodes { + if err := pe.putString(groupID); err != nil { + return err + } + pe.putInt16(int16(errorCode)) + } + + return nil +} + +func (r *DeleteGroupsResponse) decode(pd packetDecoder, version int16) error { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupErrorCodes = make(map[string]KError, n) + for i := 0; i < n; i++ { + groupID, err := pd.getString() + if err != nil { + return err + } + errorCode, err := pd.getInt16() + if err != nil { + return err + } + + r.GroupErrorCodes[groupID] = KError(errorCode) + } + + return nil +} + +func (r *DeleteGroupsResponse) key() int16 { + return 42 +} + +func (r *DeleteGroupsResponse) version() int16 { + return 0 +} + +func (r *DeleteGroupsResponse) requiredVersion() KafkaVersion { + return V1_1_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/delete_records_request.go b/vendor/github.com/Shopify/sarama/delete_records_request.go new file mode 100644 index 0000000..93efafd --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_records_request.go @@ -0,0 +1,126 @@ +package sarama + +import ( + "sort" + "time" +) + +// request message format is: +// [topic] timeout(int32) +// where topic is: +// name(string) [partition] +// where partition is: +// id(int32) offset(int64) + +type DeleteRecordsRequest struct { + Topics map[string]*DeleteRecordsRequestTopic + Timeout time.Duration +} + +func (d *DeleteRecordsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(d.Topics)); err != nil { + return err + } + keys := make([]string, 0, len(d.Topics)) + for topic := range d.Topics { + keys = append(keys, topic) + } + sort.Strings(keys) + for _, topic := range keys { + if err := pe.putString(topic); err != nil { + return err + } + if err := d.Topics[topic].encode(pe); err != nil { + return err + } + } + pe.putInt32(int32(d.Timeout / time.Millisecond)) + + return nil +} + +func (d *DeleteRecordsRequest) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + d.Topics = make(map[string]*DeleteRecordsRequestTopic, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + details := new(DeleteRecordsRequestTopic) + if err = details.decode(pd, version); err != nil { + return err + } + d.Topics[topic] = details + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + d.Timeout = time.Duration(timeout) * time.Millisecond + + return nil +} + +func (d *DeleteRecordsRequest) key() int16 { + return 21 +} + +func (d *DeleteRecordsRequest) version() int16 { + return 0 +} + +func (d *DeleteRecordsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type DeleteRecordsRequestTopic struct { + PartitionOffsets map[int32]int64 // partition => offset +} + +func (t *DeleteRecordsRequestTopic) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(t.PartitionOffsets)); err != nil { + return err + } + keys := make([]int32, 0, len(t.PartitionOffsets)) + for partition := range t.PartitionOffsets { + keys = append(keys, partition) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + for _, partition := range keys { + pe.putInt32(partition) + pe.putInt64(t.PartitionOffsets[partition]) + } + return nil +} + +func (t *DeleteRecordsRequestTopic) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.PartitionOffsets = make(map[int32]int64, n) + for i := 0; i < n; i++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + offset, err := pd.getInt64() + if err != nil { + return err + } + t.PartitionOffsets[partition] = offset + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/delete_records_response.go b/vendor/github.com/Shopify/sarama/delete_records_response.go new file mode 100644 index 0000000..733a58b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_records_response.go @@ -0,0 +1,158 @@ +package sarama + +import ( + "sort" + "time" +) + +// response message format is: +// throttleMs(int32) [topic] +// where topic is: +// name(string) [partition] +// where partition is: +// id(int32) low_watermark(int64) error_code(int16) + +type DeleteRecordsResponse struct { + Version int16 + ThrottleTime time.Duration + Topics map[string]*DeleteRecordsResponseTopic +} + +func (d *DeleteRecordsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(d.Topics)); err != nil { + return err + } + keys := make([]string, 0, len(d.Topics)) + for topic := range d.Topics { + keys = append(keys, topic) + } + sort.Strings(keys) + for _, topic := range keys { + if err := pe.putString(topic); err != nil { + return err + } + if err := d.Topics[topic].encode(pe); err != nil { + return err + } + } + return nil +} + +func (d *DeleteRecordsResponse) decode(pd packetDecoder, version int16) error { + d.Version = version + + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + d.Topics = make(map[string]*DeleteRecordsResponseTopic, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + details := new(DeleteRecordsResponseTopic) + if err = details.decode(pd, version); err != nil { + return err + } + d.Topics[topic] = details + } + } + + return nil +} + +func (d *DeleteRecordsResponse) key() int16 { + return 21 +} + +func (d *DeleteRecordsResponse) version() int16 { + return 0 +} + +func (d *DeleteRecordsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type DeleteRecordsResponseTopic struct { + Partitions map[int32]*DeleteRecordsResponsePartition +} + +func (t *DeleteRecordsResponseTopic) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(t.Partitions)); err != nil { + return err + } + keys := make([]int32, 0, len(t.Partitions)) + for partition := range t.Partitions { + keys = append(keys, partition) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + for _, partition := range keys { + pe.putInt32(partition) + if err := t.Partitions[partition].encode(pe); err != nil { + return err + } + } + return nil +} + +func (t *DeleteRecordsResponseTopic) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.Partitions = make(map[int32]*DeleteRecordsResponsePartition, n) + for i := 0; i < n; i++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + details := new(DeleteRecordsResponsePartition) + if err = details.decode(pd, version); err != nil { + return err + } + t.Partitions[partition] = details + } + } + + return nil +} + +type DeleteRecordsResponsePartition struct { + LowWatermark int64 + Err KError +} + +func (t *DeleteRecordsResponsePartition) encode(pe packetEncoder) error { + pe.putInt64(t.LowWatermark) + pe.putInt16(int16(t.Err)) + return nil +} + +func (t *DeleteRecordsResponsePartition) decode(pd packetDecoder, version int16) error { + lowWatermark, err := pd.getInt64() + if err != nil { + return err + } + t.LowWatermark = lowWatermark + + kErr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kErr) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_request.go b/vendor/github.com/Shopify/sarama/delete_topics_request.go new file mode 100644 index 0000000..911f67d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_topics_request.go @@ -0,0 +1,48 @@ +package sarama + +import "time" + +type DeleteTopicsRequest struct { + Version int16 + Topics []string + Timeout time.Duration +} + +func (d *DeleteTopicsRequest) encode(pe packetEncoder) error { + if err := pe.putStringArray(d.Topics); err != nil { + return err + } + pe.putInt32(int32(d.Timeout / time.Millisecond)) + + return nil +} + +func (d *DeleteTopicsRequest) decode(pd packetDecoder, version int16) (err error) { + if d.Topics, err = pd.getStringArray(); err != nil { + return err + } + timeout, err := pd.getInt32() + if err != nil { + return err + } + d.Timeout = time.Duration(timeout) * time.Millisecond + d.Version = version + return nil +} + +func (d *DeleteTopicsRequest) key() int16 { + return 20 +} + +func (d *DeleteTopicsRequest) version() int16 { + return d.Version +} + +func (d *DeleteTopicsRequest) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_response.go b/vendor/github.com/Shopify/sarama/delete_topics_response.go new file mode 100644 index 0000000..3422546 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_topics_response.go @@ -0,0 +1,78 @@ +package sarama + +import "time" + +type DeleteTopicsResponse struct { + Version int16 + ThrottleTime time.Duration + TopicErrorCodes map[string]KError +} + +func (d *DeleteTopicsResponse) encode(pe packetEncoder) error { + if d.Version >= 1 { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + } + + if err := pe.putArrayLength(len(d.TopicErrorCodes)); err != nil { + return err + } + for topic, errorCode := range d.TopicErrorCodes { + if err := pe.putString(topic); err != nil { + return err + } + pe.putInt16(int16(errorCode)) + } + + return nil +} + +func (d *DeleteTopicsResponse) decode(pd packetDecoder, version int16) (err error) { + if version >= 1 { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + d.Version = version + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + d.TopicErrorCodes = make(map[string]KError, n) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + errorCode, err := pd.getInt16() + if err != nil { + return err + } + + d.TopicErrorCodes[topic] = KError(errorCode) + } + + return nil +} + +func (d *DeleteTopicsResponse) key() int16 { + return 20 +} + +func (d *DeleteTopicsResponse) version() int16 { + return d.Version +} + +func (d *DeleteTopicsResponse) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_request.go b/vendor/github.com/Shopify/sarama/describe_configs_request.go new file mode 100644 index 0000000..ccb587b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_configs_request.go @@ -0,0 +1,112 @@ +package sarama + +type DescribeConfigsRequest struct { + Version int16 + Resources []*ConfigResource + IncludeSynonyms bool +} + +type ConfigResource struct { + Type ConfigResourceType + Name string + ConfigNames []string +} + +func (r *DescribeConfigsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Resources)); err != nil { + return err + } + + for _, c := range r.Resources { + pe.putInt8(int8(c.Type)) + if err := pe.putString(c.Name); err != nil { + return err + } + + if len(c.ConfigNames) == 0 { + pe.putInt32(-1) + continue + } + if err := pe.putStringArray(c.ConfigNames); err != nil { + return err + } + } + + if r.Version >= 1 { + pe.putBool(r.IncludeSynonyms) + } + + return nil +} + +func (r *DescribeConfigsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Resources = make([]*ConfigResource, n) + + for i := 0; i < n; i++ { + r.Resources[i] = &ConfigResource{} + t, err := pd.getInt8() + if err != nil { + return err + } + r.Resources[i].Type = ConfigResourceType(t) + name, err := pd.getString() + if err != nil { + return err + } + r.Resources[i].Name = name + + confLength, err := pd.getArrayLength() + + if err != nil { + return err + } + + if confLength == -1 { + continue + } + + cfnames := make([]string, confLength) + for i := 0; i < confLength; i++ { + s, err := pd.getString() + if err != nil { + return err + } + cfnames[i] = s + } + r.Resources[i].ConfigNames = cfnames + } + r.Version = version + if r.Version >= 1 { + b, err := pd.getBool() + if err != nil { + return err + } + r.IncludeSynonyms = b + } + + return nil +} + +func (r *DescribeConfigsRequest) key() int16 { + return 32 +} + +func (r *DescribeConfigsRequest) version() int16 { + return r.Version +} + +func (r *DescribeConfigsRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V1_1_0_0 + case 2: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_response.go b/vendor/github.com/Shopify/sarama/describe_configs_response.go new file mode 100644 index 0000000..5737232 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_configs_response.go @@ -0,0 +1,320 @@ +package sarama + +import ( + "fmt" + "time" +) + +type ConfigSource int8 + +func (s ConfigSource) String() string { + switch s { + case SourceUnknown: + return "Unknown" + case SourceTopic: + return "Topic" + case SourceDynamicBroker: + return "DynamicBroker" + case SourceDynamicDefaultBroker: + return "DynamicDefaultBroker" + case SourceStaticBroker: + return "StaticBroker" + case SourceDefault: + return "Default" + } + return fmt.Sprintf("Source Invalid: %d", int(s)) +} + +const ( + SourceUnknown ConfigSource = iota + SourceTopic + SourceDynamicBroker + SourceDynamicDefaultBroker + SourceStaticBroker + SourceDefault +) + +type DescribeConfigsResponse struct { + Version int16 + ThrottleTime time.Duration + Resources []*ResourceResponse +} + +type ResourceResponse struct { + ErrorCode int16 + ErrorMsg string + Type ConfigResourceType + Name string + Configs []*ConfigEntry +} + +type ConfigEntry struct { + Name string + Value string + ReadOnly bool + Default bool + Source ConfigSource + Sensitive bool + Synonyms []*ConfigSynonym +} + +type ConfigSynonym struct { + ConfigName string + ConfigValue string + Source ConfigSource +} + +func (r *DescribeConfigsResponse) encode(pe packetEncoder) (err error) { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + if err = pe.putArrayLength(len(r.Resources)); err != nil { + return err + } + + for _, c := range r.Resources { + if err = c.encode(pe, r.Version); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeConfigsResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Resources = make([]*ResourceResponse, n) + for i := 0; i < n; i++ { + rr := &ResourceResponse{} + if err := rr.decode(pd, version); err != nil { + return err + } + r.Resources[i] = rr + } + + return nil +} + +func (r *DescribeConfigsResponse) key() int16 { + return 32 +} + +func (r *DescribeConfigsResponse) version() int16 { + return r.Version +} + +func (r *DescribeConfigsResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V1_0_0_0 + case 2: + return V2_0_0_0 + default: + return V0_11_0_0 + } +} + +func (r *ResourceResponse) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(r.ErrorCode) + + if err = pe.putString(r.ErrorMsg); err != nil { + return err + } + + pe.putInt8(int8(r.Type)) + + if err = pe.putString(r.Name); err != nil { + return err + } + + if err = pe.putArrayLength(len(r.Configs)); err != nil { + return err + } + + for _, c := range r.Configs { + if err = c.encode(pe, version); err != nil { + return err + } + } + return nil +} + +func (r *ResourceResponse) decode(pd packetDecoder, version int16) (err error) { + ec, err := pd.getInt16() + if err != nil { + return err + } + r.ErrorCode = ec + + em, err := pd.getString() + if err != nil { + return err + } + r.ErrorMsg = em + + t, err := pd.getInt8() + if err != nil { + return err + } + r.Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + r.Name = name + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Configs = make([]*ConfigEntry, n) + for i := 0; i < n; i++ { + c := &ConfigEntry{} + if err := c.decode(pd, version); err != nil { + return err + } + r.Configs[i] = c + } + return nil +} + +func (r *ConfigEntry) encode(pe packetEncoder, version int16) (err error) { + if err = pe.putString(r.Name); err != nil { + return err + } + + if err = pe.putString(r.Value); err != nil { + return err + } + + pe.putBool(r.ReadOnly) + + if version <= 0 { + pe.putBool(r.Default) + pe.putBool(r.Sensitive) + } else { + pe.putInt8(int8(r.Source)) + pe.putBool(r.Sensitive) + + if err := pe.putArrayLength(len(r.Synonyms)); err != nil { + return err + } + for _, c := range r.Synonyms { + if err = c.encode(pe, version); err != nil { + return err + } + } + } + + return nil +} + +//https://cwiki.apache.org/confluence/display/KAFKA/KIP-226+-+Dynamic+Broker+Configuration +func (r *ConfigEntry) decode(pd packetDecoder, version int16) (err error) { + if version == 0 { + r.Source = SourceUnknown + } + name, err := pd.getString() + if err != nil { + return err + } + r.Name = name + + value, err := pd.getString() + if err != nil { + return err + } + r.Value = value + + read, err := pd.getBool() + if err != nil { + return err + } + r.ReadOnly = read + + if version == 0 { + defaultB, err := pd.getBool() + if err != nil { + return err + } + r.Default = defaultB + } else { + source, err := pd.getInt8() + if err != nil { + return err + } + r.Source = ConfigSource(source) + } + + sensitive, err := pd.getBool() + if err != nil { + return err + } + r.Sensitive = sensitive + + if version > 0 { + n, err := pd.getArrayLength() + if err != nil { + return err + } + r.Synonyms = make([]*ConfigSynonym, n) + + for i := 0; i < n; i++ { + s := &ConfigSynonym{} + if err := s.decode(pd, version); err != nil { + return err + } + r.Synonyms[i] = s + } + + } + return nil +} + +func (c *ConfigSynonym) encode(pe packetEncoder, version int16) (err error) { + err = pe.putString(c.ConfigName) + if err != nil { + return err + } + + err = pe.putString(c.ConfigValue) + if err != nil { + return err + } + + pe.putInt8(int8(c.Source)) + + return nil +} + +func (c *ConfigSynonym) decode(pd packetDecoder, version int16) error { + name, err := pd.getString() + if err != nil { + return nil + } + c.ConfigName = name + + value, err := pd.getString() + if err != nil { + return nil + } + c.ConfigValue = value + + source, err := pd.getInt8() + if err != nil { + return nil + } + c.Source = ConfigSource(source) + return nil +} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_request.go b/vendor/github.com/Shopify/sarama/describe_groups_request.go new file mode 100644 index 0000000..1fb3567 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_groups_request.go @@ -0,0 +1,30 @@ +package sarama + +type DescribeGroupsRequest struct { + Groups []string +} + +func (r *DescribeGroupsRequest) encode(pe packetEncoder) error { + return pe.putStringArray(r.Groups) +} + +func (r *DescribeGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + r.Groups, err = pd.getStringArray() + return +} + +func (r *DescribeGroupsRequest) key() int16 { + return 15 +} + +func (r *DescribeGroupsRequest) version() int16 { + return 0 +} + +func (r *DescribeGroupsRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +func (r *DescribeGroupsRequest) AddGroup(group string) { + r.Groups = append(r.Groups, group) +} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_response.go b/vendor/github.com/Shopify/sarama/describe_groups_response.go new file mode 100644 index 0000000..542b3a9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_groups_response.go @@ -0,0 +1,187 @@ +package sarama + +type DescribeGroupsResponse struct { + Groups []*GroupDescription +} + +func (r *DescribeGroupsResponse) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Groups)); err != nil { + return err + } + + for _, groupDescription := range r.Groups { + if err := groupDescription.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeGroupsResponse) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Groups = make([]*GroupDescription, n) + for i := 0; i < n; i++ { + r.Groups[i] = new(GroupDescription) + if err := r.Groups[i].decode(pd); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeGroupsResponse) key() int16 { + return 15 +} + +func (r *DescribeGroupsResponse) version() int16 { + return 0 +} + +func (r *DescribeGroupsResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +type GroupDescription struct { + Err KError + GroupId string + State string + ProtocolType string + Protocol string + Members map[string]*GroupMemberDescription +} + +func (gd *GroupDescription) encode(pe packetEncoder) error { + pe.putInt16(int16(gd.Err)) + + if err := pe.putString(gd.GroupId); err != nil { + return err + } + if err := pe.putString(gd.State); err != nil { + return err + } + if err := pe.putString(gd.ProtocolType); err != nil { + return err + } + if err := pe.putString(gd.Protocol); err != nil { + return err + } + + if err := pe.putArrayLength(len(gd.Members)); err != nil { + return err + } + + for memberId, groupMemberDescription := range gd.Members { + if err := pe.putString(memberId); err != nil { + return err + } + if err := groupMemberDescription.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (gd *GroupDescription) decode(pd packetDecoder) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + gd.Err = KError(kerr) + + if gd.GroupId, err = pd.getString(); err != nil { + return + } + if gd.State, err = pd.getString(); err != nil { + return + } + if gd.ProtocolType, err = pd.getString(); err != nil { + return + } + if gd.Protocol, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + gd.Members = make(map[string]*GroupMemberDescription) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + + gd.Members[memberId] = new(GroupMemberDescription) + if err := gd.Members[memberId].decode(pd); err != nil { + return err + } + } + + return nil +} + +type GroupMemberDescription struct { + ClientId string + ClientHost string + MemberMetadata []byte + MemberAssignment []byte +} + +func (gmd *GroupMemberDescription) encode(pe packetEncoder) error { + if err := pe.putString(gmd.ClientId); err != nil { + return err + } + if err := pe.putString(gmd.ClientHost); err != nil { + return err + } + if err := pe.putBytes(gmd.MemberMetadata); err != nil { + return err + } + if err := pe.putBytes(gmd.MemberAssignment); err != nil { + return err + } + + return nil +} + +func (gmd *GroupMemberDescription) decode(pd packetDecoder) (err error) { + if gmd.ClientId, err = pd.getString(); err != nil { + return + } + if gmd.ClientHost, err = pd.getString(); err != nil { + return + } + if gmd.MemberMetadata, err = pd.getBytes(); err != nil { + return + } + if gmd.MemberAssignment, err = pd.getBytes(); err != nil { + return + } + + return nil +} + +func (gmd *GroupMemberDescription) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { + assignment := new(ConsumerGroupMemberAssignment) + err := decode(gmd.MemberAssignment, assignment) + return assignment, err +} + +func (gmd *GroupMemberDescription) GetMemberMetadata() (*ConsumerGroupMemberMetadata, error) { + metadata := new(ConsumerGroupMemberMetadata) + err := decode(gmd.MemberMetadata, metadata) + return metadata, err +} diff --git a/vendor/github.com/Shopify/sarama/describe_log_dirs_request.go b/vendor/github.com/Shopify/sarama/describe_log_dirs_request.go new file mode 100644 index 0000000..cb1e781 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_log_dirs_request.go @@ -0,0 +1,83 @@ +package sarama + +// DescribeLogDirsRequest is a describe request to get partitions' log size +type DescribeLogDirsRequest struct { + // Version 0 and 1 are equal + // The version number is bumped to indicate that on quota violation brokers send out responses before throttling. + Version int16 + + // If this is an empty array, all topics will be queried + DescribeTopics []DescribeLogDirsRequestTopic +} + +// DescribeLogDirsRequestTopic is a describe request about the log dir of one or more partitions within a Topic +type DescribeLogDirsRequestTopic struct { + Topic string + PartitionIDs []int32 +} + +func (r *DescribeLogDirsRequest) encode(pe packetEncoder) error { + length := len(r.DescribeTopics) + if length == 0 { + // In order to query all topics we must send null + length = -1 + } + + if err := pe.putArrayLength(length); err != nil { + return err + } + + for _, d := range r.DescribeTopics { + if err := pe.putString(d.Topic); err != nil { + return err + } + + if err := pe.putInt32Array(d.PartitionIDs); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeLogDirsRequest) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == -1 { + n = 0 + } + + topics := make([]DescribeLogDirsRequestTopic, n) + for i := 0; i < n; i++ { + topics[i] = DescribeLogDirsRequestTopic{} + + topic, err := pd.getString() + if err != nil { + return err + } + topics[i].Topic = topic + + pIDs, err := pd.getInt32Array() + if err != nil { + return err + } + topics[i].PartitionIDs = pIDs + } + r.DescribeTopics = topics + + return nil +} + +func (r *DescribeLogDirsRequest) key() int16 { + return 35 +} + +func (r *DescribeLogDirsRequest) version() int16 { + return r.Version +} + +func (r *DescribeLogDirsRequest) requiredVersion() KafkaVersion { + return V1_0_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/describe_log_dirs_response.go b/vendor/github.com/Shopify/sarama/describe_log_dirs_response.go new file mode 100644 index 0000000..d207312 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_log_dirs_response.go @@ -0,0 +1,219 @@ +package sarama + +import "time" + +type DescribeLogDirsResponse struct { + ThrottleTime time.Duration + + // Version 0 and 1 are equal + // The version number is bumped to indicate that on quota violation brokers send out responses before throttling. + Version int16 + + LogDirs []DescribeLogDirsResponseDirMetadata +} + +func (r *DescribeLogDirsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(r.LogDirs)); err != nil { + return err + } + + for _, dir := range r.LogDirs { + if err := dir.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeLogDirsResponse) decode(pd packetDecoder, version int16) error { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + // Decode array of DescribeLogDirsResponseDirMetadata + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.LogDirs = make([]DescribeLogDirsResponseDirMetadata, n) + for i := 0; i < n; i++ { + dir := DescribeLogDirsResponseDirMetadata{} + if err := dir.decode(pd, version); err != nil { + return err + } + r.LogDirs[i] = dir + } + + return nil +} + +func (r *DescribeLogDirsResponse) key() int16 { + return 35 +} + +func (r *DescribeLogDirsResponse) version() int16 { + return r.Version +} + +func (r *DescribeLogDirsResponse) requiredVersion() KafkaVersion { + return V1_0_0_0 +} + +type DescribeLogDirsResponseDirMetadata struct { + ErrorCode KError + + // The absolute log directory path + Path string + Topics []DescribeLogDirsResponseTopic +} + +func (r *DescribeLogDirsResponseDirMetadata) encode(pe packetEncoder) error { + pe.putInt16(int16(r.ErrorCode)) + + if err := pe.putString(r.Path); err != nil { + return err + } + + for _, topic := range r.Topics { + if err := topic.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeLogDirsResponseDirMetadata) decode(pd packetDecoder, version int16) error { + errCode, err := pd.getInt16() + if err != nil { + return err + } + r.ErrorCode = KError(errCode) + + path, err := pd.getString() + if err != nil { + return err + } + r.Path = path + + // Decode array of DescribeLogDirsResponseTopic + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Topics = make([]DescribeLogDirsResponseTopic, n) + for i := 0; i < n; i++ { + t := DescribeLogDirsResponseTopic{} + + if err := t.decode(pd, version); err != nil { + return err + } + + r.Topics[i] = t + } + + return nil +} + +// DescribeLogDirsResponseTopic contains a topic's partitions descriptions +type DescribeLogDirsResponseTopic struct { + Topic string + Partitions []DescribeLogDirsResponsePartition +} + +func (r *DescribeLogDirsResponseTopic) encode(pe packetEncoder) error { + if err := pe.putString(r.Topic); err != nil { + return err + } + + for _, partition := range r.Partitions { + if err := partition.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeLogDirsResponseTopic) decode(pd packetDecoder, version int16) error { + t, err := pd.getString() + if err != nil { + return err + } + r.Topic = t + + n, err := pd.getArrayLength() + if err != nil { + return err + } + r.Partitions = make([]DescribeLogDirsResponsePartition, n) + for i := 0; i < n; i++ { + p := DescribeLogDirsResponsePartition{} + if err := p.decode(pd, version); err != nil { + return err + } + r.Partitions[i] = p + } + + return nil +} + +// DescribeLogDirsResponsePartition describes a partition's log directory +type DescribeLogDirsResponsePartition struct { + PartitionID int32 + + // The size of the log segments of the partition in bytes. + Size int64 + + // The lag of the log's LEO w.r.t. partition's HW (if it is the current log for the partition) or + // current replica's LEO (if it is the future log for the partition) + OffsetLag int64 + + // True if this log is created by AlterReplicaLogDirsRequest and will replace the current log of + // the replica in the future. + IsTemporary bool +} + +func (r *DescribeLogDirsResponsePartition) encode(pe packetEncoder) error { + pe.putInt32(r.PartitionID) + pe.putInt64(r.Size) + pe.putInt64(r.OffsetLag) + pe.putBool(r.IsTemporary) + + return nil +} + +func (r *DescribeLogDirsResponsePartition) decode(pd packetDecoder, version int16) error { + pID, err := pd.getInt32() + if err != nil { + return err + } + r.PartitionID = pID + + size, err := pd.getInt64() + if err != nil { + return err + } + r.Size = size + + lag, err := pd.getInt64() + if err != nil { + return err + } + r.OffsetLag = lag + + isTemp, err := pd.getBool() + if err != nil { + return err + } + r.IsTemporary = isTemp + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/dev.yml b/vendor/github.com/Shopify/sarama/dev.yml new file mode 100644 index 0000000..481f681 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/dev.yml @@ -0,0 +1,10 @@ +name: sarama + +up: + - go: + version: '1.13.1' + +commands: + test: + run: make test + desc: 'run unit tests' diff --git a/vendor/github.com/Shopify/sarama/encoder_decoder.go b/vendor/github.com/Shopify/sarama/encoder_decoder.go new file mode 100644 index 0000000..7ce3bc0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/encoder_decoder.go @@ -0,0 +1,89 @@ +package sarama + +import ( + "fmt" + + "github.com/rcrowley/go-metrics" +) + +// Encoder is the interface that wraps the basic Encode method. +// Anything implementing Encoder can be turned into bytes using Kafka's encoding rules. +type encoder interface { + encode(pe packetEncoder) error +} + +// Encode takes an Encoder and turns it into bytes while potentially recording metrics. +func encode(e encoder, metricRegistry metrics.Registry) ([]byte, error) { + if e == nil { + return nil, nil + } + + var prepEnc prepEncoder + var realEnc realEncoder + + err := e.encode(&prepEnc) + if err != nil { + return nil, err + } + + if prepEnc.length < 0 || prepEnc.length > int(MaxRequestSize) { + return nil, PacketEncodingError{fmt.Sprintf("invalid request size (%d)", prepEnc.length)} + } + + realEnc.raw = make([]byte, prepEnc.length) + realEnc.registry = metricRegistry + err = e.encode(&realEnc) + if err != nil { + return nil, err + } + + return realEnc.raw, nil +} + +// Decoder is the interface that wraps the basic Decode method. +// Anything implementing Decoder can be extracted from bytes using Kafka's encoding rules. +type decoder interface { + decode(pd packetDecoder) error +} + +type versionedDecoder interface { + decode(pd packetDecoder, version int16) error +} + +// Decode takes bytes and a Decoder and fills the fields of the decoder from the bytes, +// interpreted using Kafka's encoding rules. +func decode(buf []byte, in decoder) error { + if buf == nil { + return nil + } + + helper := realDecoder{raw: buf} + err := in.decode(&helper) + if err != nil { + return err + } + + if helper.off != len(buf) { + return PacketDecodingError{"invalid length"} + } + + return nil +} + +func versionedDecode(buf []byte, in versionedDecoder, version int16) error { + if buf == nil { + return nil + } + + helper := realDecoder{raw: buf} + err := in.decode(&helper, version) + if err != nil { + return err + } + + if helper.off != len(buf) { + return PacketDecodingError{"invalid length"} + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/end_txn_request.go b/vendor/github.com/Shopify/sarama/end_txn_request.go new file mode 100644 index 0000000..2cd9b50 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/end_txn_request.go @@ -0,0 +1,50 @@ +package sarama + +type EndTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + TransactionResult bool +} + +func (a *EndTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + + pe.putInt64(a.ProducerID) + + pe.putInt16(a.ProducerEpoch) + + pe.putBool(a.TransactionResult) + + return nil +} + +func (a *EndTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + if a.TransactionResult, err = pd.getBool(); err != nil { + return err + } + return nil +} + +func (a *EndTxnRequest) key() int16 { + return 26 +} + +func (a *EndTxnRequest) version() int16 { + return 0 +} + +func (a *EndTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/end_txn_response.go b/vendor/github.com/Shopify/sarama/end_txn_response.go new file mode 100644 index 0000000..33b27e3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/end_txn_response.go @@ -0,0 +1,44 @@ +package sarama + +import ( + "time" +) + +type EndTxnResponse struct { + ThrottleTime time.Duration + Err KError +} + +func (e *EndTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(e.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(e.Err)) + return nil +} + +func (e *EndTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + e.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + e.Err = KError(kerr) + + return nil +} + +func (e *EndTxnResponse) key() int16 { + return 25 +} + +func (e *EndTxnResponse) version() int16 { + return 0 +} + +func (e *EndTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/errors.go b/vendor/github.com/Shopify/sarama/errors.go new file mode 100644 index 0000000..97be3c0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/errors.go @@ -0,0 +1,369 @@ +package sarama + +import ( + "errors" + "fmt" +) + +// ErrOutOfBrokers is the error returned when the client has run out of brokers to talk to because all of them errored +// or otherwise failed to respond. +var ErrOutOfBrokers = errors.New("kafka: client has run out of available brokers to talk to (Is your cluster reachable?)") + +// ErrClosedClient is the error returned when a method is called on a client that has been closed. +var ErrClosedClient = errors.New("kafka: tried to use a client that was closed") + +// ErrIncompleteResponse is the error returned when the server returns a syntactically valid response, but it does +// not contain the expected information. +var ErrIncompleteResponse = errors.New("kafka: response did not contain all the expected topic/partition blocks") + +// ErrInvalidPartition is the error returned when a partitioner returns an invalid partition index +// (meaning one outside of the range [0...numPartitions-1]). +var ErrInvalidPartition = errors.New("kafka: partitioner returned an invalid partition index") + +// ErrAlreadyConnected is the error returned when calling Open() on a Broker that is already connected or connecting. +var ErrAlreadyConnected = errors.New("kafka: broker connection already initiated") + +// ErrNotConnected is the error returned when trying to send or call Close() on a Broker that is not connected. +var ErrNotConnected = errors.New("kafka: broker not connected") + +// ErrInsufficientData is returned when decoding and the packet is truncated. This can be expected +// when requesting messages, since as an optimization the server is allowed to return a partial message at the end +// of the message set. +var ErrInsufficientData = errors.New("kafka: insufficient data to decode packet, more bytes expected") + +// ErrShuttingDown is returned when a producer receives a message during shutdown. +var ErrShuttingDown = errors.New("kafka: message received by producer in process of shutting down") + +// ErrMessageTooLarge is returned when the next message to consume is larger than the configured Consumer.Fetch.Max +var ErrMessageTooLarge = errors.New("kafka: message is larger than Consumer.Fetch.Max") + +// ErrConsumerOffsetNotAdvanced is returned when a partition consumer didn't advance its offset after parsing +// a RecordBatch. +var ErrConsumerOffsetNotAdvanced = errors.New("kafka: consumer offset was not advanced after a RecordBatch") + +// ErrControllerNotAvailable is returned when server didn't give correct controller id. May be kafka server's version +// is lower than 0.10.0.0. +var ErrControllerNotAvailable = errors.New("kafka: controller is not available") + +// ErrNoTopicsToUpdateMetadata is returned when Meta.Full is set to false but no specific topics were found to update +// the metadata. +var ErrNoTopicsToUpdateMetadata = errors.New("kafka: no specific topics to update metadata") + +// PacketEncodingError is returned from a failure while encoding a Kafka packet. This can happen, for example, +// if you try to encode a string over 2^15 characters in length, since Kafka's encoding rules do not permit that. +type PacketEncodingError struct { + Info string +} + +func (err PacketEncodingError) Error() string { + return fmt.Sprintf("kafka: error encoding packet: %s", err.Info) +} + +// PacketDecodingError is returned when there was an error (other than truncated data) decoding the Kafka broker's response. +// This can be a bad CRC or length field, or any other invalid value. +type PacketDecodingError struct { + Info string +} + +func (err PacketDecodingError) Error() string { + return fmt.Sprintf("kafka: error decoding packet: %s", err.Info) +} + +// ConfigurationError is the type of error returned from a constructor (e.g. NewClient, or NewConsumer) +// when the specified configuration is invalid. +type ConfigurationError string + +func (err ConfigurationError) Error() string { + return "kafka: invalid configuration (" + string(err) + ")" +} + +// KError is the type of error that can be returned directly by the Kafka broker. +// See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ErrorCodes +type KError int16 + +// MultiError is used to contain multi error. +type MultiError struct { + Errors *[]error +} + +func (mErr MultiError) Error() string { + var errString = "" + for _, err := range *mErr.Errors { + errString += err.Error() + "," + } + return errString +} + +// ErrDeleteRecords is the type of error returned when fail to delete the required records +type ErrDeleteRecords struct { + MultiError +} + +func (err ErrDeleteRecords) Error() string { + return "kafka server: failed to delete records " + err.MultiError.Error() +} + +// Numeric error codes returned by the Kafka server. +const ( + ErrNoError KError = 0 + ErrUnknown KError = -1 + ErrOffsetOutOfRange KError = 1 + ErrInvalidMessage KError = 2 + ErrUnknownTopicOrPartition KError = 3 + ErrInvalidMessageSize KError = 4 + ErrLeaderNotAvailable KError = 5 + ErrNotLeaderForPartition KError = 6 + ErrRequestTimedOut KError = 7 + ErrBrokerNotAvailable KError = 8 + ErrReplicaNotAvailable KError = 9 + ErrMessageSizeTooLarge KError = 10 + ErrStaleControllerEpochCode KError = 11 + ErrOffsetMetadataTooLarge KError = 12 + ErrNetworkException KError = 13 + ErrOffsetsLoadInProgress KError = 14 + ErrConsumerCoordinatorNotAvailable KError = 15 + ErrNotCoordinatorForConsumer KError = 16 + ErrInvalidTopic KError = 17 + ErrMessageSetSizeTooLarge KError = 18 + ErrNotEnoughReplicas KError = 19 + ErrNotEnoughReplicasAfterAppend KError = 20 + ErrInvalidRequiredAcks KError = 21 + ErrIllegalGeneration KError = 22 + ErrInconsistentGroupProtocol KError = 23 + ErrInvalidGroupId KError = 24 + ErrUnknownMemberId KError = 25 + ErrInvalidSessionTimeout KError = 26 + ErrRebalanceInProgress KError = 27 + ErrInvalidCommitOffsetSize KError = 28 + ErrTopicAuthorizationFailed KError = 29 + ErrGroupAuthorizationFailed KError = 30 + ErrClusterAuthorizationFailed KError = 31 + ErrInvalidTimestamp KError = 32 + ErrUnsupportedSASLMechanism KError = 33 + ErrIllegalSASLState KError = 34 + ErrUnsupportedVersion KError = 35 + ErrTopicAlreadyExists KError = 36 + ErrInvalidPartitions KError = 37 + ErrInvalidReplicationFactor KError = 38 + ErrInvalidReplicaAssignment KError = 39 + ErrInvalidConfig KError = 40 + ErrNotController KError = 41 + ErrInvalidRequest KError = 42 + ErrUnsupportedForMessageFormat KError = 43 + ErrPolicyViolation KError = 44 + ErrOutOfOrderSequenceNumber KError = 45 + ErrDuplicateSequenceNumber KError = 46 + ErrInvalidProducerEpoch KError = 47 + ErrInvalidTxnState KError = 48 + ErrInvalidProducerIDMapping KError = 49 + ErrInvalidTransactionTimeout KError = 50 + ErrConcurrentTransactions KError = 51 + ErrTransactionCoordinatorFenced KError = 52 + ErrTransactionalIDAuthorizationFailed KError = 53 + ErrSecurityDisabled KError = 54 + ErrOperationNotAttempted KError = 55 + ErrKafkaStorageError KError = 56 + ErrLogDirNotFound KError = 57 + ErrSASLAuthenticationFailed KError = 58 + ErrUnknownProducerID KError = 59 + ErrReassignmentInProgress KError = 60 + ErrDelegationTokenAuthDisabled KError = 61 + ErrDelegationTokenNotFound KError = 62 + ErrDelegationTokenOwnerMismatch KError = 63 + ErrDelegationTokenRequestNotAllowed KError = 64 + ErrDelegationTokenAuthorizationFailed KError = 65 + ErrDelegationTokenExpired KError = 66 + ErrInvalidPrincipalType KError = 67 + ErrNonEmptyGroup KError = 68 + ErrGroupIDNotFound KError = 69 + ErrFetchSessionIDNotFound KError = 70 + ErrInvalidFetchSessionEpoch KError = 71 + ErrListenerNotFound KError = 72 + ErrTopicDeletionDisabled KError = 73 + ErrFencedLeaderEpoch KError = 74 + ErrUnknownLeaderEpoch KError = 75 + ErrUnsupportedCompressionType KError = 76 + ErrStaleBrokerEpoch KError = 77 + ErrOffsetNotAvailable KError = 78 + ErrMemberIdRequired KError = 79 + ErrPreferredLeaderNotAvailable KError = 80 + ErrGroupMaxSizeReached KError = 81 + ErrFencedInstancedId KError = 82 +) + +func (err KError) Error() string { + // Error messages stolen/adapted from + // https://kafka.apache.org/protocol#protocol_error_codes + switch err { + case ErrNoError: + return "kafka server: Not an error, why are you printing me?" + case ErrUnknown: + return "kafka server: Unexpected (unknown?) server error." + case ErrOffsetOutOfRange: + return "kafka server: The requested offset is outside the range of offsets maintained by the server for the given topic/partition." + case ErrInvalidMessage: + return "kafka server: Message contents does not match its CRC." + case ErrUnknownTopicOrPartition: + return "kafka server: Request was for a topic or partition that does not exist on this broker." + case ErrInvalidMessageSize: + return "kafka server: The message has a negative size." + case ErrLeaderNotAvailable: + return "kafka server: In the middle of a leadership election, there is currently no leader for this partition and hence it is unavailable for writes." + case ErrNotLeaderForPartition: + return "kafka server: Tried to send a message to a replica that is not the leader for some partition. Your metadata is out of date." + case ErrRequestTimedOut: + return "kafka server: Request exceeded the user-specified time limit in the request." + case ErrBrokerNotAvailable: + return "kafka server: Broker not available. Not a client facing error, we should never receive this!!!" + case ErrReplicaNotAvailable: + return "kafka server: Replica information not available, one or more brokers are down." + case ErrMessageSizeTooLarge: + return "kafka server: Message was too large, server rejected it to avoid allocation error." + case ErrStaleControllerEpochCode: + return "kafka server: StaleControllerEpochCode (internal error code for broker-to-broker communication)." + case ErrOffsetMetadataTooLarge: + return "kafka server: Specified a string larger than the configured maximum for offset metadata." + case ErrNetworkException: + return "kafka server: The server disconnected before a response was received." + case ErrOffsetsLoadInProgress: + return "kafka server: The broker is still loading offsets after a leader change for that offset's topic partition." + case ErrConsumerCoordinatorNotAvailable: + return "kafka server: Offset's topic has not yet been created." + case ErrNotCoordinatorForConsumer: + return "kafka server: Request was for a consumer group that is not coordinated by this broker." + case ErrInvalidTopic: + return "kafka server: The request attempted to perform an operation on an invalid topic." + case ErrMessageSetSizeTooLarge: + return "kafka server: The request included message batch larger than the configured segment size on the server." + case ErrNotEnoughReplicas: + return "kafka server: Messages are rejected since there are fewer in-sync replicas than required." + case ErrNotEnoughReplicasAfterAppend: + return "kafka server: Messages are written to the log, but to fewer in-sync replicas than required." + case ErrInvalidRequiredAcks: + return "kafka server: The number of required acks is invalid (should be either -1, 0, or 1)." + case ErrIllegalGeneration: + return "kafka server: The provided generation id is not the current generation." + case ErrInconsistentGroupProtocol: + return "kafka server: The provider group protocol type is incompatible with the other members." + case ErrInvalidGroupId: + return "kafka server: The provided group id was empty." + case ErrUnknownMemberId: + return "kafka server: The provided member is not known in the current generation." + case ErrInvalidSessionTimeout: + return "kafka server: The provided session timeout is outside the allowed range." + case ErrRebalanceInProgress: + return "kafka server: A rebalance for the group is in progress. Please re-join the group." + case ErrInvalidCommitOffsetSize: + return "kafka server: The provided commit metadata was too large." + case ErrTopicAuthorizationFailed: + return "kafka server: The client is not authorized to access this topic." + case ErrGroupAuthorizationFailed: + return "kafka server: The client is not authorized to access this group." + case ErrClusterAuthorizationFailed: + return "kafka server: The client is not authorized to send this request type." + case ErrInvalidTimestamp: + return "kafka server: The timestamp of the message is out of acceptable range." + case ErrUnsupportedSASLMechanism: + return "kafka server: The broker does not support the requested SASL mechanism." + case ErrIllegalSASLState: + return "kafka server: Request is not valid given the current SASL state." + case ErrUnsupportedVersion: + return "kafka server: The version of API is not supported." + case ErrTopicAlreadyExists: + return "kafka server: Topic with this name already exists." + case ErrInvalidPartitions: + return "kafka server: Number of partitions is invalid." + case ErrInvalidReplicationFactor: + return "kafka server: Replication-factor is invalid." + case ErrInvalidReplicaAssignment: + return "kafka server: Replica assignment is invalid." + case ErrInvalidConfig: + return "kafka server: Configuration is invalid." + case ErrNotController: + return "kafka server: This is not the correct controller for this cluster." + case ErrInvalidRequest: + return "kafka server: This most likely occurs because of a request being malformed by the client library or the message was sent to an incompatible broker. See the broker logs for more details." + case ErrUnsupportedForMessageFormat: + return "kafka server: The requested operation is not supported by the message format version." + case ErrPolicyViolation: + return "kafka server: Request parameters do not satisfy the configured policy." + case ErrOutOfOrderSequenceNumber: + return "kafka server: The broker received an out of order sequence number." + case ErrDuplicateSequenceNumber: + return "kafka server: The broker received a duplicate sequence number." + case ErrInvalidProducerEpoch: + return "kafka server: Producer attempted an operation with an old epoch." + case ErrInvalidTxnState: + return "kafka server: The producer attempted a transactional operation in an invalid state." + case ErrInvalidProducerIDMapping: + return "kafka server: The producer attempted to use a producer id which is not currently assigned to its transactional id." + case ErrInvalidTransactionTimeout: + return "kafka server: The transaction timeout is larger than the maximum value allowed by the broker (as configured by max.transaction.timeout.ms)." + case ErrConcurrentTransactions: + return "kafka server: The producer attempted to update a transaction while another concurrent operation on the same transaction was ongoing." + case ErrTransactionCoordinatorFenced: + return "kafka server: The transaction coordinator sending a WriteTxnMarker is no longer the current coordinator for a given producer." + case ErrTransactionalIDAuthorizationFailed: + return "kafka server: Transactional ID authorization failed." + case ErrSecurityDisabled: + return "kafka server: Security features are disabled." + case ErrOperationNotAttempted: + return "kafka server: The broker did not attempt to execute this operation." + case ErrKafkaStorageError: + return "kafka server: Disk error when trying to access log file on the disk." + case ErrLogDirNotFound: + return "kafka server: The specified log directory is not found in the broker config." + case ErrSASLAuthenticationFailed: + return "kafka server: SASL Authentication failed." + case ErrUnknownProducerID: + return "kafka server: The broker could not locate the producer metadata associated with the Producer ID." + case ErrReassignmentInProgress: + return "kafka server: A partition reassignment is in progress." + case ErrDelegationTokenAuthDisabled: + return "kafka server: Delegation Token feature is not enabled." + case ErrDelegationTokenNotFound: + return "kafka server: Delegation Token is not found on server." + case ErrDelegationTokenOwnerMismatch: + return "kafka server: Specified Principal is not valid Owner/Renewer." + case ErrDelegationTokenRequestNotAllowed: + return "kafka server: Delegation Token requests are not allowed on PLAINTEXT/1-way SSL channels and on delegation token authenticated channels." + case ErrDelegationTokenAuthorizationFailed: + return "kafka server: Delegation Token authorization failed." + case ErrDelegationTokenExpired: + return "kafka server: Delegation Token is expired." + case ErrInvalidPrincipalType: + return "kafka server: Supplied principalType is not supported." + case ErrNonEmptyGroup: + return "kafka server: The group is not empty." + case ErrGroupIDNotFound: + return "kafka server: The group id does not exist." + case ErrFetchSessionIDNotFound: + return "kafka server: The fetch session ID was not found." + case ErrInvalidFetchSessionEpoch: + return "kafka server: The fetch session epoch is invalid." + case ErrListenerNotFound: + return "kafka server: There is no listener on the leader broker that matches the listener on which metadata request was processed." + case ErrTopicDeletionDisabled: + return "kafka server: Topic deletion is disabled." + case ErrFencedLeaderEpoch: + return "kafka server: The leader epoch in the request is older than the epoch on the broker." + case ErrUnknownLeaderEpoch: + return "kafka server: The leader epoch in the request is newer than the epoch on the broker." + case ErrUnsupportedCompressionType: + return "kafka server: The requesting client does not support the compression type of given partition." + case ErrStaleBrokerEpoch: + return "kafka server: Broker epoch has changed" + case ErrOffsetNotAvailable: + return "kafka server: The leader high watermark has not caught up from a recent leader election so the offsets cannot be guaranteed to be monotonically increasing" + case ErrMemberIdRequired: + return "kafka server: The group member needs to have a valid member id before actually entering a consumer group" + case ErrPreferredLeaderNotAvailable: + return "kafka server: The preferred leader was not available" + case ErrGroupMaxSizeReached: + return "kafka server: Consumer group The consumer group has reached its max size. already has the configured maximum number of members." + case ErrFencedInstancedId: + return "kafka server: The broker rejected this static consumer since another consumer with the same group.instance.id has registered with a different member.id." + } + + return fmt.Sprintf("Unknown error, how did this happen? Error code = %d", err) +} diff --git a/vendor/github.com/Shopify/sarama/fetch_request.go b/vendor/github.com/Shopify/sarama/fetch_request.go new file mode 100644 index 0000000..4db9ddd --- /dev/null +++ b/vendor/github.com/Shopify/sarama/fetch_request.go @@ -0,0 +1,170 @@ +package sarama + +type fetchRequestBlock struct { + fetchOffset int64 + maxBytes int32 +} + +func (b *fetchRequestBlock) encode(pe packetEncoder) error { + pe.putInt64(b.fetchOffset) + pe.putInt32(b.maxBytes) + return nil +} + +func (b *fetchRequestBlock) decode(pd packetDecoder) (err error) { + if b.fetchOffset, err = pd.getInt64(); err != nil { + return err + } + if b.maxBytes, err = pd.getInt32(); err != nil { + return err + } + return nil +} + +// FetchRequest (API key 1) will fetch Kafka messages. Version 3 introduced the MaxBytes field. See +// https://issues.apache.org/jira/browse/KAFKA-2063 for a discussion of the issues leading up to that. The KIP is at +// https://cwiki.apache.org/confluence/display/KAFKA/KIP-74%3A+Add+Fetch+Response+Size+Limit+in+Bytes +type FetchRequest struct { + MaxWaitTime int32 + MinBytes int32 + MaxBytes int32 + Version int16 + Isolation IsolationLevel + blocks map[string]map[int32]*fetchRequestBlock +} + +type IsolationLevel int8 + +const ( + ReadUncommitted IsolationLevel = iota + ReadCommitted +) + +func (r *FetchRequest) encode(pe packetEncoder) (err error) { + pe.putInt32(-1) // replica ID is always -1 for clients + pe.putInt32(r.MaxWaitTime) + pe.putInt32(r.MinBytes) + if r.Version >= 3 { + pe.putInt32(r.MaxBytes) + } + if r.Version >= 4 { + pe.putInt8(int8(r.Isolation)) + } + err = pe.putArrayLength(len(r.blocks)) + if err != nil { + return err + } + for topic, blocks := range r.blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(blocks)) + if err != nil { + return err + } + for partition, block := range blocks { + pe.putInt32(partition) + err = block.encode(pe) + if err != nil { + return err + } + } + } + return nil +} + +func (r *FetchRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + if _, err = pd.getInt32(); err != nil { + return err + } + if r.MaxWaitTime, err = pd.getInt32(); err != nil { + return err + } + if r.MinBytes, err = pd.getInt32(); err != nil { + return err + } + if r.Version >= 3 { + if r.MaxBytes, err = pd.getInt32(); err != nil { + return err + } + } + if r.Version >= 4 { + isolation, err := pd.getInt8() + if err != nil { + return err + } + r.Isolation = IsolationLevel(isolation) + } + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*fetchRequestBlock) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*fetchRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + fetchBlock := &fetchRequestBlock{} + if err = fetchBlock.decode(pd); err != nil { + return err + } + r.blocks[topic][partition] = fetchBlock + } + } + return nil +} + +func (r *FetchRequest) key() int16 { + return 1 +} + +func (r *FetchRequest) version() int16 { + return r.Version +} + +func (r *FetchRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_10_1_0 + case 4: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *FetchRequest) AddBlock(topic string, partitionID int32, fetchOffset int64, maxBytes int32) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*fetchRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*fetchRequestBlock) + } + + tmp := new(fetchRequestBlock) + tmp.maxBytes = maxBytes + tmp.fetchOffset = fetchOffset + + r.blocks[topic][partitionID] = tmp +} diff --git a/vendor/github.com/Shopify/sarama/fetch_response.go b/vendor/github.com/Shopify/sarama/fetch_response.go new file mode 100644 index 0000000..3afc187 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/fetch_response.go @@ -0,0 +1,489 @@ +package sarama + +import ( + "sort" + "time" +) + +type AbortedTransaction struct { + ProducerID int64 + FirstOffset int64 +} + +func (t *AbortedTransaction) decode(pd packetDecoder) (err error) { + if t.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if t.FirstOffset, err = pd.getInt64(); err != nil { + return err + } + + return nil +} + +func (t *AbortedTransaction) encode(pe packetEncoder) (err error) { + pe.putInt64(t.ProducerID) + pe.putInt64(t.FirstOffset) + + return nil +} + +type FetchResponseBlock struct { + Err KError + HighWaterMarkOffset int64 + LastStableOffset int64 + AbortedTransactions []*AbortedTransaction + Records *Records // deprecated: use FetchResponseBlock.RecordsSet + RecordsSet []*Records + Partial bool +} + +func (b *FetchResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + b.HighWaterMarkOffset, err = pd.getInt64() + if err != nil { + return err + } + + if version >= 4 { + b.LastStableOffset, err = pd.getInt64() + if err != nil { + return err + } + + numTransact, err := pd.getArrayLength() + if err != nil { + return err + } + + if numTransact >= 0 { + b.AbortedTransactions = make([]*AbortedTransaction, numTransact) + } + + for i := 0; i < numTransact; i++ { + transact := new(AbortedTransaction) + if err = transact.decode(pd); err != nil { + return err + } + b.AbortedTransactions[i] = transact + } + } + + recordsSize, err := pd.getInt32() + if err != nil { + return err + } + + recordsDecoder, err := pd.getSubset(int(recordsSize)) + if err != nil { + return err + } + + b.RecordsSet = []*Records{} + + for recordsDecoder.remaining() > 0 { + records := &Records{} + if err := records.decode(recordsDecoder); err != nil { + // If we have at least one decoded records, this is not an error + if err == ErrInsufficientData { + if len(b.RecordsSet) == 0 { + b.Partial = true + } + break + } + return err + } + + partial, err := records.isPartial() + if err != nil { + return err + } + + n, err := records.numRecords() + if err != nil { + return err + } + + if n > 0 || (partial && len(b.RecordsSet) == 0) { + b.RecordsSet = append(b.RecordsSet, records) + + if b.Records == nil { + b.Records = records + } + } + + overflow, err := records.isOverflow() + if err != nil { + return err + } + + if partial || overflow { + break + } + } + + return nil +} + +func (b *FetchResponseBlock) numRecords() (int, error) { + sum := 0 + + for _, records := range b.RecordsSet { + count, err := records.numRecords() + if err != nil { + return 0, err + } + + sum += count + } + + return sum, nil +} + +func (b *FetchResponseBlock) isPartial() (bool, error) { + if b.Partial { + return true, nil + } + + if len(b.RecordsSet) == 1 { + return b.RecordsSet[0].isPartial() + } + + return false, nil +} + +func (b *FetchResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + + pe.putInt64(b.HighWaterMarkOffset) + + if version >= 4 { + pe.putInt64(b.LastStableOffset) + + if err = pe.putArrayLength(len(b.AbortedTransactions)); err != nil { + return err + } + for _, transact := range b.AbortedTransactions { + if err = transact.encode(pe); err != nil { + return err + } + } + } + + pe.push(&lengthField{}) + for _, records := range b.RecordsSet { + err = records.encode(pe) + if err != nil { + return err + } + } + return pe.pop() +} + +func (b *FetchResponseBlock) getAbortedTransactions() []*AbortedTransaction { + // I can't find any doc that guarantee the field `fetchResponse.AbortedTransactions` is ordered + // plus Java implementation use a PriorityQueue based on `FirstOffset`. I guess we have to order it ourself + at := b.AbortedTransactions + sort.Slice( + at, + func(i, j int) bool { return at[i].FirstOffset < at[j].FirstOffset }, + ) + return at +} + +type FetchResponse struct { + Blocks map[string]map[int32]*FetchResponseBlock + ThrottleTime time.Duration + Version int16 // v1 requires 0.9+, v2 requires 0.10+ + LogAppendTime bool + Timestamp time.Time +} + +func (r *FetchResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.Version >= 1 { + throttle, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttle) * time.Millisecond + } + + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*FetchResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*FetchResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(FetchResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + return nil +} + +func (r *FetchResponse) encode(pe packetEncoder) (err error) { + if r.Version >= 1 { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + } + + err = pe.putArrayLength(len(r.Blocks)) + if err != nil { + return err + } + + for topic, partitions := range r.Blocks { + err = pe.putString(topic) + if err != nil { + return err + } + + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + + for id, block := range partitions { + pe.putInt32(id) + err = block.encode(pe, r.Version) + if err != nil { + return err + } + } + + } + return nil +} + +func (r *FetchResponse) key() int16 { + return 1 +} + +func (r *FetchResponse) version() int16 { + return r.Version +} + +func (r *FetchResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_10_1_0 + case 4: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *FetchResponse) GetBlock(topic string, partition int32) *FetchResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +func (r *FetchResponse) AddError(topic string, partition int32, err KError) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*FetchResponseBlock) + } + partitions, ok := r.Blocks[topic] + if !ok { + partitions = make(map[int32]*FetchResponseBlock) + r.Blocks[topic] = partitions + } + frb, ok := partitions[partition] + if !ok { + frb = new(FetchResponseBlock) + partitions[partition] = frb + } + frb.Err = err +} + +func (r *FetchResponse) getOrCreateBlock(topic string, partition int32) *FetchResponseBlock { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*FetchResponseBlock) + } + partitions, ok := r.Blocks[topic] + if !ok { + partitions = make(map[int32]*FetchResponseBlock) + r.Blocks[topic] = partitions + } + frb, ok := partitions[partition] + if !ok { + frb = new(FetchResponseBlock) + partitions[partition] = frb + } + + return frb +} + +func encodeKV(key, value Encoder) ([]byte, []byte) { + var kb []byte + var vb []byte + if key != nil { + kb, _ = key.Encode() + } + if value != nil { + vb, _ = value.Encode() + } + + return kb, vb +} + +func (r *FetchResponse) AddMessageWithTimestamp(topic string, partition int32, key, value Encoder, offset int64, timestamp time.Time, version int8) { + frb := r.getOrCreateBlock(topic, partition) + kb, vb := encodeKV(key, value) + if r.LogAppendTime { + timestamp = r.Timestamp + } + msg := &Message{Key: kb, Value: vb, LogAppendTime: r.LogAppendTime, Timestamp: timestamp, Version: version} + msgBlock := &MessageBlock{Msg: msg, Offset: offset} + if len(frb.RecordsSet) == 0 { + records := newLegacyRecords(&MessageSet{}) + frb.RecordsSet = []*Records{&records} + } + set := frb.RecordsSet[0].MsgSet + set.Messages = append(set.Messages, msgBlock) +} + +func (r *FetchResponse) AddRecordWithTimestamp(topic string, partition int32, key, value Encoder, offset int64, timestamp time.Time) { + frb := r.getOrCreateBlock(topic, partition) + kb, vb := encodeKV(key, value) + if len(frb.RecordsSet) == 0 { + records := newDefaultRecords(&RecordBatch{Version: 2, LogAppendTime: r.LogAppendTime, FirstTimestamp: timestamp, MaxTimestamp: r.Timestamp}) + frb.RecordsSet = []*Records{&records} + } + batch := frb.RecordsSet[0].RecordBatch + rec := &Record{Key: kb, Value: vb, OffsetDelta: offset, TimestampDelta: timestamp.Sub(batch.FirstTimestamp)} + batch.addRecord(rec) +} + +// AddRecordBatchWithTimestamp is similar to AddRecordWithTimestamp +// But instead of appending 1 record to a batch, it append a new batch containing 1 record to the fetchResponse +// Since transaction are handled on batch level (the whole batch is either committed or aborted), use this to test transactions +func (r *FetchResponse) AddRecordBatchWithTimestamp(topic string, partition int32, key, value Encoder, offset int64, producerID int64, isTransactional bool, timestamp time.Time) { + frb := r.getOrCreateBlock(topic, partition) + kb, vb := encodeKV(key, value) + + records := newDefaultRecords(&RecordBatch{Version: 2, LogAppendTime: r.LogAppendTime, FirstTimestamp: timestamp, MaxTimestamp: r.Timestamp}) + batch := &RecordBatch{ + Version: 2, + LogAppendTime: r.LogAppendTime, + FirstTimestamp: timestamp, + MaxTimestamp: r.Timestamp, + FirstOffset: offset, + LastOffsetDelta: 0, + ProducerID: producerID, + IsTransactional: isTransactional, + } + rec := &Record{Key: kb, Value: vb, OffsetDelta: 0, TimestampDelta: timestamp.Sub(batch.FirstTimestamp)} + batch.addRecord(rec) + records.RecordBatch = batch + + frb.RecordsSet = append(frb.RecordsSet, &records) +} + +func (r *FetchResponse) AddControlRecordWithTimestamp(topic string, partition int32, offset int64, producerID int64, recordType ControlRecordType, timestamp time.Time) { + frb := r.getOrCreateBlock(topic, partition) + + // batch + batch := &RecordBatch{ + Version: 2, + LogAppendTime: r.LogAppendTime, + FirstTimestamp: timestamp, + MaxTimestamp: r.Timestamp, + FirstOffset: offset, + LastOffsetDelta: 0, + ProducerID: producerID, + IsTransactional: true, + Control: true, + } + + // records + records := newDefaultRecords(nil) + records.RecordBatch = batch + + // record + crAbort := ControlRecord{ + Version: 0, + Type: recordType, + } + crKey := &realEncoder{raw: make([]byte, 4)} + crValue := &realEncoder{raw: make([]byte, 6)} + crAbort.encode(crKey, crValue) + rec := &Record{Key: ByteEncoder(crKey.raw), Value: ByteEncoder(crValue.raw), OffsetDelta: 0, TimestampDelta: timestamp.Sub(batch.FirstTimestamp)} + batch.addRecord(rec) + + frb.RecordsSet = append(frb.RecordsSet, &records) +} + +func (r *FetchResponse) AddMessage(topic string, partition int32, key, value Encoder, offset int64) { + r.AddMessageWithTimestamp(topic, partition, key, value, offset, time.Time{}, 0) +} + +func (r *FetchResponse) AddRecord(topic string, partition int32, key, value Encoder, offset int64) { + r.AddRecordWithTimestamp(topic, partition, key, value, offset, time.Time{}) +} + +func (r *FetchResponse) AddRecordBatch(topic string, partition int32, key, value Encoder, offset int64, producerID int64, isTransactional bool) { + r.AddRecordBatchWithTimestamp(topic, partition, key, value, offset, producerID, isTransactional, time.Time{}) +} + +func (r *FetchResponse) AddControlRecord(topic string, partition int32, offset int64, producerID int64, recordType ControlRecordType) { + // define controlRecord key and value + r.AddControlRecordWithTimestamp(topic, partition, offset, producerID, recordType, time.Time{}) +} + +func (r *FetchResponse) SetLastOffsetDelta(topic string, partition int32, offset int32) { + frb := r.getOrCreateBlock(topic, partition) + if len(frb.RecordsSet) == 0 { + records := newDefaultRecords(&RecordBatch{Version: 2}) + frb.RecordsSet = []*Records{&records} + } + batch := frb.RecordsSet[0].RecordBatch + batch.LastOffsetDelta = offset +} + +func (r *FetchResponse) SetLastStableOffset(topic string, partition int32, offset int64) { + frb := r.getOrCreateBlock(topic, partition) + frb.LastStableOffset = offset +} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_request.go b/vendor/github.com/Shopify/sarama/find_coordinator_request.go new file mode 100644 index 0000000..ff2ad20 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/find_coordinator_request.go @@ -0,0 +1,61 @@ +package sarama + +type CoordinatorType int8 + +const ( + CoordinatorGroup CoordinatorType = iota + CoordinatorTransaction +) + +type FindCoordinatorRequest struct { + Version int16 + CoordinatorKey string + CoordinatorType CoordinatorType +} + +func (f *FindCoordinatorRequest) encode(pe packetEncoder) error { + if err := pe.putString(f.CoordinatorKey); err != nil { + return err + } + + if f.Version >= 1 { + pe.putInt8(int8(f.CoordinatorType)) + } + + return nil +} + +func (f *FindCoordinatorRequest) decode(pd packetDecoder, version int16) (err error) { + if f.CoordinatorKey, err = pd.getString(); err != nil { + return err + } + + if version >= 1 { + f.Version = version + coordinatorType, err := pd.getInt8() + if err != nil { + return err + } + + f.CoordinatorType = CoordinatorType(coordinatorType) + } + + return nil +} + +func (f *FindCoordinatorRequest) key() int16 { + return 10 +} + +func (f *FindCoordinatorRequest) version() int16 { + return f.Version +} + +func (f *FindCoordinatorRequest) requiredVersion() KafkaVersion { + switch f.Version { + case 1: + return V0_11_0_0 + default: + return V0_8_2_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_response.go b/vendor/github.com/Shopify/sarama/find_coordinator_response.go new file mode 100644 index 0000000..9c900e8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/find_coordinator_response.go @@ -0,0 +1,92 @@ +package sarama + +import ( + "time" +) + +var NoNode = &Broker{id: -1, addr: ":-1"} + +type FindCoordinatorResponse struct { + Version int16 + ThrottleTime time.Duration + Err KError + ErrMsg *string + Coordinator *Broker +} + +func (f *FindCoordinatorResponse) decode(pd packetDecoder, version int16) (err error) { + if version >= 1 { + f.Version = version + + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + f.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + f.Err = KError(tmp) + + if version >= 1 { + if f.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + } + + coordinator := new(Broker) + // The version is hardcoded to 0, as version 1 of the Broker-decode + // contains the rack-field which is not present in the FindCoordinatorResponse. + if err := coordinator.decode(pd, 0); err != nil { + return err + } + if coordinator.addr == ":0" { + return nil + } + f.Coordinator = coordinator + + return nil +} + +func (f *FindCoordinatorResponse) encode(pe packetEncoder) error { + if f.Version >= 1 { + pe.putInt32(int32(f.ThrottleTime / time.Millisecond)) + } + + pe.putInt16(int16(f.Err)) + + if f.Version >= 1 { + if err := pe.putNullableString(f.ErrMsg); err != nil { + return err + } + } + + coordinator := f.Coordinator + if coordinator == nil { + coordinator = NoNode + } + if err := coordinator.encode(pe, 0); err != nil { + return err + } + return nil +} + +func (f *FindCoordinatorResponse) key() int16 { + return 10 +} + +func (f *FindCoordinatorResponse) version() int16 { + return f.Version +} + +func (f *FindCoordinatorResponse) requiredVersion() KafkaVersion { + switch f.Version { + case 1: + return V0_11_0_0 + default: + return V0_8_2_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/go.mod b/vendor/github.com/Shopify/sarama/go.mod new file mode 100644 index 0000000..4337c00 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/go.mod @@ -0,0 +1,29 @@ +module github.com/Shopify/sarama + +go 1.13 + +require ( + github.com/Shopify/toxiproxy v2.1.4+incompatible + github.com/davecgh/go-spew v1.1.1 + github.com/eapache/go-resiliency v1.1.0 + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 + github.com/eapache/queue v1.1.0 + github.com/fortytw2/leaktest v1.3.0 + github.com/frankban/quicktest v1.4.1 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/hashicorp/go-uuid v1.0.1 // indirect + github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 // indirect + github.com/klauspost/compress v1.8.2 + github.com/pierrec/lz4 v2.2.6+incompatible + github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a + github.com/stretchr/testify v1.3.0 + github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c + github.com/xdg/stringprep v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 // indirect + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect + gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect + gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect + gopkg.in/jcmturner/gokrb5.v7 v7.2.3 + gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect +) diff --git a/vendor/github.com/Shopify/sarama/go.sum b/vendor/github.com/Shopify/sarama/go.sum new file mode 100644 index 0000000..d2f04ee --- /dev/null +++ b/vendor/github.com/Shopify/sarama/go.sum @@ -0,0 +1,67 @@ +github.com/DataDog/zstd v1.4.0 h1:vhoV+DUHnRZdKW1i5UMjAk2G4JY8wN4ayRfYDNdEhwo= +github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5GPSg= +github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/klauspost/compress v1.8.1 h1:oygt2ychZFHOB6M9gUgajzgKrwRgHbGC77NwA4COVgI= +github.com/klauspost/compress v1.8.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw= +github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE= +golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= +gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= +gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.2.3 h1:hHMV/yKPwMnJhPuPx7pH2Uw/3Qyf+thJYlisUc44010= +gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= +gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= diff --git a/vendor/github.com/Shopify/sarama/gssapi_kerberos.go b/vendor/github.com/Shopify/sarama/gssapi_kerberos.go new file mode 100644 index 0000000..57f3ecb --- /dev/null +++ b/vendor/github.com/Shopify/sarama/gssapi_kerberos.go @@ -0,0 +1,258 @@ +package sarama + +import ( + "encoding/asn1" + "encoding/binary" + "fmt" + "io" + "strings" + "time" + + "gopkg.in/jcmturner/gokrb5.v7/asn1tools" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/iana/chksumtype" + "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage" + "gopkg.in/jcmturner/gokrb5.v7/messages" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +const ( + TOK_ID_KRB_AP_REQ = 256 + GSS_API_GENERIC_TAG = 0x60 + KRB5_USER_AUTH = 1 + KRB5_KEYTAB_AUTH = 2 + GSS_API_INITIAL = 1 + GSS_API_VERIFY = 2 + GSS_API_FINISH = 3 +) + +type GSSAPIConfig struct { + AuthType int + KeyTabPath string + KerberosConfigPath string + ServiceName string + Username string + Password string + Realm string +} + +type GSSAPIKerberosAuth struct { + Config *GSSAPIConfig + ticket messages.Ticket + encKey types.EncryptionKey + NewKerberosClientFunc func(config *GSSAPIConfig) (KerberosClient, error) + step int +} + +type KerberosClient interface { + Login() error + GetServiceTicket(spn string) (messages.Ticket, types.EncryptionKey, error) + Domain() string + CName() types.PrincipalName + Destroy() +} + +/* +* +* Appends length in big endian before payload, and send it to kafka +* + */ + +func (krbAuth *GSSAPIKerberosAuth) writePackage(broker *Broker, payload []byte) (int, error) { + length := len(payload) + finalPackage := make([]byte, length+4) //4 byte length header + payload + copy(finalPackage[4:], payload) + binary.BigEndian.PutUint32(finalPackage, uint32(length)) + bytes, err := broker.conn.Write(finalPackage) + if err != nil { + return bytes, err + } + return bytes, nil +} + +/* +* +* Read length (4 bytes) and then read the payload +* + */ + +func (krbAuth *GSSAPIKerberosAuth) readPackage(broker *Broker) ([]byte, int, error) { + bytesRead := 0 + lengthInBytes := make([]byte, 4) + bytes, err := io.ReadFull(broker.conn, lengthInBytes) + if err != nil { + return nil, bytesRead, err + } + bytesRead += bytes + payloadLength := binary.BigEndian.Uint32(lengthInBytes) + payloadBytes := make([]byte, payloadLength) // buffer for read.. + bytes, err = io.ReadFull(broker.conn, payloadBytes) // read bytes + if err != nil { + return payloadBytes, bytesRead, err + } + bytesRead += bytes + return payloadBytes, bytesRead, nil +} + +func (krbAuth *GSSAPIKerberosAuth) newAuthenticatorChecksum() []byte { + a := make([]byte, 24) + flags := []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf} + binary.LittleEndian.PutUint32(a[:4], 16) + for _, i := range flags { + f := binary.LittleEndian.Uint32(a[20:24]) + f |= uint32(i) + binary.LittleEndian.PutUint32(a[20:24], f) + } + return a +} + +/* +* +* Construct Kerberos AP_REQ package, conforming to RFC-4120 +* https://tools.ietf.org/html/rfc4120#page-84 +* + */ +func (krbAuth *GSSAPIKerberosAuth) createKrb5Token( + domain string, cname types.PrincipalName, + ticket messages.Ticket, + sessionKey types.EncryptionKey) ([]byte, error) { + auth, err := types.NewAuthenticator(domain, cname) + if err != nil { + return nil, err + } + auth.Cksum = types.Checksum{ + CksumType: chksumtype.GSSAPI, + Checksum: krbAuth.newAuthenticatorChecksum(), + } + APReq, err := messages.NewAPReq( + ticket, + sessionKey, + auth, + ) + if err != nil { + return nil, err + } + aprBytes := make([]byte, 2) + binary.BigEndian.PutUint16(aprBytes, TOK_ID_KRB_AP_REQ) + tb, err := APReq.Marshal() + if err != nil { + return nil, err + } + aprBytes = append(aprBytes, tb...) + return aprBytes, nil +} + +/* +* +* Append the GSS-API header to the payload, conforming to RFC-2743 +* Section 3.1, Mechanism-Independent Token Format +* +* https://tools.ietf.org/html/rfc2743#page-81 +* +* GSSAPIHeader + +* + */ +func (krbAuth *GSSAPIKerberosAuth) appendGSSAPIHeader(payload []byte) ([]byte, error) { + oidBytes, err := asn1.Marshal(gssapi.OID(gssapi.OIDKRB5)) + if err != nil { + return nil, err + } + tkoLengthBytes := asn1tools.MarshalLengthBytes(len(oidBytes) + len(payload)) + GSSHeader := append([]byte{GSS_API_GENERIC_TAG}, tkoLengthBytes...) + GSSHeader = append(GSSHeader, oidBytes...) + GSSPackage := append(GSSHeader, payload...) + return GSSPackage, nil +} + +func (krbAuth *GSSAPIKerberosAuth) initSecContext(bytes []byte, kerberosClient KerberosClient) ([]byte, error) { + switch krbAuth.step { + case GSS_API_INITIAL: + aprBytes, err := krbAuth.createKrb5Token( + kerberosClient.Domain(), + kerberosClient.CName(), + krbAuth.ticket, + krbAuth.encKey) + if err != nil { + return nil, err + } + krbAuth.step = GSS_API_VERIFY + return krbAuth.appendGSSAPIHeader(aprBytes) + case GSS_API_VERIFY: + wrapTokenReq := gssapi.WrapToken{} + if err := wrapTokenReq.Unmarshal(bytes, true); err != nil { + return nil, err + } + // Validate response. + isValid, err := wrapTokenReq.Verify(krbAuth.encKey, keyusage.GSSAPI_ACCEPTOR_SEAL) + if !isValid { + return nil, err + } + + wrapTokenResponse, err := gssapi.NewInitiatorWrapToken(wrapTokenReq.Payload, krbAuth.encKey) + if err != nil { + return nil, err + } + krbAuth.step = GSS_API_FINISH + return wrapTokenResponse.Marshal() + } + return nil, nil +} + +/* This does the handshake for authorization */ +func (krbAuth *GSSAPIKerberosAuth) Authorize(broker *Broker) error { + + kerberosClient, err := krbAuth.NewKerberosClientFunc(krbAuth.Config) + if err != nil { + Logger.Printf("Kerberos client error: %s", err) + return err + } + + err = kerberosClient.Login() + if err != nil { + Logger.Printf("Kerberos client error: %s", err) + return err + } + // Construct SPN using serviceName and host + // SPN format: / + + host := strings.SplitN(broker.addr, ":", 2)[0] // Strip port part + spn := fmt.Sprintf("%s/%s", broker.conf.Net.SASL.GSSAPI.ServiceName, host) + + ticket, encKey, err := kerberosClient.GetServiceTicket(spn) + + if err != nil { + Logger.Printf("Error getting Kerberos service ticket : %s", err) + return err + } + krbAuth.ticket = ticket + krbAuth.encKey = encKey + krbAuth.step = GSS_API_INITIAL + var receivedBytes []byte = nil + defer kerberosClient.Destroy() + for { + packBytes, err := krbAuth.initSecContext(receivedBytes, kerberosClient) + if err != nil { + Logger.Printf("Error while performing GSSAPI Kerberos Authentication: %s\n", err) + return err + } + requestTime := time.Now() + bytesWritten, err := krbAuth.writePackage(broker, packBytes) + if err != nil { + Logger.Printf("Error while performing GSSAPI Kerberos Authentication: %s\n", err) + return err + } + broker.updateOutgoingCommunicationMetrics(bytesWritten) + if krbAuth.step == GSS_API_VERIFY { + var bytesRead = 0 + receivedBytes, bytesRead, err = krbAuth.readPackage(broker) + requestLatency := time.Since(requestTime) + broker.updateIncomingCommunicationMetrics(bytesRead, requestLatency) + if err != nil { + Logger.Printf("Error while performing GSSAPI Kerberos Authentication: %s\n", err) + return err + } + } else if krbAuth.step == GSS_API_FINISH { + return nil + } + } +} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_request.go b/vendor/github.com/Shopify/sarama/heartbeat_request.go new file mode 100644 index 0000000..ce49c47 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/heartbeat_request.go @@ -0,0 +1,47 @@ +package sarama + +type HeartbeatRequest struct { + GroupId string + GenerationId int32 + MemberId string +} + +func (r *HeartbeatRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.MemberId); err != nil { + return err + } + + return nil +} + +func (r *HeartbeatRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + return nil +} + +func (r *HeartbeatRequest) key() int16 { + return 12 +} + +func (r *HeartbeatRequest) version() int16 { + return 0 +} + +func (r *HeartbeatRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_response.go b/vendor/github.com/Shopify/sarama/heartbeat_response.go new file mode 100644 index 0000000..766f5fd --- /dev/null +++ b/vendor/github.com/Shopify/sarama/heartbeat_response.go @@ -0,0 +1,32 @@ +package sarama + +type HeartbeatResponse struct { + Err KError +} + +func (r *HeartbeatResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return nil +} + +func (r *HeartbeatResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + r.Err = KError(kerr) + + return nil +} + +func (r *HeartbeatResponse) key() int16 { + return 12 +} + +func (r *HeartbeatResponse) version() int16 { + return 0 +} + +func (r *HeartbeatResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_request.go b/vendor/github.com/Shopify/sarama/init_producer_id_request.go new file mode 100644 index 0000000..8ceb6c2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/init_producer_id_request.go @@ -0,0 +1,43 @@ +package sarama + +import "time" + +type InitProducerIDRequest struct { + TransactionalID *string + TransactionTimeout time.Duration +} + +func (i *InitProducerIDRequest) encode(pe packetEncoder) error { + if err := pe.putNullableString(i.TransactionalID); err != nil { + return err + } + pe.putInt32(int32(i.TransactionTimeout / time.Millisecond)) + + return nil +} + +func (i *InitProducerIDRequest) decode(pd packetDecoder, version int16) (err error) { + if i.TransactionalID, err = pd.getNullableString(); err != nil { + return err + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + i.TransactionTimeout = time.Duration(timeout) * time.Millisecond + + return nil +} + +func (i *InitProducerIDRequest) key() int16 { + return 22 +} + +func (i *InitProducerIDRequest) version() int16 { + return 0 +} + +func (i *InitProducerIDRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_response.go b/vendor/github.com/Shopify/sarama/init_producer_id_response.go new file mode 100644 index 0000000..1b32eb0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/init_producer_id_response.go @@ -0,0 +1,55 @@ +package sarama + +import "time" + +type InitProducerIDResponse struct { + ThrottleTime time.Duration + Err KError + ProducerID int64 + ProducerEpoch int16 +} + +func (i *InitProducerIDResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(i.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(i.Err)) + pe.putInt64(i.ProducerID) + pe.putInt16(i.ProducerEpoch) + + return nil +} + +func (i *InitProducerIDResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + i.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + i.Err = KError(kerr) + + if i.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if i.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + return nil +} + +func (i *InitProducerIDResponse) key() int16 { + return 22 +} + +func (i *InitProducerIDResponse) version() int16 { + return 0 +} + +func (i *InitProducerIDResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/join_group_request.go b/vendor/github.com/Shopify/sarama/join_group_request.go new file mode 100644 index 0000000..97e9299 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/join_group_request.go @@ -0,0 +1,163 @@ +package sarama + +type GroupProtocol struct { + Name string + Metadata []byte +} + +func (p *GroupProtocol) decode(pd packetDecoder) (err error) { + p.Name, err = pd.getString() + if err != nil { + return err + } + p.Metadata, err = pd.getBytes() + return err +} + +func (p *GroupProtocol) encode(pe packetEncoder) (err error) { + if err := pe.putString(p.Name); err != nil { + return err + } + if err := pe.putBytes(p.Metadata); err != nil { + return err + } + return nil +} + +type JoinGroupRequest struct { + Version int16 + GroupId string + SessionTimeout int32 + RebalanceTimeout int32 + MemberId string + ProtocolType string + GroupProtocols map[string][]byte // deprecated; use OrderedGroupProtocols + OrderedGroupProtocols []*GroupProtocol +} + +func (r *JoinGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + pe.putInt32(r.SessionTimeout) + if r.Version >= 1 { + pe.putInt32(r.RebalanceTimeout) + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + if err := pe.putString(r.ProtocolType); err != nil { + return err + } + + if len(r.GroupProtocols) > 0 { + if len(r.OrderedGroupProtocols) > 0 { + return PacketDecodingError{"cannot specify both GroupProtocols and OrderedGroupProtocols on JoinGroupRequest"} + } + + if err := pe.putArrayLength(len(r.GroupProtocols)); err != nil { + return err + } + for name, metadata := range r.GroupProtocols { + if err := pe.putString(name); err != nil { + return err + } + if err := pe.putBytes(metadata); err != nil { + return err + } + } + } else { + if err := pe.putArrayLength(len(r.OrderedGroupProtocols)); err != nil { + return err + } + for _, protocol := range r.OrderedGroupProtocols { + if err := protocol.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (r *JoinGroupRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.GroupId, err = pd.getString(); err != nil { + return + } + + if r.SessionTimeout, err = pd.getInt32(); err != nil { + return + } + + if version >= 1 { + if r.RebalanceTimeout, err = pd.getInt32(); err != nil { + return err + } + } + + if r.MemberId, err = pd.getString(); err != nil { + return + } + + if r.ProtocolType, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupProtocols = make(map[string][]byte) + for i := 0; i < n; i++ { + protocol := &GroupProtocol{} + if err := protocol.decode(pd); err != nil { + return err + } + r.GroupProtocols[protocol.Name] = protocol.Metadata + r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, protocol) + } + + return nil +} + +func (r *JoinGroupRequest) key() int16 { + return 11 +} + +func (r *JoinGroupRequest) version() int16 { + return r.Version +} + +func (r *JoinGroupRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 2: + return V0_11_0_0 + case 1: + return V0_10_1_0 + default: + return V0_9_0_0 + } +} + +func (r *JoinGroupRequest) AddGroupProtocol(name string, metadata []byte) { + r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, &GroupProtocol{ + Name: name, + Metadata: metadata, + }) +} + +func (r *JoinGroupRequest) AddGroupProtocolMetadata(name string, metadata *ConsumerGroupMemberMetadata) error { + bin, err := encode(metadata, nil) + if err != nil { + return err + } + + r.AddGroupProtocol(name, bin) + return nil +} diff --git a/vendor/github.com/Shopify/sarama/join_group_response.go b/vendor/github.com/Shopify/sarama/join_group_response.go new file mode 100644 index 0000000..5752acc --- /dev/null +++ b/vendor/github.com/Shopify/sarama/join_group_response.go @@ -0,0 +1,135 @@ +package sarama + +type JoinGroupResponse struct { + Version int16 + ThrottleTime int32 + Err KError + GenerationId int32 + GroupProtocol string + LeaderId string + MemberId string + Members map[string][]byte +} + +func (r *JoinGroupResponse) GetMembers() (map[string]ConsumerGroupMemberMetadata, error) { + members := make(map[string]ConsumerGroupMemberMetadata, len(r.Members)) + for id, bin := range r.Members { + meta := new(ConsumerGroupMemberMetadata) + if err := decode(bin, meta); err != nil { + return nil, err + } + members[id] = *meta + } + return members, nil +} + +func (r *JoinGroupResponse) encode(pe packetEncoder) error { + if r.Version >= 2 { + pe.putInt32(r.ThrottleTime) + } + pe.putInt16(int16(r.Err)) + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.GroupProtocol); err != nil { + return err + } + if err := pe.putString(r.LeaderId); err != nil { + return err + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.Members)); err != nil { + return err + } + + for memberId, memberMetadata := range r.Members { + if err := pe.putString(memberId); err != nil { + return err + } + + if err := pe.putBytes(memberMetadata); err != nil { + return err + } + } + + return nil +} + +func (r *JoinGroupResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 2 { + if r.ThrottleTime, err = pd.getInt32(); err != nil { + return + } + } + + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + + if r.GroupProtocol, err = pd.getString(); err != nil { + return + } + + if r.LeaderId, err = pd.getString(); err != nil { + return + } + + if r.MemberId, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.Members = make(map[string][]byte) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + + memberMetadata, err := pd.getBytes() + if err != nil { + return err + } + + r.Members[memberId] = memberMetadata + } + + return nil +} + +func (r *JoinGroupResponse) key() int16 { + return 11 +} + +func (r *JoinGroupResponse) version() int16 { + return r.Version +} + +func (r *JoinGroupResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 2: + return V0_11_0_0 + case 1: + return V0_10_1_0 + default: + return V0_9_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/kerberos_client.go b/vendor/github.com/Shopify/sarama/kerberos_client.go new file mode 100644 index 0000000..91b998f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/kerberos_client.go @@ -0,0 +1,51 @@ +package sarama + +import ( + krb5client "gopkg.in/jcmturner/gokrb5.v7/client" + krb5config "gopkg.in/jcmturner/gokrb5.v7/config" + "gopkg.in/jcmturner/gokrb5.v7/keytab" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +type KerberosGoKrb5Client struct { + krb5client.Client +} + +func (c *KerberosGoKrb5Client) Domain() string { + return c.Credentials.Domain() +} + +func (c *KerberosGoKrb5Client) CName() types.PrincipalName { + return c.Credentials.CName() +} + +/* +* +* Create kerberos client used to obtain TGT and TGS tokens +* used gokrb5 library, which is a pure go kerberos client with +* some GSS-API capabilities, and SPNEGO support. Kafka does not use SPNEGO +* it uses pure Kerberos 5 solution (RFC-4121 and RFC-4120). +* + */ +func NewKerberosClient(config *GSSAPIConfig) (KerberosClient, error) { + cfg, err := krb5config.Load(config.KerberosConfigPath) + if err != nil { + return nil, err + } + return createClient(config, cfg) +} + +func createClient(config *GSSAPIConfig, cfg *krb5config.Config) (KerberosClient, error) { + var client *krb5client.Client + if config.AuthType == KRB5_KEYTAB_AUTH { + kt, err := keytab.Load(config.KeyTabPath) + if err != nil { + return nil, err + } + client = krb5client.NewClientWithKeytab(config.Username, config.Realm, kt, cfg) + } else { + client = krb5client.NewClientWithPassword(config.Username, + config.Realm, config.Password, cfg) + } + return &KerberosGoKrb5Client{*client}, nil +} diff --git a/vendor/github.com/Shopify/sarama/leave_group_request.go b/vendor/github.com/Shopify/sarama/leave_group_request.go new file mode 100644 index 0000000..e177427 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/leave_group_request.go @@ -0,0 +1,40 @@ +package sarama + +type LeaveGroupRequest struct { + GroupId string + MemberId string +} + +func (r *LeaveGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + + return nil +} + +func (r *LeaveGroupRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + return nil +} + +func (r *LeaveGroupRequest) key() int16 { + return 13 +} + +func (r *LeaveGroupRequest) version() int16 { + return 0 +} + +func (r *LeaveGroupRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/leave_group_response.go b/vendor/github.com/Shopify/sarama/leave_group_response.go new file mode 100644 index 0000000..d60c626 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/leave_group_response.go @@ -0,0 +1,32 @@ +package sarama + +type LeaveGroupResponse struct { + Err KError +} + +func (r *LeaveGroupResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return nil +} + +func (r *LeaveGroupResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + r.Err = KError(kerr) + + return nil +} + +func (r *LeaveGroupResponse) key() int16 { + return 13 +} + +func (r *LeaveGroupResponse) version() int16 { + return 0 +} + +func (r *LeaveGroupResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/length_field.go b/vendor/github.com/Shopify/sarama/length_field.go new file mode 100644 index 0000000..7d864f6 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/length_field.go @@ -0,0 +1,99 @@ +package sarama + +import ( + "encoding/binary" + "sync" +) + +// LengthField implements the PushEncoder and PushDecoder interfaces for calculating 4-byte lengths. +type lengthField struct { + startOffset int + length int32 +} + +var lengthFieldPool = sync.Pool{} + +func acquireLengthField() *lengthField { + val := lengthFieldPool.Get() + if val != nil { + return val.(*lengthField) + } + return &lengthField{} +} + +func releaseLengthField(m *lengthField) { + lengthFieldPool.Put(m) +} + +func (l *lengthField) decode(pd packetDecoder) error { + var err error + l.length, err = pd.getInt32() + if err != nil { + return err + } + if l.length > int32(pd.remaining()) { + return ErrInsufficientData + } + return nil +} + +func (l *lengthField) saveOffset(in int) { + l.startOffset = in +} + +func (l *lengthField) reserveLength() int { + return 4 +} + +func (l *lengthField) run(curOffset int, buf []byte) error { + binary.BigEndian.PutUint32(buf[l.startOffset:], uint32(curOffset-l.startOffset-4)) + return nil +} + +func (l *lengthField) check(curOffset int, buf []byte) error { + if int32(curOffset-l.startOffset-4) != l.length { + return PacketDecodingError{"length field invalid"} + } + + return nil +} + +type varintLengthField struct { + startOffset int + length int64 +} + +func (l *varintLengthField) decode(pd packetDecoder) error { + var err error + l.length, err = pd.getVarint() + return err +} + +func (l *varintLengthField) saveOffset(in int) { + l.startOffset = in +} + +func (l *varintLengthField) adjustLength(currOffset int) int { + oldFieldSize := l.reserveLength() + l.length = int64(currOffset - l.startOffset - oldFieldSize) + + return l.reserveLength() - oldFieldSize +} + +func (l *varintLengthField) reserveLength() int { + var tmp [binary.MaxVarintLen64]byte + return binary.PutVarint(tmp[:], l.length) +} + +func (l *varintLengthField) run(curOffset int, buf []byte) error { + binary.PutVarint(buf[l.startOffset:], l.length) + return nil +} + +func (l *varintLengthField) check(curOffset int, buf []byte) error { + if int64(curOffset-l.startOffset-l.reserveLength()) != l.length { + return PacketDecodingError{"length field invalid"} + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/list_groups_request.go b/vendor/github.com/Shopify/sarama/list_groups_request.go new file mode 100644 index 0000000..3b16abf --- /dev/null +++ b/vendor/github.com/Shopify/sarama/list_groups_request.go @@ -0,0 +1,24 @@ +package sarama + +type ListGroupsRequest struct { +} + +func (r *ListGroupsRequest) encode(pe packetEncoder) error { + return nil +} + +func (r *ListGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + return nil +} + +func (r *ListGroupsRequest) key() int16 { + return 16 +} + +func (r *ListGroupsRequest) version() int16 { + return 0 +} + +func (r *ListGroupsRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/list_groups_response.go b/vendor/github.com/Shopify/sarama/list_groups_response.go new file mode 100644 index 0000000..56115d4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/list_groups_response.go @@ -0,0 +1,69 @@ +package sarama + +type ListGroupsResponse struct { + Err KError + Groups map[string]string +} + +func (r *ListGroupsResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + + if err := pe.putArrayLength(len(r.Groups)); err != nil { + return err + } + for groupId, protocolType := range r.Groups { + if err := pe.putString(groupId); err != nil { + return err + } + if err := pe.putString(protocolType); err != nil { + return err + } + } + + return nil +} + +func (r *ListGroupsResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.Groups = make(map[string]string) + for i := 0; i < n; i++ { + groupId, err := pd.getString() + if err != nil { + return err + } + protocolType, err := pd.getString() + if err != nil { + return err + } + + r.Groups[groupId] = protocolType + } + + return nil +} + +func (r *ListGroupsResponse) key() int16 { + return 16 +} + +func (r *ListGroupsResponse) version() int16 { + return 0 +} + +func (r *ListGroupsResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/message.go b/vendor/github.com/Shopify/sarama/message.go new file mode 100644 index 0000000..7c54748 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/message.go @@ -0,0 +1,175 @@ +package sarama + +import ( + "fmt" + "time" +) + +const ( + //CompressionNone no compression + CompressionNone CompressionCodec = iota + //CompressionGZIP compression using GZIP + CompressionGZIP + //CompressionSnappy compression using snappy + CompressionSnappy + //CompressionLZ4 compression using LZ4 + CompressionLZ4 + //CompressionZSTD compression using ZSTD + CompressionZSTD + + // The lowest 3 bits contain the compression codec used for the message + compressionCodecMask int8 = 0x07 + + // Bit 3 set for "LogAppend" timestamps + timestampTypeMask = 0x08 + + // CompressionLevelDefault is the constant to use in CompressionLevel + // to have the default compression level for any codec. The value is picked + // that we don't use any existing compression levels. + CompressionLevelDefault = -1000 +) + +// CompressionCodec represents the various compression codecs recognized by Kafka in messages. +type CompressionCodec int8 + +func (cc CompressionCodec) String() string { + return []string{ + "none", + "gzip", + "snappy", + "lz4", + "zstd", + }[int(cc)] +} + +//Message is a kafka message type +type Message struct { + Codec CompressionCodec // codec used to compress the message contents + CompressionLevel int // compression level + LogAppendTime bool // the used timestamp is LogAppendTime + Key []byte // the message key, may be nil + Value []byte // the message contents + Set *MessageSet // the message set a message might wrap + Version int8 // v1 requires Kafka 0.10 + Timestamp time.Time // the timestamp of the message (version 1+ only) + + compressedCache []byte + compressedSize int // used for computing the compression ratio metrics +} + +func (m *Message) encode(pe packetEncoder) error { + pe.push(newCRC32Field(crcIEEE)) + + pe.putInt8(m.Version) + + attributes := int8(m.Codec) & compressionCodecMask + if m.LogAppendTime { + attributes |= timestampTypeMask + } + pe.putInt8(attributes) + + if m.Version >= 1 { + if err := (Timestamp{&m.Timestamp}).encode(pe); err != nil { + return err + } + } + + err := pe.putBytes(m.Key) + if err != nil { + return err + } + + var payload []byte + + if m.compressedCache != nil { + payload = m.compressedCache + m.compressedCache = nil + } else if m.Value != nil { + + payload, err = compress(m.Codec, m.CompressionLevel, m.Value) + if err != nil { + return err + } + m.compressedCache = payload + // Keep in mind the compressed payload size for metric gathering + m.compressedSize = len(payload) + } + + if err = pe.putBytes(payload); err != nil { + return err + } + + return pe.pop() +} + +func (m *Message) decode(pd packetDecoder) (err error) { + crc32Decoder := acquireCrc32Field(crcIEEE) + defer releaseCrc32Field(crc32Decoder) + + err = pd.push(crc32Decoder) + if err != nil { + return err + } + + m.Version, err = pd.getInt8() + if err != nil { + return err + } + + if m.Version > 1 { + return PacketDecodingError{fmt.Sprintf("unknown magic byte (%v)", m.Version)} + } + + attribute, err := pd.getInt8() + if err != nil { + return err + } + m.Codec = CompressionCodec(attribute & compressionCodecMask) + m.LogAppendTime = attribute×tampTypeMask == timestampTypeMask + + if m.Version == 1 { + if err := (Timestamp{&m.Timestamp}).decode(pd); err != nil { + return err + } + } + + m.Key, err = pd.getBytes() + if err != nil { + return err + } + + m.Value, err = pd.getBytes() + if err != nil { + return err + } + + // Required for deep equal assertion during tests but might be useful + // for future metrics about the compression ratio in fetch requests + m.compressedSize = len(m.Value) + + switch m.Codec { + case CompressionNone: + // nothing to do + default: + if m.Value == nil { + break + } + + m.Value, err = decompress(m.Codec, m.Value) + if err != nil { + return err + } + if err := m.decodeSet(); err != nil { + return err + } + } + + return pd.pop() +} + +// decodes a message set from a previously encoded bulk-message +func (m *Message) decodeSet() (err error) { + pd := realDecoder{raw: m.Value} + m.Set = &MessageSet{} + return m.Set.decode(&pd) +} diff --git a/vendor/github.com/Shopify/sarama/message_set.go b/vendor/github.com/Shopify/sarama/message_set.go new file mode 100644 index 0000000..6523ec2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/message_set.go @@ -0,0 +1,111 @@ +package sarama + +type MessageBlock struct { + Offset int64 + Msg *Message +} + +// Messages convenience helper which returns either all the +// messages that are wrapped in this block +func (msb *MessageBlock) Messages() []*MessageBlock { + if msb.Msg.Set != nil { + return msb.Msg.Set.Messages + } + return []*MessageBlock{msb} +} + +func (msb *MessageBlock) encode(pe packetEncoder) error { + pe.putInt64(msb.Offset) + pe.push(&lengthField{}) + err := msb.Msg.encode(pe) + if err != nil { + return err + } + return pe.pop() +} + +func (msb *MessageBlock) decode(pd packetDecoder) (err error) { + if msb.Offset, err = pd.getInt64(); err != nil { + return err + } + + lengthDecoder := acquireLengthField() + defer releaseLengthField(lengthDecoder) + + if err = pd.push(lengthDecoder); err != nil { + return err + } + + msb.Msg = new(Message) + if err = msb.Msg.decode(pd); err != nil { + return err + } + + if err = pd.pop(); err != nil { + return err + } + + return nil +} + +type MessageSet struct { + PartialTrailingMessage bool // whether the set on the wire contained an incomplete trailing MessageBlock + OverflowMessage bool // whether the set on the wire contained an overflow message + Messages []*MessageBlock +} + +func (ms *MessageSet) encode(pe packetEncoder) error { + for i := range ms.Messages { + err := ms.Messages[i].encode(pe) + if err != nil { + return err + } + } + return nil +} + +func (ms *MessageSet) decode(pd packetDecoder) (err error) { + ms.Messages = nil + + for pd.remaining() > 0 { + magic, err := magicValue(pd) + if err != nil { + if err == ErrInsufficientData { + ms.PartialTrailingMessage = true + return nil + } + return err + } + + if magic > 1 { + return nil + } + + msb := new(MessageBlock) + err = msb.decode(pd) + switch err { + case nil: + ms.Messages = append(ms.Messages, msb) + case ErrInsufficientData: + // As an optimization the server is allowed to return a partial message at the + // end of the message set. Clients should handle this case. So we just ignore such things. + if msb.Offset == -1 { + // This is an overflow message caused by chunked down conversion + ms.OverflowMessage = true + } else { + ms.PartialTrailingMessage = true + } + return nil + default: + return err + } + } + + return nil +} + +func (ms *MessageSet) addMessage(msg *Message) { + block := new(MessageBlock) + block.Msg = msg + ms.Messages = append(ms.Messages, block) +} diff --git a/vendor/github.com/Shopify/sarama/metadata_request.go b/vendor/github.com/Shopify/sarama/metadata_request.go new file mode 100644 index 0000000..1b590d3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metadata_request.go @@ -0,0 +1,81 @@ +package sarama + +type MetadataRequest struct { + Version int16 + Topics []string + AllowAutoTopicCreation bool +} + +func (r *MetadataRequest) encode(pe packetEncoder) error { + if r.Version < 0 || r.Version > 5 { + return PacketEncodingError{"invalid or unsupported MetadataRequest version field"} + } + if r.Version == 0 || len(r.Topics) > 0 { + err := pe.putArrayLength(len(r.Topics)) + if err != nil { + return err + } + + for i := range r.Topics { + err = pe.putString(r.Topics[i]) + if err != nil { + return err + } + } + } else { + pe.putInt32(-1) + } + if r.Version > 3 { + pe.putBool(r.AllowAutoTopicCreation) + } + return nil +} + +func (r *MetadataRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + size, err := pd.getInt32() + if err != nil { + return err + } + if size > 0 { + r.Topics = make([]string, size) + for i := range r.Topics { + topic, err := pd.getString() + if err != nil { + return err + } + r.Topics[i] = topic + } + } + if r.Version > 3 { + autoCreation, err := pd.getBool() + if err != nil { + return err + } + r.AllowAutoTopicCreation = autoCreation + } + return nil +} + +func (r *MetadataRequest) key() int16 { + return 3 +} + +func (r *MetadataRequest) version() int16 { + return r.Version +} + +func (r *MetadataRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_0_0 + case 2: + return V0_10_1_0 + case 3, 4: + return V0_11_0_0 + case 5: + return V1_0_0_0 + default: + return MinVersion + } +} diff --git a/vendor/github.com/Shopify/sarama/metadata_response.go b/vendor/github.com/Shopify/sarama/metadata_response.go new file mode 100644 index 0000000..b2d532e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metadata_response.go @@ -0,0 +1,322 @@ +package sarama + +type PartitionMetadata struct { + Err KError + ID int32 + Leader int32 + Replicas []int32 + Isr []int32 + OfflineReplicas []int32 +} + +func (pm *PartitionMetadata) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + pm.Err = KError(tmp) + + pm.ID, err = pd.getInt32() + if err != nil { + return err + } + + pm.Leader, err = pd.getInt32() + if err != nil { + return err + } + + pm.Replicas, err = pd.getInt32Array() + if err != nil { + return err + } + + pm.Isr, err = pd.getInt32Array() + if err != nil { + return err + } + + if version >= 5 { + pm.OfflineReplicas, err = pd.getInt32Array() + if err != nil { + return err + } + } + + return nil +} + +func (pm *PartitionMetadata) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(pm.Err)) + pe.putInt32(pm.ID) + pe.putInt32(pm.Leader) + + err = pe.putInt32Array(pm.Replicas) + if err != nil { + return err + } + + err = pe.putInt32Array(pm.Isr) + if err != nil { + return err + } + + if version >= 5 { + err = pe.putInt32Array(pm.OfflineReplicas) + if err != nil { + return err + } + } + + return nil +} + +type TopicMetadata struct { + Err KError + Name string + IsInternal bool // Only valid for Version >= 1 + Partitions []*PartitionMetadata +} + +func (tm *TopicMetadata) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + tm.Err = KError(tmp) + + tm.Name, err = pd.getString() + if err != nil { + return err + } + + if version >= 1 { + tm.IsInternal, err = pd.getBool() + if err != nil { + return err + } + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + tm.Partitions = make([]*PartitionMetadata, n) + for i := 0; i < n; i++ { + tm.Partitions[i] = new(PartitionMetadata) + err = tm.Partitions[i].decode(pd, version) + if err != nil { + return err + } + } + + return nil +} + +func (tm *TopicMetadata) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(tm.Err)) + + err = pe.putString(tm.Name) + if err != nil { + return err + } + + if version >= 1 { + pe.putBool(tm.IsInternal) + } + + err = pe.putArrayLength(len(tm.Partitions)) + if err != nil { + return err + } + + for _, pm := range tm.Partitions { + err = pm.encode(pe, version) + if err != nil { + return err + } + } + + return nil +} + +type MetadataResponse struct { + Version int16 + ThrottleTimeMs int32 + Brokers []*Broker + ClusterID *string + ControllerID int32 + Topics []*TopicMetadata +} + +func (r *MetadataResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 3 { + r.ThrottleTimeMs, err = pd.getInt32() + if err != nil { + return err + } + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Brokers = make([]*Broker, n) + for i := 0; i < n; i++ { + r.Brokers[i] = new(Broker) + err = r.Brokers[i].decode(pd, version) + if err != nil { + return err + } + } + + if version >= 2 { + r.ClusterID, err = pd.getNullableString() + if err != nil { + return err + } + } + + if version >= 1 { + r.ControllerID, err = pd.getInt32() + if err != nil { + return err + } + } else { + r.ControllerID = -1 + } + + n, err = pd.getArrayLength() + if err != nil { + return err + } + + r.Topics = make([]*TopicMetadata, n) + for i := 0; i < n; i++ { + r.Topics[i] = new(TopicMetadata) + err = r.Topics[i].decode(pd, version) + if err != nil { + return err + } + } + + return nil +} + +func (r *MetadataResponse) encode(pe packetEncoder) error { + if r.Version >= 3 { + pe.putInt32(r.ThrottleTimeMs) + } + + err := pe.putArrayLength(len(r.Brokers)) + if err != nil { + return err + } + for _, broker := range r.Brokers { + err = broker.encode(pe, r.Version) + if err != nil { + return err + } + } + + if r.Version >= 2 { + err := pe.putNullableString(r.ClusterID) + if err != nil { + return err + } + } + + if r.Version >= 1 { + pe.putInt32(r.ControllerID) + } + + err = pe.putArrayLength(len(r.Topics)) + if err != nil { + return err + } + for _, tm := range r.Topics { + err = tm.encode(pe, r.Version) + if err != nil { + return err + } + } + + return nil +} + +func (r *MetadataResponse) key() int16 { + return 3 +} + +func (r *MetadataResponse) version() int16 { + return r.Version +} + +func (r *MetadataResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_0_0 + case 2: + return V0_10_1_0 + case 3, 4: + return V0_11_0_0 + case 5: + return V1_0_0_0 + default: + return MinVersion + } +} + +// testing API + +func (r *MetadataResponse) AddBroker(addr string, id int32) { + r.Brokers = append(r.Brokers, &Broker{id: id, addr: addr}) +} + +func (r *MetadataResponse) AddTopic(topic string, err KError) *TopicMetadata { + var tmatch *TopicMetadata + + for _, tm := range r.Topics { + if tm.Name == topic { + tmatch = tm + goto foundTopic + } + } + + tmatch = new(TopicMetadata) + tmatch.Name = topic + r.Topics = append(r.Topics, tmatch) + +foundTopic: + + tmatch.Err = err + return tmatch +} + +func (r *MetadataResponse) AddTopicPartition(topic string, partition, brokerID int32, replicas, isr []int32, offline []int32, err KError) { + tmatch := r.AddTopic(topic, ErrNoError) + var pmatch *PartitionMetadata + + for _, pm := range tmatch.Partitions { + if pm.ID == partition { + pmatch = pm + goto foundPartition + } + } + + pmatch = new(PartitionMetadata) + pmatch.ID = partition + tmatch.Partitions = append(tmatch.Partitions, pmatch) + +foundPartition: + + pmatch.Leader = brokerID + pmatch.Replicas = replicas + pmatch.Isr = isr + pmatch.OfflineReplicas = offline + pmatch.Err = err + +} diff --git a/vendor/github.com/Shopify/sarama/metrics.go b/vendor/github.com/Shopify/sarama/metrics.go new file mode 100644 index 0000000..90e5a87 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metrics.go @@ -0,0 +1,43 @@ +package sarama + +import ( + "fmt" + "strings" + + "github.com/rcrowley/go-metrics" +) + +// Use exponentially decaying reservoir for sampling histograms with the same defaults as the Java library: +// 1028 elements, which offers a 99.9% confidence level with a 5% margin of error assuming a normal distribution, +// and an alpha factor of 0.015, which heavily biases the reservoir to the past 5 minutes of measurements. +// See https://github.com/dropwizard/metrics/blob/v3.1.0/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java#L38 +const ( + metricsReservoirSize = 1028 + metricsAlphaFactor = 0.015 +) + +func getOrRegisterHistogram(name string, r metrics.Registry) metrics.Histogram { + return r.GetOrRegister(name, func() metrics.Histogram { + return metrics.NewHistogram(metrics.NewExpDecaySample(metricsReservoirSize, metricsAlphaFactor)) + }).(metrics.Histogram) +} + +func getMetricNameForBroker(name string, broker *Broker) string { + // Use broker id like the Java client as it does not contain '.' or ':' characters that + // can be interpreted as special character by monitoring tool (e.g. Graphite) + return fmt.Sprintf(name+"-for-broker-%d", broker.ID()) +} + +func getMetricNameForTopic(name string, topic string) string { + // Convert dot to _ since reporters like Graphite typically use dot to represent hierarchy + // cf. KAFKA-1902 and KAFKA-2337 + return fmt.Sprintf(name+"-for-topic-%s", strings.Replace(topic, ".", "_", -1)) +} + +func getOrRegisterTopicMeter(name string, topic string, r metrics.Registry) metrics.Meter { + return metrics.GetOrRegisterMeter(getMetricNameForTopic(name, topic), r) +} + +func getOrRegisterTopicHistogram(name string, topic string, r metrics.Registry) metrics.Histogram { + return getOrRegisterHistogram(getMetricNameForTopic(name, topic), r) +} diff --git a/vendor/github.com/Shopify/sarama/mockbroker.go b/vendor/github.com/Shopify/sarama/mockbroker.go new file mode 100644 index 0000000..4ed46a6 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/mockbroker.go @@ -0,0 +1,403 @@ +package sarama + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "reflect" + "strconv" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" +) + +const ( + expectationTimeout = 500 * time.Millisecond +) + +type GSSApiHandlerFunc func([]byte) []byte + +type requestHandlerFunc func(req *request) (res encoder) + +// RequestNotifierFunc is invoked when a mock broker processes a request successfully +// and will provides the number of bytes read and written. +type RequestNotifierFunc func(bytesRead, bytesWritten int) + +// MockBroker is a mock Kafka broker that is used in unit tests. It is exposed +// to facilitate testing of higher level or specialized consumers and producers +// built on top of Sarama. Note that it does not 'mimic' the Kafka API protocol, +// but rather provides a facility to do that. It takes care of the TCP +// transport, request unmarshaling, response marshaling, and makes it the test +// writer responsibility to program correct according to the Kafka API protocol +// MockBroker behaviour. +// +// MockBroker is implemented as a TCP server listening on a kernel-selected +// localhost port that can accept many connections. It reads Kafka requests +// from that connection and returns responses programmed by the SetHandlerByMap +// function. If a MockBroker receives a request that it has no programmed +// response for, then it returns nothing and the request times out. +// +// A set of MockRequest builders to define mappings used by MockBroker is +// provided by Sarama. But users can develop MockRequests of their own and use +// them along with or instead of the standard ones. +// +// When running tests with MockBroker it is strongly recommended to specify +// a timeout to `go test` so that if the broker hangs waiting for a response, +// the test panics. +// +// It is not necessary to prefix message length or correlation ID to your +// response bytes, the server does that automatically as a convenience. +type MockBroker struct { + brokerID int32 + port int32 + closing chan none + stopper chan none + expectations chan encoder + listener net.Listener + t TestReporter + latency time.Duration + handler requestHandlerFunc + notifier RequestNotifierFunc + history []RequestResponse + lock sync.Mutex + gssApiHandler GSSApiHandlerFunc +} + +// RequestResponse represents a Request/Response pair processed by MockBroker. +type RequestResponse struct { + Request protocolBody + Response encoder +} + +// SetLatency makes broker pause for the specified period every time before +// replying. +func (b *MockBroker) SetLatency(latency time.Duration) { + b.latency = latency +} + +// SetHandlerByMap defines mapping of Request types to MockResponses. When a +// request is received by the broker, it looks up the request type in the map +// and uses the found MockResponse instance to generate an appropriate reply. +// If the request type is not found in the map then nothing is sent. +func (b *MockBroker) SetHandlerByMap(handlerMap map[string]MockResponse) { + b.setHandler(func(req *request) (res encoder) { + reqTypeName := reflect.TypeOf(req.body).Elem().Name() + mockResponse := handlerMap[reqTypeName] + if mockResponse == nil { + return nil + } + return mockResponse.For(req.body) + }) +} + +// SetNotifier set a function that will get invoked whenever a request has been +// processed successfully and will provide the number of bytes read and written +func (b *MockBroker) SetNotifier(notifier RequestNotifierFunc) { + b.lock.Lock() + b.notifier = notifier + b.lock.Unlock() +} + +// BrokerID returns broker ID assigned to the broker. +func (b *MockBroker) BrokerID() int32 { + return b.brokerID +} + +// History returns a slice of RequestResponse pairs in the order they were +// processed by the broker. Note that in case of multiple connections to the +// broker the order expected by a test can be different from the order recorded +// in the history, unless some synchronization is implemented in the test. +func (b *MockBroker) History() []RequestResponse { + b.lock.Lock() + history := make([]RequestResponse, len(b.history)) + copy(history, b.history) + b.lock.Unlock() + return history +} + +// Port returns the TCP port number the broker is listening for requests on. +func (b *MockBroker) Port() int32 { + return b.port +} + +// Addr returns the broker connection string in the form "
:". +func (b *MockBroker) Addr() string { + return b.listener.Addr().String() +} + +// Close terminates the broker blocking until it stops internal goroutines and +// releases all resources. +func (b *MockBroker) Close() { + close(b.expectations) + if len(b.expectations) > 0 { + buf := bytes.NewBufferString(fmt.Sprintf("mockbroker/%d: not all expectations were satisfied! Still waiting on:\n", b.BrokerID())) + for e := range b.expectations { + _, _ = buf.WriteString(spew.Sdump(e)) + } + b.t.Error(buf.String()) + } + close(b.closing) + <-b.stopper +} + +// setHandler sets the specified function as the request handler. Whenever +// a mock broker reads a request from the wire it passes the request to the +// function and sends back whatever the handler function returns. +func (b *MockBroker) setHandler(handler requestHandlerFunc) { + b.lock.Lock() + b.handler = handler + b.lock.Unlock() +} + +func (b *MockBroker) serverLoop() { + defer close(b.stopper) + var err error + var conn net.Conn + + go func() { + <-b.closing + err := b.listener.Close() + if err != nil { + b.t.Error(err) + } + }() + + wg := &sync.WaitGroup{} + i := 0 + for conn, err = b.listener.Accept(); err == nil; conn, err = b.listener.Accept() { + wg.Add(1) + go b.handleRequests(conn, i, wg) + i++ + } + wg.Wait() + Logger.Printf("*** mockbroker/%d: listener closed, err=%v", b.BrokerID(), err) +} + +func (b *MockBroker) SetGSSAPIHandler(handler GSSApiHandlerFunc) { + b.gssApiHandler = handler +} + +func (b *MockBroker) readToBytes(r io.Reader) ([]byte, error) { + var ( + bytesRead int + lengthBytes = make([]byte, 4) + ) + + if _, err := io.ReadFull(r, lengthBytes); err != nil { + return nil, err + } + + bytesRead += len(lengthBytes) + length := int32(binary.BigEndian.Uint32(lengthBytes)) + + if length <= 4 || length > MaxRequestSize { + return nil, PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", length)} + } + + encodedReq := make([]byte, length) + if _, err := io.ReadFull(r, encodedReq); err != nil { + return nil, err + } + + bytesRead += len(encodedReq) + + fullBytes := append(lengthBytes, encodedReq...) + + return fullBytes, nil +} + +func (b *MockBroker) isGSSAPI(buffer []byte) bool { + return buffer[4] == 0x60 || bytes.Equal(buffer[4:6], []byte{0x05, 0x04}) +} + +func (b *MockBroker) handleRequests(conn net.Conn, idx int, wg *sync.WaitGroup) { + defer wg.Done() + defer func() { + _ = conn.Close() + }() + Logger.Printf("*** mockbroker/%d/%d: connection opened", b.BrokerID(), idx) + var err error + + abort := make(chan none) + defer close(abort) + go func() { + select { + case <-b.closing: + _ = conn.Close() + case <-abort: + } + }() + + resHeader := make([]byte, 8) + var bytesWritten int + var bytesRead int + for { + + buffer, err := b.readToBytes(conn) + if err != nil { + Logger.Printf("*** mockbroker/%d/%d: invalid request: err=%+v, %+v", b.brokerID, idx, err, spew.Sdump(buffer)) + b.serverError(err) + break + } + + bytesWritten = 0 + if !b.isGSSAPI(buffer) { + + req, br, err := decodeRequest(bytes.NewReader(buffer)) + bytesRead = br + if err != nil { + Logger.Printf("*** mockbroker/%d/%d: invalid request: err=%+v, %+v", b.brokerID, idx, err, spew.Sdump(req)) + b.serverError(err) + break + } + + if b.latency > 0 { + time.Sleep(b.latency) + } + + b.lock.Lock() + res := b.handler(req) + b.history = append(b.history, RequestResponse{req.body, res}) + b.lock.Unlock() + + if res == nil { + Logger.Printf("*** mockbroker/%d/%d: ignored %v", b.brokerID, idx, spew.Sdump(req)) + continue + } + Logger.Printf("*** mockbroker/%d/%d: served %v -> %v", b.brokerID, idx, req, res) + + encodedRes, err := encode(res, nil) + if err != nil { + b.serverError(err) + break + } + if len(encodedRes) == 0 { + b.lock.Lock() + if b.notifier != nil { + b.notifier(bytesRead, 0) + } + b.lock.Unlock() + continue + } + + binary.BigEndian.PutUint32(resHeader, uint32(len(encodedRes)+4)) + binary.BigEndian.PutUint32(resHeader[4:], uint32(req.correlationID)) + if _, err = conn.Write(resHeader); err != nil { + b.serverError(err) + break + } + if _, err = conn.Write(encodedRes); err != nil { + b.serverError(err) + break + } + bytesWritten = len(resHeader) + len(encodedRes) + + } else { + // GSSAPI is not part of kafka protocol, but is supported for authentication proposes. + // Don't support history for this kind of request as is only used for test GSSAPI authentication mechanism + b.lock.Lock() + res := b.gssApiHandler(buffer) + b.lock.Unlock() + if res == nil { + Logger.Printf("*** mockbroker/%d/%d: ignored %v", b.brokerID, idx, spew.Sdump(buffer)) + continue + } + if _, err = conn.Write(res); err != nil { + b.serverError(err) + break + } + bytesWritten = len(res) + } + + b.lock.Lock() + if b.notifier != nil { + b.notifier(bytesRead, bytesWritten) + } + b.lock.Unlock() + + } + Logger.Printf("*** mockbroker/%d/%d: connection closed, err=%v", b.BrokerID(), idx, err) +} + +func (b *MockBroker) defaultRequestHandler(req *request) (res encoder) { + select { + case res, ok := <-b.expectations: + if !ok { + return nil + } + return res + case <-time.After(expectationTimeout): + return nil + } +} + +func (b *MockBroker) serverError(err error) { + isConnectionClosedError := false + if _, ok := err.(*net.OpError); ok { + isConnectionClosedError = true + } else if err == io.EOF { + isConnectionClosedError = true + } else if err.Error() == "use of closed network connection" { + isConnectionClosedError = true + } + + if isConnectionClosedError { + return + } + + b.t.Errorf(err.Error()) +} + +// NewMockBroker launches a fake Kafka broker. It takes a TestReporter as provided by the +// test framework and a channel of responses to use. If an error occurs it is +// simply logged to the TestReporter and the broker exits. +func NewMockBroker(t TestReporter, brokerID int32) *MockBroker { + return NewMockBrokerAddr(t, brokerID, "localhost:0") +} + +// NewMockBrokerAddr behaves like newMockBroker but listens on the address you give +// it rather than just some ephemeral port. +func NewMockBrokerAddr(t TestReporter, brokerID int32, addr string) *MockBroker { + listener, err := net.Listen("tcp", addr) + if err != nil { + t.Fatal(err) + } + return NewMockBrokerListener(t, brokerID, listener) +} + +// NewMockBrokerListener behaves like newMockBrokerAddr but accepts connections on the listener specified. +func NewMockBrokerListener(t TestReporter, brokerID int32, listener net.Listener) *MockBroker { + var err error + + broker := &MockBroker{ + closing: make(chan none), + stopper: make(chan none), + t: t, + brokerID: brokerID, + expectations: make(chan encoder, 512), + listener: listener, + } + broker.handler = broker.defaultRequestHandler + + Logger.Printf("*** mockbroker/%d listening on %s\n", brokerID, broker.listener.Addr().String()) + _, portStr, err := net.SplitHostPort(broker.listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + tmp, err := strconv.ParseInt(portStr, 10, 32) + if err != nil { + t.Fatal(err) + } + broker.port = int32(tmp) + + go broker.serverLoop() + + return broker +} + +func (b *MockBroker) Returns(e encoder) { + b.expectations <- e +} diff --git a/vendor/github.com/Shopify/sarama/mockkerberos.go b/vendor/github.com/Shopify/sarama/mockkerberos.go new file mode 100644 index 0000000..affeb2d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/mockkerberos.go @@ -0,0 +1,123 @@ +package sarama + +import ( + "encoding/binary" + "encoding/hex" + "gopkg.in/jcmturner/gokrb5.v7/credentials" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage" + "gopkg.in/jcmturner/gokrb5.v7/messages" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +type KafkaGSSAPIHandler struct { + client *MockKerberosClient + badResponse bool + badKeyChecksum bool +} + +func (h *KafkaGSSAPIHandler) MockKafkaGSSAPI(buffer []byte) []byte { + // Default payload used for verify + err := h.client.Login() // Mock client construct keys when login + if err != nil { + return nil + } + if h.badResponse { // Returns trash + return []byte{0x00, 0x00, 0x00, 0x01, 0xAD} + } + + var pack = gssapi.WrapToken{ + Flags: KRB5_USER_AUTH, + EC: 12, + RRC: 0, + SndSeqNum: 3398292281, + Payload: []byte{0x11, 0x00}, // 1100 + } + // Compute checksum + if h.badKeyChecksum { + pack.CheckSum = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + } else { + err = pack.SetCheckSum(h.client.ASRep.DecryptedEncPart.Key, keyusage.GSSAPI_ACCEPTOR_SEAL) + if err != nil { + return nil + } + } + + packBytes, err := pack.Marshal() + if err != nil { + return nil + } + lenBytes := len(packBytes) + response := make([]byte, lenBytes+4) + copy(response[4:], packBytes) + binary.BigEndian.PutUint32(response, uint32(lenBytes)) + return response +} + +type MockKerberosClient struct { + asReqBytes string + asRepBytes string + ASRep messages.ASRep + credentials *credentials.Credentials + mockError error + errorStage string +} + +func (c *MockKerberosClient) Login() error { + if c.errorStage == "login" && c.mockError != nil { + return c.mockError + } + c.asRepBytes = "6b8202e9308202e5a003020105a10302010ba22b30293027a103020113a220041e301c301aa003020112a1131b114" + + "558414d504c452e434f4d636c69656e74a30d1b0b4558414d504c452e434f4da4133011a003020101a10a30081b06636c69656e7" + + "4a5820156618201523082014ea003020105a10d1b0b4558414d504c452e434f4da220301ea003020102a11730151b066b7262746" + + "7741b0b4558414d504c452e434f4da382011430820110a003020112a103020101a28201020481ffdb9891175d106818e61008c51" + + "d0b3462bca92f3bf9d4cfa82de4c4d7aff9994ec87c573e3a3d54dcb2bb79618c76f2bf4a3d006f90d5bdbd049bc18f48be39203" + + "549ca02acaf63f292b12404f9b74c34b83687119d8f56552ccc0c50ebee2a53bb114c1b4619bb1d5d31f0f49b4d40a08a9b4c046" + + "2e1398d0b648be1c0e50c552ad16e1d8d8e74263dd0bf0ec591e4797dfd40a9a1be4ae830d03a306e053fd7586fef84ffc5e4a83" + + "7c3122bf3e6a40fe87e84019f6283634461b955712b44a5f7386c278bff94ec2c2dc0403247e29c2450e853471ceababf9b8911f" + + "997f2e3010b046d2c49eb438afb0f4c210821e80d4ffa4c9521eb895dcd68610b3feaa682012c30820128a003020112a282011f0" + + "482011bce73cbce3f1dd17661c412005f0f2257c756fe8e98ff97e6ec24b7bab66e5fd3a3827aeeae4757af0c6e892948122d8b2" + + "03c8df48df0ef5d142d0e416d688f11daa0fcd63d96bdd431d02b8e951c664eeff286a2be62383d274a04016d5f0e141da58cb86" + + "331de64063062f4f885e8e9ce5b181ca2fdc67897c5995e0ae1ae0c171a64493ff7bd91bc6d89cd4fce1e2b3ea0a10e34b0d5eda" + + "aa38ee727b50c5632ed1d2f2b457908e616178d0d80b72af209fb8ac9dbaa1768fa45931392b36b6d8c12400f8ded2efaa0654d0" + + "da1db966e8b5aab4706c800f95d559664646041fdb38b411c62fc0fbe0d25083a28562b0e1c8df16e62e9d5626b0addee489835f" + + "eedb0f26c05baa596b69b17f47920aa64b29dc77cfcc97ba47885" + apRepBytes, err := hex.DecodeString(c.asRepBytes) + if err != nil { + return err + } + err = c.ASRep.Unmarshal(apRepBytes) + if err != nil { + return err + } + c.credentials = credentials.New("client", "EXAMPLE.COM").WithPassword("qwerty") + _, err = c.ASRep.DecryptEncPart(c.credentials) + if err != nil { + return err + } + return nil +} + +func (c *MockKerberosClient) GetServiceTicket(spn string) (messages.Ticket, types.EncryptionKey, error) { + if c.errorStage == "service_ticket" && c.mockError != nil { + return messages.Ticket{}, types.EncryptionKey{}, c.mockError + } + return c.ASRep.Ticket, c.ASRep.DecryptedEncPart.Key, nil +} + +func (c *MockKerberosClient) Domain() string { + return "EXAMPLE.COM" +} +func (c *MockKerberosClient) CName() types.PrincipalName { + var p = types.PrincipalName{ + NameType: KRB5_USER_AUTH, + NameString: []string{ + "kafka", + "kafka", + }, + } + return p +} +func (c *MockKerberosClient) Destroy() { + // Do nothing. +} diff --git a/vendor/github.com/Shopify/sarama/mockresponses.go b/vendor/github.com/Shopify/sarama/mockresponses.go new file mode 100644 index 0000000..7dcc93e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/mockresponses.go @@ -0,0 +1,941 @@ +package sarama + +import ( + "fmt" + "strings" +) + +// TestReporter has methods matching go's testing.T to avoid importing +// `testing` in the main part of the library. +type TestReporter interface { + Error(...interface{}) + Errorf(string, ...interface{}) + Fatal(...interface{}) + Fatalf(string, ...interface{}) +} + +// MockResponse is a response builder interface it defines one method that +// allows generating a response based on a request body. MockResponses are used +// to program behavior of MockBroker in tests. +type MockResponse interface { + For(reqBody versionedDecoder) (res encoder) +} + +// MockWrapper is a mock response builder that returns a particular concrete +// response regardless of the actual request passed to the `For` method. +type MockWrapper struct { + res encoder +} + +func (mw *MockWrapper) For(reqBody versionedDecoder) (res encoder) { + return mw.res +} + +func NewMockWrapper(res encoder) *MockWrapper { + return &MockWrapper{res: res} +} + +// MockSequence is a mock response builder that is created from a sequence of +// concrete responses. Every time when a `MockBroker` calls its `For` method +// the next response from the sequence is returned. When the end of the +// sequence is reached the last element from the sequence is returned. +type MockSequence struct { + responses []MockResponse +} + +func NewMockSequence(responses ...interface{}) *MockSequence { + ms := &MockSequence{} + ms.responses = make([]MockResponse, len(responses)) + for i, res := range responses { + switch res := res.(type) { + case MockResponse: + ms.responses[i] = res + case encoder: + ms.responses[i] = NewMockWrapper(res) + default: + panic(fmt.Sprintf("Unexpected response type: %T", res)) + } + } + return ms +} + +func (mc *MockSequence) For(reqBody versionedDecoder) (res encoder) { + res = mc.responses[0].For(reqBody) + if len(mc.responses) > 1 { + mc.responses = mc.responses[1:] + } + return res +} + +type MockListGroupsResponse struct { + groups map[string]string + t TestReporter +} + +func NewMockListGroupsResponse(t TestReporter) *MockListGroupsResponse { + return &MockListGroupsResponse{ + groups: make(map[string]string), + t: t, + } +} + +func (m *MockListGroupsResponse) For(reqBody versionedDecoder) encoder { + request := reqBody.(*ListGroupsRequest) + _ = request + response := &ListGroupsResponse{ + Groups: m.groups, + } + return response +} + +func (m *MockListGroupsResponse) AddGroup(groupID, protocolType string) *MockListGroupsResponse { + m.groups[groupID] = protocolType + return m +} + +type MockDescribeGroupsResponse struct { + groups map[string]*GroupDescription + t TestReporter +} + +func NewMockDescribeGroupsResponse(t TestReporter) *MockDescribeGroupsResponse { + return &MockDescribeGroupsResponse{ + t: t, + groups: make(map[string]*GroupDescription), + } +} + +func (m *MockDescribeGroupsResponse) AddGroupDescription(groupID string, description *GroupDescription) *MockDescribeGroupsResponse { + m.groups[groupID] = description + return m +} + +func (m *MockDescribeGroupsResponse) For(reqBody versionedDecoder) encoder { + request := reqBody.(*DescribeGroupsRequest) + + response := &DescribeGroupsResponse{} + for _, requestedGroup := range request.Groups { + if group, ok := m.groups[requestedGroup]; ok { + response.Groups = append(response.Groups, group) + } else { + // Mimic real kafka - if a group doesn't exist, return + // an entry with state "Dead" + response.Groups = append(response.Groups, &GroupDescription{ + GroupId: requestedGroup, + State: "Dead", + }) + } + } + + return response +} + +// MockMetadataResponse is a `MetadataResponse` builder. +type MockMetadataResponse struct { + controllerID int32 + leaders map[string]map[int32]int32 + brokers map[string]int32 + t TestReporter +} + +func NewMockMetadataResponse(t TestReporter) *MockMetadataResponse { + return &MockMetadataResponse{ + leaders: make(map[string]map[int32]int32), + brokers: make(map[string]int32), + t: t, + } +} + +func (mmr *MockMetadataResponse) SetLeader(topic string, partition, brokerID int32) *MockMetadataResponse { + partitions := mmr.leaders[topic] + if partitions == nil { + partitions = make(map[int32]int32) + mmr.leaders[topic] = partitions + } + partitions[partition] = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) SetBroker(addr string, brokerID int32) *MockMetadataResponse { + mmr.brokers[addr] = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) SetController(brokerID int32) *MockMetadataResponse { + mmr.controllerID = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) For(reqBody versionedDecoder) encoder { + metadataRequest := reqBody.(*MetadataRequest) + metadataResponse := &MetadataResponse{ + Version: metadataRequest.version(), + ControllerID: mmr.controllerID, + } + for addr, brokerID := range mmr.brokers { + metadataResponse.AddBroker(addr, brokerID) + } + + // Generate set of replicas + replicas := []int32{} + offlineReplicas := []int32{} + for _, brokerID := range mmr.brokers { + replicas = append(replicas, brokerID) + } + + if len(metadataRequest.Topics) == 0 { + for topic, partitions := range mmr.leaders { + for partition, brokerID := range partitions { + metadataResponse.AddTopicPartition(topic, partition, brokerID, replicas, replicas, offlineReplicas, ErrNoError) + } + } + return metadataResponse + } + for _, topic := range metadataRequest.Topics { + for partition, brokerID := range mmr.leaders[topic] { + metadataResponse.AddTopicPartition(topic, partition, brokerID, replicas, replicas, offlineReplicas, ErrNoError) + } + } + return metadataResponse +} + +// MockOffsetResponse is an `OffsetResponse` builder. +type MockOffsetResponse struct { + offsets map[string]map[int32]map[int64]int64 + t TestReporter + version int16 +} + +func NewMockOffsetResponse(t TestReporter) *MockOffsetResponse { + return &MockOffsetResponse{ + offsets: make(map[string]map[int32]map[int64]int64), + t: t, + } +} + +func (mor *MockOffsetResponse) SetVersion(version int16) *MockOffsetResponse { + mor.version = version + return mor +} + +func (mor *MockOffsetResponse) SetOffset(topic string, partition int32, time, offset int64) *MockOffsetResponse { + partitions := mor.offsets[topic] + if partitions == nil { + partitions = make(map[int32]map[int64]int64) + mor.offsets[topic] = partitions + } + times := partitions[partition] + if times == nil { + times = make(map[int64]int64) + partitions[partition] = times + } + times[time] = offset + return mor +} + +func (mor *MockOffsetResponse) For(reqBody versionedDecoder) encoder { + offsetRequest := reqBody.(*OffsetRequest) + offsetResponse := &OffsetResponse{Version: mor.version} + for topic, partitions := range offsetRequest.blocks { + for partition, block := range partitions { + offset := mor.getOffset(topic, partition, block.time) + offsetResponse.AddTopicPartition(topic, partition, offset) + } + } + return offsetResponse +} + +func (mor *MockOffsetResponse) getOffset(topic string, partition int32, time int64) int64 { + partitions := mor.offsets[topic] + if partitions == nil { + mor.t.Errorf("missing topic: %s", topic) + } + times := partitions[partition] + if times == nil { + mor.t.Errorf("missing partition: %d", partition) + } + offset, ok := times[time] + if !ok { + mor.t.Errorf("missing time: %d", time) + } + return offset +} + +// MockFetchResponse is a `FetchResponse` builder. +type MockFetchResponse struct { + messages map[string]map[int32]map[int64]Encoder + highWaterMarks map[string]map[int32]int64 + t TestReporter + batchSize int + version int16 +} + +func NewMockFetchResponse(t TestReporter, batchSize int) *MockFetchResponse { + return &MockFetchResponse{ + messages: make(map[string]map[int32]map[int64]Encoder), + highWaterMarks: make(map[string]map[int32]int64), + t: t, + batchSize: batchSize, + } +} + +func (mfr *MockFetchResponse) SetVersion(version int16) *MockFetchResponse { + mfr.version = version + return mfr +} + +func (mfr *MockFetchResponse) SetMessage(topic string, partition int32, offset int64, msg Encoder) *MockFetchResponse { + partitions := mfr.messages[topic] + if partitions == nil { + partitions = make(map[int32]map[int64]Encoder) + mfr.messages[topic] = partitions + } + messages := partitions[partition] + if messages == nil { + messages = make(map[int64]Encoder) + partitions[partition] = messages + } + messages[offset] = msg + return mfr +} + +func (mfr *MockFetchResponse) SetHighWaterMark(topic string, partition int32, offset int64) *MockFetchResponse { + partitions := mfr.highWaterMarks[topic] + if partitions == nil { + partitions = make(map[int32]int64) + mfr.highWaterMarks[topic] = partitions + } + partitions[partition] = offset + return mfr +} + +func (mfr *MockFetchResponse) For(reqBody versionedDecoder) encoder { + fetchRequest := reqBody.(*FetchRequest) + res := &FetchResponse{ + Version: mfr.version, + } + for topic, partitions := range fetchRequest.blocks { + for partition, block := range partitions { + initialOffset := block.fetchOffset + offset := initialOffset + maxOffset := initialOffset + int64(mfr.getMessageCount(topic, partition)) + for i := 0; i < mfr.batchSize && offset < maxOffset; { + msg := mfr.getMessage(topic, partition, offset) + if msg != nil { + res.AddMessage(topic, partition, nil, msg, offset) + i++ + } + offset++ + } + fb := res.GetBlock(topic, partition) + if fb == nil { + res.AddError(topic, partition, ErrNoError) + fb = res.GetBlock(topic, partition) + } + fb.HighWaterMarkOffset = mfr.getHighWaterMark(topic, partition) + } + } + return res +} + +func (mfr *MockFetchResponse) getMessage(topic string, partition int32, offset int64) Encoder { + partitions := mfr.messages[topic] + if partitions == nil { + return nil + } + messages := partitions[partition] + if messages == nil { + return nil + } + return messages[offset] +} + +func (mfr *MockFetchResponse) getMessageCount(topic string, partition int32) int { + partitions := mfr.messages[topic] + if partitions == nil { + return 0 + } + messages := partitions[partition] + if messages == nil { + return 0 + } + return len(messages) +} + +func (mfr *MockFetchResponse) getHighWaterMark(topic string, partition int32) int64 { + partitions := mfr.highWaterMarks[topic] + if partitions == nil { + return 0 + } + return partitions[partition] +} + +// MockConsumerMetadataResponse is a `ConsumerMetadataResponse` builder. +type MockConsumerMetadataResponse struct { + coordinators map[string]interface{} + t TestReporter +} + +func NewMockConsumerMetadataResponse(t TestReporter) *MockConsumerMetadataResponse { + return &MockConsumerMetadataResponse{ + coordinators: make(map[string]interface{}), + t: t, + } +} + +func (mr *MockConsumerMetadataResponse) SetCoordinator(group string, broker *MockBroker) *MockConsumerMetadataResponse { + mr.coordinators[group] = broker + return mr +} + +func (mr *MockConsumerMetadataResponse) SetError(group string, kerror KError) *MockConsumerMetadataResponse { + mr.coordinators[group] = kerror + return mr +} + +func (mr *MockConsumerMetadataResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*ConsumerMetadataRequest) + group := req.ConsumerGroup + res := &ConsumerMetadataResponse{} + v := mr.coordinators[group] + switch v := v.(type) { + case *MockBroker: + res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} + case KError: + res.Err = v + } + return res +} + +// MockFindCoordinatorResponse is a `FindCoordinatorResponse` builder. +type MockFindCoordinatorResponse struct { + groupCoordinators map[string]interface{} + transCoordinators map[string]interface{} + t TestReporter +} + +func NewMockFindCoordinatorResponse(t TestReporter) *MockFindCoordinatorResponse { + return &MockFindCoordinatorResponse{ + groupCoordinators: make(map[string]interface{}), + transCoordinators: make(map[string]interface{}), + t: t, + } +} + +func (mr *MockFindCoordinatorResponse) SetCoordinator(coordinatorType CoordinatorType, group string, broker *MockBroker) *MockFindCoordinatorResponse { + switch coordinatorType { + case CoordinatorGroup: + mr.groupCoordinators[group] = broker + case CoordinatorTransaction: + mr.transCoordinators[group] = broker + } + return mr +} + +func (mr *MockFindCoordinatorResponse) SetError(coordinatorType CoordinatorType, group string, kerror KError) *MockFindCoordinatorResponse { + switch coordinatorType { + case CoordinatorGroup: + mr.groupCoordinators[group] = kerror + case CoordinatorTransaction: + mr.transCoordinators[group] = kerror + } + return mr +} + +func (mr *MockFindCoordinatorResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*FindCoordinatorRequest) + res := &FindCoordinatorResponse{} + var v interface{} + switch req.CoordinatorType { + case CoordinatorGroup: + v = mr.groupCoordinators[req.CoordinatorKey] + case CoordinatorTransaction: + v = mr.transCoordinators[req.CoordinatorKey] + } + switch v := v.(type) { + case *MockBroker: + res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} + case KError: + res.Err = v + } + return res +} + +// MockOffsetCommitResponse is a `OffsetCommitResponse` builder. +type MockOffsetCommitResponse struct { + errors map[string]map[string]map[int32]KError + t TestReporter +} + +func NewMockOffsetCommitResponse(t TestReporter) *MockOffsetCommitResponse { + return &MockOffsetCommitResponse{t: t} +} + +func (mr *MockOffsetCommitResponse) SetError(group, topic string, partition int32, kerror KError) *MockOffsetCommitResponse { + if mr.errors == nil { + mr.errors = make(map[string]map[string]map[int32]KError) + } + topics := mr.errors[group] + if topics == nil { + topics = make(map[string]map[int32]KError) + mr.errors[group] = topics + } + partitions := topics[topic] + if partitions == nil { + partitions = make(map[int32]KError) + topics[topic] = partitions + } + partitions[partition] = kerror + return mr +} + +func (mr *MockOffsetCommitResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*OffsetCommitRequest) + group := req.ConsumerGroup + res := &OffsetCommitResponse{} + for topic, partitions := range req.blocks { + for partition := range partitions { + res.AddError(topic, partition, mr.getError(group, topic, partition)) + } + } + return res +} + +func (mr *MockOffsetCommitResponse) getError(group, topic string, partition int32) KError { + topics := mr.errors[group] + if topics == nil { + return ErrNoError + } + partitions := topics[topic] + if partitions == nil { + return ErrNoError + } + kerror, ok := partitions[partition] + if !ok { + return ErrNoError + } + return kerror +} + +// MockProduceResponse is a `ProduceResponse` builder. +type MockProduceResponse struct { + version int16 + errors map[string]map[int32]KError + t TestReporter +} + +func NewMockProduceResponse(t TestReporter) *MockProduceResponse { + return &MockProduceResponse{t: t} +} + +func (mr *MockProduceResponse) SetVersion(version int16) *MockProduceResponse { + mr.version = version + return mr +} + +func (mr *MockProduceResponse) SetError(topic string, partition int32, kerror KError) *MockProduceResponse { + if mr.errors == nil { + mr.errors = make(map[string]map[int32]KError) + } + partitions := mr.errors[topic] + if partitions == nil { + partitions = make(map[int32]KError) + mr.errors[topic] = partitions + } + partitions[partition] = kerror + return mr +} + +func (mr *MockProduceResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*ProduceRequest) + res := &ProduceResponse{ + Version: mr.version, + } + for topic, partitions := range req.records { + for partition := range partitions { + res.AddTopicPartition(topic, partition, mr.getError(topic, partition)) + } + } + return res +} + +func (mr *MockProduceResponse) getError(topic string, partition int32) KError { + partitions := mr.errors[topic] + if partitions == nil { + return ErrNoError + } + kerror, ok := partitions[partition] + if !ok { + return ErrNoError + } + return kerror +} + +// MockOffsetFetchResponse is a `OffsetFetchResponse` builder. +type MockOffsetFetchResponse struct { + offsets map[string]map[string]map[int32]*OffsetFetchResponseBlock + error KError + t TestReporter +} + +func NewMockOffsetFetchResponse(t TestReporter) *MockOffsetFetchResponse { + return &MockOffsetFetchResponse{t: t} +} + +func (mr *MockOffsetFetchResponse) SetOffset(group, topic string, partition int32, offset int64, metadata string, kerror KError) *MockOffsetFetchResponse { + if mr.offsets == nil { + mr.offsets = make(map[string]map[string]map[int32]*OffsetFetchResponseBlock) + } + topics := mr.offsets[group] + if topics == nil { + topics = make(map[string]map[int32]*OffsetFetchResponseBlock) + mr.offsets[group] = topics + } + partitions := topics[topic] + if partitions == nil { + partitions = make(map[int32]*OffsetFetchResponseBlock) + topics[topic] = partitions + } + partitions[partition] = &OffsetFetchResponseBlock{offset, 0, metadata, kerror} + return mr +} + +func (mr *MockOffsetFetchResponse) SetError(kerror KError) *MockOffsetFetchResponse { + mr.error = kerror + return mr +} + +func (mr *MockOffsetFetchResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*OffsetFetchRequest) + group := req.ConsumerGroup + res := &OffsetFetchResponse{Version: req.Version} + + for topic, partitions := range mr.offsets[group] { + for partition, block := range partitions { + res.AddBlock(topic, partition, block) + } + } + + if res.Version >= 2 { + res.Err = mr.error + } + return res +} + +type MockCreateTopicsResponse struct { + t TestReporter +} + +func NewMockCreateTopicsResponse(t TestReporter) *MockCreateTopicsResponse { + return &MockCreateTopicsResponse{t: t} +} + +func (mr *MockCreateTopicsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreateTopicsRequest) + res := &CreateTopicsResponse{ + Version: req.Version, + } + res.TopicErrors = make(map[string]*TopicError) + + for topic := range req.TopicDetails { + if res.Version >= 1 && strings.HasPrefix(topic, "_") { + msg := "insufficient permissions to create topic with reserved prefix" + res.TopicErrors[topic] = &TopicError{ + Err: ErrTopicAuthorizationFailed, + ErrMsg: &msg, + } + continue + } + res.TopicErrors[topic] = &TopicError{Err: ErrNoError} + } + return res +} + +type MockDeleteTopicsResponse struct { + t TestReporter +} + +func NewMockDeleteTopicsResponse(t TestReporter) *MockDeleteTopicsResponse { + return &MockDeleteTopicsResponse{t: t} +} + +func (mr *MockDeleteTopicsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteTopicsRequest) + res := &DeleteTopicsResponse{} + res.TopicErrorCodes = make(map[string]KError) + + for _, topic := range req.Topics { + res.TopicErrorCodes[topic] = ErrNoError + } + res.Version = int16(req.Version) + return res +} + +type MockCreatePartitionsResponse struct { + t TestReporter +} + +func NewMockCreatePartitionsResponse(t TestReporter) *MockCreatePartitionsResponse { + return &MockCreatePartitionsResponse{t: t} +} + +func (mr *MockCreatePartitionsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreatePartitionsRequest) + res := &CreatePartitionsResponse{} + res.TopicPartitionErrors = make(map[string]*TopicPartitionError) + + for topic := range req.TopicPartitions { + if strings.HasPrefix(topic, "_") { + msg := "insufficient permissions to create partition on topic with reserved prefix" + res.TopicPartitionErrors[topic] = &TopicPartitionError{ + Err: ErrTopicAuthorizationFailed, + ErrMsg: &msg, + } + continue + } + res.TopicPartitionErrors[topic] = &TopicPartitionError{Err: ErrNoError} + } + return res +} + +type MockDeleteRecordsResponse struct { + t TestReporter +} + +func NewMockDeleteRecordsResponse(t TestReporter) *MockDeleteRecordsResponse { + return &MockDeleteRecordsResponse{t: t} +} + +func (mr *MockDeleteRecordsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteRecordsRequest) + res := &DeleteRecordsResponse{} + res.Topics = make(map[string]*DeleteRecordsResponseTopic) + + for topic, deleteRecordRequestTopic := range req.Topics { + partitions := make(map[int32]*DeleteRecordsResponsePartition) + for partition := range deleteRecordRequestTopic.PartitionOffsets { + partitions[partition] = &DeleteRecordsResponsePartition{Err: ErrNoError} + } + res.Topics[topic] = &DeleteRecordsResponseTopic{Partitions: partitions} + } + return res +} + +type MockDescribeConfigsResponse struct { + t TestReporter +} + +func NewMockDescribeConfigsResponse(t TestReporter) *MockDescribeConfigsResponse { + return &MockDescribeConfigsResponse{t: t} +} + +func (mr *MockDescribeConfigsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DescribeConfigsRequest) + res := &DescribeConfigsResponse{} + + for _, r := range req.Resources { + var configEntries []*ConfigEntry + switch r.Type { + case TopicResource: + configEntries = append(configEntries, + &ConfigEntry{Name: "max.message.bytes", + Value: "1000000", + ReadOnly: false, + Default: true, + Sensitive: false, + }, &ConfigEntry{Name: "retention.ms", + Value: "5000", + ReadOnly: false, + Default: false, + Sensitive: false, + }, &ConfigEntry{Name: "password", + Value: "12345", + ReadOnly: false, + Default: false, + Sensitive: true, + }) + res.Resources = append(res.Resources, &ResourceResponse{ + Name: r.Name, + Configs: configEntries, + }) + } + } + return res +} + +type MockAlterConfigsResponse struct { + t TestReporter +} + +func NewMockAlterConfigsResponse(t TestReporter) *MockAlterConfigsResponse { + return &MockAlterConfigsResponse{t: t} +} + +func (mr *MockAlterConfigsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*AlterConfigsRequest) + res := &AlterConfigsResponse{} + + for _, r := range req.Resources { + res.Resources = append(res.Resources, &AlterConfigsResourceResponse{Name: r.Name, + Type: TopicResource, + ErrorMsg: "", + }) + } + return res +} + +type MockCreateAclsResponse struct { + t TestReporter +} + +func NewMockCreateAclsResponse(t TestReporter) *MockCreateAclsResponse { + return &MockCreateAclsResponse{t: t} +} + +func (mr *MockCreateAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreateAclsRequest) + res := &CreateAclsResponse{} + + for range req.AclCreations { + res.AclCreationResponses = append(res.AclCreationResponses, &AclCreationResponse{Err: ErrNoError}) + } + return res +} + +type MockListAclsResponse struct { + t TestReporter +} + +func NewMockListAclsResponse(t TestReporter) *MockListAclsResponse { + return &MockListAclsResponse{t: t} +} + +func (mr *MockListAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DescribeAclsRequest) + res := &DescribeAclsResponse{} + res.Err = ErrNoError + acl := &ResourceAcls{} + if req.ResourceName != nil { + acl.Resource.ResourceName = *req.ResourceName + } + acl.Resource.ResourcePatternType = req.ResourcePatternTypeFilter + acl.Resource.ResourceType = req.ResourceType + + host := "*" + if req.Host != nil { + host = *req.Host + } + + principal := "User:test" + if req.Principal != nil { + principal = *req.Principal + } + + permissionType := req.PermissionType + if permissionType == AclPermissionAny { + permissionType = AclPermissionAllow + } + + acl.Acls = append(acl.Acls, &Acl{Operation: req.Operation, PermissionType: permissionType, Host: host, Principal: principal}) + res.ResourceAcls = append(res.ResourceAcls, acl) + res.Version = int16(req.Version) + return res +} + +type MockSaslAuthenticateResponse struct { + t TestReporter + kerror KError + saslAuthBytes []byte +} + +func NewMockSaslAuthenticateResponse(t TestReporter) *MockSaslAuthenticateResponse { + return &MockSaslAuthenticateResponse{t: t} +} + +func (msar *MockSaslAuthenticateResponse) For(reqBody versionedDecoder) encoder { + res := &SaslAuthenticateResponse{} + res.Err = msar.kerror + res.SaslAuthBytes = msar.saslAuthBytes + return res +} + +func (msar *MockSaslAuthenticateResponse) SetError(kerror KError) *MockSaslAuthenticateResponse { + msar.kerror = kerror + return msar +} + +func (msar *MockSaslAuthenticateResponse) SetAuthBytes(saslAuthBytes []byte) *MockSaslAuthenticateResponse { + msar.saslAuthBytes = saslAuthBytes + return msar +} + +type MockDeleteAclsResponse struct { + t TestReporter +} + +type MockSaslHandshakeResponse struct { + enabledMechanisms []string + kerror KError + t TestReporter +} + +func NewMockSaslHandshakeResponse(t TestReporter) *MockSaslHandshakeResponse { + return &MockSaslHandshakeResponse{t: t} +} + +func (mshr *MockSaslHandshakeResponse) For(reqBody versionedDecoder) encoder { + res := &SaslHandshakeResponse{} + res.Err = mshr.kerror + res.EnabledMechanisms = mshr.enabledMechanisms + return res +} + +func (mshr *MockSaslHandshakeResponse) SetError(kerror KError) *MockSaslHandshakeResponse { + mshr.kerror = kerror + return mshr +} + +func (mshr *MockSaslHandshakeResponse) SetEnabledMechanisms(enabledMechanisms []string) *MockSaslHandshakeResponse { + mshr.enabledMechanisms = enabledMechanisms + return mshr +} + +func NewMockDeleteAclsResponse(t TestReporter) *MockDeleteAclsResponse { + return &MockDeleteAclsResponse{t: t} +} + +func (mr *MockDeleteAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteAclsRequest) + res := &DeleteAclsResponse{} + + for range req.Filters { + response := &FilterResponse{Err: ErrNoError} + response.MatchingAcls = append(response.MatchingAcls, &MatchingAcl{Err: ErrNoError}) + res.FilterResponses = append(res.FilterResponses, response) + } + res.Version = int16(req.Version) + return res +} + +type MockDeleteGroupsResponse struct { + deletedGroups []string +} + +func NewMockDeleteGroupsRequest(t TestReporter) *MockDeleteGroupsResponse { + return &MockDeleteGroupsResponse{} +} + +func (m *MockDeleteGroupsResponse) SetDeletedGroups(groups []string) *MockDeleteGroupsResponse { + m.deletedGroups = groups + return m +} + +func (m *MockDeleteGroupsResponse) For(reqBody versionedDecoder) encoder { + resp := &DeleteGroupsResponse{ + GroupErrorCodes: map[string]KError{}, + } + for _, group := range m.deletedGroups { + resp.GroupErrorCodes[group] = ErrNoError + } + return resp +} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_request.go b/vendor/github.com/Shopify/sarama/offset_commit_request.go new file mode 100644 index 0000000..5732ed9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_commit_request.go @@ -0,0 +1,210 @@ +package sarama + +import "errors" + +// ReceiveTime is a special value for the timestamp field of Offset Commit Requests which +// tells the broker to set the timestamp to the time at which the request was received. +// The timestamp is only used if message version 1 is used, which requires kafka 0.8.2. +const ReceiveTime int64 = -1 + +// GroupGenerationUndefined is a special value for the group generation field of +// Offset Commit Requests that should be used when a consumer group does not rely +// on Kafka for partition management. +const GroupGenerationUndefined = -1 + +type offsetCommitRequestBlock struct { + offset int64 + timestamp int64 + metadata string +} + +func (b *offsetCommitRequestBlock) encode(pe packetEncoder, version int16) error { + pe.putInt64(b.offset) + if version == 1 { + pe.putInt64(b.timestamp) + } else if b.timestamp != 0 { + Logger.Println("Non-zero timestamp specified for OffsetCommitRequest not v1, it will be ignored") + } + + return pe.putString(b.metadata) +} + +func (b *offsetCommitRequestBlock) decode(pd packetDecoder, version int16) (err error) { + if b.offset, err = pd.getInt64(); err != nil { + return err + } + if version == 1 { + if b.timestamp, err = pd.getInt64(); err != nil { + return err + } + } + b.metadata, err = pd.getString() + return err +} + +type OffsetCommitRequest struct { + ConsumerGroup string + ConsumerGroupGeneration int32 // v1 or later + ConsumerID string // v1 or later + RetentionTime int64 // v2 or later + + // Version can be: + // - 0 (kafka 0.8.1 and later) + // - 1 (kafka 0.8.2 and later) + // - 2 (kafka 0.9.0 and later) + // - 3 (kafka 0.11.0 and later) + // - 4 (kafka 2.0.0 and later) + Version int16 + blocks map[string]map[int32]*offsetCommitRequestBlock +} + +func (r *OffsetCommitRequest) encode(pe packetEncoder) error { + if r.Version < 0 || r.Version > 4 { + return PacketEncodingError{"invalid or unsupported OffsetCommitRequest version field"} + } + + if err := pe.putString(r.ConsumerGroup); err != nil { + return err + } + + if r.Version >= 1 { + pe.putInt32(r.ConsumerGroupGeneration) + if err := pe.putString(r.ConsumerID); err != nil { + return err + } + } else { + if r.ConsumerGroupGeneration != 0 { + Logger.Println("Non-zero ConsumerGroupGeneration specified for OffsetCommitRequest v0, it will be ignored") + } + if r.ConsumerID != "" { + Logger.Println("Non-empty ConsumerID specified for OffsetCommitRequest v0, it will be ignored") + } + } + + if r.Version >= 2 { + pe.putInt64(r.RetentionTime) + } else if r.RetentionTime != 0 { + Logger.Println("Non-zero RetentionTime specified for OffsetCommitRequest version <2, it will be ignored") + } + + if err := pe.putArrayLength(len(r.blocks)); err != nil { + return err + } + for topic, partitions := range r.blocks { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err := block.encode(pe, r.Version); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.ConsumerGroup, err = pd.getString(); err != nil { + return err + } + + if r.Version >= 1 { + if r.ConsumerGroupGeneration, err = pd.getInt32(); err != nil { + return err + } + if r.ConsumerID, err = pd.getString(); err != nil { + return err + } + } + + if r.Version >= 2 { + if r.RetentionTime, err = pd.getInt64(); err != nil { + return err + } + } + + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + block := &offsetCommitRequestBlock{} + if err := block.decode(pd, r.Version); err != nil { + return err + } + r.blocks[topic][partition] = block + } + } + return nil +} + +func (r *OffsetCommitRequest) key() int16 { + return 8 +} + +func (r *OffsetCommitRequest) version() int16 { + return r.Version +} + +func (r *OffsetCommitRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + case 2: + return V0_9_0_0 + case 3: + return V0_11_0_0 + case 4: + return V2_0_0_0 + default: + return MinVersion + } +} + +func (r *OffsetCommitRequest) AddBlock(topic string, partitionID int32, offset int64, timestamp int64, metadata string) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) + } + + r.blocks[topic][partitionID] = &offsetCommitRequestBlock{offset, timestamp, metadata} +} + +func (r *OffsetCommitRequest) Offset(topic string, partitionID int32) (int64, string, error) { + partitions := r.blocks[topic] + if partitions == nil { + return 0, "", errors.New("no such offset") + } + block := partitions[partitionID] + if block == nil { + return 0, "", errors.New("no such offset") + } + return block.offset, block.metadata, nil +} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_response.go b/vendor/github.com/Shopify/sarama/offset_commit_response.go new file mode 100644 index 0000000..e842298 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_commit_response.go @@ -0,0 +1,110 @@ +package sarama + +type OffsetCommitResponse struct { + Version int16 + ThrottleTimeMs int32 + Errors map[string]map[int32]KError +} + +func (r *OffsetCommitResponse) AddError(topic string, partition int32, kerror KError) { + if r.Errors == nil { + r.Errors = make(map[string]map[int32]KError) + } + partitions := r.Errors[topic] + if partitions == nil { + partitions = make(map[int32]KError) + r.Errors[topic] = partitions + } + partitions[partition] = kerror +} + +func (r *OffsetCommitResponse) encode(pe packetEncoder) error { + if r.Version >= 3 { + pe.putInt32(r.ThrottleTimeMs) + } + if err := pe.putArrayLength(len(r.Errors)); err != nil { + return err + } + for topic, partitions := range r.Errors { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, kerror := range partitions { + pe.putInt32(partition) + pe.putInt16(int16(kerror)) + } + } + return nil +} + +func (r *OffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 3 { + r.ThrottleTimeMs, err = pd.getInt32() + if err != nil { + return err + } + } + + numTopics, err := pd.getArrayLength() + if err != nil || numTopics == 0 { + return err + } + + r.Errors = make(map[string]map[int32]KError, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numErrors, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Errors[name] = make(map[int32]KError, numErrors) + + for j := 0; j < numErrors; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + r.Errors[name][id] = KError(tmp) + } + } + + return nil +} + +func (r *OffsetCommitResponse) key() int16 { + return 8 +} + +func (r *OffsetCommitResponse) version() int16 { + return r.Version +} + +func (r *OffsetCommitResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + case 2: + return V0_9_0_0 + case 3: + return V0_11_0_0 + case 4: + return V2_0_0_0 + default: + return MinVersion + } +} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_request.go b/vendor/github.com/Shopify/sarama/offset_fetch_request.go new file mode 100644 index 0000000..6860824 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_fetch_request.go @@ -0,0 +1,100 @@ +package sarama + +type OffsetFetchRequest struct { + Version int16 + ConsumerGroup string + partitions map[string][]int32 +} + +func (r *OffsetFetchRequest) encode(pe packetEncoder) (err error) { + if r.Version < 0 || r.Version > 5 { + return PacketEncodingError{"invalid or unsupported OffsetFetchRequest version field"} + } + + if err = pe.putString(r.ConsumerGroup); err != nil { + return err + } + + if r.Version >= 2 && r.partitions == nil { + pe.putInt32(-1) + } else { + if err = pe.putArrayLength(len(r.partitions)); err != nil { + return err + } + for topic, partitions := range r.partitions { + if err = pe.putString(topic); err != nil { + return err + } + if err = pe.putInt32Array(partitions); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetFetchRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + if r.ConsumerGroup, err = pd.getString(); err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + if (partitionCount == 0 && version < 2) || partitionCount < 0 { + return nil + } + r.partitions = make(map[string][]int32) + for i := 0; i < partitionCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitions, err := pd.getInt32Array() + if err != nil { + return err + } + r.partitions[topic] = partitions + } + return nil +} + +func (r *OffsetFetchRequest) key() int16 { + return 9 +} + +func (r *OffsetFetchRequest) version() int16 { + return r.Version +} + +func (r *OffsetFetchRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + case 2: + return V0_10_2_0 + case 3: + return V0_11_0_0 + case 4: + return V2_0_0_0 + case 5: + return V2_1_0_0 + default: + return MinVersion + } +} + +func (r *OffsetFetchRequest) ZeroPartitions() { + if r.partitions == nil && r.Version >= 2 { + r.partitions = make(map[string][]int32) + } +} + +func (r *OffsetFetchRequest) AddPartition(topic string, partitionID int32) { + if r.partitions == nil { + r.partitions = make(map[string][]int32) + } + + r.partitions[topic] = append(r.partitions[topic], partitionID) +} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_response.go b/vendor/github.com/Shopify/sarama/offset_fetch_response.go new file mode 100644 index 0000000..9e25702 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_fetch_response.go @@ -0,0 +1,197 @@ +package sarama + +type OffsetFetchResponseBlock struct { + Offset int64 + LeaderEpoch int32 + Metadata string + Err KError +} + +func (b *OffsetFetchResponseBlock) decode(pd packetDecoder, version int16) (err error) { + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + if version >= 5 { + b.LeaderEpoch, err = pd.getInt32() + if err != nil { + return err + } + } + + b.Metadata, err = pd.getString() + if err != nil { + return err + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + return nil +} + +func (b *OffsetFetchResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt64(b.Offset) + + if version >= 5 { + pe.putInt32(b.LeaderEpoch) + } + + err = pe.putString(b.Metadata) + if err != nil { + return err + } + + pe.putInt16(int16(b.Err)) + + return nil +} + +type OffsetFetchResponse struct { + Version int16 + ThrottleTimeMs int32 + Blocks map[string]map[int32]*OffsetFetchResponseBlock + Err KError +} + +func (r *OffsetFetchResponse) encode(pe packetEncoder) error { + if r.Version >= 3 { + pe.putInt32(r.ThrottleTimeMs) + } + + if err := pe.putArrayLength(len(r.Blocks)); err != nil { + return err + } + for topic, partitions := range r.Blocks { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err := block.encode(pe, r.Version); err != nil { + return err + } + } + } + if r.Version >= 2 { + pe.putInt16(int16(r.Err)) + } + return nil +} + +func (r *OffsetFetchResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 3 { + r.ThrottleTimeMs, err = pd.getInt32() + if err != nil { + return err + } + } + + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + if numTopics > 0 { + r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + if numBlocks == 0 { + r.Blocks[name] = nil + continue + } + r.Blocks[name] = make(map[int32]*OffsetFetchResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(OffsetFetchResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + } + + if version >= 2 { + kerr, err := pd.getInt16() + if err != nil { + return err + } + r.Err = KError(kerr) + } + + return nil +} + +func (r *OffsetFetchResponse) key() int16 { + return 9 +} + +func (r *OffsetFetchResponse) version() int16 { + return r.Version +} + +func (r *OffsetFetchResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + case 2: + return V0_10_2_0 + case 3: + return V0_11_0_0 + case 4: + return V2_0_0_0 + case 5: + return V2_1_0_0 + default: + return MinVersion + } +} + +func (r *OffsetFetchResponse) GetBlock(topic string, partition int32) *OffsetFetchResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +func (r *OffsetFetchResponse) AddBlock(topic string, partition int32, block *OffsetFetchResponseBlock) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock) + } + partitions := r.Blocks[topic] + if partitions == nil { + partitions = make(map[int32]*OffsetFetchResponseBlock) + r.Blocks[topic] = partitions + } + partitions[partition] = block +} diff --git a/vendor/github.com/Shopify/sarama/offset_manager.go b/vendor/github.com/Shopify/sarama/offset_manager.go new file mode 100644 index 0000000..923972f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_manager.go @@ -0,0 +1,580 @@ +package sarama + +import ( + "sync" + "time" +) + +// Offset Manager + +// OffsetManager uses Kafka to store and fetch consumed partition offsets. +type OffsetManager interface { + // ManagePartition creates a PartitionOffsetManager on the given topic/partition. + // It will return an error if this OffsetManager is already managing the given + // topic/partition. + ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) + + // Close stops the OffsetManager from managing offsets. It is required to call + // this function before an OffsetManager object passes out of scope, as it + // will otherwise leak memory. You must call this after all the + // PartitionOffsetManagers are closed. + Close() error +} + +type offsetManager struct { + client Client + conf *Config + group string + ticker *time.Ticker + + memberID string + generation int32 + + broker *Broker + brokerLock sync.RWMutex + + poms map[string]map[int32]*partitionOffsetManager + pomsLock sync.RWMutex + + closeOnce sync.Once + closing chan none + closed chan none +} + +// NewOffsetManagerFromClient creates a new OffsetManager from the given client. +// It is still necessary to call Close() on the underlying client when finished with the partition manager. +func NewOffsetManagerFromClient(group string, client Client) (OffsetManager, error) { + return newOffsetManagerFromClient(group, "", GroupGenerationUndefined, client) +} + +func newOffsetManagerFromClient(group, memberID string, generation int32, client Client) (*offsetManager, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + conf := client.Config() + om := &offsetManager{ + client: client, + conf: conf, + group: group, + ticker: time.NewTicker(conf.Consumer.Offsets.CommitInterval), + poms: make(map[string]map[int32]*partitionOffsetManager), + + memberID: memberID, + generation: generation, + + closing: make(chan none), + closed: make(chan none), + } + go withRecover(om.mainLoop) + + return om, nil +} + +func (om *offsetManager) ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) { + pom, err := om.newPartitionOffsetManager(topic, partition) + if err != nil { + return nil, err + } + + om.pomsLock.Lock() + defer om.pomsLock.Unlock() + + topicManagers := om.poms[topic] + if topicManagers == nil { + topicManagers = make(map[int32]*partitionOffsetManager) + om.poms[topic] = topicManagers + } + + if topicManagers[partition] != nil { + return nil, ConfigurationError("That topic/partition is already being managed") + } + + topicManagers[partition] = pom + return pom, nil +} + +func (om *offsetManager) Close() error { + om.closeOnce.Do(func() { + // exit the mainLoop + close(om.closing) + <-om.closed + + // mark all POMs as closed + om.asyncClosePOMs() + + // flush one last time + for attempt := 0; attempt <= om.conf.Consumer.Offsets.Retry.Max; attempt++ { + om.flushToBroker() + if om.releasePOMs(false) == 0 { + break + } + } + + om.releasePOMs(true) + om.brokerLock.Lock() + om.broker = nil + om.brokerLock.Unlock() + }) + return nil +} + +func (om *offsetManager) computeBackoff(retries int) time.Duration { + if om.conf.Metadata.Retry.BackoffFunc != nil { + return om.conf.Metadata.Retry.BackoffFunc(retries, om.conf.Metadata.Retry.Max) + } else { + return om.conf.Metadata.Retry.Backoff + } +} + +func (om *offsetManager) fetchInitialOffset(topic string, partition int32, retries int) (int64, string, error) { + broker, err := om.coordinator() + if err != nil { + if retries <= 0 { + return 0, "", err + } + return om.fetchInitialOffset(topic, partition, retries-1) + } + + req := new(OffsetFetchRequest) + req.Version = 1 + req.ConsumerGroup = om.group + req.AddPartition(topic, partition) + + resp, err := broker.FetchOffset(req) + if err != nil { + if retries <= 0 { + return 0, "", err + } + om.releaseCoordinator(broker) + return om.fetchInitialOffset(topic, partition, retries-1) + } + + block := resp.GetBlock(topic, partition) + if block == nil { + return 0, "", ErrIncompleteResponse + } + + switch block.Err { + case ErrNoError: + return block.Offset, block.Metadata, nil + case ErrNotCoordinatorForConsumer: + if retries <= 0 { + return 0, "", block.Err + } + om.releaseCoordinator(broker) + return om.fetchInitialOffset(topic, partition, retries-1) + case ErrOffsetsLoadInProgress: + if retries <= 0 { + return 0, "", block.Err + } + backoff := om.computeBackoff(retries) + select { + case <-om.closing: + return 0, "", block.Err + case <-time.After(backoff): + } + return om.fetchInitialOffset(topic, partition, retries-1) + default: + return 0, "", block.Err + } +} + +func (om *offsetManager) coordinator() (*Broker, error) { + om.brokerLock.RLock() + broker := om.broker + om.brokerLock.RUnlock() + + if broker != nil { + return broker, nil + } + + om.brokerLock.Lock() + defer om.brokerLock.Unlock() + + if broker := om.broker; broker != nil { + return broker, nil + } + + if err := om.client.RefreshCoordinator(om.group); err != nil { + return nil, err + } + + broker, err := om.client.Coordinator(om.group) + if err != nil { + return nil, err + } + + om.broker = broker + return broker, nil +} + +func (om *offsetManager) releaseCoordinator(b *Broker) { + om.brokerLock.Lock() + if om.broker == b { + om.broker = nil + } + om.brokerLock.Unlock() +} + +func (om *offsetManager) mainLoop() { + defer om.ticker.Stop() + defer close(om.closed) + + for { + select { + case <-om.ticker.C: + om.flushToBroker() + om.releasePOMs(false) + case <-om.closing: + return + } + } +} + +func (om *offsetManager) flushToBroker() { + req := om.constructRequest() + if req == nil { + return + } + + broker, err := om.coordinator() + if err != nil { + om.handleError(err) + return + } + + resp, err := broker.CommitOffset(req) + if err != nil { + om.handleError(err) + om.releaseCoordinator(broker) + _ = broker.Close() + return + } + + om.handleResponse(broker, req, resp) +} + +func (om *offsetManager) constructRequest() *OffsetCommitRequest { + var r *OffsetCommitRequest + var perPartitionTimestamp int64 + if om.conf.Consumer.Offsets.Retention == 0 { + perPartitionTimestamp = ReceiveTime + r = &OffsetCommitRequest{ + Version: 1, + ConsumerGroup: om.group, + ConsumerID: om.memberID, + ConsumerGroupGeneration: om.generation, + } + } else { + r = &OffsetCommitRequest{ + Version: 2, + RetentionTime: int64(om.conf.Consumer.Offsets.Retention / time.Millisecond), + ConsumerGroup: om.group, + ConsumerID: om.memberID, + ConsumerGroupGeneration: om.generation, + } + + } + + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.lock.Lock() + if pom.dirty { + r.AddBlock(pom.topic, pom.partition, pom.offset, perPartitionTimestamp, pom.metadata) + } + pom.lock.Unlock() + } + } + + if len(r.blocks) > 0 { + return r + } + + return nil +} + +func (om *offsetManager) handleResponse(broker *Broker, req *OffsetCommitRequest, resp *OffsetCommitResponse) { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + if req.blocks[pom.topic] == nil || req.blocks[pom.topic][pom.partition] == nil { + continue + } + + var err KError + var ok bool + + if resp.Errors[pom.topic] == nil { + pom.handleError(ErrIncompleteResponse) + continue + } + if err, ok = resp.Errors[pom.topic][pom.partition]; !ok { + pom.handleError(ErrIncompleteResponse) + continue + } + + switch err { + case ErrNoError: + block := req.blocks[pom.topic][pom.partition] + pom.updateCommitted(block.offset, block.metadata) + case ErrNotLeaderForPartition, ErrLeaderNotAvailable, + ErrConsumerCoordinatorNotAvailable, ErrNotCoordinatorForConsumer: + // not a critical error, we just need to redispatch + om.releaseCoordinator(broker) + case ErrOffsetMetadataTooLarge, ErrInvalidCommitOffsetSize: + // nothing we can do about this, just tell the user and carry on + pom.handleError(err) + case ErrOffsetsLoadInProgress: + // nothing wrong but we didn't commit, we'll get it next time round + case ErrUnknownTopicOrPartition: + // let the user know *and* try redispatching - if topic-auto-create is + // enabled, redispatching should trigger a metadata req and create the + // topic; if not then re-dispatching won't help, but we've let the user + // know and it shouldn't hurt either (see https://github.com/Shopify/sarama/issues/706) + fallthrough + default: + // dunno, tell the user and try redispatching + pom.handleError(err) + om.releaseCoordinator(broker) + } + } + } +} + +func (om *offsetManager) handleError(err error) { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.handleError(err) + } + } +} + +func (om *offsetManager) asyncClosePOMs() { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.AsyncClose() + } + } +} + +// Releases/removes closed POMs once they are clean (or when forced) +func (om *offsetManager) releasePOMs(force bool) (remaining int) { + om.pomsLock.Lock() + defer om.pomsLock.Unlock() + + for topic, topicManagers := range om.poms { + for partition, pom := range topicManagers { + pom.lock.Lock() + releaseDue := pom.done && (force || !pom.dirty) + pom.lock.Unlock() + + if releaseDue { + pom.release() + + delete(om.poms[topic], partition) + if len(om.poms[topic]) == 0 { + delete(om.poms, topic) + } + } + } + remaining += len(om.poms[topic]) + } + return +} + +func (om *offsetManager) findPOM(topic string, partition int32) *partitionOffsetManager { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + if partitions, ok := om.poms[topic]; ok { + if pom, ok := partitions[partition]; ok { + return pom + } + } + return nil +} + +// Partition Offset Manager + +// PartitionOffsetManager uses Kafka to store and fetch consumed partition offsets. You MUST call Close() +// on a partition offset manager to avoid leaks, it will not be garbage-collected automatically when it passes +// out of scope. +type PartitionOffsetManager interface { + // NextOffset returns the next offset that should be consumed for the managed + // partition, accompanied by metadata which can be used to reconstruct the state + // of the partition consumer when it resumes. NextOffset() will return + // `config.Consumer.Offsets.Initial` and an empty metadata string if no offset + // was committed for this partition yet. + NextOffset() (int64, string) + + // MarkOffset marks the provided offset, alongside a metadata string + // that represents the state of the partition consumer at that point in time. The + // metadata string can be used by another consumer to restore that state, so it + // can resume consumption. + // + // To follow upstream conventions, you are expected to mark the offset of the + // next message to read, not the last message read. Thus, when calling `MarkOffset` + // you should typically add one to the offset of the last consumed message. + // + // Note: calling MarkOffset does not necessarily commit the offset to the backend + // store immediately for efficiency reasons, and it may never be committed if + // your application crashes. This means that you may end up processing the same + // message twice, and your processing should ideally be idempotent. + MarkOffset(offset int64, metadata string) + + // ResetOffset resets to the provided offset, alongside a metadata string that + // represents the state of the partition consumer at that point in time. Reset + // acts as a counterpart to MarkOffset, the difference being that it allows to + // reset an offset to an earlier or smaller value, where MarkOffset only + // allows incrementing the offset. cf MarkOffset for more details. + ResetOffset(offset int64, metadata string) + + // Errors returns a read channel of errors that occur during offset management, if + // enabled. By default, errors are logged and not returned over this channel. If + // you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan *ConsumerError + + // AsyncClose initiates a shutdown of the PartitionOffsetManager. This method will + // return immediately, after which you should wait until the 'errors' channel has + // been drained and closed. It is required to call this function, or Close before + // a consumer object passes out of scope, as it will otherwise leak memory. You + // must call this before calling Close on the underlying client. + AsyncClose() + + // Close stops the PartitionOffsetManager from managing offsets. It is required to + // call this function (or AsyncClose) before a PartitionOffsetManager object + // passes out of scope, as it will otherwise leak memory. You must call this + // before calling Close on the underlying client. + Close() error +} + +type partitionOffsetManager struct { + parent *offsetManager + topic string + partition int32 + + lock sync.Mutex + offset int64 + metadata string + dirty bool + done bool + + releaseOnce sync.Once + errors chan *ConsumerError +} + +func (om *offsetManager) newPartitionOffsetManager(topic string, partition int32) (*partitionOffsetManager, error) { + offset, metadata, err := om.fetchInitialOffset(topic, partition, om.conf.Metadata.Retry.Max) + if err != nil { + return nil, err + } + + return &partitionOffsetManager{ + parent: om, + topic: topic, + partition: partition, + errors: make(chan *ConsumerError, om.conf.ChannelBufferSize), + offset: offset, + metadata: metadata, + }, nil +} + +func (pom *partitionOffsetManager) Errors() <-chan *ConsumerError { + return pom.errors +} + +func (pom *partitionOffsetManager) MarkOffset(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if offset > pom.offset { + pom.offset = offset + pom.metadata = metadata + pom.dirty = true + } +} + +func (pom *partitionOffsetManager) ResetOffset(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if offset <= pom.offset { + pom.offset = offset + pom.metadata = metadata + pom.dirty = true + } +} + +func (pom *partitionOffsetManager) updateCommitted(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if pom.offset == offset && pom.metadata == metadata { + pom.dirty = false + } +} + +func (pom *partitionOffsetManager) NextOffset() (int64, string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if pom.offset >= 0 { + return pom.offset, pom.metadata + } + + return pom.parent.conf.Consumer.Offsets.Initial, "" +} + +func (pom *partitionOffsetManager) AsyncClose() { + pom.lock.Lock() + pom.done = true + pom.lock.Unlock() +} + +func (pom *partitionOffsetManager) Close() error { + pom.AsyncClose() + + var errors ConsumerErrors + for err := range pom.errors { + errors = append(errors, err) + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (pom *partitionOffsetManager) handleError(err error) { + cErr := &ConsumerError{ + Topic: pom.topic, + Partition: pom.partition, + Err: err, + } + + if pom.parent.conf.Consumer.Return.Errors { + pom.errors <- cErr + } else { + Logger.Println(cErr) + } +} + +func (pom *partitionOffsetManager) release() { + pom.releaseOnce.Do(func() { + close(pom.errors) + }) +} diff --git a/vendor/github.com/Shopify/sarama/offset_request.go b/vendor/github.com/Shopify/sarama/offset_request.go new file mode 100644 index 0000000..326c372 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_request.go @@ -0,0 +1,156 @@ +package sarama + +type offsetRequestBlock struct { + time int64 + maxOffsets int32 // Only used in version 0 +} + +func (b *offsetRequestBlock) encode(pe packetEncoder, version int16) error { + pe.putInt64(int64(b.time)) + if version == 0 { + pe.putInt32(b.maxOffsets) + } + + return nil +} + +func (b *offsetRequestBlock) decode(pd packetDecoder, version int16) (err error) { + if b.time, err = pd.getInt64(); err != nil { + return err + } + if version == 0 { + if b.maxOffsets, err = pd.getInt32(); err != nil { + return err + } + } + return nil +} + +type OffsetRequest struct { + Version int16 + replicaID int32 + isReplicaIDSet bool + blocks map[string]map[int32]*offsetRequestBlock +} + +func (r *OffsetRequest) encode(pe packetEncoder) error { + if r.isReplicaIDSet { + pe.putInt32(r.replicaID) + } else { + // default replica ID is always -1 for clients + pe.putInt32(-1) + } + + err := pe.putArrayLength(len(r.blocks)) + if err != nil { + return err + } + for topic, partitions := range r.blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err = block.encode(pe, r.Version); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + + replicaID, err := pd.getInt32() + if err != nil { + return err + } + if replicaID >= 0 { + r.SetReplicaID(replicaID) + } + + blockCount, err := pd.getArrayLength() + if err != nil { + return err + } + if blockCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*offsetRequestBlock) + for i := 0; i < blockCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*offsetRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + block := &offsetRequestBlock{} + if err := block.decode(pd, version); err != nil { + return err + } + r.blocks[topic][partition] = block + } + } + return nil +} + +func (r *OffsetRequest) key() int16 { + return 2 +} + +func (r *OffsetRequest) version() int16 { + return r.Version +} + +func (r *OffsetRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_1_0 + default: + return MinVersion + } +} + +func (r *OffsetRequest) SetReplicaID(id int32) { + r.replicaID = id + r.isReplicaIDSet = true +} + +func (r *OffsetRequest) ReplicaID() int32 { + if r.isReplicaIDSet { + return r.replicaID + } + return -1 +} + +func (r *OffsetRequest) AddBlock(topic string, partitionID int32, time int64, maxOffsets int32) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*offsetRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*offsetRequestBlock) + } + + tmp := new(offsetRequestBlock) + tmp.time = time + if r.Version == 0 { + tmp.maxOffsets = maxOffsets + } + + r.blocks[topic][partitionID] = tmp +} diff --git a/vendor/github.com/Shopify/sarama/offset_response.go b/vendor/github.com/Shopify/sarama/offset_response.go new file mode 100644 index 0000000..8b2193f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_response.go @@ -0,0 +1,174 @@ +package sarama + +type OffsetResponseBlock struct { + Err KError + Offsets []int64 // Version 0 + Offset int64 // Version 1 + Timestamp int64 // Version 1 +} + +func (b *OffsetResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + if version == 0 { + b.Offsets, err = pd.getInt64Array() + + return err + } + + b.Timestamp, err = pd.getInt64() + if err != nil { + return err + } + + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + // For backwards compatibility put the offset in the offsets array too + b.Offsets = []int64{b.Offset} + + return nil +} + +func (b *OffsetResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + + if version == 0 { + return pe.putInt64Array(b.Offsets) + } + + pe.putInt64(b.Timestamp) + pe.putInt64(b.Offset) + + return nil +} + +type OffsetResponse struct { + Version int16 + Blocks map[string]map[int32]*OffsetResponseBlock +} + +func (r *OffsetResponse) decode(pd packetDecoder, version int16) (err error) { + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*OffsetResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*OffsetResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(OffsetResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + return nil +} + +func (r *OffsetResponse) GetBlock(topic string, partition int32) *OffsetResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +/* +// [0 0 0 1 ntopics +0 8 109 121 95 116 111 112 105 99 topic +0 0 0 1 npartitions +0 0 0 0 id +0 0 + +0 0 0 1 0 0 0 0 +0 1 1 1 0 0 0 1 +0 8 109 121 95 116 111 112 +105 99 0 0 0 1 0 0 +0 0 0 0 0 0 0 1 +0 0 0 0 0 1 1 1] + +*/ +func (r *OffsetResponse) encode(pe packetEncoder) (err error) { + if err = pe.putArrayLength(len(r.Blocks)); err != nil { + return err + } + + for topic, partitions := range r.Blocks { + if err = pe.putString(topic); err != nil { + return err + } + if err = pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err = block.encode(pe, r.version()); err != nil { + return err + } + } + } + + return nil +} + +func (r *OffsetResponse) key() int16 { + return 2 +} + +func (r *OffsetResponse) version() int16 { + return r.Version +} + +func (r *OffsetResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_1_0 + default: + return MinVersion + } +} + +// testing API + +func (r *OffsetResponse) AddTopicPartition(topic string, partition int32, offset int64) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*OffsetResponseBlock) + } + byTopic, ok := r.Blocks[topic] + if !ok { + byTopic = make(map[int32]*OffsetResponseBlock) + r.Blocks[topic] = byTopic + } + byTopic[partition] = &OffsetResponseBlock{Offsets: []int64{offset}, Offset: offset} +} diff --git a/vendor/github.com/Shopify/sarama/packet_decoder.go b/vendor/github.com/Shopify/sarama/packet_decoder.go new file mode 100644 index 0000000..9be854c --- /dev/null +++ b/vendor/github.com/Shopify/sarama/packet_decoder.go @@ -0,0 +1,61 @@ +package sarama + +// PacketDecoder is the interface providing helpers for reading with Kafka's encoding rules. +// Types implementing Decoder only need to worry about calling methods like GetString, +// not about how a string is represented in Kafka. +type packetDecoder interface { + // Primitives + getInt8() (int8, error) + getInt16() (int16, error) + getInt32() (int32, error) + getInt64() (int64, error) + getVarint() (int64, error) + getArrayLength() (int, error) + getBool() (bool, error) + + // Collections + getBytes() ([]byte, error) + getVarintBytes() ([]byte, error) + getRawBytes(length int) ([]byte, error) + getString() (string, error) + getNullableString() (*string, error) + getInt32Array() ([]int32, error) + getInt64Array() ([]int64, error) + getStringArray() ([]string, error) + + // Subsets + remaining() int + getSubset(length int) (packetDecoder, error) + peek(offset, length int) (packetDecoder, error) // similar to getSubset, but it doesn't advance the offset + peekInt8(offset int) (int8, error) // similar to peek, but just one byte + + // Stacks, see PushDecoder + push(in pushDecoder) error + pop() error +} + +// PushDecoder is the interface for decoding fields like CRCs and lengths where the validity +// of the field depends on what is after it in the packet. Start them with PacketDecoder.Push() where +// the actual value is located in the packet, then PacketDecoder.Pop() them when all the bytes they +// depend upon have been decoded. +type pushDecoder interface { + // Saves the offset into the input buffer as the location to actually read the calculated value when able. + saveOffset(in int) + + // Returns the length of data to reserve for the input of this encoder (eg 4 bytes for a CRC32). + reserveLength() int + + // Indicates that all required data is now available to calculate and check the field. + // SaveOffset is guaranteed to have been called first. The implementation should read ReserveLength() bytes + // of data from the saved offset, and verify it based on the data between the saved offset and curOffset. + check(curOffset int, buf []byte) error +} + +// dynamicPushDecoder extends the interface of pushDecoder for uses cases where the length of the +// fields itself is unknown until its value was decoded (for instance varint encoded length +// fields). +// During push, dynamicPushDecoder.decode() method will be called instead of reserveLength() +type dynamicPushDecoder interface { + pushDecoder + decoder +} diff --git a/vendor/github.com/Shopify/sarama/packet_encoder.go b/vendor/github.com/Shopify/sarama/packet_encoder.go new file mode 100644 index 0000000..67b8dae --- /dev/null +++ b/vendor/github.com/Shopify/sarama/packet_encoder.go @@ -0,0 +1,65 @@ +package sarama + +import "github.com/rcrowley/go-metrics" + +// PacketEncoder is the interface providing helpers for writing with Kafka's encoding rules. +// Types implementing Encoder only need to worry about calling methods like PutString, +// not about how a string is represented in Kafka. +type packetEncoder interface { + // Primitives + putInt8(in int8) + putInt16(in int16) + putInt32(in int32) + putInt64(in int64) + putVarint(in int64) + putArrayLength(in int) error + putBool(in bool) + + // Collections + putBytes(in []byte) error + putVarintBytes(in []byte) error + putRawBytes(in []byte) error + putString(in string) error + putNullableString(in *string) error + putStringArray(in []string) error + putInt32Array(in []int32) error + putInt64Array(in []int64) error + + // Provide the current offset to record the batch size metric + offset() int + + // Stacks, see PushEncoder + push(in pushEncoder) + pop() error + + // To record metrics when provided + metricRegistry() metrics.Registry +} + +// PushEncoder is the interface for encoding fields like CRCs and lengths where the value +// of the field depends on what is encoded after it in the packet. Start them with PacketEncoder.Push() where +// the actual value is located in the packet, then PacketEncoder.Pop() them when all the bytes they +// depend upon have been written. +type pushEncoder interface { + // Saves the offset into the input buffer as the location to actually write the calculated value when able. + saveOffset(in int) + + // Returns the length of data to reserve for the output of this encoder (eg 4 bytes for a CRC32). + reserveLength() int + + // Indicates that all required data is now available to calculate and write the field. + // SaveOffset is guaranteed to have been called first. The implementation should write ReserveLength() bytes + // of data to the saved offset, based on the data between the saved offset and curOffset. + run(curOffset int, buf []byte) error +} + +// dynamicPushEncoder extends the interface of pushEncoder for uses cases where the length of the +// fields itself is unknown until its value was computed (for instance varint encoded length +// fields). +type dynamicPushEncoder interface { + pushEncoder + + // Called during pop() to adjust the length of the field. + // It should return the difference in bytes between the last computed length and current length. + adjustLength(currOffset int) int +} diff --git a/vendor/github.com/Shopify/sarama/partitioner.go b/vendor/github.com/Shopify/sarama/partitioner.go new file mode 100644 index 0000000..6a708e7 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/partitioner.go @@ -0,0 +1,217 @@ +package sarama + +import ( + "hash" + "hash/fnv" + "math/rand" + "time" +) + +// Partitioner is anything that, given a Kafka message and a number of partitions indexed [0...numPartitions-1], +// decides to which partition to send the message. RandomPartitioner, RoundRobinPartitioner and HashPartitioner are provided +// as simple default implementations. +type Partitioner interface { + // Partition takes a message and partition count and chooses a partition + Partition(message *ProducerMessage, numPartitions int32) (int32, error) + + // RequiresConsistency indicates to the user of the partitioner whether the + // mapping of key->partition is consistent or not. Specifically, if a + // partitioner requires consistency then it must be allowed to choose from all + // partitions (even ones known to be unavailable), and its choice must be + // respected by the caller. The obvious example is the HashPartitioner. + RequiresConsistency() bool +} + +// DynamicConsistencyPartitioner can optionally be implemented by Partitioners +// in order to allow more flexibility than is originally allowed by the +// RequiresConsistency method in the Partitioner interface. This allows +// partitioners to require consistency sometimes, but not all times. It's useful +// for, e.g., the HashPartitioner, which does not require consistency if the +// message key is nil. +type DynamicConsistencyPartitioner interface { + Partitioner + + // MessageRequiresConsistency is similar to Partitioner.RequiresConsistency, + // but takes in the message being partitioned so that the partitioner can + // make a per-message determination. + MessageRequiresConsistency(message *ProducerMessage) bool +} + +// PartitionerConstructor is the type for a function capable of constructing new Partitioners. +type PartitionerConstructor func(topic string) Partitioner + +type manualPartitioner struct{} + +// HashPartitionOption lets you modify default values of the partitioner +type HashPartitionerOption func(*hashPartitioner) + +// WithAbsFirst means that the partitioner handles absolute values +// in the same way as the reference Java implementation +func WithAbsFirst() HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.referenceAbs = true + } +} + +// WithCustomHashFunction lets you specify what hash function to use for the partitioning +func WithCustomHashFunction(hasher func() hash.Hash32) HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.hasher = hasher() + } +} + +// WithCustomFallbackPartitioner lets you specify what HashPartitioner should be used in case a Distribution Key is empty +func WithCustomFallbackPartitioner(randomHP *hashPartitioner) HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.random = hp + } +} + +// NewManualPartitioner returns a Partitioner which uses the partition manually set in the provided +// ProducerMessage's Partition field as the partition to produce to. +func NewManualPartitioner(topic string) Partitioner { + return new(manualPartitioner) +} + +func (p *manualPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + return message.Partition, nil +} + +func (p *manualPartitioner) RequiresConsistency() bool { + return true +} + +type randomPartitioner struct { + generator *rand.Rand +} + +// NewRandomPartitioner returns a Partitioner which chooses a random partition each time. +func NewRandomPartitioner(topic string) Partitioner { + p := new(randomPartitioner) + p.generator = rand.New(rand.NewSource(time.Now().UTC().UnixNano())) + return p +} + +func (p *randomPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + return int32(p.generator.Intn(int(numPartitions))), nil +} + +func (p *randomPartitioner) RequiresConsistency() bool { + return false +} + +type roundRobinPartitioner struct { + partition int32 +} + +// NewRoundRobinPartitioner returns a Partitioner which walks through the available partitions one at a time. +func NewRoundRobinPartitioner(topic string) Partitioner { + return &roundRobinPartitioner{} +} + +func (p *roundRobinPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + if p.partition >= numPartitions { + p.partition = 0 + } + ret := p.partition + p.partition++ + return ret, nil +} + +func (p *roundRobinPartitioner) RequiresConsistency() bool { + return false +} + +type hashPartitioner struct { + random Partitioner + hasher hash.Hash32 + referenceAbs bool +} + +// NewCustomHashPartitioner is a wrapper around NewHashPartitioner, allowing the use of custom hasher. +// The argument is a function providing the instance, implementing the hash.Hash32 interface. This is to ensure that +// each partition dispatcher gets its own hasher, to avoid concurrency issues by sharing an instance. +func NewCustomHashPartitioner(hasher func() hash.Hash32) PartitionerConstructor { + return func(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = hasher() + p.referenceAbs = false + return p + } +} + +// NewCustomPartitioner creates a default Partitioner but lets you specify the behavior of each component via options +func NewCustomPartitioner(options ...HashPartitionerOption) PartitionerConstructor { + return func(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = false + for _, option := range options { + option(p) + } + return p + } +} + +// NewHashPartitioner returns a Partitioner which behaves as follows. If the message's key is nil then a +// random partition is chosen. Otherwise the FNV-1a hash of the encoded bytes of the message key is used, +// modulus the number of partitions. This ensures that messages with the same key always end up on the +// same partition. +func NewHashPartitioner(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = false + return p +} + +// NewReferenceHashPartitioner is like NewHashPartitioner except that it handles absolute values +// in the same way as the reference Java implementation. NewHashPartitioner was supposed to do +// that but it had a mistake and now there are people depending on both behaviours. This will +// all go away on the next major version bump. +func NewReferenceHashPartitioner(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = true + return p +} + +func (p *hashPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + if message.Key == nil { + return p.random.Partition(message, numPartitions) + } + bytes, err := message.Key.Encode() + if err != nil { + return -1, err + } + p.hasher.Reset() + _, err = p.hasher.Write(bytes) + if err != nil { + return -1, err + } + var partition int32 + // Turns out we were doing our absolute value in a subtly different way from the upstream + // implementation, but now we need to maintain backwards compat for people who started using + // the old version; if referenceAbs is set we are compatible with the reference java client + // but not past Sarama versions + if p.referenceAbs { + partition = (int32(p.hasher.Sum32()) & 0x7fffffff) % numPartitions + } else { + partition = int32(p.hasher.Sum32()) % numPartitions + if partition < 0 { + partition = -partition + } + } + return partition, nil +} + +func (p *hashPartitioner) RequiresConsistency() bool { + return true +} + +func (p *hashPartitioner) MessageRequiresConsistency(message *ProducerMessage) bool { + return message.Key != nil +} diff --git a/vendor/github.com/Shopify/sarama/prep_encoder.go b/vendor/github.com/Shopify/sarama/prep_encoder.go new file mode 100644 index 0000000..b633cd1 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/prep_encoder.go @@ -0,0 +1,153 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/rcrowley/go-metrics" +) + +type prepEncoder struct { + stack []pushEncoder + length int +} + +// primitives + +func (pe *prepEncoder) putInt8(in int8) { + pe.length++ +} + +func (pe *prepEncoder) putInt16(in int16) { + pe.length += 2 +} + +func (pe *prepEncoder) putInt32(in int32) { + pe.length += 4 +} + +func (pe *prepEncoder) putInt64(in int64) { + pe.length += 8 +} + +func (pe *prepEncoder) putVarint(in int64) { + var buf [binary.MaxVarintLen64]byte + pe.length += binary.PutVarint(buf[:], in) +} + +func (pe *prepEncoder) putArrayLength(in int) error { + if in > math.MaxInt32 { + return PacketEncodingError{fmt.Sprintf("array too long (%d)", in)} + } + pe.length += 4 + return nil +} + +func (pe *prepEncoder) putBool(in bool) { + pe.length++ +} + +// arrays + +func (pe *prepEncoder) putBytes(in []byte) error { + pe.length += 4 + if in == nil { + return nil + } + return pe.putRawBytes(in) +} + +func (pe *prepEncoder) putVarintBytes(in []byte) error { + if in == nil { + pe.putVarint(-1) + return nil + } + pe.putVarint(int64(len(in))) + return pe.putRawBytes(in) +} + +func (pe *prepEncoder) putRawBytes(in []byte) error { + if len(in) > math.MaxInt32 { + return PacketEncodingError{fmt.Sprintf("byteslice too long (%d)", len(in))} + } + pe.length += len(in) + return nil +} + +func (pe *prepEncoder) putNullableString(in *string) error { + if in == nil { + pe.length += 2 + return nil + } + return pe.putString(*in) +} + +func (pe *prepEncoder) putString(in string) error { + pe.length += 2 + if len(in) > math.MaxInt16 { + return PacketEncodingError{fmt.Sprintf("string too long (%d)", len(in))} + } + pe.length += len(in) + return nil +} + +func (pe *prepEncoder) putStringArray(in []string) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + + for _, str := range in { + if err := pe.putString(str); err != nil { + return err + } + } + + return nil +} + +func (pe *prepEncoder) putInt32Array(in []int32) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + pe.length += 4 * len(in) + return nil +} + +func (pe *prepEncoder) putInt64Array(in []int64) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + pe.length += 8 * len(in) + return nil +} + +func (pe *prepEncoder) offset() int { + return pe.length +} + +// stackable + +func (pe *prepEncoder) push(in pushEncoder) { + in.saveOffset(pe.length) + pe.length += in.reserveLength() + pe.stack = append(pe.stack, in) +} + +func (pe *prepEncoder) pop() error { + in := pe.stack[len(pe.stack)-1] + pe.stack = pe.stack[:len(pe.stack)-1] + if dpe, ok := in.(dynamicPushEncoder); ok { + pe.length += dpe.adjustLength(pe.length) + } + + return nil +} + +// we do not record metrics during the prep encoder pass +func (pe *prepEncoder) metricRegistry() metrics.Registry { + return nil +} diff --git a/vendor/github.com/Shopify/sarama/produce_request.go b/vendor/github.com/Shopify/sarama/produce_request.go new file mode 100644 index 0000000..0c755d0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_request.go @@ -0,0 +1,252 @@ +package sarama + +import "github.com/rcrowley/go-metrics" + +// RequiredAcks is used in Produce Requests to tell the broker how many replica acknowledgements +// it must see before responding. Any of the constants defined here are valid. On broker versions +// prior to 0.8.2.0 any other positive int16 is also valid (the broker will wait for that many +// acknowledgements) but in 0.8.2.0 and later this will raise an exception (it has been replaced +// by setting the `min.isr` value in the brokers configuration). +type RequiredAcks int16 + +const ( + // NoResponse doesn't send any response, the TCP ACK is all you get. + NoResponse RequiredAcks = 0 + // WaitForLocal waits for only the local commit to succeed before responding. + WaitForLocal RequiredAcks = 1 + // WaitForAll waits for all in-sync replicas to commit before responding. + // The minimum number of in-sync replicas is configured on the broker via + // the `min.insync.replicas` configuration key. + WaitForAll RequiredAcks = -1 +) + +type ProduceRequest struct { + TransactionalID *string + RequiredAcks RequiredAcks + Timeout int32 + Version int16 // v1 requires Kafka 0.9, v2 requires Kafka 0.10, v3 requires Kafka 0.11 + records map[string]map[int32]Records +} + +func updateMsgSetMetrics(msgSet *MessageSet, compressionRatioMetric metrics.Histogram, + topicCompressionRatioMetric metrics.Histogram) int64 { + var topicRecordCount int64 + for _, messageBlock := range msgSet.Messages { + // Is this a fake "message" wrapping real messages? + if messageBlock.Msg.Set != nil { + topicRecordCount += int64(len(messageBlock.Msg.Set.Messages)) + } else { + // A single uncompressed message + topicRecordCount++ + } + // Better be safe than sorry when computing the compression ratio + if messageBlock.Msg.compressedSize != 0 { + compressionRatio := float64(len(messageBlock.Msg.Value)) / + float64(messageBlock.Msg.compressedSize) + // Histogram do not support decimal values, let's multiple it by 100 for better precision + intCompressionRatio := int64(100 * compressionRatio) + compressionRatioMetric.Update(intCompressionRatio) + topicCompressionRatioMetric.Update(intCompressionRatio) + } + } + return topicRecordCount +} + +func updateBatchMetrics(recordBatch *RecordBatch, compressionRatioMetric metrics.Histogram, + topicCompressionRatioMetric metrics.Histogram) int64 { + if recordBatch.compressedRecords != nil { + compressionRatio := int64(float64(recordBatch.recordsLen) / float64(len(recordBatch.compressedRecords)) * 100) + compressionRatioMetric.Update(compressionRatio) + topicCompressionRatioMetric.Update(compressionRatio) + } + + return int64(len(recordBatch.Records)) +} + +func (r *ProduceRequest) encode(pe packetEncoder) error { + if r.Version >= 3 { + if err := pe.putNullableString(r.TransactionalID); err != nil { + return err + } + } + pe.putInt16(int16(r.RequiredAcks)) + pe.putInt32(r.Timeout) + metricRegistry := pe.metricRegistry() + var batchSizeMetric metrics.Histogram + var compressionRatioMetric metrics.Histogram + if metricRegistry != nil { + batchSizeMetric = getOrRegisterHistogram("batch-size", metricRegistry) + compressionRatioMetric = getOrRegisterHistogram("compression-ratio", metricRegistry) + } + totalRecordCount := int64(0) + + err := pe.putArrayLength(len(r.records)) + if err != nil { + return err + } + + for topic, partitions := range r.records { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + topicRecordCount := int64(0) + var topicCompressionRatioMetric metrics.Histogram + if metricRegistry != nil { + topicCompressionRatioMetric = getOrRegisterTopicHistogram("compression-ratio", topic, metricRegistry) + } + for id, records := range partitions { + startOffset := pe.offset() + pe.putInt32(id) + pe.push(&lengthField{}) + err = records.encode(pe) + if err != nil { + return err + } + err = pe.pop() + if err != nil { + return err + } + if metricRegistry != nil { + if r.Version >= 3 { + topicRecordCount += updateBatchMetrics(records.RecordBatch, compressionRatioMetric, topicCompressionRatioMetric) + } else { + topicRecordCount += updateMsgSetMetrics(records.MsgSet, compressionRatioMetric, topicCompressionRatioMetric) + } + batchSize := int64(pe.offset() - startOffset) + batchSizeMetric.Update(batchSize) + getOrRegisterTopicHistogram("batch-size", topic, metricRegistry).Update(batchSize) + } + } + if topicRecordCount > 0 { + getOrRegisterTopicMeter("record-send-rate", topic, metricRegistry).Mark(topicRecordCount) + getOrRegisterTopicHistogram("records-per-request", topic, metricRegistry).Update(topicRecordCount) + totalRecordCount += topicRecordCount + } + } + if totalRecordCount > 0 { + metrics.GetOrRegisterMeter("record-send-rate", metricRegistry).Mark(totalRecordCount) + getOrRegisterHistogram("records-per-request", metricRegistry).Update(totalRecordCount) + } + + return nil +} + +func (r *ProduceRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + + if version >= 3 { + id, err := pd.getNullableString() + if err != nil { + return err + } + r.TransactionalID = id + } + requiredAcks, err := pd.getInt16() + if err != nil { + return err + } + r.RequiredAcks = RequiredAcks(requiredAcks) + if r.Timeout, err = pd.getInt32(); err != nil { + return err + } + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + + r.records = make(map[string]map[int32]Records) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.records[topic] = make(map[int32]Records) + + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + size, err := pd.getInt32() + if err != nil { + return err + } + recordsDecoder, err := pd.getSubset(int(size)) + if err != nil { + return err + } + var records Records + if err := records.decode(recordsDecoder); err != nil { + return err + } + r.records[topic][partition] = records + } + } + + return nil +} + +func (r *ProduceRequest) key() int16 { + return 0 +} + +func (r *ProduceRequest) version() int16 { + return r.Version +} + +func (r *ProduceRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *ProduceRequest) ensureRecords(topic string, partition int32) { + if r.records == nil { + r.records = make(map[string]map[int32]Records) + } + + if r.records[topic] == nil { + r.records[topic] = make(map[int32]Records) + } +} + +func (r *ProduceRequest) AddMessage(topic string, partition int32, msg *Message) { + r.ensureRecords(topic, partition) + set := r.records[topic][partition].MsgSet + + if set == nil { + set = new(MessageSet) + r.records[topic][partition] = newLegacyRecords(set) + } + + set.addMessage(msg) +} + +func (r *ProduceRequest) AddSet(topic string, partition int32, set *MessageSet) { + r.ensureRecords(topic, partition) + r.records[topic][partition] = newLegacyRecords(set) +} + +func (r *ProduceRequest) AddBatch(topic string, partition int32, batch *RecordBatch) { + r.ensureRecords(topic, partition) + r.records[topic][partition] = newDefaultRecords(batch) +} diff --git a/vendor/github.com/Shopify/sarama/produce_response.go b/vendor/github.com/Shopify/sarama/produce_response.go new file mode 100644 index 0000000..4c5cd35 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_response.go @@ -0,0 +1,189 @@ +package sarama + +import ( + "fmt" + "time" +) + +type ProduceResponseBlock struct { + Err KError + Offset int64 + // only provided if Version >= 2 and the broker is configured with `LogAppendTime` + Timestamp time.Time +} + +func (b *ProduceResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + if version >= 2 { + if millis, err := pd.getInt64(); err != nil { + return err + } else if millis != -1 { + b.Timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) + } + } + + return nil +} + +func (b *ProduceResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + pe.putInt64(b.Offset) + + if version >= 2 { + timestamp := int64(-1) + if !b.Timestamp.Before(time.Unix(0, 0)) { + timestamp = b.Timestamp.UnixNano() / int64(time.Millisecond) + } else if !b.Timestamp.IsZero() { + return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", b.Timestamp)} + } + pe.putInt64(timestamp) + } + + return nil +} + +type ProduceResponse struct { + Blocks map[string]map[int32]*ProduceResponseBlock + Version int16 + ThrottleTime time.Duration // only provided if Version >= 1 +} + +func (r *ProduceResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*ProduceResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*ProduceResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(ProduceResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + if r.Version >= 1 { + millis, err := pd.getInt32() + if err != nil { + return err + } + + r.ThrottleTime = time.Duration(millis) * time.Millisecond + } + + return nil +} + +func (r *ProduceResponse) encode(pe packetEncoder) error { + err := pe.putArrayLength(len(r.Blocks)) + if err != nil { + return err + } + for topic, partitions := range r.Blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + for id, prb := range partitions { + pe.putInt32(id) + err = prb.encode(pe, r.Version) + if err != nil { + return err + } + } + } + if r.Version >= 1 { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + } + return nil +} + +func (r *ProduceResponse) key() int16 { + return 0 +} + +func (r *ProduceResponse) version() int16 { + return r.Version +} + +func (r *ProduceResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *ProduceResponse) GetBlock(topic string, partition int32) *ProduceResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +// Testing API + +func (r *ProduceResponse) AddTopicPartition(topic string, partition int32, err KError) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*ProduceResponseBlock) + } + byTopic, ok := r.Blocks[topic] + if !ok { + byTopic = make(map[int32]*ProduceResponseBlock) + r.Blocks[topic] = byTopic + } + block := &ProduceResponseBlock{ + Err: err, + } + if r.Version >= 2 { + block.Timestamp = time.Now() + } + byTopic[partition] = block +} diff --git a/vendor/github.com/Shopify/sarama/produce_set.go b/vendor/github.com/Shopify/sarama/produce_set.go new file mode 100644 index 0000000..bba0f7e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_set.go @@ -0,0 +1,258 @@ +package sarama + +import ( + "encoding/binary" + "errors" + "time" +) + +type partitionSet struct { + msgs []*ProducerMessage + recordsToSend Records + bufferBytes int +} + +type produceSet struct { + parent *asyncProducer + msgs map[string]map[int32]*partitionSet + + bufferBytes int + bufferCount int +} + +func newProduceSet(parent *asyncProducer) *produceSet { + return &produceSet{ + msgs: make(map[string]map[int32]*partitionSet), + parent: parent, + } +} + +func (ps *produceSet) add(msg *ProducerMessage) error { + var err error + var key, val []byte + + if msg.Key != nil { + if key, err = msg.Key.Encode(); err != nil { + return err + } + } + + if msg.Value != nil { + if val, err = msg.Value.Encode(); err != nil { + return err + } + } + + timestamp := msg.Timestamp + if msg.Timestamp.IsZero() { + timestamp = time.Now() + } + + partitions := ps.msgs[msg.Topic] + if partitions == nil { + partitions = make(map[int32]*partitionSet) + ps.msgs[msg.Topic] = partitions + } + + var size int + + set := partitions[msg.Partition] + if set == nil { + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + batch := &RecordBatch{ + FirstTimestamp: timestamp, + Version: 2, + Codec: ps.parent.conf.Producer.Compression, + CompressionLevel: ps.parent.conf.Producer.CompressionLevel, + ProducerID: ps.parent.txnmgr.producerID, + ProducerEpoch: ps.parent.txnmgr.producerEpoch, + } + if ps.parent.conf.Producer.Idempotent { + batch.FirstSequence = msg.sequenceNumber + } + set = &partitionSet{recordsToSend: newDefaultRecords(batch)} + size = recordBatchOverhead + } else { + set = &partitionSet{recordsToSend: newLegacyRecords(new(MessageSet))} + } + partitions[msg.Partition] = set + } + set.msgs = append(set.msgs, msg) + + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + if ps.parent.conf.Producer.Idempotent && msg.sequenceNumber < set.recordsToSend.RecordBatch.FirstSequence { + return errors.New("assertion failed: message out of sequence added to a batch") + } + // We are being conservative here to avoid having to prep encode the record + size += maximumRecordOverhead + rec := &Record{ + Key: key, + Value: val, + TimestampDelta: timestamp.Sub(set.recordsToSend.RecordBatch.FirstTimestamp), + } + size += len(key) + len(val) + if len(msg.Headers) > 0 { + rec.Headers = make([]*RecordHeader, len(msg.Headers)) + for i := range msg.Headers { + rec.Headers[i] = &msg.Headers[i] + size += len(rec.Headers[i].Key) + len(rec.Headers[i].Value) + 2*binary.MaxVarintLen32 + } + } + set.recordsToSend.RecordBatch.addRecord(rec) + } else { + msgToSend := &Message{Codec: CompressionNone, Key: key, Value: val} + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + msgToSend.Timestamp = timestamp + msgToSend.Version = 1 + } + set.recordsToSend.MsgSet.addMessage(msgToSend) + size = producerMessageOverhead + len(key) + len(val) + } + + set.bufferBytes += size + ps.bufferBytes += size + ps.bufferCount++ + + return nil +} + +func (ps *produceSet) buildRequest() *ProduceRequest { + req := &ProduceRequest{ + RequiredAcks: ps.parent.conf.Producer.RequiredAcks, + Timeout: int32(ps.parent.conf.Producer.Timeout / time.Millisecond), + } + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + req.Version = 2 + } + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + req.Version = 3 + } + + for topic, partitionSets := range ps.msgs { + for partition, set := range partitionSets { + if req.Version >= 3 { + // If the API version we're hitting is 3 or greater, we need to calculate + // offsets for each record in the batch relative to FirstOffset. + // Additionally, we must set LastOffsetDelta to the value of the last offset + // in the batch. Since the OffsetDelta of the first record is 0, we know that the + // final record of any batch will have an offset of (# of records in batch) - 1. + // (See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-Messagesets + // under the RecordBatch section for details.) + rb := set.recordsToSend.RecordBatch + if len(rb.Records) > 0 { + rb.LastOffsetDelta = int32(len(rb.Records) - 1) + for i, record := range rb.Records { + record.OffsetDelta = int64(i) + } + } + req.AddBatch(topic, partition, rb) + continue + } + if ps.parent.conf.Producer.Compression == CompressionNone { + req.AddSet(topic, partition, set.recordsToSend.MsgSet) + } else { + // When compression is enabled, the entire set for each partition is compressed + // and sent as the payload of a single fake "message" with the appropriate codec + // set and no key. When the server sees a message with a compression codec, it + // decompresses the payload and treats the result as its message set. + + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + // If our version is 0.10 or later, assign relative offsets + // to the inner messages. This lets the broker avoid + // recompressing the message set. + // (See https://cwiki.apache.org/confluence/display/KAFKA/KIP-31+-+Move+to+relative+offsets+in+compressed+message+sets + // for details on relative offsets.) + for i, msg := range set.recordsToSend.MsgSet.Messages { + msg.Offset = int64(i) + } + } + payload, err := encode(set.recordsToSend.MsgSet, ps.parent.conf.MetricRegistry) + if err != nil { + Logger.Println(err) // if this happens, it's basically our fault. + panic(err) + } + compMsg := &Message{ + Codec: ps.parent.conf.Producer.Compression, + CompressionLevel: ps.parent.conf.Producer.CompressionLevel, + Key: nil, + Value: payload, + Set: set.recordsToSend.MsgSet, // Provide the underlying message set for accurate metrics + } + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + compMsg.Version = 1 + compMsg.Timestamp = set.recordsToSend.MsgSet.Messages[0].Msg.Timestamp + } + req.AddMessage(topic, partition, compMsg) + } + } + } + + return req +} + +func (ps *produceSet) eachPartition(cb func(topic string, partition int32, pSet *partitionSet)) { + for topic, partitionSet := range ps.msgs { + for partition, set := range partitionSet { + cb(topic, partition, set) + } + } +} + +func (ps *produceSet) dropPartition(topic string, partition int32) []*ProducerMessage { + if ps.msgs[topic] == nil { + return nil + } + set := ps.msgs[topic][partition] + if set == nil { + return nil + } + ps.bufferBytes -= set.bufferBytes + ps.bufferCount -= len(set.msgs) + delete(ps.msgs[topic], partition) + return set.msgs +} + +func (ps *produceSet) wouldOverflow(msg *ProducerMessage) bool { + version := 1 + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + version = 2 + } + + switch { + // Would we overflow our maximum possible size-on-the-wire? 10KiB is arbitrary overhead for safety. + case ps.bufferBytes+msg.byteSize(version) >= int(MaxRequestSize-(10*1024)): + return true + // Would we overflow the size-limit of a message-batch for this partition? + case ps.msgs[msg.Topic] != nil && ps.msgs[msg.Topic][msg.Partition] != nil && + ps.msgs[msg.Topic][msg.Partition].bufferBytes+msg.byteSize(version) >= ps.parent.conf.Producer.MaxMessageBytes: + return true + // Would we overflow simply in number of messages? + case ps.parent.conf.Producer.Flush.MaxMessages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.MaxMessages: + return true + default: + return false + } +} + +func (ps *produceSet) readyToFlush() bool { + switch { + // If we don't have any messages, nothing else matters + case ps.empty(): + return false + // If all three config values are 0, we always flush as-fast-as-possible + case ps.parent.conf.Producer.Flush.Frequency == 0 && ps.parent.conf.Producer.Flush.Bytes == 0 && ps.parent.conf.Producer.Flush.Messages == 0: + return true + // If we've passed the message trigger-point + case ps.parent.conf.Producer.Flush.Messages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.Messages: + return true + // If we've passed the byte trigger-point + case ps.parent.conf.Producer.Flush.Bytes > 0 && ps.bufferBytes >= ps.parent.conf.Producer.Flush.Bytes: + return true + default: + return false + } +} + +func (ps *produceSet) empty() bool { + return ps.bufferCount == 0 +} diff --git a/vendor/github.com/Shopify/sarama/real_decoder.go b/vendor/github.com/Shopify/sarama/real_decoder.go new file mode 100644 index 0000000..085cbb3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/real_decoder.go @@ -0,0 +1,332 @@ +package sarama + +import ( + "encoding/binary" + "math" +) + +var errInvalidArrayLength = PacketDecodingError{"invalid array length"} +var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"} +var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"} +var errInvalidStringLength = PacketDecodingError{"invalid string length"} +var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"} +var errVarintOverflow = PacketDecodingError{"varint overflow"} +var errInvalidBool = PacketDecodingError{"invalid bool"} + +type realDecoder struct { + raw []byte + off int + stack []pushDecoder +} + +// primitives + +func (rd *realDecoder) getInt8() (int8, error) { + if rd.remaining() < 1 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int8(rd.raw[rd.off]) + rd.off++ + return tmp, nil +} + +func (rd *realDecoder) getInt16() (int16, error) { + if rd.remaining() < 2 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:])) + rd.off += 2 + return tmp, nil +} + +func (rd *realDecoder) getInt32() (int32, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + return tmp, nil +} + +func (rd *realDecoder) getInt64() (int64, error) { + if rd.remaining() < 8 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) + rd.off += 8 + return tmp, nil +} + +func (rd *realDecoder) getVarint() (int64, error) { + tmp, n := binary.Varint(rd.raw[rd.off:]) + if n == 0 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + if n < 0 { + rd.off -= n + return -1, errVarintOverflow + } + rd.off += n + return tmp, nil +} + +func (rd *realDecoder) getArrayLength() (int, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int(int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))) + rd.off += 4 + if tmp > rd.remaining() { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } else if tmp > 2*math.MaxUint16 { + return -1, errInvalidArrayLength + } + return tmp, nil +} + +func (rd *realDecoder) getBool() (bool, error) { + b, err := rd.getInt8() + if err != nil || b == 0 { + return false, err + } + if b != 1 { + return false, errInvalidBool + } + return true, nil +} + +// collections + +func (rd *realDecoder) getBytes() ([]byte, error) { + tmp, err := rd.getInt32() + if err != nil { + return nil, err + } + if tmp == -1 { + return nil, nil + } + + return rd.getRawBytes(int(tmp)) +} + +func (rd *realDecoder) getVarintBytes() ([]byte, error) { + tmp, err := rd.getVarint() + if err != nil { + return nil, err + } + if tmp == -1 { + return nil, nil + } + + return rd.getRawBytes(int(tmp)) +} + +func (rd *realDecoder) getStringLength() (int, error) { + length, err := rd.getInt16() + if err != nil { + return 0, err + } + + n := int(length) + + switch { + case n < -1: + return 0, errInvalidStringLength + case n > rd.remaining(): + rd.off = len(rd.raw) + return 0, ErrInsufficientData + } + + return n, nil +} + +func (rd *realDecoder) getString() (string, error) { + n, err := rd.getStringLength() + if err != nil || n == -1 { + return "", err + } + + tmpStr := string(rd.raw[rd.off : rd.off+n]) + rd.off += n + return tmpStr, nil +} + +func (rd *realDecoder) getNullableString() (*string, error) { + n, err := rd.getStringLength() + if err != nil || n == -1 { + return nil, err + } + + tmpStr := string(rd.raw[rd.off : rd.off+n]) + rd.off += n + return &tmpStr, err +} + +func (rd *realDecoder) getInt32Array() ([]int32, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if rd.remaining() < 4*n { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]int32, n) + for i := range ret { + ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + } + return ret, nil +} + +func (rd *realDecoder) getInt64Array() ([]int64, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if rd.remaining() < 8*n { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]int64, n) + for i := range ret { + ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) + rd.off += 8 + } + return ret, nil +} + +func (rd *realDecoder) getStringArray() ([]string, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]string, n) + for i := range ret { + str, err := rd.getString() + if err != nil { + return nil, err + } + + ret[i] = str + } + return ret, nil +} + +// subsets + +func (rd *realDecoder) remaining() int { + return len(rd.raw) - rd.off +} + +func (rd *realDecoder) getSubset(length int) (packetDecoder, error) { + buf, err := rd.getRawBytes(length) + if err != nil { + return nil, err + } + return &realDecoder{raw: buf}, nil +} + +func (rd *realDecoder) getRawBytes(length int) ([]byte, error) { + if length < 0 { + return nil, errInvalidByteSliceLength + } else if length > rd.remaining() { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + start := rd.off + rd.off += length + return rd.raw[start:rd.off], nil +} + +func (rd *realDecoder) peek(offset, length int) (packetDecoder, error) { + if rd.remaining() < offset+length { + return nil, ErrInsufficientData + } + off := rd.off + offset + return &realDecoder{raw: rd.raw[off : off+length]}, nil +} + +func (rd *realDecoder) peekInt8(offset int) (int8, error) { + const byteLen = 1 + if rd.remaining() < offset+byteLen { + return -1, ErrInsufficientData + } + return int8(rd.raw[rd.off+offset]), nil +} + +// stacks + +func (rd *realDecoder) push(in pushDecoder) error { + in.saveOffset(rd.off) + + var reserve int + if dpd, ok := in.(dynamicPushDecoder); ok { + if err := dpd.decode(rd); err != nil { + return err + } + } else { + reserve = in.reserveLength() + if rd.remaining() < reserve { + rd.off = len(rd.raw) + return ErrInsufficientData + } + } + + rd.stack = append(rd.stack, in) + + rd.off += reserve + + return nil +} + +func (rd *realDecoder) pop() error { + // this is go's ugly pop pattern (the inverse of append) + in := rd.stack[len(rd.stack)-1] + rd.stack = rd.stack[:len(rd.stack)-1] + + return in.check(rd.off, rd.raw) +} diff --git a/vendor/github.com/Shopify/sarama/real_encoder.go b/vendor/github.com/Shopify/sarama/real_encoder.go new file mode 100644 index 0000000..3c75387 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/real_encoder.go @@ -0,0 +1,156 @@ +package sarama + +import ( + "encoding/binary" + + "github.com/rcrowley/go-metrics" +) + +type realEncoder struct { + raw []byte + off int + stack []pushEncoder + registry metrics.Registry +} + +// primitives + +func (re *realEncoder) putInt8(in int8) { + re.raw[re.off] = byte(in) + re.off++ +} + +func (re *realEncoder) putInt16(in int16) { + binary.BigEndian.PutUint16(re.raw[re.off:], uint16(in)) + re.off += 2 +} + +func (re *realEncoder) putInt32(in int32) { + binary.BigEndian.PutUint32(re.raw[re.off:], uint32(in)) + re.off += 4 +} + +func (re *realEncoder) putInt64(in int64) { + binary.BigEndian.PutUint64(re.raw[re.off:], uint64(in)) + re.off += 8 +} + +func (re *realEncoder) putVarint(in int64) { + re.off += binary.PutVarint(re.raw[re.off:], in) +} + +func (re *realEncoder) putArrayLength(in int) error { + re.putInt32(int32(in)) + return nil +} + +func (re *realEncoder) putBool(in bool) { + if in { + re.putInt8(1) + return + } + re.putInt8(0) +} + +// collection + +func (re *realEncoder) putRawBytes(in []byte) error { + copy(re.raw[re.off:], in) + re.off += len(in) + return nil +} + +func (re *realEncoder) putBytes(in []byte) error { + if in == nil { + re.putInt32(-1) + return nil + } + re.putInt32(int32(len(in))) + return re.putRawBytes(in) +} + +func (re *realEncoder) putVarintBytes(in []byte) error { + if in == nil { + re.putVarint(-1) + return nil + } + re.putVarint(int64(len(in))) + return re.putRawBytes(in) +} + +func (re *realEncoder) putString(in string) error { + re.putInt16(int16(len(in))) + copy(re.raw[re.off:], in) + re.off += len(in) + return nil +} + +func (re *realEncoder) putNullableString(in *string) error { + if in == nil { + re.putInt16(-1) + return nil + } + return re.putString(*in) +} + +func (re *realEncoder) putStringArray(in []string) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + + for _, val := range in { + if err := re.putString(val); err != nil { + return err + } + } + + return nil +} + +func (re *realEncoder) putInt32Array(in []int32) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + for _, val := range in { + re.putInt32(val) + } + return nil +} + +func (re *realEncoder) putInt64Array(in []int64) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + for _, val := range in { + re.putInt64(val) + } + return nil +} + +func (re *realEncoder) offset() int { + return re.off +} + +// stacks + +func (re *realEncoder) push(in pushEncoder) { + in.saveOffset(re.off) + re.off += in.reserveLength() + re.stack = append(re.stack, in) +} + +func (re *realEncoder) pop() error { + // this is go's ugly pop pattern (the inverse of append) + in := re.stack[len(re.stack)-1] + re.stack = re.stack[:len(re.stack)-1] + + return in.run(re.off, re.raw) +} + +// we do record metrics during the real encoder pass +func (re *realEncoder) metricRegistry() metrics.Registry { + return re.registry +} diff --git a/vendor/github.com/Shopify/sarama/record.go b/vendor/github.com/Shopify/sarama/record.go new file mode 100644 index 0000000..cdccfe3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/record.go @@ -0,0 +1,116 @@ +package sarama + +import ( + "encoding/binary" + "time" +) + +const ( + isTransactionalMask = 0x10 + controlMask = 0x20 + maximumRecordOverhead = 5*binary.MaxVarintLen32 + binary.MaxVarintLen64 + 1 +) + +//RecordHeader stores key and value for a record header +type RecordHeader struct { + Key []byte + Value []byte +} + +func (h *RecordHeader) encode(pe packetEncoder) error { + if err := pe.putVarintBytes(h.Key); err != nil { + return err + } + return pe.putVarintBytes(h.Value) +} + +func (h *RecordHeader) decode(pd packetDecoder) (err error) { + if h.Key, err = pd.getVarintBytes(); err != nil { + return err + } + + if h.Value, err = pd.getVarintBytes(); err != nil { + return err + } + return nil +} + +//Record is kafka record type +type Record struct { + Headers []*RecordHeader + + Attributes int8 + TimestampDelta time.Duration + OffsetDelta int64 + Key []byte + Value []byte + length varintLengthField +} + +func (r *Record) encode(pe packetEncoder) error { + pe.push(&r.length) + pe.putInt8(r.Attributes) + pe.putVarint(int64(r.TimestampDelta / time.Millisecond)) + pe.putVarint(r.OffsetDelta) + if err := pe.putVarintBytes(r.Key); err != nil { + return err + } + if err := pe.putVarintBytes(r.Value); err != nil { + return err + } + pe.putVarint(int64(len(r.Headers))) + + for _, h := range r.Headers { + if err := h.encode(pe); err != nil { + return err + } + } + + return pe.pop() +} + +func (r *Record) decode(pd packetDecoder) (err error) { + if err = pd.push(&r.length); err != nil { + return err + } + + if r.Attributes, err = pd.getInt8(); err != nil { + return err + } + + timestamp, err := pd.getVarint() + if err != nil { + return err + } + r.TimestampDelta = time.Duration(timestamp) * time.Millisecond + + if r.OffsetDelta, err = pd.getVarint(); err != nil { + return err + } + + if r.Key, err = pd.getVarintBytes(); err != nil { + return err + } + + if r.Value, err = pd.getVarintBytes(); err != nil { + return err + } + + numHeaders, err := pd.getVarint() + if err != nil { + return err + } + + if numHeaders >= 0 { + r.Headers = make([]*RecordHeader, numHeaders) + } + for i := int64(0); i < numHeaders; i++ { + hdr := new(RecordHeader) + if err := hdr.decode(pd); err != nil { + return err + } + r.Headers[i] = hdr + } + + return pd.pop() +} diff --git a/vendor/github.com/Shopify/sarama/record_batch.go b/vendor/github.com/Shopify/sarama/record_batch.go new file mode 100644 index 0000000..c653763 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/record_batch.go @@ -0,0 +1,225 @@ +package sarama + +import ( + "fmt" + "time" +) + +const recordBatchOverhead = 49 + +type recordsArray []*Record + +func (e recordsArray) encode(pe packetEncoder) error { + for _, r := range e { + if err := r.encode(pe); err != nil { + return err + } + } + return nil +} + +func (e recordsArray) decode(pd packetDecoder) error { + for i := range e { + rec := &Record{} + if err := rec.decode(pd); err != nil { + return err + } + e[i] = rec + } + return nil +} + +type RecordBatch struct { + FirstOffset int64 + PartitionLeaderEpoch int32 + Version int8 + Codec CompressionCodec + CompressionLevel int + Control bool + LogAppendTime bool + LastOffsetDelta int32 + FirstTimestamp time.Time + MaxTimestamp time.Time + ProducerID int64 + ProducerEpoch int16 + FirstSequence int32 + Records []*Record + PartialTrailingRecord bool + IsTransactional bool + + compressedRecords []byte + recordsLen int // uncompressed records size +} + +func (b *RecordBatch) LastOffset() int64 { + return b.FirstOffset + int64(b.LastOffsetDelta) +} + +func (b *RecordBatch) encode(pe packetEncoder) error { + if b.Version != 2 { + return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", b.Codec)} + } + pe.putInt64(b.FirstOffset) + pe.push(&lengthField{}) + pe.putInt32(b.PartitionLeaderEpoch) + pe.putInt8(b.Version) + pe.push(newCRC32Field(crcCastagnoli)) + pe.putInt16(b.computeAttributes()) + pe.putInt32(b.LastOffsetDelta) + + if err := (Timestamp{&b.FirstTimestamp}).encode(pe); err != nil { + return err + } + + if err := (Timestamp{&b.MaxTimestamp}).encode(pe); err != nil { + return err + } + + pe.putInt64(b.ProducerID) + pe.putInt16(b.ProducerEpoch) + pe.putInt32(b.FirstSequence) + + if err := pe.putArrayLength(len(b.Records)); err != nil { + return err + } + + if b.compressedRecords == nil { + if err := b.encodeRecords(pe); err != nil { + return err + } + } + if err := pe.putRawBytes(b.compressedRecords); err != nil { + return err + } + + if err := pe.pop(); err != nil { + return err + } + return pe.pop() +} + +func (b *RecordBatch) decode(pd packetDecoder) (err error) { + if b.FirstOffset, err = pd.getInt64(); err != nil { + return err + } + + batchLen, err := pd.getInt32() + if err != nil { + return err + } + + if b.PartitionLeaderEpoch, err = pd.getInt32(); err != nil { + return err + } + + if b.Version, err = pd.getInt8(); err != nil { + return err + } + + crc32Decoder := acquireCrc32Field(crcCastagnoli) + defer releaseCrc32Field(crc32Decoder) + + if err = pd.push(crc32Decoder); err != nil { + return err + } + + attributes, err := pd.getInt16() + if err != nil { + return err + } + b.Codec = CompressionCodec(int8(attributes) & compressionCodecMask) + b.Control = attributes&controlMask == controlMask + b.LogAppendTime = attributes×tampTypeMask == timestampTypeMask + b.IsTransactional = attributes&isTransactionalMask == isTransactionalMask + + if b.LastOffsetDelta, err = pd.getInt32(); err != nil { + return err + } + + if err = (Timestamp{&b.FirstTimestamp}).decode(pd); err != nil { + return err + } + + if err = (Timestamp{&b.MaxTimestamp}).decode(pd); err != nil { + return err + } + + if b.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if b.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + if b.FirstSequence, err = pd.getInt32(); err != nil { + return err + } + + numRecs, err := pd.getArrayLength() + if err != nil { + return err + } + if numRecs >= 0 { + b.Records = make([]*Record, numRecs) + } + + bufSize := int(batchLen) - recordBatchOverhead + recBuffer, err := pd.getRawBytes(bufSize) + if err != nil { + if err == ErrInsufficientData { + b.PartialTrailingRecord = true + b.Records = nil + return nil + } + return err + } + + if err = pd.pop(); err != nil { + return err + } + + recBuffer, err = decompress(b.Codec, recBuffer) + if err != nil { + return err + } + + b.recordsLen = len(recBuffer) + err = decode(recBuffer, recordsArray(b.Records)) + if err == ErrInsufficientData { + b.PartialTrailingRecord = true + b.Records = nil + return nil + } + return err +} + +func (b *RecordBatch) encodeRecords(pe packetEncoder) error { + var raw []byte + var err error + if raw, err = encode(recordsArray(b.Records), pe.metricRegistry()); err != nil { + return err + } + b.recordsLen = len(raw) + + b.compressedRecords, err = compress(b.Codec, b.CompressionLevel, raw) + return err +} + +func (b *RecordBatch) computeAttributes() int16 { + attr := int16(b.Codec) & int16(compressionCodecMask) + if b.Control { + attr |= controlMask + } + if b.LogAppendTime { + attr |= timestampTypeMask + } + if b.IsTransactional { + attr |= isTransactionalMask + } + return attr +} + +func (b *RecordBatch) addRecord(r *Record) { + b.Records = append(b.Records, r) +} diff --git a/vendor/github.com/Shopify/sarama/records.go b/vendor/github.com/Shopify/sarama/records.go new file mode 100644 index 0000000..98160c7 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/records.go @@ -0,0 +1,204 @@ +package sarama + +import "fmt" + +const ( + unknownRecords = iota + legacyRecords + defaultRecords + + magicOffset = 16 + magicLength = 1 +) + +// Records implements a union type containing either a RecordBatch or a legacy MessageSet. +type Records struct { + recordsType int + MsgSet *MessageSet + RecordBatch *RecordBatch +} + +func newLegacyRecords(msgSet *MessageSet) Records { + return Records{recordsType: legacyRecords, MsgSet: msgSet} +} + +func newDefaultRecords(batch *RecordBatch) Records { + return Records{recordsType: defaultRecords, RecordBatch: batch} +} + +// setTypeFromFields sets type of Records depending on which of MsgSet or RecordBatch is not nil. +// The first return value indicates whether both fields are nil (and the type is not set). +// If both fields are not nil, it returns an error. +func (r *Records) setTypeFromFields() (bool, error) { + if r.MsgSet == nil && r.RecordBatch == nil { + return true, nil + } + if r.MsgSet != nil && r.RecordBatch != nil { + return false, fmt.Errorf("both MsgSet and RecordBatch are set, but record type is unknown") + } + r.recordsType = defaultRecords + if r.MsgSet != nil { + r.recordsType = legacyRecords + } + return false, nil +} + +func (r *Records) encode(pe packetEncoder) error { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return err + } + } + + switch r.recordsType { + case legacyRecords: + if r.MsgSet == nil { + return nil + } + return r.MsgSet.encode(pe) + case defaultRecords: + if r.RecordBatch == nil { + return nil + } + return r.RecordBatch.encode(pe) + } + + return fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) setTypeFromMagic(pd packetDecoder) error { + magic, err := magicValue(pd) + if err != nil { + return err + } + + r.recordsType = defaultRecords + if magic < 2 { + r.recordsType = legacyRecords + } + + return nil +} + +func (r *Records) decode(pd packetDecoder) error { + if r.recordsType == unknownRecords { + if err := r.setTypeFromMagic(pd); err != nil { + return err + } + } + + switch r.recordsType { + case legacyRecords: + r.MsgSet = &MessageSet{} + return r.MsgSet.decode(pd) + case defaultRecords: + r.RecordBatch = &RecordBatch{} + return r.RecordBatch.decode(pd) + } + return fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) numRecords() (int, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return 0, err + } + } + + switch r.recordsType { + case legacyRecords: + if r.MsgSet == nil { + return 0, nil + } + return len(r.MsgSet.Messages), nil + case defaultRecords: + if r.RecordBatch == nil { + return 0, nil + } + return len(r.RecordBatch.Records), nil + } + return 0, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isPartial() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case unknownRecords: + return false, nil + case legacyRecords: + if r.MsgSet == nil { + return false, nil + } + return r.MsgSet.PartialTrailingMessage, nil + case defaultRecords: + if r.RecordBatch == nil { + return false, nil + } + return r.RecordBatch.PartialTrailingRecord, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isControl() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case legacyRecords: + return false, nil + case defaultRecords: + if r.RecordBatch == nil { + return false, nil + } + return r.RecordBatch.Control, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isOverflow() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case unknownRecords: + return false, nil + case legacyRecords: + if r.MsgSet == nil { + return false, nil + } + return r.MsgSet.OverflowMessage, nil + case defaultRecords: + return false, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func magicValue(pd packetDecoder) (int8, error) { + return pd.peekInt8(magicOffset) +} + +func (r *Records) getControlRecord() (ControlRecord, error) { + if r.RecordBatch == nil || len(r.RecordBatch.Records) <= 0 { + return ControlRecord{}, fmt.Errorf("cannot get control record, record batch is empty") + } + + firstRecord := r.RecordBatch.Records[0] + controlRecord := ControlRecord{} + err := controlRecord.decode(&realDecoder{raw: firstRecord.Key}, &realDecoder{raw: firstRecord.Value}) + if err != nil { + return ControlRecord{}, err + } + + return controlRecord, nil +} diff --git a/vendor/github.com/Shopify/sarama/request.go b/vendor/github.com/Shopify/sarama/request.go new file mode 100644 index 0000000..97437d6 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/request.go @@ -0,0 +1,171 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "io" +) + +type protocolBody interface { + encoder + versionedDecoder + key() int16 + version() int16 + requiredVersion() KafkaVersion +} + +type request struct { + correlationID int32 + clientID string + body protocolBody +} + +func (r *request) encode(pe packetEncoder) error { + pe.push(&lengthField{}) + pe.putInt16(r.body.key()) + pe.putInt16(r.body.version()) + pe.putInt32(r.correlationID) + + err := pe.putString(r.clientID) + if err != nil { + return err + } + + err = r.body.encode(pe) + if err != nil { + return err + } + + return pe.pop() +} + +func (r *request) decode(pd packetDecoder) (err error) { + key, err := pd.getInt16() + if err != nil { + return err + } + + version, err := pd.getInt16() + if err != nil { + return err + } + + r.correlationID, err = pd.getInt32() + if err != nil { + return err + } + + r.clientID, err = pd.getString() + if err != nil { + return err + } + + r.body = allocateBody(key, version) + if r.body == nil { + return PacketDecodingError{fmt.Sprintf("unknown request key (%d)", key)} + } + + return r.body.decode(pd, version) +} + +func decodeRequest(r io.Reader) (*request, int, error) { + var ( + bytesRead int + lengthBytes = make([]byte, 4) + ) + + if _, err := io.ReadFull(r, lengthBytes); err != nil { + return nil, bytesRead, err + } + + bytesRead += len(lengthBytes) + length := int32(binary.BigEndian.Uint32(lengthBytes)) + + if length <= 4 || length > MaxRequestSize { + return nil, bytesRead, PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", length)} + } + + encodedReq := make([]byte, length) + if _, err := io.ReadFull(r, encodedReq); err != nil { + return nil, bytesRead, err + } + + bytesRead += len(encodedReq) + + req := &request{} + if err := decode(encodedReq, req); err != nil { + return nil, bytesRead, err + } + + return req, bytesRead, nil +} + +func allocateBody(key, version int16) protocolBody { + switch key { + case 0: + return &ProduceRequest{} + case 1: + return &FetchRequest{} + case 2: + return &OffsetRequest{Version: version} + case 3: + return &MetadataRequest{} + case 8: + return &OffsetCommitRequest{Version: version} + case 9: + return &OffsetFetchRequest{} + case 10: + return &FindCoordinatorRequest{} + case 11: + return &JoinGroupRequest{} + case 12: + return &HeartbeatRequest{} + case 13: + return &LeaveGroupRequest{} + case 14: + return &SyncGroupRequest{} + case 15: + return &DescribeGroupsRequest{} + case 16: + return &ListGroupsRequest{} + case 17: + return &SaslHandshakeRequest{} + case 18: + return &ApiVersionsRequest{} + case 19: + return &CreateTopicsRequest{} + case 20: + return &DeleteTopicsRequest{} + case 21: + return &DeleteRecordsRequest{} + case 22: + return &InitProducerIDRequest{} + case 24: + return &AddPartitionsToTxnRequest{} + case 25: + return &AddOffsetsToTxnRequest{} + case 26: + return &EndTxnRequest{} + case 28: + return &TxnOffsetCommitRequest{} + case 29: + return &DescribeAclsRequest{} + case 30: + return &CreateAclsRequest{} + case 31: + return &DeleteAclsRequest{} + case 32: + return &DescribeConfigsRequest{} + case 33: + return &AlterConfigsRequest{} + case 35: + return &DescribeLogDirsRequest{} + case 36: + return &SaslAuthenticateRequest{} + case 37: + return &CreatePartitionsRequest{} + case 42: + return &DeleteGroupsRequest{} + } + return nil +} diff --git a/vendor/github.com/Shopify/sarama/response_header.go b/vendor/github.com/Shopify/sarama/response_header.go new file mode 100644 index 0000000..7a75918 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/response_header.go @@ -0,0 +1,24 @@ +package sarama + +import "fmt" + +const responseLengthSize = 4 +const correlationIDSize = 4 + +type responseHeader struct { + length int32 + correlationID int32 +} + +func (r *responseHeader) decode(pd packetDecoder) (err error) { + r.length, err = pd.getInt32() + if err != nil { + return err + } + if r.length <= 4 || r.length > MaxResponseSize { + return PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", r.length)} + } + + r.correlationID, err = pd.getInt32() + return err +} diff --git a/vendor/github.com/Shopify/sarama/sarama.go b/vendor/github.com/Shopify/sarama/sarama.go new file mode 100644 index 0000000..1e0277a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sarama.go @@ -0,0 +1,106 @@ +/* +Package sarama is a pure Go client library for dealing with Apache Kafka (versions 0.8 and later). It includes a high-level +API for easily producing and consuming messages, and a low-level API for controlling bytes on the wire when the high-level +API is insufficient. Usage examples for the high-level APIs are provided inline with their full documentation. + +To produce messages, use either the AsyncProducer or the SyncProducer. The AsyncProducer accepts messages on a channel +and produces them asynchronously in the background as efficiently as possible; it is preferred in most cases. +The SyncProducer provides a method which will block until Kafka acknowledges the message as produced. This can be +useful but comes with two caveats: it will generally be less efficient, and the actual durability guarantees +depend on the configured value of `Producer.RequiredAcks`. There are configurations where a message acknowledged by the +SyncProducer can still sometimes be lost. + +To consume messages, use Consumer or Consumer-Group API. + +For lower-level needs, the Broker and Request/Response objects permit precise control over each connection +and message sent on the wire; the Client provides higher-level metadata management that is shared between +the producers and the consumer. The Request/Response objects and properties are mostly undocumented, as they line up +exactly with the protocol fields documented by Kafka at +https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol + +Metrics are exposed through https://github.com/rcrowley/go-metrics library in a local registry. + +Broker related metrics: + + +----------------------------------------------+------------+---------------------------------------------------------------+ + | Name | Type | Description | + +----------------------------------------------+------------+---------------------------------------------------------------+ + | incoming-byte-rate | meter | Bytes/second read off all brokers | + | incoming-byte-rate-for-broker- | meter | Bytes/second read off a given broker | + | outgoing-byte-rate | meter | Bytes/second written off all brokers | + | outgoing-byte-rate-for-broker- | meter | Bytes/second written off a given broker | + | request-rate | meter | Requests/second sent to all brokers | + | request-rate-for-broker- | meter | Requests/second sent to a given broker | + | request-size | histogram | Distribution of the request size in bytes for all brokers | + | request-size-for-broker- | histogram | Distribution of the request size in bytes for a given broker | + | request-latency-in-ms | histogram | Distribution of the request latency in ms for all brokers | + | request-latency-in-ms-for-broker- | histogram | Distribution of the request latency in ms for a given broker | + | response-rate | meter | Responses/second received from all brokers | + | response-rate-for-broker- | meter | Responses/second received from a given broker | + | response-size | histogram | Distribution of the response size in bytes for all brokers | + | response-size-for-broker- | histogram | Distribution of the response size in bytes for a given broker | + +----------------------------------------------+------------+---------------------------------------------------------------+ + +Note that we do not gather specific metrics for seed brokers but they are part of the "all brokers" metrics. + +Producer related metrics: + + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | Name | Type | Description | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | batch-size | histogram | Distribution of the number of bytes sent per partition per request for all topics | + | batch-size-for-topic- | histogram | Distribution of the number of bytes sent per partition per request for a given topic | + | record-send-rate | meter | Records/second sent to all topics | + | record-send-rate-for-topic- | meter | Records/second sent to a given topic | + | records-per-request | histogram | Distribution of the number of records sent per request for all topics | + | records-per-request-for-topic- | histogram | Distribution of the number of records sent per request for a given topic | + | compression-ratio | histogram | Distribution of the compression ratio times 100 of record batches for all topics | + | compression-ratio-for-topic- | histogram | Distribution of the compression ratio times 100 of record batches for a given topic | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + +Consumer related metrics: + + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | Name | Type | Description | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | consumer-batch-size | histogram | Distribution of the number of messages in a batch | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + +*/ +package sarama + +import ( + "io/ioutil" + "log" +) + +var ( + // Logger is the instance of a StdLogger interface that Sarama writes connection + // management events to. By default it is set to discard all log messages via ioutil.Discard, + // but you can set it to redirect wherever you want. + Logger StdLogger = log.New(ioutil.Discard, "[Sarama] ", log.LstdFlags) + + // PanicHandler is called for recovering from panics spawned internally to the library (and thus + // not recoverable by the caller's goroutine). Defaults to nil, which means panics are not recovered. + PanicHandler func(interface{}) + + // MaxRequestSize is the maximum size (in bytes) of any request that Sarama will attempt to send. Trying + // to send a request larger than this will result in an PacketEncodingError. The default of 100 MiB is aligned + // with Kafka's default `socket.request.max.bytes`, which is the largest request the broker will attempt + // to process. + MaxRequestSize int32 = 100 * 1024 * 1024 + + // MaxResponseSize is the maximum size (in bytes) of any response that Sarama will attempt to parse. If + // a broker returns a response message larger than this value, Sarama will return a PacketDecodingError to + // protect the client from running out of memory. Please note that brokers do not have any natural limit on + // the size of responses they send. In particular, they can send arbitrarily large fetch responses to consumers + // (see https://issues.apache.org/jira/browse/KAFKA-2063). + MaxResponseSize int32 = 100 * 1024 * 1024 +) + +// StdLogger is used to log error messages. +type StdLogger interface { + Print(v ...interface{}) + Printf(format string, v ...interface{}) + Println(v ...interface{}) +} diff --git a/vendor/github.com/Shopify/sarama/sasl_authenticate_request.go b/vendor/github.com/Shopify/sarama/sasl_authenticate_request.go new file mode 100644 index 0000000..54c8b09 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_authenticate_request.go @@ -0,0 +1,29 @@ +package sarama + +type SaslAuthenticateRequest struct { + SaslAuthBytes []byte +} + +// APIKeySASLAuth is the API key for the SaslAuthenticate Kafka API +const APIKeySASLAuth = 36 + +func (r *SaslAuthenticateRequest) encode(pe packetEncoder) error { + return pe.putBytes(r.SaslAuthBytes) +} + +func (r *SaslAuthenticateRequest) decode(pd packetDecoder, version int16) (err error) { + r.SaslAuthBytes, err = pd.getBytes() + return err +} + +func (r *SaslAuthenticateRequest) key() int16 { + return APIKeySASLAuth +} + +func (r *SaslAuthenticateRequest) version() int16 { + return 0 +} + +func (r *SaslAuthenticateRequest) requiredVersion() KafkaVersion { + return V1_0_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sasl_authenticate_response.go b/vendor/github.com/Shopify/sarama/sasl_authenticate_response.go new file mode 100644 index 0000000..0038c3f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_authenticate_response.go @@ -0,0 +1,44 @@ +package sarama + +type SaslAuthenticateResponse struct { + Err KError + ErrorMessage *string + SaslAuthBytes []byte +} + +func (r *SaslAuthenticateResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + if err := pe.putNullableString(r.ErrorMessage); err != nil { + return err + } + return pe.putBytes(r.SaslAuthBytes) +} + +func (r *SaslAuthenticateResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + if r.ErrorMessage, err = pd.getNullableString(); err != nil { + return err + } + + r.SaslAuthBytes, err = pd.getBytes() + + return err +} + +func (r *SaslAuthenticateResponse) key() int16 { + return APIKeySASLAuth +} + +func (r *SaslAuthenticateResponse) version() int16 { + return 0 +} + +func (r *SaslAuthenticateResponse) requiredVersion() KafkaVersion { + return V1_0_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_request.go b/vendor/github.com/Shopify/sarama/sasl_handshake_request.go new file mode 100644 index 0000000..fe5ba05 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_handshake_request.go @@ -0,0 +1,34 @@ +package sarama + +type SaslHandshakeRequest struct { + Mechanism string + Version int16 +} + +func (r *SaslHandshakeRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.Mechanism); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeRequest) decode(pd packetDecoder, version int16) (err error) { + if r.Mechanism, err = pd.getString(); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeRequest) key() int16 { + return 17 +} + +func (r *SaslHandshakeRequest) version() int16 { + return r.Version +} + +func (r *SaslHandshakeRequest) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_response.go b/vendor/github.com/Shopify/sarama/sasl_handshake_response.go new file mode 100644 index 0000000..ef290d4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_handshake_response.go @@ -0,0 +1,38 @@ +package sarama + +type SaslHandshakeResponse struct { + Err KError + EnabledMechanisms []string +} + +func (r *SaslHandshakeResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return pe.putStringArray(r.EnabledMechanisms) +} + +func (r *SaslHandshakeResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + if r.EnabledMechanisms, err = pd.getStringArray(); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeResponse) key() int16 { + return 17 +} + +func (r *SaslHandshakeResponse) version() int16 { + return 0 +} + +func (r *SaslHandshakeResponse) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sticky_assignor_user_data.go b/vendor/github.com/Shopify/sarama/sticky_assignor_user_data.go new file mode 100644 index 0000000..bb0c82c --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sticky_assignor_user_data.go @@ -0,0 +1,124 @@ +package sarama + +type topicPartitionAssignment struct { + Topic string + Partition int32 +} + +type StickyAssignorUserData interface { + partitions() []topicPartitionAssignment + hasGeneration() bool + generation() int +} + +//StickyAssignorUserDataV0 holds topic partition information for an assignment +type StickyAssignorUserDataV0 struct { + Topics map[string][]int32 + + topicPartitions []topicPartitionAssignment +} + +func (m *StickyAssignorUserDataV0) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(m.Topics)); err != nil { + return err + } + + for topic, partitions := range m.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + return nil +} + +func (m *StickyAssignorUserDataV0) decode(pd packetDecoder) (err error) { + var topicLen int + if topicLen, err = pd.getArrayLength(); err != nil { + return + } + + m.Topics = make(map[string][]int32, topicLen) + for i := 0; i < topicLen; i++ { + var topic string + if topic, err = pd.getString(); err != nil { + return + } + if m.Topics[topic], err = pd.getInt32Array(); err != nil { + return + } + } + m.topicPartitions = populateTopicPartitions(m.Topics) + return nil +} + +func (m *StickyAssignorUserDataV0) partitions() []topicPartitionAssignment { return m.topicPartitions } +func (m *StickyAssignorUserDataV0) hasGeneration() bool { return false } +func (m *StickyAssignorUserDataV0) generation() int { return defaultGeneration } + +//StickyAssignorUserDataV1 holds topic partition information for an assignment +type StickyAssignorUserDataV1 struct { + Topics map[string][]int32 + Generation int32 + + topicPartitions []topicPartitionAssignment +} + +func (m *StickyAssignorUserDataV1) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(m.Topics)); err != nil { + return err + } + + for topic, partitions := range m.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + + pe.putInt32(m.Generation) + return nil +} + +func (m *StickyAssignorUserDataV1) decode(pd packetDecoder) (err error) { + var topicLen int + if topicLen, err = pd.getArrayLength(); err != nil { + return + } + + m.Topics = make(map[string][]int32, topicLen) + for i := 0; i < topicLen; i++ { + var topic string + if topic, err = pd.getString(); err != nil { + return + } + if m.Topics[topic], err = pd.getInt32Array(); err != nil { + return + } + } + + m.Generation, err = pd.getInt32() + if err != nil { + return err + } + m.topicPartitions = populateTopicPartitions(m.Topics) + return nil +} + +func (m *StickyAssignorUserDataV1) partitions() []topicPartitionAssignment { return m.topicPartitions } +func (m *StickyAssignorUserDataV1) hasGeneration() bool { return true } +func (m *StickyAssignorUserDataV1) generation() int { return int(m.Generation) } + +func populateTopicPartitions(topics map[string][]int32) []topicPartitionAssignment { + topicPartitions := make([]topicPartitionAssignment, 0) + for topic, partitions := range topics { + for _, partition := range partitions { + topicPartitions = append(topicPartitions, topicPartitionAssignment{Topic: topic, Partition: partition}) + } + } + return topicPartitions +} diff --git a/vendor/github.com/Shopify/sarama/sync_group_request.go b/vendor/github.com/Shopify/sarama/sync_group_request.go new file mode 100644 index 0000000..fe20708 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_group_request.go @@ -0,0 +1,100 @@ +package sarama + +type SyncGroupRequest struct { + GroupId string + GenerationId int32 + MemberId string + GroupAssignments map[string][]byte +} + +func (r *SyncGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.MemberId); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.GroupAssignments)); err != nil { + return err + } + for memberId, memberAssignment := range r.GroupAssignments { + if err := pe.putString(memberId); err != nil { + return err + } + if err := pe.putBytes(memberAssignment); err != nil { + return err + } + } + + return nil +} + +func (r *SyncGroupRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupAssignments = make(map[string][]byte) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + memberAssignment, err := pd.getBytes() + if err != nil { + return err + } + + r.GroupAssignments[memberId] = memberAssignment + } + + return nil +} + +func (r *SyncGroupRequest) key() int16 { + return 14 +} + +func (r *SyncGroupRequest) version() int16 { + return 0 +} + +func (r *SyncGroupRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +func (r *SyncGroupRequest) AddGroupAssignment(memberId string, memberAssignment []byte) { + if r.GroupAssignments == nil { + r.GroupAssignments = make(map[string][]byte) + } + + r.GroupAssignments[memberId] = memberAssignment +} + +func (r *SyncGroupRequest) AddGroupAssignmentMember(memberId string, memberAssignment *ConsumerGroupMemberAssignment) error { + bin, err := encode(memberAssignment, nil) + if err != nil { + return err + } + + r.AddGroupAssignment(memberId, bin) + return nil +} diff --git a/vendor/github.com/Shopify/sarama/sync_group_response.go b/vendor/github.com/Shopify/sarama/sync_group_response.go new file mode 100644 index 0000000..194b382 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_group_response.go @@ -0,0 +1,41 @@ +package sarama + +type SyncGroupResponse struct { + Err KError + MemberAssignment []byte +} + +func (r *SyncGroupResponse) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { + assignment := new(ConsumerGroupMemberAssignment) + err := decode(r.MemberAssignment, assignment) + return assignment, err +} + +func (r *SyncGroupResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return pe.putBytes(r.MemberAssignment) +} + +func (r *SyncGroupResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + r.MemberAssignment, err = pd.getBytes() + return +} + +func (r *SyncGroupResponse) key() int16 { + return 14 +} + +func (r *SyncGroupResponse) version() int16 { + return 0 +} + +func (r *SyncGroupResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sync_producer.go b/vendor/github.com/Shopify/sarama/sync_producer.go new file mode 100644 index 0000000..021c5a0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_producer.go @@ -0,0 +1,149 @@ +package sarama + +import "sync" + +// SyncProducer publishes Kafka messages, blocking until they have been acknowledged. It routes messages to the correct +// broker, refreshing metadata as appropriate, and parses responses for errors. You must call Close() on a producer +// to avoid leaks, it may not be garbage-collected automatically when it passes out of scope. +// +// The SyncProducer comes with two caveats: it will generally be less efficient than the AsyncProducer, and the actual +// durability guarantee provided when a message is acknowledged depend on the configured value of `Producer.RequiredAcks`. +// There are configurations where a message acknowledged by the SyncProducer can still sometimes be lost. +// +// For implementation reasons, the SyncProducer requires `Producer.Return.Errors` and `Producer.Return.Successes` to +// be set to true in its configuration. +type SyncProducer interface { + + // SendMessage produces a given message, and returns only when it either has + // succeeded or failed to produce. It will return the partition and the offset + // of the produced message, or an error if the message failed to produce. + SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) + + // SendMessages produces a given set of messages, and returns only when all + // messages in the set have either succeeded or failed. Note that messages + // can succeed and fail individually; if some succeed and some fail, + // SendMessages will return an error. + SendMessages(msgs []*ProducerMessage) error + + // Close shuts down the producer and waits for any buffered messages to be + // flushed. You must call this function before a producer object passes out of + // scope, as it may otherwise leak memory. You must call this before calling + // Close on the underlying client. + Close() error +} + +type syncProducer struct { + producer *asyncProducer + wg sync.WaitGroup +} + +// NewSyncProducer creates a new SyncProducer using the given broker addresses and configuration. +func NewSyncProducer(addrs []string, config *Config) (SyncProducer, error) { + if config == nil { + config = NewConfig() + config.Producer.Return.Successes = true + } + + if err := verifyProducerConfig(config); err != nil { + return nil, err + } + + p, err := NewAsyncProducer(addrs, config) + if err != nil { + return nil, err + } + return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil +} + +// NewSyncProducerFromClient creates a new SyncProducer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this producer. +func NewSyncProducerFromClient(client Client) (SyncProducer, error) { + if err := verifyProducerConfig(client.Config()); err != nil { + return nil, err + } + + p, err := NewAsyncProducerFromClient(client) + if err != nil { + return nil, err + } + return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil +} + +func newSyncProducerFromAsyncProducer(p *asyncProducer) *syncProducer { + sp := &syncProducer{producer: p} + + sp.wg.Add(2) + go withRecover(sp.handleSuccesses) + go withRecover(sp.handleErrors) + + return sp +} + +func verifyProducerConfig(config *Config) error { + if !config.Producer.Return.Errors { + return ConfigurationError("Producer.Return.Errors must be true to be used in a SyncProducer") + } + if !config.Producer.Return.Successes { + return ConfigurationError("Producer.Return.Successes must be true to be used in a SyncProducer") + } + return nil +} + +func (sp *syncProducer) SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) { + expectation := make(chan *ProducerError, 1) + msg.expectation = expectation + sp.producer.Input() <- msg + + if err := <-expectation; err != nil { + return -1, -1, err.Err + } + + return msg.Partition, msg.Offset, nil +} + +func (sp *syncProducer) SendMessages(msgs []*ProducerMessage) error { + expectations := make(chan chan *ProducerError, len(msgs)) + go func() { + for _, msg := range msgs { + expectation := make(chan *ProducerError, 1) + msg.expectation = expectation + sp.producer.Input() <- msg + expectations <- expectation + } + close(expectations) + }() + + var errors ProducerErrors + for expectation := range expectations { + if err := <-expectation; err != nil { + errors = append(errors, err) + } + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (sp *syncProducer) handleSuccesses() { + defer sp.wg.Done() + for msg := range sp.producer.Successes() { + expectation := msg.expectation + expectation <- nil + } +} + +func (sp *syncProducer) handleErrors() { + defer sp.wg.Done() + for err := range sp.producer.Errors() { + expectation := err.Msg.expectation + expectation <- err + } +} + +func (sp *syncProducer) Close() error { + sp.producer.AsyncClose() + sp.wg.Wait() + return nil +} diff --git a/vendor/github.com/Shopify/sarama/timestamp.go b/vendor/github.com/Shopify/sarama/timestamp.go new file mode 100644 index 0000000..372278d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/timestamp.go @@ -0,0 +1,40 @@ +package sarama + +import ( + "fmt" + "time" +) + +type Timestamp struct { + *time.Time +} + +func (t Timestamp) encode(pe packetEncoder) error { + timestamp := int64(-1) + + if !t.Before(time.Unix(0, 0)) { + timestamp = t.UnixNano() / int64(time.Millisecond) + } else if !t.IsZero() { + return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", t)} + } + + pe.putInt64(timestamp) + return nil +} + +func (t Timestamp) decode(pd packetDecoder) error { + millis, err := pd.getInt64() + if err != nil { + return err + } + + // negative timestamps are invalid, in these cases we should return + // a zero time + timestamp := time.Time{} + if millis >= 0 { + timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) + } + + *t.Time = timestamp + return nil +} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go new file mode 100644 index 0000000..71e95b8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go @@ -0,0 +1,126 @@ +package sarama + +type TxnOffsetCommitRequest struct { + TransactionalID string + GroupID string + ProducerID int64 + ProducerEpoch int16 + Topics map[string][]*PartitionOffsetMetadata +} + +func (t *TxnOffsetCommitRequest) encode(pe packetEncoder) error { + if err := pe.putString(t.TransactionalID); err != nil { + return err + } + if err := pe.putString(t.GroupID); err != nil { + return err + } + pe.putInt64(t.ProducerID) + pe.putInt16(t.ProducerEpoch) + + if err := pe.putArrayLength(len(t.Topics)); err != nil { + return err + } + for topic, partitions := range t.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for _, partition := range partitions { + if err := partition.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (t *TxnOffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { + if t.TransactionalID, err = pd.getString(); err != nil { + return err + } + if t.GroupID, err = pd.getString(); err != nil { + return err + } + if t.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if t.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics = make(map[string][]*PartitionOffsetMetadata) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics[topic] = make([]*PartitionOffsetMetadata, m) + + for j := 0; j < m; j++ { + partitionOffsetMetadata := new(PartitionOffsetMetadata) + if err := partitionOffsetMetadata.decode(pd, version); err != nil { + return err + } + t.Topics[topic][j] = partitionOffsetMetadata + } + } + + return nil +} + +func (a *TxnOffsetCommitRequest) key() int16 { + return 28 +} + +func (a *TxnOffsetCommitRequest) version() int16 { + return 0 +} + +func (a *TxnOffsetCommitRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type PartitionOffsetMetadata struct { + Partition int32 + Offset int64 + Metadata *string +} + +func (p *PartitionOffsetMetadata) encode(pe packetEncoder) error { + pe.putInt32(p.Partition) + pe.putInt64(p.Offset) + if err := pe.putNullableString(p.Metadata); err != nil { + return err + } + + return nil +} + +func (p *PartitionOffsetMetadata) decode(pd packetDecoder, version int16) (err error) { + if p.Partition, err = pd.getInt32(); err != nil { + return err + } + if p.Offset, err = pd.getInt64(); err != nil { + return err + } + if p.Metadata, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go new file mode 100644 index 0000000..6c980f4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go @@ -0,0 +1,83 @@ +package sarama + +import ( + "time" +) + +type TxnOffsetCommitResponse struct { + ThrottleTime time.Duration + Topics map[string][]*PartitionError +} + +func (t *TxnOffsetCommitResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(t.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(t.Topics)); err != nil { + return err + } + + for topic, e := range t.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(e)); err != nil { + return err + } + for _, partitionError := range e { + if err := partitionError.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (t *TxnOffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + t.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics = make(map[string][]*PartitionError) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics[topic] = make([]*PartitionError, m) + + for j := 0; j < m; j++ { + t.Topics[topic][j] = new(PartitionError) + if err := t.Topics[topic][j].decode(pd, version); err != nil { + return err + } + } + } + + return nil +} + +func (a *TxnOffsetCommitResponse) key() int16 { + return 28 +} + +func (a *TxnOffsetCommitResponse) version() int16 { + return 0 +} + +func (a *TxnOffsetCommitResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/utils.go b/vendor/github.com/Shopify/sarama/utils.go new file mode 100644 index 0000000..7c815cd --- /dev/null +++ b/vendor/github.com/Shopify/sarama/utils.go @@ -0,0 +1,225 @@ +package sarama + +import ( + "bufio" + "fmt" + "net" + "regexp" +) + +type none struct{} + +// make []int32 sortable so we can sort partition numbers +type int32Slice []int32 + +func (slice int32Slice) Len() int { + return len(slice) +} + +func (slice int32Slice) Less(i, j int) bool { + return slice[i] < slice[j] +} + +func (slice int32Slice) Swap(i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} + +func dupInt32Slice(input []int32) []int32 { + ret := make([]int32, 0, len(input)) + for _, val := range input { + ret = append(ret, val) + } + return ret +} + +func withRecover(fn func()) { + defer func() { + handler := PanicHandler + if handler != nil { + if err := recover(); err != nil { + handler(err) + } + } + }() + + fn() +} + +func safeAsyncClose(b *Broker) { + tmp := b // local var prevents clobbering in goroutine + go withRecover(func() { + if connected, _ := tmp.Connected(); connected { + if err := tmp.Close(); err != nil { + Logger.Println("Error closing broker", tmp.ID(), ":", err) + } + } + }) +} + +// Encoder is a simple interface for any type that can be encoded as an array of bytes +// in order to be sent as the key or value of a Kafka message. Length() is provided as an +// optimization, and must return the same as len() on the result of Encode(). +type Encoder interface { + Encode() ([]byte, error) + Length() int +} + +// make strings and byte slices encodable for convenience so they can be used as keys +// and/or values in kafka messages + +// StringEncoder implements the Encoder interface for Go strings so that they can be used +// as the Key or Value in a ProducerMessage. +type StringEncoder string + +func (s StringEncoder) Encode() ([]byte, error) { + return []byte(s), nil +} + +func (s StringEncoder) Length() int { + return len(s) +} + +// ByteEncoder implements the Encoder interface for Go byte slices so that they can be used +// as the Key or Value in a ProducerMessage. +type ByteEncoder []byte + +func (b ByteEncoder) Encode() ([]byte, error) { + return b, nil +} + +func (b ByteEncoder) Length() int { + return len(b) +} + +// bufConn wraps a net.Conn with a buffer for reads to reduce the number of +// reads that trigger syscalls. +type bufConn struct { + net.Conn + buf *bufio.Reader +} + +func newBufConn(conn net.Conn) *bufConn { + return &bufConn{ + Conn: conn, + buf: bufio.NewReader(conn), + } +} + +func (bc *bufConn) Read(b []byte) (n int, err error) { + return bc.buf.Read(b) +} + +// KafkaVersion instances represent versions of the upstream Kafka broker. +type KafkaVersion struct { + // it's a struct rather than just typing the array directly to make it opaque and stop people + // generating their own arbitrary versions + version [4]uint +} + +func newKafkaVersion(major, minor, veryMinor, patch uint) KafkaVersion { + return KafkaVersion{ + version: [4]uint{major, minor, veryMinor, patch}, + } +} + +// IsAtLeast return true if and only if the version it is called on is +// greater than or equal to the version passed in: +// V1.IsAtLeast(V2) // false +// V2.IsAtLeast(V1) // true +func (v KafkaVersion) IsAtLeast(other KafkaVersion) bool { + for i := range v.version { + if v.version[i] > other.version[i] { + return true + } else if v.version[i] < other.version[i] { + return false + } + } + return true +} + +// Effective constants defining the supported kafka versions. +var ( + V0_8_2_0 = newKafkaVersion(0, 8, 2, 0) + V0_8_2_1 = newKafkaVersion(0, 8, 2, 1) + V0_8_2_2 = newKafkaVersion(0, 8, 2, 2) + V0_9_0_0 = newKafkaVersion(0, 9, 0, 0) + V0_9_0_1 = newKafkaVersion(0, 9, 0, 1) + V0_10_0_0 = newKafkaVersion(0, 10, 0, 0) + V0_10_0_1 = newKafkaVersion(0, 10, 0, 1) + V0_10_1_0 = newKafkaVersion(0, 10, 1, 0) + V0_10_1_1 = newKafkaVersion(0, 10, 1, 1) + V0_10_2_0 = newKafkaVersion(0, 10, 2, 0) + V0_10_2_1 = newKafkaVersion(0, 10, 2, 1) + V0_11_0_0 = newKafkaVersion(0, 11, 0, 0) + V0_11_0_1 = newKafkaVersion(0, 11, 0, 1) + V0_11_0_2 = newKafkaVersion(0, 11, 0, 2) + V1_0_0_0 = newKafkaVersion(1, 0, 0, 0) + V1_1_0_0 = newKafkaVersion(1, 1, 0, 0) + V1_1_1_0 = newKafkaVersion(1, 1, 1, 0) + V2_0_0_0 = newKafkaVersion(2, 0, 0, 0) + V2_0_1_0 = newKafkaVersion(2, 0, 1, 0) + V2_1_0_0 = newKafkaVersion(2, 1, 0, 0) + V2_2_0_0 = newKafkaVersion(2, 2, 0, 0) + V2_3_0_0 = newKafkaVersion(2, 3, 0, 0) + + SupportedVersions = []KafkaVersion{ + V0_8_2_0, + V0_8_2_1, + V0_8_2_2, + V0_9_0_0, + V0_9_0_1, + V0_10_0_0, + V0_10_0_1, + V0_10_1_0, + V0_10_1_1, + V0_10_2_0, + V0_10_2_1, + V0_11_0_0, + V0_11_0_1, + V0_11_0_2, + V1_0_0_0, + V1_1_0_0, + V1_1_1_0, + V2_0_0_0, + V2_0_1_0, + V2_1_0_0, + V2_2_0_0, + V2_3_0_0, + } + MinVersion = V0_8_2_0 + MaxVersion = V2_3_0_0 +) + +//ParseKafkaVersion parses and returns kafka version or error from a string +func ParseKafkaVersion(s string) (KafkaVersion, error) { + if len(s) < 5 { + return MinVersion, fmt.Errorf("invalid version `%s`", s) + } + var major, minor, veryMinor, patch uint + var err error + if s[0] == '0' { + err = scanKafkaVersion(s, `^0\.\d+\.\d+\.\d+$`, "0.%d.%d.%d", [3]*uint{&minor, &veryMinor, &patch}) + } else { + err = scanKafkaVersion(s, `^\d+\.\d+\.\d+$`, "%d.%d.%d", [3]*uint{&major, &minor, &veryMinor}) + } + if err != nil { + return MinVersion, err + } + return newKafkaVersion(major, minor, veryMinor, patch), nil +} + +func scanKafkaVersion(s string, pattern string, format string, v [3]*uint) error { + if !regexp.MustCompile(pattern).MatchString(s) { + return fmt.Errorf("invalid version `%s`", s) + } + _, err := fmt.Sscanf(s, format, v[0], v[1], v[2]) + return err +} + +func (v KafkaVersion) String() string { + if v.version[0] == 0 { + return fmt.Sprintf("0.%d.%d.%d", v.version[1], v.version[2], v.version[3]) + } + + return fmt.Sprintf("%d.%d.%d", v.version[0], v.version[1], v.version[2]) +} diff --git a/vendor/github.com/Shopify/sarama/zstd.go b/vendor/github.com/Shopify/sarama/zstd.go new file mode 100644 index 0000000..58880e2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/zstd.go @@ -0,0 +1,27 @@ +package sarama + +import ( + "github.com/klauspost/compress/zstd" + "sync" +) + +var ( + zstdDec *zstd.Decoder + zstdEnc *zstd.Encoder + + zstdEncOnce, zstdDecOnce sync.Once +) + +func zstdDecompress(dst, src []byte) ([]byte, error) { + zstdDecOnce.Do(func() { + zstdDec, _ = zstd.NewReader(nil) + }) + return zstdDec.DecodeAll(src, dst) +} + +func zstdCompress(dst, src []byte) ([]byte, error) { + zstdEncOnce.Do(func() { + zstdEnc, _ = zstd.NewWriter(nil, zstd.WithZeroFrames(true)) + }) + return zstdEnc.EncodeAll(src, dst), nil +} diff --git a/vendor/github.com/SkyAPM/go2sky/.gitignore b/vendor/github.com/SkyAPM/go2sky/.gitignore new file mode 100644 index 0000000..d393418 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin/ + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# IDE +.idea/ + +# Coverage +coverage.txt \ No newline at end of file diff --git a/vendor/github.com/SkyAPM/go2sky/LICENSE b/vendor/github.com/SkyAPM/go2sky/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/SkyAPM/go2sky/Makefile b/vendor/github.com/SkyAPM/go2sky/Makefile new file mode 100644 index 0000000..0aa6b3d --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/Makefile @@ -0,0 +1,72 @@ +# +# Licensed to the SkyAPM org under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +export GO111MODULE=on +export GO2SKY_GO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +GRPC_PATH := $(GO2SKY_GO)/reporter/grpc + +.DEFAULT_GOAL := test + +.PHONY: deps +deps: + go get -v -t -d ./... + +.PHONY: test +test: + go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic `go list ./... | grep -v github.com/SkyAPM/go2sky/reporter/grpc | grep -v github.com/SkyAPM/go2sky/test` + +.PHONY: proto-gen +proto-gen: + cd $(GRPC_PATH) && \ + protoc common/*.proto --go_out=plugins=grpc:$(GOPATH)/src && \ + cp ${GOPATH}/src/github.com/SkyAPM/go2sky/reporter/grpc/common/*.go common/ + cd $(GRPC_PATH) && \ + protoc language-agent/*.proto --go_out=plugins=grpc:$(GOPATH)/src && \ + cp ${GOPATH}/src/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/*.go language-agent/ + cd $(GRPC_PATH) && \ + protoc management/*.proto --go_out=plugins=grpc:$(GOPATH)/src && \ + cp ${GOPATH}/src/github.com/SkyAPM/go2sky/reporter/grpc/management/*.go management/ + +.PHONY: mock-gen +mock-gen: + cd $(GRPC_PATH)/language-agent && \ + mkdir -p mock_trace && \ + mockgen github.com/SkyAPM/go2sky/reporter/grpc/language-agent TraceSegmentReportServiceClient > mock_trace/Tracing.mock.go + cd $(GRPC_PATH)/management && \ + mkdir -p mock_management && \ + mockgen github.com/SkyAPM/go2sky/reporter/grpc/management ManagementServiceClient > mock_management/Management.mock.go + +LINTER := bin/golangci-lint +$(LINTER): + wget -q -O- https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.20.1 + +.PHONY: lint +lint: $(LINTER) ./golangci.yml ## Run the linters + @echo "linting..." + $(LINTER) run --config ./golangci.yml + +.PHONY: fix +fix: $(LINTER) + @echo "fix..." + $(LINTER) run -v --fix ./... + +.PHONY: all +all: test lint + +.PHONY: license +license: + python3 tools/check-license-header.py \ No newline at end of file diff --git a/vendor/github.com/SkyAPM/go2sky/NOTICE b/vendor/github.com/SkyAPM/go2sky/NOTICE new file mode 100644 index 0000000..748ca17 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/NOTICE @@ -0,0 +1,7 @@ +The source files contained in the reporter/grpc directory have been +taken from: +Apache SkyWalking +Copyright 2017-2019 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/) diff --git a/vendor/github.com/SkyAPM/go2sky/README.md b/vendor/github.com/SkyAPM/go2sky/README.md new file mode 100644 index 0000000..2f9cf2d --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/README.md @@ -0,0 +1,130 @@ +# GO2Sky + +[![Build](https://github.com/SkyAPM/go2sky/workflows/Build/badge.svg?branch=master)](https://github.com/SkyAPM/go2sky/actions?query=branch%3Amaster+event%3Apush+workflow%3ABuild) +[![Coverage](https://codecov.io/gh/SkyAPM/go2sky/branch/master/graph/badge.svg)](https://codecov.io/gh/SkyAPM/go2sky) +[![GoDoc](https://godoc.org/github.com/SkyAPM/go2sky?status.svg)](https://godoc.org/github.com/SkyAPM/go2sky) + + +**GO2Sky** is an instrument SDK library, written in Go, by following [Apache SkyWalking](https://github.com/apache/incubator-skywalking) tracing and metrics formats. + +# Installation +``` +$ go get -u github.com/SkyAPM/go2sky +``` + +The API of this project is still evolving. The use of vendoring tool is recommended. + +# Quickstart + +By completing this quickstart, you will learn how to trace local methods. For more details, please view +[the example](example_trace_test.go). + +## Configuration + +GO2Sky can export traces to Apache SkyWalking OAP server or local logger. In the following example, we configure GO2Sky to export to OAP server, +which is listening on `oap-skywalking` port `11800`, and all the spans from this program will be associated with a service name `example`. +`reporter.GRPCReporter` can also adjust the behavior through `reporter.GRPCReporterOption`, [view all](docs/GRPC-Reporter-Option.md). + +```go +r, err := reporter.NewGRPCReporter("oap-skywalking:11800") +if err != nil { + log.Fatalf("new reporter error %v \n", err) +} +defer r.Close() +tracer, err := go2sky.NewTracer("example", go2sky.WithReporter(r)) +``` + +## Create span + +To create a span in a trace, we used the `Tracer` to start a new span. We indicate this as the root span because of +passing `context.Background()`. We must also be sure to end this span, which will be show in [End span](#end-span). + +```go +span, ctx, err := tracer.CreateLocalSpan(context.Background()) +``` + +## Get Global TraceID + +Get the `TraceID` of the `activeSpan` in the `Context`. + +```go +go2sky.TraceID(ctx) +``` + +## Create a sub span + +A sub span created as the children of root span links to its parent with `Context`. + +```go +subSpan, newCtx, err := tracer.CreateLocalSpan(ctx) +``` + +## End span + +We must end the spans so they becomes available for sending to the backend by a reporter. + +```go +subSpan.End() +span.End() +``` + +# Advanced Concepts + +We cover some advanced topics about GO2Sky. + +## Context propagation + +Trace links spans belong to it by using context propagation which varies between different scenario. + +### In process + +We use `context` package to link spans. The root span usually pick `context.Background()`, and sub spans +will inject the context generated by its parent. + +```go +//Create a new context +entrySpan, entryCtx, err := tracer.CreateEntrySpan(context.Background(), ...) + +// Some operation +... + +// Link two spans by injecting entrySpan context into exitSpan +exitSpan, err := tracer.CreateExitSpan(entryCtx, ...) +``` + +### Crossing process + +We use `Entry` span to extract context from downstream service, and use `Exit` span to inject context to +upstream service. + +`Entry` and `Exit` spans make sense to OAP analysis which generates topology map and service metrics. + +```go +//Extract context from HTTP request header `sw8` +span, ctx, err := tracer.CreateEntrySpan(r.Context(), "/api/login", func() (string, error) { + return r.Header.Get("sw8"), nil +}) + +// Some operation +... + +// Inject context into HTTP request header `sw8` +span, err := tracer.CreateExitSpan(req.Context(), "/service/validate", "tomcat-service:8080", func(header string) error { + req.Header.Set(propagation.Header, header) + return nil +}) +``` + +## Tag + +We set tags into a span which is stored in the backend, but some tags have special purpose. OAP server +may use them to aggregate metrics, generate topology map and etc. + +They are defined as constant in root package with prefix `Tag`. + +## Plugins + +Go to go2sky-plugins repo to see all the plugins, [click here](https://github.com/SkyAPM/go2sky-plugins). + +# License +Apache License 2.0. See [LICENSE](LICENSE) file for details. diff --git a/vendor/github.com/SkyAPM/go2sky/doc.go b/vendor/github.com/SkyAPM/go2sky/doc.go new file mode 100644 index 0000000..a7b068f --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/doc.go @@ -0,0 +1,23 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky implements a native Apache SkyWalking agent library for Go. + +See http://skywalking.apache.org/ for more information about Apache SkyWalking. +*/ +package go2sky diff --git a/vendor/github.com/SkyAPM/go2sky/go.mod b/vendor/github.com/SkyAPM/go2sky/go.mod new file mode 100644 index 0000000..ee4386d --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/go.mod @@ -0,0 +1,14 @@ +module github.com/SkyAPM/go2sky + +go 1.12 + +require ( + github.com/golang/mock v1.2.0 + github.com/golang/protobuf v1.3.2 + github.com/google/uuid v1.1.1 + github.com/pkg/errors v0.8.1 + golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 // indirect + golang.org/x/sys v0.0.0-20190422165155-953cdadca894 // indirect + golang.org/x/text v0.3.1-0.20181010134911-4d1c5fb19474 // indirect + google.golang.org/grpc v1.27.0 +) diff --git a/vendor/github.com/SkyAPM/go2sky/go.sum b/vendor/github.com/SkyAPM/go2sky/go.sum new file mode 100644 index 0000000..cbfbe2b --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/go.sum @@ -0,0 +1,65 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181010134911-4d1c5fb19474 h1:4l+CHZwCUFzGF11IlLbqggmpYvJyXOKSlGBZ8M0Ag/w= +golang.org/x/text v0.3.1-0.20181010134911-4d1c5fb19474/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/SkyAPM/go2sky/golangci.yml b/vendor/github.com/SkyAPM/go2sky/golangci.yml new file mode 100644 index 0000000..b9014cd --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/golangci.yml @@ -0,0 +1,45 @@ +# Licensed to the SkyAPM org under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +linters: + enable: + - deadcode + - errcheck + - goconst + - golint + - ineffassign + - lll + - maligned + - misspell + - structcheck + - unconvert + - varcheck + - govet + - goimports + - prealloc + - unused + - staticcheck + - gosimple + - megacheck + disable: + - interfacer +linters-settings: + lll: + line-length: 160 + goconst: + min-occurrences: 4 + govet: + check-shadowing: true \ No newline at end of file diff --git a/vendor/github.com/SkyAPM/go2sky/internal/idgen/idgen.go b/vendor/github.com/SkyAPM/go2sky/internal/idgen/idgen.go new file mode 100644 index 0000000..824c90c --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/internal/idgen/idgen.go @@ -0,0 +1,38 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 idgen + +import ( + "strings" + + "github.com/google/uuid" +) + +// UUID generate UUID +func UUID() (string, error) { + id, err := uuid.NewUUID() + if err != nil { + return "", err + } + return strings.ReplaceAll(id.String(), "-", ""), nil +} + +// GenerateGlobalID generates global unique id +func GenerateGlobalID() (globalID string, err error) { + return UUID() +} diff --git a/vendor/github.com/SkyAPM/go2sky/internal/tool/error.go b/vendor/github.com/SkyAPM/go2sky/internal/tool/error.go new file mode 100644 index 0000000..bb6dbc6 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/internal/tool/error.go @@ -0,0 +1,24 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 tool + +type Error string + +func (e Error) Error() string { + return string(e) +} diff --git a/vendor/github.com/SkyAPM/go2sky/internal/tool/os_util.go b/vendor/github.com/SkyAPM/go2sky/internal/tool/os_util.go new file mode 100644 index 0000000..603cc44 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/internal/tool/os_util.go @@ -0,0 +1,71 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 tool + +import ( + "net" + "os" + "runtime" + "strconv" +) + +func ProcessNo() string { + if os.Getpid() > 0 { + return strconv.Itoa(os.Getpid()) + } + return "" +} + +func HostName() string { + if hs, err := os.Hostname(); err == nil { + return hs + } + return "unknown" +} + +func OSName() string { + return runtime.GOOS +} + +func AllIPV4() (ipv4s []string) { + adders, err := net.InterfaceAddrs() + if err != nil { + return + } + + for _, addr := range adders { + if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { + if ipNet.IP.To4() != nil { + ipv4 := ipNet.IP.String() + if ipv4 == "127.0.0.1" || ipv4 == "localhost" { + continue + } + ipv4s = append(ipv4s, ipv4) + } + } + } + return +} + +func IPV4() string { + ipv4s := AllIPV4() + if len(ipv4s) > 0 { + return ipv4s[0] + } + return "no-hostname" +} diff --git a/vendor/github.com/SkyAPM/go2sky/internal/tool/time.go b/vendor/github.com/SkyAPM/go2sky/internal/tool/time.go new file mode 100644 index 0000000..b1a4cd1 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/internal/tool/time.go @@ -0,0 +1,25 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 tool + +import "time" + +// Millisecond converts time to unix millisecond +func Millisecond(t time.Time) int64 { + return t.UnixNano() / int64(time.Millisecond) +} diff --git a/vendor/github.com/SkyAPM/go2sky/noop.go b/vendor/github.com/SkyAPM/go2sky/noop.go new file mode 100644 index 0000000..3a4f45d --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/noop.go @@ -0,0 +1,63 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +import ( + "time" + + v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent" +) + +type NoopSpan struct { +} + +func (*NoopSpan) SetOperationName(string) { +} + +func (*NoopSpan) GetOperationName() string { + return "" +} + +func (*NoopSpan) SetPeer(string) { +} + +func (*NoopSpan) SetSpanLayer(v3.SpanLayer) { +} + +func (*NoopSpan) SetComponent(int32) { +} + +func (*NoopSpan) Tag(Tag, string) { +} + +func (*NoopSpan) Log(time.Time, ...string) { +} + +func (*NoopSpan) Error(time.Time, ...string) { +} + +func (*NoopSpan) End() { +} + +func (*NoopSpan) IsEntry() bool { + return false +} + +func (*NoopSpan) IsExit() bool { + return false +} diff --git a/vendor/github.com/SkyAPM/go2sky/propagation/propagation.go b/vendor/github.com/SkyAPM/go2sky/propagation/propagation.go new file mode 100644 index 0000000..9b4a499 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/propagation/propagation.go @@ -0,0 +1,138 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 propagation holds the required function signatures for Injection and +Extraction. It also contains decoder and encoder of SkyWalking propagation protocol. +*/ +package propagation + +import ( + "encoding/base64" + "fmt" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +const ( + Header string = "sw8" + headerLen int = 8 + splitToken string = "-" +) + +var ( + errEmptyHeader = errors.New("empty header") + errInsufficientHeaderEntities = errors.New("insufficient header entities") +) + +// Extractor is a tool specification which define how to +// extract trace parent context from propagation context +type Extractor func() (string, error) + +// Injector is a tool specification which define how to +// inject trace context into propagation context +type Injector func(header string) error + +// SpanContext defines propagation specification of SkyWalking +type SpanContext struct { + TraceID string `json:"trace_id"` + ParentSegmentID string `json:"parent_segment_id"` + ParentService string `json:"parent_service"` + ParentServiceInstance string `json:"parent_service_instance"` + ParentEndpoint string `json:"parent_endpoint"` + AddressUsedAtClient string `json:"address_used_at_client"` + ParentSpanID int32 `json:"parent_span_id"` + Sample int8 `json:"sample"` +} + +// DecodeSW6 converts string header to SpanContext +func (tc *SpanContext) DecodeSW8(header string) error { + if header == "" { + return errEmptyHeader + } + hh := strings.Split(header, splitToken) + if len(hh) < headerLen { + return errors.WithMessagef(errInsufficientHeaderEntities, "header string: %s", header) + } + sample, err := strconv.ParseInt(hh[0], 10, 8) + if err != nil { + return errors.Errorf("str to int8 error %s", hh[0]) + } + tc.Sample = int8(sample) + tc.TraceID, err = decodeBase64(hh[1]) + if err != nil { + return errors.Wrap(err, "trace id parse error") + } + tc.ParentSegmentID, err = decodeBase64(hh[2]) + if err != nil { + return errors.Wrap(err, "parent segment id parse error") + } + tc.ParentSpanID, err = stringConvertInt32(hh[3]) + if err != nil { + return errors.Wrap(err, "parent span id parse error") + } + tc.ParentService, err = decodeBase64(hh[4]) + if err != nil { + return errors.Wrap(err, "parent service parse error") + } + tc.ParentServiceInstance, err = decodeBase64(hh[5]) + if err != nil { + return errors.Wrap(err, "parent service instance parse error") + } + tc.ParentEndpoint, err = decodeBase64(hh[6]) + if err != nil { + return errors.Wrap(err, "parent endpoint parse error") + } + tc.AddressUsedAtClient, err = decodeBase64(hh[7]) + if err != nil { + return errors.Wrap(err, "network address parse error") + } + return nil +} + +// EncodeSW6 converts SpanContext to string header +func (tc *SpanContext) EncodeSW8() string { + return strings.Join([]string{ + fmt.Sprint(tc.Sample), + encodeBase64(tc.TraceID), + encodeBase64(tc.ParentSegmentID), + fmt.Sprint(tc.ParentSpanID), + encodeBase64(tc.ParentService), + encodeBase64(tc.ParentServiceInstance), + encodeBase64(tc.ParentEndpoint), + encodeBase64(tc.AddressUsedAtClient), + }, "-") +} + +func stringConvertInt32(str string) (int32, error) { + i, err := strconv.ParseInt(str, 0, 32) + return int32(i), err +} + +func decodeBase64(str string) (string, error) { + ret, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return "", err + } + return string(ret), nil +} + +func encodeBase64(str string) string { + return base64.StdEncoding.EncodeToString([]byte(str)) +} diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.pb.go b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.pb.go new file mode 100644 index 0000000..8329ed8 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.pb.go @@ -0,0 +1,302 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: common/Common.proto + +package common + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// In most cases, detect point should be `server` or `client`. +// Even in service mesh, this means `server`/`client` side sidecar +// `proxy` is reserved only. +type DetectPoint int32 + +const ( + DetectPoint_client DetectPoint = 0 + DetectPoint_server DetectPoint = 1 + DetectPoint_proxy DetectPoint = 2 +) + +var DetectPoint_name = map[int32]string{ + 0: "client", + 1: "server", + 2: "proxy", +} + +var DetectPoint_value = map[string]int32{ + "client": 0, + "server": 1, + "proxy": 2, +} + +func (x DetectPoint) String() string { + return proto.EnumName(DetectPoint_name, int32(x)) +} + +func (DetectPoint) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{0} +} + +type ServiceType int32 + +const ( + // An agent works inside the normal business application. + ServiceType_normal ServiceType = 0 + // An agent works inside the database. + ServiceType_database ServiceType = 1 + // An agent works inside the MQ. + ServiceType_mq ServiceType = 2 + // An agent works inside the cache server. + ServiceType_cache ServiceType = 3 + // An agent works inside the browser. + ServiceType_browser ServiceType = 4 +) + +var ServiceType_name = map[int32]string{ + 0: "normal", + 1: "database", + 2: "mq", + 3: "cache", + 4: "browser", +} + +var ServiceType_value = map[string]int32{ + "normal": 0, + "database": 1, + "mq": 2, + "cache": 3, + "browser": 4, +} + +func (x ServiceType) String() string { + return proto.EnumName(ServiceType_name, int32(x)) +} + +func (ServiceType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{1} +} + +type KeyStringValuePair struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyStringValuePair) Reset() { *m = KeyStringValuePair{} } +func (m *KeyStringValuePair) String() string { return proto.CompactTextString(m) } +func (*KeyStringValuePair) ProtoMessage() {} +func (*KeyStringValuePair) Descriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{0} +} + +func (m *KeyStringValuePair) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_KeyStringValuePair.Unmarshal(m, b) +} +func (m *KeyStringValuePair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_KeyStringValuePair.Marshal(b, m, deterministic) +} +func (m *KeyStringValuePair) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyStringValuePair.Merge(m, src) +} +func (m *KeyStringValuePair) XXX_Size() int { + return xxx_messageInfo_KeyStringValuePair.Size(m) +} +func (m *KeyStringValuePair) XXX_DiscardUnknown() { + xxx_messageInfo_KeyStringValuePair.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyStringValuePair proto.InternalMessageInfo + +func (m *KeyStringValuePair) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *KeyStringValuePair) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type CPU struct { + UsagePercent float64 `protobuf:"fixed64,2,opt,name=usagePercent,proto3" json:"usagePercent,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPU) Reset() { *m = CPU{} } +func (m *CPU) String() string { return proto.CompactTextString(m) } +func (*CPU) ProtoMessage() {} +func (*CPU) Descriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{1} +} + +func (m *CPU) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CPU.Unmarshal(m, b) +} +func (m *CPU) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CPU.Marshal(b, m, deterministic) +} +func (m *CPU) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPU.Merge(m, src) +} +func (m *CPU) XXX_Size() int { + return xxx_messageInfo_CPU.Size(m) +} +func (m *CPU) XXX_DiscardUnknown() { + xxx_messageInfo_CPU.DiscardUnknown(m) +} + +var xxx_messageInfo_CPU proto.InternalMessageInfo + +func (m *CPU) GetUsagePercent() float64 { + if m != nil { + return m.UsagePercent + } + return 0 +} + +type Commands struct { + Commands []*Command `protobuf:"bytes,1,rep,name=commands,proto3" json:"commands,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Commands) Reset() { *m = Commands{} } +func (m *Commands) String() string { return proto.CompactTextString(m) } +func (*Commands) ProtoMessage() {} +func (*Commands) Descriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{2} +} + +func (m *Commands) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Commands.Unmarshal(m, b) +} +func (m *Commands) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Commands.Marshal(b, m, deterministic) +} +func (m *Commands) XXX_Merge(src proto.Message) { + xxx_messageInfo_Commands.Merge(m, src) +} +func (m *Commands) XXX_Size() int { + return xxx_messageInfo_Commands.Size(m) +} +func (m *Commands) XXX_DiscardUnknown() { + xxx_messageInfo_Commands.DiscardUnknown(m) +} + +var xxx_messageInfo_Commands proto.InternalMessageInfo + +func (m *Commands) GetCommands() []*Command { + if m != nil { + return m.Commands + } + return nil +} + +type Command struct { + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` + Args []*KeyStringValuePair `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Command) Reset() { *m = Command{} } +func (m *Command) String() string { return proto.CompactTextString(m) } +func (*Command) ProtoMessage() {} +func (*Command) Descriptor() ([]byte, []int) { + return fileDescriptor_f11d9de53f8ae4d5, []int{3} +} + +func (m *Command) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Command.Unmarshal(m, b) +} +func (m *Command) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Command.Marshal(b, m, deterministic) +} +func (m *Command) XXX_Merge(src proto.Message) { + xxx_messageInfo_Command.Merge(m, src) +} +func (m *Command) XXX_Size() int { + return xxx_messageInfo_Command.Size(m) +} +func (m *Command) XXX_DiscardUnknown() { + xxx_messageInfo_Command.DiscardUnknown(m) +} + +var xxx_messageInfo_Command proto.InternalMessageInfo + +func (m *Command) GetCommand() string { + if m != nil { + return m.Command + } + return "" +} + +func (m *Command) GetArgs() []*KeyStringValuePair { + if m != nil { + return m.Args + } + return nil +} + +func init() { + proto.RegisterEnum("DetectPoint", DetectPoint_name, DetectPoint_value) + proto.RegisterEnum("ServiceType", ServiceType_name, ServiceType_value) + proto.RegisterType((*KeyStringValuePair)(nil), "KeyStringValuePair") + proto.RegisterType((*CPU)(nil), "CPU") + proto.RegisterType((*Commands)(nil), "Commands") + proto.RegisterType((*Command)(nil), "Command") +} + +func init() { proto.RegisterFile("common/Common.proto", fileDescriptor_f11d9de53f8ae4d5) } + +var fileDescriptor_f11d9de53f8ae4d5 = []byte{ + // 374 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0x4f, 0x6f, 0xd4, 0x30, + 0x10, 0xc5, 0x9b, 0x6c, 0xbb, 0xbb, 0x9d, 0xed, 0xc1, 0x72, 0x39, 0x44, 0x9c, 0xaa, 0x08, 0x89, + 0x52, 0x84, 0x53, 0xb5, 0x57, 0x2e, 0x50, 0x24, 0x0e, 0xfc, 0x51, 0x94, 0xf0, 0x47, 0xe2, 0xe6, + 0xb8, 0xa3, 0x34, 0x4a, 0x62, 0x87, 0xb1, 0x37, 0x8b, 0xc5, 0x37, 0xe2, 0x53, 0x22, 0x27, 0x01, + 0x09, 0x71, 0xf2, 0xcc, 0xef, 0xcd, 0x3c, 0xdb, 0x7a, 0x70, 0xae, 0x4c, 0xdf, 0x1b, 0x9d, 0xdd, + 0x4d, 0x87, 0x18, 0xc8, 0x38, 0x93, 0xbe, 0x04, 0xfe, 0x0e, 0x7d, 0xe9, 0xa8, 0xd1, 0xf5, 0x17, + 0xd9, 0xed, 0x31, 0x97, 0x0d, 0x71, 0x06, 0xab, 0x16, 0x7d, 0x12, 0x5d, 0x44, 0x97, 0xa7, 0x45, + 0x28, 0xf9, 0x23, 0x38, 0x19, 0x83, 0x9c, 0xc4, 0x13, 0x9b, 0x9b, 0xf4, 0x19, 0xac, 0xee, 0xf2, + 0xcf, 0x3c, 0x85, 0xb3, 0xbd, 0x95, 0x35, 0xe6, 0x48, 0x0a, 0xb5, 0x9b, 0x66, 0xa2, 0xe2, 0x1f, + 0x96, 0x5e, 0xc3, 0x36, 0x5c, 0x2c, 0xf5, 0xbd, 0xe5, 0x4f, 0x60, 0xab, 0x96, 0x3a, 0x89, 0x2e, + 0x56, 0x97, 0xbb, 0x9b, 0xad, 0x58, 0xc4, 0xe2, 0xaf, 0x92, 0xbe, 0x87, 0xcd, 0x02, 0x79, 0x02, + 0x9b, 0x05, 0x2f, 0x6f, 0xfa, 0xd3, 0xf2, 0xa7, 0x70, 0x2c, 0xa9, 0xb6, 0x49, 0x3c, 0xd9, 0x9c, + 0x8b, 0xff, 0x3f, 0x53, 0x4c, 0x03, 0x57, 0xd7, 0xb0, 0x7b, 0x83, 0x0e, 0x95, 0xcb, 0x4d, 0xa3, + 0x1d, 0x07, 0x58, 0xab, 0xae, 0x41, 0xed, 0xd8, 0x51, 0xa8, 0x2d, 0xd2, 0x88, 0xc4, 0x22, 0x7e, + 0x0a, 0x27, 0x03, 0x99, 0x1f, 0x9e, 0xc5, 0x57, 0x6f, 0x61, 0x57, 0x22, 0x8d, 0x8d, 0xc2, 0x4f, + 0x7e, 0xc0, 0x30, 0xa5, 0x0d, 0xf5, 0xb2, 0x63, 0x47, 0xfc, 0x0c, 0xb6, 0xf7, 0xd2, 0xc9, 0x4a, + 0x5a, 0x64, 0x11, 0x5f, 0x43, 0xdc, 0x7f, 0x67, 0x71, 0xd8, 0x55, 0x52, 0x3d, 0x20, 0x5b, 0xf1, + 0x1d, 0x6c, 0x2a, 0x32, 0x07, 0x8b, 0xc4, 0x8e, 0x5f, 0xff, 0x84, 0xe7, 0x86, 0x6a, 0x21, 0x87, + 0x20, 0x0a, 0xdb, 0xfa, 0x83, 0xec, 0xda, 0x46, 0x07, 0xd2, 0x0b, 0x8d, 0xee, 0x60, 0xa8, 0x15, + 0x73, 0x38, 0x62, 0xbc, 0xcd, 0xa3, 0x6f, 0x2f, 0xea, 0xc6, 0x3d, 0xec, 0xab, 0xc0, 0xb2, 0xb2, + 0xf5, 0xaf, 0xf2, 0x0f, 0x59, 0x6d, 0x6e, 0x6c, 0xeb, 0x33, 0xc2, 0xc1, 0x90, 0x43, 0xca, 0x6a, + 0x1a, 0x54, 0x36, 0xef, 0xfc, 0x8a, 0x1f, 0x97, 0xad, 0xff, 0xba, 0x78, 0x7e, 0x9c, 0xfd, 0xf2, + 0x90, 0xae, 0x32, 0x5d, 0xb5, 0x9e, 0x72, 0xbe, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xe3, 0xb6, + 0x97, 0x96, 0xfe, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.proto b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.proto new file mode 100644 index 0000000..2d37ea3 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/common/Common.proto @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.skywalking.apm.network.common.v3"; +option csharp_namespace = "SkyWalking.NetworkProtocol"; +option go_package = "github.com/SkyAPM/go2sky/reporter/grpc/common"; + +message KeyStringValuePair { + string key = 1; + string value = 2; +} + +message CPU { + double usagePercent = 2; +} + +// In most cases, detect point should be `server` or `client`. +// Even in service mesh, this means `server`/`client` side sidecar +// `proxy` is reserved only. +enum DetectPoint { + client = 0; + server = 1; + proxy = 2; +} + +message Commands { + repeated Command commands = 1; +} + +message Command { + string command = 1; + repeated KeyStringValuePair args = 2; +} + +enum ServiceType { + // An agent works inside the normal business application. + normal = 0; + // An agent works inside the database. + database = 1; + // An agent works inside the MQ. + mq = 2; + // An agent works inside the cache server. + cache = 3; + // An agent works inside the browser. + browser = 4; +} diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.pb.go b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.pb.go new file mode 100644 index 0000000..563e40a --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.pb.go @@ -0,0 +1,693 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: language-agent/Tracing.proto + +package language_agent + +import ( + context "context" + fmt "fmt" + common "github.com/SkyAPM/go2sky/reporter/grpc/common" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type SpanType int32 + +const ( + SpanType_Entry SpanType = 0 + SpanType_Exit SpanType = 1 + SpanType_Local SpanType = 2 +) + +var SpanType_name = map[int32]string{ + 0: "Entry", + 1: "Exit", + 2: "Local", +} + +var SpanType_value = map[string]int32{ + "Entry": 0, + "Exit": 1, + "Local": 2, +} + +func (x SpanType) String() string { + return proto.EnumName(SpanType_name, int32(x)) +} + +func (SpanType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{0} +} + +type RefType int32 + +const ( + RefType_CrossProcess RefType = 0 + RefType_CrossThread RefType = 1 +) + +var RefType_name = map[int32]string{ + 0: "CrossProcess", + 1: "CrossThread", +} + +var RefType_value = map[string]int32{ + "CrossProcess": 0, + "CrossThread": 1, +} + +func (x RefType) String() string { + return proto.EnumName(RefType_name, int32(x)) +} + +func (RefType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{1} +} + +type SpanLayer int32 + +const ( + SpanLayer_Unknown SpanLayer = 0 + SpanLayer_Database SpanLayer = 1 + SpanLayer_RPCFramework SpanLayer = 2 + SpanLayer_Http SpanLayer = 3 + SpanLayer_MQ SpanLayer = 4 + SpanLayer_Cache SpanLayer = 5 +) + +var SpanLayer_name = map[int32]string{ + 0: "Unknown", + 1: "Database", + 2: "RPCFramework", + 3: "Http", + 4: "MQ", + 5: "Cache", +} + +var SpanLayer_value = map[string]int32{ + "Unknown": 0, + "Database": 1, + "RPCFramework": 2, + "Http": 3, + "MQ": 4, + "Cache": 5, +} + +func (x SpanLayer) String() string { + return proto.EnumName(SpanLayer_name, int32(x)) +} + +func (SpanLayer) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{2} +} + +type SegmentObject struct { + TraceId string `protobuf:"bytes,1,opt,name=traceId,proto3" json:"traceId,omitempty"` + TraceSegmentId string `protobuf:"bytes,2,opt,name=traceSegmentId,proto3" json:"traceSegmentId,omitempty"` + Spans []*SpanObject `protobuf:"bytes,3,rep,name=spans,proto3" json:"spans,omitempty"` + Service string `protobuf:"bytes,4,opt,name=service,proto3" json:"service,omitempty"` + ServiceInstance string `protobuf:"bytes,5,opt,name=serviceInstance,proto3" json:"serviceInstance,omitempty"` + IsSizeLimited bool `protobuf:"varint,6,opt,name=isSizeLimited,proto3" json:"isSizeLimited,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SegmentObject) Reset() { *m = SegmentObject{} } +func (m *SegmentObject) String() string { return proto.CompactTextString(m) } +func (*SegmentObject) ProtoMessage() {} +func (*SegmentObject) Descriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{0} +} + +func (m *SegmentObject) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SegmentObject.Unmarshal(m, b) +} +func (m *SegmentObject) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SegmentObject.Marshal(b, m, deterministic) +} +func (m *SegmentObject) XXX_Merge(src proto.Message) { + xxx_messageInfo_SegmentObject.Merge(m, src) +} +func (m *SegmentObject) XXX_Size() int { + return xxx_messageInfo_SegmentObject.Size(m) +} +func (m *SegmentObject) XXX_DiscardUnknown() { + xxx_messageInfo_SegmentObject.DiscardUnknown(m) +} + +var xxx_messageInfo_SegmentObject proto.InternalMessageInfo + +func (m *SegmentObject) GetTraceId() string { + if m != nil { + return m.TraceId + } + return "" +} + +func (m *SegmentObject) GetTraceSegmentId() string { + if m != nil { + return m.TraceSegmentId + } + return "" +} + +func (m *SegmentObject) GetSpans() []*SpanObject { + if m != nil { + return m.Spans + } + return nil +} + +func (m *SegmentObject) GetService() string { + if m != nil { + return m.Service + } + return "" +} + +func (m *SegmentObject) GetServiceInstance() string { + if m != nil { + return m.ServiceInstance + } + return "" +} + +func (m *SegmentObject) GetIsSizeLimited() bool { + if m != nil { + return m.IsSizeLimited + } + return false +} + +type SegmentReference struct { + RefType RefType `protobuf:"varint,1,opt,name=refType,proto3,enum=RefType" json:"refType,omitempty"` + TraceId string `protobuf:"bytes,2,opt,name=traceId,proto3" json:"traceId,omitempty"` + ParentTraceSegmentId string `protobuf:"bytes,3,opt,name=parentTraceSegmentId,proto3" json:"parentTraceSegmentId,omitempty"` + ParentSpanId int32 `protobuf:"varint,4,opt,name=parentSpanId,proto3" json:"parentSpanId,omitempty"` + ParentService string `protobuf:"bytes,5,opt,name=parentService,proto3" json:"parentService,omitempty"` + ParentServiceInstance string `protobuf:"bytes,6,opt,name=parentServiceInstance,proto3" json:"parentServiceInstance,omitempty"` + ParentEndpoint string `protobuf:"bytes,7,opt,name=parentEndpoint,proto3" json:"parentEndpoint,omitempty"` + NetworkAddressUsedAtPeer string `protobuf:"bytes,8,opt,name=networkAddressUsedAtPeer,proto3" json:"networkAddressUsedAtPeer,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SegmentReference) Reset() { *m = SegmentReference{} } +func (m *SegmentReference) String() string { return proto.CompactTextString(m) } +func (*SegmentReference) ProtoMessage() {} +func (*SegmentReference) Descriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{1} +} + +func (m *SegmentReference) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SegmentReference.Unmarshal(m, b) +} +func (m *SegmentReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SegmentReference.Marshal(b, m, deterministic) +} +func (m *SegmentReference) XXX_Merge(src proto.Message) { + xxx_messageInfo_SegmentReference.Merge(m, src) +} +func (m *SegmentReference) XXX_Size() int { + return xxx_messageInfo_SegmentReference.Size(m) +} +func (m *SegmentReference) XXX_DiscardUnknown() { + xxx_messageInfo_SegmentReference.DiscardUnknown(m) +} + +var xxx_messageInfo_SegmentReference proto.InternalMessageInfo + +func (m *SegmentReference) GetRefType() RefType { + if m != nil { + return m.RefType + } + return RefType_CrossProcess +} + +func (m *SegmentReference) GetTraceId() string { + if m != nil { + return m.TraceId + } + return "" +} + +func (m *SegmentReference) GetParentTraceSegmentId() string { + if m != nil { + return m.ParentTraceSegmentId + } + return "" +} + +func (m *SegmentReference) GetParentSpanId() int32 { + if m != nil { + return m.ParentSpanId + } + return 0 +} + +func (m *SegmentReference) GetParentService() string { + if m != nil { + return m.ParentService + } + return "" +} + +func (m *SegmentReference) GetParentServiceInstance() string { + if m != nil { + return m.ParentServiceInstance + } + return "" +} + +func (m *SegmentReference) GetParentEndpoint() string { + if m != nil { + return m.ParentEndpoint + } + return "" +} + +func (m *SegmentReference) GetNetworkAddressUsedAtPeer() string { + if m != nil { + return m.NetworkAddressUsedAtPeer + } + return "" +} + +type SpanObject struct { + SpanId int32 `protobuf:"varint,1,opt,name=spanId,proto3" json:"spanId,omitempty"` + ParentSpanId int32 `protobuf:"varint,2,opt,name=parentSpanId,proto3" json:"parentSpanId,omitempty"` + StartTime int64 `protobuf:"varint,3,opt,name=startTime,proto3" json:"startTime,omitempty"` + EndTime int64 `protobuf:"varint,4,opt,name=endTime,proto3" json:"endTime,omitempty"` + Refs []*SegmentReference `protobuf:"bytes,5,rep,name=refs,proto3" json:"refs,omitempty"` + OperationName string `protobuf:"bytes,6,opt,name=operationName,proto3" json:"operationName,omitempty"` + Peer string `protobuf:"bytes,7,opt,name=peer,proto3" json:"peer,omitempty"` + SpanType SpanType `protobuf:"varint,8,opt,name=spanType,proto3,enum=SpanType" json:"spanType,omitempty"` + SpanLayer SpanLayer `protobuf:"varint,9,opt,name=spanLayer,proto3,enum=SpanLayer" json:"spanLayer,omitempty"` + ComponentId int32 `protobuf:"varint,10,opt,name=componentId,proto3" json:"componentId,omitempty"` + IsError bool `protobuf:"varint,11,opt,name=isError,proto3" json:"isError,omitempty"` + Tags []*common.KeyStringValuePair `protobuf:"bytes,12,rep,name=tags,proto3" json:"tags,omitempty"` + Logs []*Log `protobuf:"bytes,13,rep,name=logs,proto3" json:"logs,omitempty"` + SkipAnalysis bool `protobuf:"varint,14,opt,name=skipAnalysis,proto3" json:"skipAnalysis,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SpanObject) Reset() { *m = SpanObject{} } +func (m *SpanObject) String() string { return proto.CompactTextString(m) } +func (*SpanObject) ProtoMessage() {} +func (*SpanObject) Descriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{2} +} + +func (m *SpanObject) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SpanObject.Unmarshal(m, b) +} +func (m *SpanObject) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SpanObject.Marshal(b, m, deterministic) +} +func (m *SpanObject) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpanObject.Merge(m, src) +} +func (m *SpanObject) XXX_Size() int { + return xxx_messageInfo_SpanObject.Size(m) +} +func (m *SpanObject) XXX_DiscardUnknown() { + xxx_messageInfo_SpanObject.DiscardUnknown(m) +} + +var xxx_messageInfo_SpanObject proto.InternalMessageInfo + +func (m *SpanObject) GetSpanId() int32 { + if m != nil { + return m.SpanId + } + return 0 +} + +func (m *SpanObject) GetParentSpanId() int32 { + if m != nil { + return m.ParentSpanId + } + return 0 +} + +func (m *SpanObject) GetStartTime() int64 { + if m != nil { + return m.StartTime + } + return 0 +} + +func (m *SpanObject) GetEndTime() int64 { + if m != nil { + return m.EndTime + } + return 0 +} + +func (m *SpanObject) GetRefs() []*SegmentReference { + if m != nil { + return m.Refs + } + return nil +} + +func (m *SpanObject) GetOperationName() string { + if m != nil { + return m.OperationName + } + return "" +} + +func (m *SpanObject) GetPeer() string { + if m != nil { + return m.Peer + } + return "" +} + +func (m *SpanObject) GetSpanType() SpanType { + if m != nil { + return m.SpanType + } + return SpanType_Entry +} + +func (m *SpanObject) GetSpanLayer() SpanLayer { + if m != nil { + return m.SpanLayer + } + return SpanLayer_Unknown +} + +func (m *SpanObject) GetComponentId() int32 { + if m != nil { + return m.ComponentId + } + return 0 +} + +func (m *SpanObject) GetIsError() bool { + if m != nil { + return m.IsError + } + return false +} + +func (m *SpanObject) GetTags() []*common.KeyStringValuePair { + if m != nil { + return m.Tags + } + return nil +} + +func (m *SpanObject) GetLogs() []*Log { + if m != nil { + return m.Logs + } + return nil +} + +func (m *SpanObject) GetSkipAnalysis() bool { + if m != nil { + return m.SkipAnalysis + } + return false +} + +type Log struct { + Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"` + Data []*common.KeyStringValuePair `protobuf:"bytes,2,rep,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Log) Reset() { *m = Log{} } +func (m *Log) String() string { return proto.CompactTextString(m) } +func (*Log) ProtoMessage() {} +func (*Log) Descriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{3} +} + +func (m *Log) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Log.Unmarshal(m, b) +} +func (m *Log) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Log.Marshal(b, m, deterministic) +} +func (m *Log) XXX_Merge(src proto.Message) { + xxx_messageInfo_Log.Merge(m, src) +} +func (m *Log) XXX_Size() int { + return xxx_messageInfo_Log.Size(m) +} +func (m *Log) XXX_DiscardUnknown() { + xxx_messageInfo_Log.DiscardUnknown(m) +} + +var xxx_messageInfo_Log proto.InternalMessageInfo + +func (m *Log) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *Log) GetData() []*common.KeyStringValuePair { + if m != nil { + return m.Data + } + return nil +} + +type ID struct { + Id []string `protobuf:"bytes,1,rep,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ID) Reset() { *m = ID{} } +func (m *ID) String() string { return proto.CompactTextString(m) } +func (*ID) ProtoMessage() {} +func (*ID) Descriptor() ([]byte, []int) { + return fileDescriptor_e1989527c3daf331, []int{4} +} + +func (m *ID) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ID.Unmarshal(m, b) +} +func (m *ID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ID.Marshal(b, m, deterministic) +} +func (m *ID) XXX_Merge(src proto.Message) { + xxx_messageInfo_ID.Merge(m, src) +} +func (m *ID) XXX_Size() int { + return xxx_messageInfo_ID.Size(m) +} +func (m *ID) XXX_DiscardUnknown() { + xxx_messageInfo_ID.DiscardUnknown(m) +} + +var xxx_messageInfo_ID proto.InternalMessageInfo + +func (m *ID) GetId() []string { + if m != nil { + return m.Id + } + return nil +} + +func init() { + proto.RegisterEnum("SpanType", SpanType_name, SpanType_value) + proto.RegisterEnum("RefType", RefType_name, RefType_value) + proto.RegisterEnum("SpanLayer", SpanLayer_name, SpanLayer_value) + proto.RegisterType((*SegmentObject)(nil), "SegmentObject") + proto.RegisterType((*SegmentReference)(nil), "SegmentReference") + proto.RegisterType((*SpanObject)(nil), "SpanObject") + proto.RegisterType((*Log)(nil), "Log") + proto.RegisterType((*ID)(nil), "ID") +} + +func init() { proto.RegisterFile("language-agent/Tracing.proto", fileDescriptor_e1989527c3daf331) } + +var fileDescriptor_e1989527c3daf331 = []byte{ + // 845 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0xdd, 0x72, 0xe3, 0x34, + 0x14, 0xae, 0x9d, 0xff, 0x93, 0x36, 0x6b, 0xb4, 0x0b, 0x63, 0x3a, 0x7b, 0x51, 0x32, 0x2c, 0x64, + 0x3a, 0xe0, 0xcc, 0xa4, 0x70, 0xc3, 0x5d, 0xb7, 0x1b, 0x86, 0x0c, 0xd9, 0x25, 0x38, 0x29, 0xcc, + 0x70, 0xa7, 0xda, 0xa7, 0xae, 0x48, 0x2c, 0x79, 0x24, 0xb5, 0x25, 0xbc, 0x02, 0x6f, 0xc2, 0xfb, + 0x70, 0xcb, 0x35, 0x8f, 0xc1, 0xe8, 0xd8, 0x69, 0xeb, 0x52, 0xf6, 0xca, 0x3a, 0xdf, 0x77, 0x2c, + 0xe9, 0x7c, 0xe7, 0x93, 0x04, 0x2f, 0x37, 0x5c, 0x66, 0xd7, 0x3c, 0xc3, 0x2f, 0x79, 0x86, 0xd2, + 0x8e, 0x57, 0x9a, 0x27, 0x42, 0x66, 0x51, 0xa1, 0x95, 0x55, 0x87, 0xcf, 0x13, 0x95, 0xe7, 0x4a, + 0x8e, 0xcf, 0xe8, 0x53, 0x82, 0xc3, 0xbf, 0x3d, 0x38, 0x58, 0x62, 0x96, 0xa3, 0xb4, 0x3f, 0x5c, + 0xfc, 0x8a, 0x89, 0x65, 0x21, 0x74, 0xac, 0xe6, 0x09, 0xce, 0xd2, 0xd0, 0x3b, 0xf2, 0x46, 0xbd, + 0x78, 0x17, 0xb2, 0xcf, 0x60, 0x40, 0xc3, 0x2a, 0x7f, 0x96, 0x86, 0x3e, 0x25, 0x3c, 0x42, 0xd9, + 0x27, 0xd0, 0x32, 0x05, 0x97, 0x26, 0x6c, 0x1c, 0x35, 0x46, 0xfd, 0x49, 0x3f, 0x5a, 0x16, 0x5c, + 0x96, 0xb3, 0xc7, 0x25, 0xe3, 0x16, 0x31, 0xa8, 0x6f, 0x44, 0x82, 0x61, 0xb3, 0x5c, 0xa4, 0x0a, + 0xd9, 0x08, 0x9e, 0x55, 0xc3, 0x99, 0x34, 0x96, 0xcb, 0x04, 0xc3, 0x16, 0x65, 0x3c, 0x86, 0xd9, + 0xa7, 0x70, 0x20, 0xcc, 0x52, 0xfc, 0x8e, 0x73, 0x91, 0x0b, 0x8b, 0x69, 0xd8, 0x3e, 0xf2, 0x46, + 0xdd, 0xb8, 0x0e, 0x0e, 0xff, 0xf1, 0x21, 0xa8, 0xb6, 0x16, 0xe3, 0x25, 0x6a, 0x74, 0xbf, 0x0e, + 0xa1, 0xa3, 0xf1, 0x72, 0xb5, 0x2d, 0x90, 0x6a, 0x1c, 0x4c, 0xba, 0x51, 0x5c, 0xc6, 0xf1, 0x8e, + 0x78, 0xa8, 0x83, 0x5f, 0xd7, 0x61, 0x02, 0x2f, 0x0a, 0xae, 0x51, 0xda, 0x55, 0x5d, 0x8d, 0x06, + 0xa5, 0x3d, 0xc9, 0xb1, 0x21, 0xec, 0x97, 0xb8, 0xd3, 0x62, 0x96, 0x52, 0xd5, 0xad, 0xb8, 0x86, + 0xb9, 0x82, 0xaa, 0xb8, 0x92, 0xa6, 0x2c, 0xbc, 0x0e, 0xb2, 0xaf, 0xe0, 0xc3, 0x1a, 0x70, 0x27, + 0x53, 0x9b, 0xb2, 0x9f, 0x26, 0x5d, 0xef, 0x4a, 0x62, 0x2a, 0xd3, 0x42, 0x09, 0x69, 0xc3, 0x4e, + 0xd9, 0xbb, 0x3a, 0xca, 0xbe, 0x81, 0x50, 0xa2, 0xbd, 0x55, 0x7a, 0x7d, 0x9a, 0xa6, 0x1a, 0x8d, + 0x39, 0x37, 0x98, 0x9e, 0xda, 0x05, 0xa2, 0x0e, 0xbb, 0xf4, 0xc7, 0xff, 0xf2, 0xc3, 0xbf, 0x1a, + 0x00, 0xf7, 0xad, 0x66, 0x1f, 0x41, 0xdb, 0x94, 0xc5, 0x7a, 0x54, 0x6c, 0x15, 0xfd, 0x47, 0x0a, + 0xff, 0x09, 0x29, 0x5e, 0x42, 0xcf, 0x58, 0xae, 0xed, 0x4a, 0xe4, 0x48, 0xba, 0x36, 0xe2, 0x7b, + 0xc0, 0xb5, 0x06, 0x65, 0x4a, 0x5c, 0x93, 0xb8, 0x5d, 0xc8, 0x5e, 0x41, 0x53, 0xe3, 0xa5, 0x09, + 0x5b, 0xe4, 0xbc, 0x0f, 0xa2, 0xc7, 0x9d, 0x8f, 0x89, 0x76, 0x4a, 0xab, 0x02, 0x35, 0xb7, 0x42, + 0xc9, 0x77, 0x3c, 0xdf, 0x69, 0x57, 0x07, 0x19, 0x83, 0x66, 0xe1, 0xea, 0x2e, 0x95, 0xa2, 0x31, + 0x7b, 0x05, 0x5d, 0x57, 0x06, 0x59, 0xa7, 0x4b, 0xd6, 0xe9, 0x91, 0xbd, 0xc9, 0x3b, 0x77, 0x14, + 0x1b, 0x41, 0xcf, 0x8d, 0xe7, 0x7c, 0x8b, 0x3a, 0xec, 0x51, 0x1e, 0x50, 0x1e, 0x21, 0xf1, 0x3d, + 0xc9, 0x8e, 0xa0, 0x9f, 0xa8, 0xbc, 0x50, 0xb2, 0xf4, 0x10, 0x90, 0x18, 0x0f, 0x21, 0x57, 0xad, + 0x30, 0x53, 0xad, 0x95, 0x0e, 0xfb, 0xe4, 0xf0, 0x5d, 0xc8, 0x3e, 0x87, 0xa6, 0xe5, 0x99, 0x09, + 0xf7, 0xa9, 0xda, 0xe7, 0xd1, 0xf7, 0xb8, 0x5d, 0x5a, 0x2d, 0x64, 0xf6, 0x13, 0xdf, 0x5c, 0xe3, + 0x82, 0x0b, 0x1d, 0x53, 0x02, 0x0b, 0xa1, 0xb9, 0x51, 0x99, 0x09, 0x0f, 0x28, 0xb1, 0x19, 0xcd, + 0x55, 0x16, 0x13, 0xe2, 0x9a, 0x61, 0xd6, 0xa2, 0x38, 0x95, 0x7c, 0xb3, 0x35, 0xc2, 0x84, 0x03, + 0x5a, 0xa1, 0x86, 0x0d, 0x5f, 0x43, 0x63, 0xae, 0x32, 0x27, 0x87, 0x75, 0x92, 0x7b, 0x24, 0x39, + 0x8d, 0xdd, 0x0e, 0x52, 0x6e, 0x79, 0xe8, 0xbf, 0x67, 0x07, 0x2e, 0x61, 0xf8, 0x02, 0xfc, 0xd9, + 0x1b, 0x36, 0x00, 0x5f, 0x38, 0x3b, 0x34, 0x46, 0xbd, 0xd8, 0x17, 0xe9, 0xf1, 0x31, 0x74, 0x77, + 0xe2, 0xb1, 0x1e, 0xb4, 0xa6, 0xd2, 0xea, 0x6d, 0xb0, 0xc7, 0xba, 0xd0, 0x9c, 0xfe, 0x26, 0x6c, + 0xe0, 0x39, 0x70, 0xae, 0x12, 0xbe, 0x09, 0xfc, 0xe3, 0x2f, 0xa0, 0x53, 0x9d, 0x51, 0x16, 0xc0, + 0xfe, 0x99, 0x56, 0xc6, 0x2c, 0xb4, 0x4a, 0xd0, 0x98, 0x60, 0x8f, 0x3d, 0x83, 0x3e, 0x21, 0xab, + 0x2b, 0x8d, 0x3c, 0x0d, 0xbc, 0xe3, 0x73, 0xe8, 0xdd, 0xc9, 0xcd, 0xfa, 0xd0, 0x39, 0x97, 0x6b, + 0xa9, 0x6e, 0x65, 0xb0, 0xc7, 0xf6, 0xa1, 0xfb, 0x86, 0x5b, 0x7e, 0xc1, 0x0d, 0x06, 0x9e, 0x9b, + 0x2a, 0x5e, 0x9c, 0x7d, 0xab, 0x79, 0x8e, 0xce, 0xd4, 0x81, 0xef, 0x16, 0xff, 0xce, 0xda, 0x22, + 0x68, 0xb0, 0x36, 0xf8, 0x6f, 0x7f, 0x0c, 0x9a, 0x6e, 0x13, 0x67, 0x3c, 0xb9, 0xc2, 0xa0, 0x35, + 0x99, 0xc2, 0xc7, 0x0f, 0x0f, 0x76, 0x8c, 0x85, 0xd2, 0x77, 0x27, 0x73, 0x04, 0x9d, 0x44, 0x6d, + 0x36, 0xce, 0xfb, 0x83, 0xa8, 0x76, 0xa9, 0x1e, 0xf6, 0x22, 0x77, 0xeb, 0x72, 0x99, 0x9a, 0xe1, + 0xde, 0xc8, 0x7b, 0xfd, 0x87, 0x07, 0x27, 0x4a, 0x67, 0x11, 0x2f, 0xdc, 0xbc, 0x91, 0x59, 0x6f, + 0x6f, 0xf9, 0x66, 0xed, 0xae, 0x6a, 0x5e, 0xe4, 0x51, 0x75, 0xc0, 0xa2, 0xdd, 0x6d, 0x1e, 0xd1, + 0x6d, 0x1e, 0xdd, 0x9c, 0x2c, 0xbc, 0x5f, 0xbe, 0xce, 0x84, 0xbd, 0xba, 0xbe, 0x88, 0x12, 0x95, + 0x8f, 0x97, 0xeb, 0xed, 0xe9, 0xe2, 0xed, 0x38, 0x53, 0x13, 0xb3, 0xde, 0x8e, 0x35, 0xed, 0x05, + 0xf5, 0x38, 0xd3, 0x45, 0x32, 0xae, 0xbf, 0x04, 0x7f, 0xfa, 0x87, 0xcb, 0xf5, 0xf6, 0xe7, 0x6a, + 0x8d, 0x77, 0xe5, 0xfc, 0x0b, 0xf7, 0x00, 0x24, 0x6a, 0x73, 0xd1, 0xa6, 0xa7, 0xe0, 0xe4, 0xdf, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x1e, 0xaa, 0xc8, 0x3f, 0x06, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// TraceSegmentReportServiceClient is the client API for TraceSegmentReportService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type TraceSegmentReportServiceClient interface { + Collect(ctx context.Context, opts ...grpc.CallOption) (TraceSegmentReportService_CollectClient, error) +} + +type traceSegmentReportServiceClient struct { + cc *grpc.ClientConn +} + +func NewTraceSegmentReportServiceClient(cc *grpc.ClientConn) TraceSegmentReportServiceClient { + return &traceSegmentReportServiceClient{cc} +} + +func (c *traceSegmentReportServiceClient) Collect(ctx context.Context, opts ...grpc.CallOption) (TraceSegmentReportService_CollectClient, error) { + stream, err := c.cc.NewStream(ctx, &_TraceSegmentReportService_serviceDesc.Streams[0], "/TraceSegmentReportService/collect", opts...) + if err != nil { + return nil, err + } + x := &traceSegmentReportServiceCollectClient{stream} + return x, nil +} + +type TraceSegmentReportService_CollectClient interface { + Send(*SegmentObject) error + CloseAndRecv() (*common.Commands, error) + grpc.ClientStream +} + +type traceSegmentReportServiceCollectClient struct { + grpc.ClientStream +} + +func (x *traceSegmentReportServiceCollectClient) Send(m *SegmentObject) error { + return x.ClientStream.SendMsg(m) +} + +func (x *traceSegmentReportServiceCollectClient) CloseAndRecv() (*common.Commands, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(common.Commands) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// TraceSegmentReportServiceServer is the server API for TraceSegmentReportService service. +type TraceSegmentReportServiceServer interface { + Collect(TraceSegmentReportService_CollectServer) error +} + +func RegisterTraceSegmentReportServiceServer(s *grpc.Server, srv TraceSegmentReportServiceServer) { + s.RegisterService(&_TraceSegmentReportService_serviceDesc, srv) +} + +func _TraceSegmentReportService_Collect_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(TraceSegmentReportServiceServer).Collect(&traceSegmentReportServiceCollectServer{stream}) +} + +type TraceSegmentReportService_CollectServer interface { + SendAndClose(*common.Commands) error + Recv() (*SegmentObject, error) + grpc.ServerStream +} + +type traceSegmentReportServiceCollectServer struct { + grpc.ServerStream +} + +func (x *traceSegmentReportServiceCollectServer) SendAndClose(m *common.Commands) error { + return x.ServerStream.SendMsg(m) +} + +func (x *traceSegmentReportServiceCollectServer) Recv() (*SegmentObject, error) { + m := new(SegmentObject) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _TraceSegmentReportService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "TraceSegmentReportService", + HandlerType: (*TraceSegmentReportServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "collect", + Handler: _TraceSegmentReportService_Collect_Handler, + ClientStreams: true, + }, + }, + Metadata: "language-agent/Tracing.proto", +} diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.proto b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.proto new file mode 100644 index 0000000..e5c7b64 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/language-agent/Tracing.proto @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.skywalking.apm.network.language.agent.v3"; +option csharp_namespace = "SkyWalking.NetworkProtocol"; +option go_package = "github.com/SkyAPM/go2sky/reporter/grpc/language-agent"; + +import "common/Common.proto"; + +service TraceSegmentReportService { + rpc collect (stream SegmentObject) returns (Commands) { + } +} + +message SegmentObject { + string traceId = 1; + string traceSegmentId = 2; + repeated SpanObject spans = 3; + string service = 4; + string serviceInstance = 5; + bool isSizeLimited = 6; +} + +message SegmentReference { + RefType refType = 1; + string traceId = 2; + string parentTraceSegmentId = 3; + int32 parentSpanId = 4; + string parentService = 5; + string parentServiceInstance = 6; + string parentEndpoint = 7; + string networkAddressUsedAtPeer = 8; +} + +message SpanObject { + int32 spanId = 1; + int32 parentSpanId = 2; + int64 startTime = 3; + int64 endTime = 4; + repeated SegmentReference refs = 5; + string operationName = 6; + string peer = 7; + SpanType spanType = 8; + SpanLayer spanLayer = 9; + int32 componentId = 10; + bool isError = 11; + repeated KeyStringValuePair tags = 12; + repeated Log logs = 13; + bool skipAnalysis = 14; +} + +message Log { + int64 time = 1; + repeated KeyStringValuePair data = 2; +} + +enum SpanType { + Entry = 0; + Exit = 1; + Local = 2; +} + +message ID { + repeated string id = 1; +} + +enum RefType { + CrossProcess = 0; + CrossThread = 1; +} + +enum SpanLayer { + Unknown = 0; + Database = 1; + RPCFramework = 2; + Http = 3; + MQ = 4; + Cache = 5; +} \ No newline at end of file diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.pb.go b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.pb.go new file mode 100644 index 0000000..d78f4e7 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.pb.go @@ -0,0 +1,262 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: management/Management.proto + +package management + +import ( + context "context" + fmt "fmt" + common "github.com/SkyAPM/go2sky/reporter/grpc/common" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type InstanceProperties struct { + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + ServiceInstance string `protobuf:"bytes,2,opt,name=serviceInstance,proto3" json:"serviceInstance,omitempty"` + Properties []*common.KeyStringValuePair `protobuf:"bytes,3,rep,name=properties,proto3" json:"properties,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InstanceProperties) Reset() { *m = InstanceProperties{} } +func (m *InstanceProperties) String() string { return proto.CompactTextString(m) } +func (*InstanceProperties) ProtoMessage() {} +func (*InstanceProperties) Descriptor() ([]byte, []int) { + return fileDescriptor_1d0d74f2a76a8a09, []int{0} +} + +func (m *InstanceProperties) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InstanceProperties.Unmarshal(m, b) +} +func (m *InstanceProperties) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InstanceProperties.Marshal(b, m, deterministic) +} +func (m *InstanceProperties) XXX_Merge(src proto.Message) { + xxx_messageInfo_InstanceProperties.Merge(m, src) +} +func (m *InstanceProperties) XXX_Size() int { + return xxx_messageInfo_InstanceProperties.Size(m) +} +func (m *InstanceProperties) XXX_DiscardUnknown() { + xxx_messageInfo_InstanceProperties.DiscardUnknown(m) +} + +var xxx_messageInfo_InstanceProperties proto.InternalMessageInfo + +func (m *InstanceProperties) GetService() string { + if m != nil { + return m.Service + } + return "" +} + +func (m *InstanceProperties) GetServiceInstance() string { + if m != nil { + return m.ServiceInstance + } + return "" +} + +func (m *InstanceProperties) GetProperties() []*common.KeyStringValuePair { + if m != nil { + return m.Properties + } + return nil +} + +type InstancePingPkg struct { + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + ServiceInstance string `protobuf:"bytes,2,opt,name=serviceInstance,proto3" json:"serviceInstance,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InstancePingPkg) Reset() { *m = InstancePingPkg{} } +func (m *InstancePingPkg) String() string { return proto.CompactTextString(m) } +func (*InstancePingPkg) ProtoMessage() {} +func (*InstancePingPkg) Descriptor() ([]byte, []int) { + return fileDescriptor_1d0d74f2a76a8a09, []int{1} +} + +func (m *InstancePingPkg) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InstancePingPkg.Unmarshal(m, b) +} +func (m *InstancePingPkg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InstancePingPkg.Marshal(b, m, deterministic) +} +func (m *InstancePingPkg) XXX_Merge(src proto.Message) { + xxx_messageInfo_InstancePingPkg.Merge(m, src) +} +func (m *InstancePingPkg) XXX_Size() int { + return xxx_messageInfo_InstancePingPkg.Size(m) +} +func (m *InstancePingPkg) XXX_DiscardUnknown() { + xxx_messageInfo_InstancePingPkg.DiscardUnknown(m) +} + +var xxx_messageInfo_InstancePingPkg proto.InternalMessageInfo + +func (m *InstancePingPkg) GetService() string { + if m != nil { + return m.Service + } + return "" +} + +func (m *InstancePingPkg) GetServiceInstance() string { + if m != nil { + return m.ServiceInstance + } + return "" +} + +func init() { + proto.RegisterType((*InstanceProperties)(nil), "InstanceProperties") + proto.RegisterType((*InstancePingPkg)(nil), "InstancePingPkg") +} + +func init() { proto.RegisterFile("management/Management.proto", fileDescriptor_1d0d74f2a76a8a09) } + +var fileDescriptor_1d0d74f2a76a8a09 = []byte{ + // 313 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x51, 0x3d, 0x4b, 0x03, 0x41, + 0x14, 0xf4, 0x12, 0x50, 0xb2, 0x16, 0xd1, 0x4b, 0x73, 0xc4, 0x26, 0xa4, 0x0a, 0x16, 0xbb, 0x98, + 0xb4, 0x36, 0xd1, 0x4a, 0x24, 0x72, 0xe4, 0x50, 0xc1, 0x6e, 0xb3, 0x3e, 0x36, 0xcb, 0xde, 0x7e, + 0xf0, 0x76, 0x93, 0x70, 0x85, 0x95, 0xb5, 0x7f, 0xc4, 0x5f, 0x29, 0xf9, 0xd6, 0x68, 0x69, 0xb5, + 0x6f, 0x87, 0x99, 0x61, 0x98, 0x21, 0x17, 0x86, 0x5b, 0x2e, 0xc1, 0x80, 0x8d, 0x6c, 0xb4, 0x3b, + 0xa9, 0x47, 0x17, 0x5d, 0xbb, 0x25, 0x9c, 0x31, 0xce, 0xb2, 0xdb, 0xd5, 0xb3, 0x06, 0xbb, 0x1f, + 0x09, 0x49, 0xef, 0x6c, 0x88, 0xdc, 0x0a, 0xc8, 0xd1, 0x79, 0xc0, 0xa8, 0x20, 0xa4, 0x19, 0x39, + 0x09, 0x80, 0x73, 0x25, 0x20, 0x4b, 0x3a, 0x49, 0xaf, 0x31, 0xde, 0x7e, 0xd3, 0x1e, 0x69, 0x6e, + 0xce, 0xad, 0x2c, 0xab, 0xad, 0x18, 0x87, 0x70, 0x3a, 0x20, 0xc4, 0xef, 0x1c, 0xb3, 0x7a, 0xa7, + 0xde, 0x3b, 0xed, 0xb7, 0xe8, 0x3d, 0x54, 0x45, 0x44, 0x65, 0xe5, 0x13, 0x2f, 0x67, 0x90, 0x73, + 0x85, 0xe3, 0x6f, 0xb4, 0xee, 0x23, 0x69, 0xee, 0xe2, 0x28, 0x2b, 0x73, 0x2d, 0xff, 0x23, 0x4b, + 0xff, 0x8d, 0x9c, 0xef, 0xfb, 0x28, 0x36, 0xf2, 0x6b, 0x92, 0x21, 0x78, 0x87, 0xf1, 0x8f, 0x02, + 0x5a, 0xf4, 0x37, 0xd8, 0x6e, 0xd0, 0x65, 0x77, 0xdc, 0xbe, 0x86, 0xee, 0x51, 0x7a, 0x49, 0x1a, + 0x1a, 0xc0, 0x0f, 0x4b, 0x35, 0x87, 0xf4, 0x8c, 0x1e, 0xa4, 0xfe, 0xc1, 0xbd, 0x79, 0x4f, 0x08, + 0x73, 0x28, 0x29, 0xf7, 0x5c, 0x4c, 0x81, 0x06, 0x5d, 0x2d, 0x78, 0xa9, 0x95, 0x5d, 0x22, 0x86, + 0x5a, 0x88, 0x0b, 0x87, 0x9a, 0xee, 0xc7, 0xa3, 0xf3, 0x41, 0x9e, 0xbc, 0x5c, 0x49, 0x15, 0xa7, + 0xb3, 0x09, 0x15, 0xce, 0xb0, 0x42, 0x57, 0xc3, 0x7c, 0xc4, 0xa4, 0xeb, 0x07, 0x5d, 0xb1, 0x75, + 0x6c, 0x40, 0x26, 0xd1, 0x0b, 0xb6, 0xd7, 0x7d, 0xd6, 0xda, 0x85, 0xae, 0x9e, 0x37, 0xde, 0x0f, + 0x6b, 0xdf, 0x7c, 0x39, 0xb4, 0x70, 0xe5, 0xe4, 0x78, 0x35, 0xf9, 0xe0, 0x2b, 0x00, 0x00, 0xff, + 0xff, 0xbb, 0x3d, 0xc3, 0x47, 0x26, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ManagementServiceClient is the client API for ManagementService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ManagementServiceClient interface { + ReportInstanceProperties(ctx context.Context, in *InstanceProperties, opts ...grpc.CallOption) (*common.Commands, error) + KeepAlive(ctx context.Context, in *InstancePingPkg, opts ...grpc.CallOption) (*common.Commands, error) +} + +type managementServiceClient struct { + cc *grpc.ClientConn +} + +func NewManagementServiceClient(cc *grpc.ClientConn) ManagementServiceClient { + return &managementServiceClient{cc} +} + +func (c *managementServiceClient) ReportInstanceProperties(ctx context.Context, in *InstanceProperties, opts ...grpc.CallOption) (*common.Commands, error) { + out := new(common.Commands) + err := c.cc.Invoke(ctx, "/ManagementService/reportInstanceProperties", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managementServiceClient) KeepAlive(ctx context.Context, in *InstancePingPkg, opts ...grpc.CallOption) (*common.Commands, error) { + out := new(common.Commands) + err := c.cc.Invoke(ctx, "/ManagementService/keepAlive", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ManagementServiceServer is the server API for ManagementService service. +type ManagementServiceServer interface { + ReportInstanceProperties(context.Context, *InstanceProperties) (*common.Commands, error) + KeepAlive(context.Context, *InstancePingPkg) (*common.Commands, error) +} + +func RegisterManagementServiceServer(s *grpc.Server, srv ManagementServiceServer) { + s.RegisterService(&_ManagementService_serviceDesc, srv) +} + +func _ManagementService_ReportInstanceProperties_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InstanceProperties) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagementServiceServer).ReportInstanceProperties(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ManagementService/ReportInstanceProperties", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagementServiceServer).ReportInstanceProperties(ctx, req.(*InstanceProperties)) + } + return interceptor(ctx, in, info, handler) +} + +func _ManagementService_KeepAlive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InstancePingPkg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagementServiceServer).KeepAlive(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ManagementService/KeepAlive", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagementServiceServer).KeepAlive(ctx, req.(*InstancePingPkg)) + } + return interceptor(ctx, in, info, handler) +} + +var _ManagementService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ManagementService", + HandlerType: (*ManagementServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "reportInstanceProperties", + Handler: _ManagementService_ReportInstanceProperties_Handler, + }, + { + MethodName: "keepAlive", + Handler: _ManagementService_KeepAlive_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "management/Management.proto", +} diff --git a/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.proto b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.proto new file mode 100644 index 0000000..b022224 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/reporter/grpc/management/Management.proto @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.skywalking.apm.network.management.v3"; +option csharp_namespace = "SkyWalking.NetworkProtocol"; +option go_package = "github.com/SkyAPM/go2sky/reporter/grpc/management"; + +import "common/Common.proto"; + +service ManagementService { + rpc reportInstanceProperties (InstanceProperties) returns (Commands) { + } + + rpc keepAlive (InstancePingPkg) returns (Commands) { + + } +} + +message InstanceProperties { + string service = 1; + string serviceInstance = 2; + repeated KeyStringValuePair properties = 3; +} + +message InstancePingPkg { + string service = 1; + string serviceInstance = 2; +} \ No newline at end of file diff --git a/vendor/github.com/SkyAPM/go2sky/segment.go b/vendor/github.com/SkyAPM/go2sky/segment.go new file mode 100644 index 0000000..36a2c01 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/segment.go @@ -0,0 +1,243 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +import ( + "sync/atomic" + + "github.com/SkyAPM/go2sky/internal/idgen" + "github.com/SkyAPM/go2sky/internal/tool" + "github.com/SkyAPM/go2sky/propagation" + "github.com/SkyAPM/go2sky/reporter/grpc/common" + v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent" +) + +func newSegmentSpan(defaultSpan *defaultSpan, parentSpan segmentSpan) (s segmentSpan, err error) { + ssi := &segmentSpanImpl{ + defaultSpan: *defaultSpan, + } + err = ssi.createSegmentContext(parentSpan) + if err != nil { + return nil, err + } + if parentSpan == nil || !parentSpan.segmentRegister() { + rs := newSegmentRoot(ssi) + err = rs.createRootSegmentContext(parentSpan) + if err != nil { + return nil, err + } + s = rs + } else { + s = ssi + } + return +} + +// SegmentContext is the context in a segment +type SegmentContext struct { + TraceID string + SegmentID string + SpanID int32 + ParentSpanID int32 + ParentSegmentID string + collect chan<- ReportedSpan + refNum *int32 + spanIDGenerator *int32 + FirstSpan Span `json:"-"` +} + +// ReportedSpan is accessed by Reporter to load reported data +type ReportedSpan interface { + Context() *SegmentContext + Refs() []*propagation.SpanContext + StartTime() int64 + EndTime() int64 + OperationName() string + Peer() string + SpanType() v3.SpanType + SpanLayer() v3.SpanLayer + IsError() bool + Tags() []*common.KeyStringValuePair + Logs() []*v3.Log + ComponentID() int32 +} + +type segmentSpan interface { + Span + context() SegmentContext + segmentRegister() bool +} + +type segmentSpanImpl struct { + defaultSpan + SegmentContext +} + +// For Span +func (s *segmentSpanImpl) End() { + s.defaultSpan.End() + go func() { + s.Context().collect <- s + }() +} + +// For Reported Span + +func (s *segmentSpanImpl) Context() *SegmentContext { + return &s.SegmentContext +} + +func (s *segmentSpanImpl) Refs() []*propagation.SpanContext { + return s.defaultSpan.Refs +} + +func (s *segmentSpanImpl) StartTime() int64 { + return tool.Millisecond(s.defaultSpan.StartTime) +} + +func (s *segmentSpanImpl) EndTime() int64 { + return tool.Millisecond(s.defaultSpan.EndTime) +} + +func (s *segmentSpanImpl) OperationName() string { + return s.defaultSpan.OperationName +} + +func (s *segmentSpanImpl) Peer() string { + return s.defaultSpan.Peer +} + +func (s *segmentSpanImpl) SpanType() v3.SpanType { + return v3.SpanType(s.defaultSpan.SpanType) +} + +func (s *segmentSpanImpl) SpanLayer() v3.SpanLayer { + return s.defaultSpan.Layer +} + +func (s *segmentSpanImpl) IsError() bool { + return s.defaultSpan.IsError +} + +func (s *segmentSpanImpl) Tags() []*common.KeyStringValuePair { + return s.defaultSpan.Tags +} + +func (s *segmentSpanImpl) Logs() []*v3.Log { + return s.defaultSpan.Logs +} + +func (s *segmentSpanImpl) ComponentID() int32 { + return s.defaultSpan.ComponentID +} + +func (s *segmentSpanImpl) context() SegmentContext { + return s.SegmentContext +} + +func (s *segmentSpanImpl) segmentRegister() bool { + for { + o := atomic.LoadInt32(s.Context().refNum) + if o < 0 { + return false + } + if atomic.CompareAndSwapInt32(s.Context().refNum, o, o+1) { + return true + } + } +} + +func (s *segmentSpanImpl) createSegmentContext(parent segmentSpan) (err error) { + if parent == nil { + s.SegmentContext = SegmentContext{} + if len(s.defaultSpan.Refs) > 0 { + s.TraceID = s.defaultSpan.Refs[0].TraceID + } else { + s.TraceID, err = idgen.GenerateGlobalID() + if err != nil { + return err + } + } + } else { + s.SegmentContext = parent.context() + s.ParentSegmentID = s.SegmentID + s.ParentSpanID = s.SpanID + s.SpanID = atomic.AddInt32(s.Context().spanIDGenerator, 1) + } + if s.SegmentContext.FirstSpan == nil { + s.SegmentContext.FirstSpan = s + } + return +} + +type rootSegmentSpan struct { + *segmentSpanImpl + notify <-chan ReportedSpan + segment []ReportedSpan + doneCh chan int32 +} + +func (rs *rootSegmentSpan) End() { + rs.defaultSpan.End() + go func() { + rs.doneCh <- atomic.SwapInt32(rs.Context().refNum, -1) + }() +} + +func (rs *rootSegmentSpan) createRootSegmentContext(parent segmentSpan) (err error) { + rs.SegmentID, err = idgen.GenerateGlobalID() + if err != nil { + return err + } + i := int32(0) + rs.spanIDGenerator = &i + rs.SpanID = i + rs.ParentSpanID = -1 + return +} + +func newSegmentRoot(segmentSpan *segmentSpanImpl) *rootSegmentSpan { + s := &rootSegmentSpan{ + segmentSpanImpl: segmentSpan, + } + var init int32 + s.refNum = &init + ch := make(chan ReportedSpan) + s.collect = ch + s.notify = ch + s.segment = make([]ReportedSpan, 0, 10) + s.doneCh = make(chan int32) + go func() { + total := -1 + defer close(ch) + defer close(s.doneCh) + for { + select { + case span := <-s.notify: + s.segment = append(s.segment, span) + case n := <-s.doneCh: + total = int(n) + } + if total == len(s.segment) { + break + } + } + s.tracer.reporter.Send(append(s.segment, s)) + }() + return s +} diff --git a/vendor/github.com/SkyAPM/go2sky/span.go b/vendor/github.com/SkyAPM/go2sky/span.go new file mode 100644 index 0000000..583819a --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/span.go @@ -0,0 +1,163 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +import ( + "math" + "time" + + "github.com/SkyAPM/go2sky/internal/tool" + "github.com/SkyAPM/go2sky/propagation" + "github.com/SkyAPM/go2sky/reporter/grpc/common" + v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent" +) + +// SpanType is used to identify entry, exit and local +type SpanType int32 + +const ( + // SpanTypeEntry is a entry span, eg http server + SpanTypeEntry SpanType = 0 + // SpanTypeExit is a exit span, eg http client + SpanTypeExit SpanType = 1 + // SpanTypeLocal is a local span, eg local method invoke + SpanTypeLocal SpanType = 2 +) + +// Span interface as common span specification +type Span interface { + SetOperationName(string) + GetOperationName() string + SetPeer(string) + SetSpanLayer(v3.SpanLayer) + SetComponent(int32) + Tag(Tag, string) + Log(time.Time, ...string) + Error(time.Time, ...string) + End() + IsEntry() bool + IsExit() bool +} + +func newLocalSpan(t *Tracer) *defaultSpan { + return &defaultSpan{ + tracer: t, + StartTime: time.Now(), + SpanType: SpanTypeLocal, + } +} + +type defaultSpan struct { + Refs []*propagation.SpanContext + tracer *Tracer + StartTime time.Time + EndTime time.Time + OperationName string + Peer string + Layer v3.SpanLayer + ComponentID int32 + Tags []*common.KeyStringValuePair + Logs []*v3.Log + IsError bool + SpanType SpanType +} + +// For Span +func (ds *defaultSpan) SetOperationName(name string) { + ds.OperationName = name +} + +func (ds *defaultSpan) GetOperationName() string { + return ds.OperationName +} + +func (ds *defaultSpan) SetPeer(peer string) { + ds.Peer = peer +} + +func (ds *defaultSpan) SetSpanLayer(layer v3.SpanLayer) { + ds.Layer = layer +} + +func (ds *defaultSpan) SetComponent(componentID int32) { + ds.ComponentID = componentID +} + +func (ds *defaultSpan) Tag(key Tag, value string) { + ds.Tags = append(ds.Tags, &common.KeyStringValuePair{Key: string(key), Value: value}) +} + +func (ds *defaultSpan) Log(time time.Time, ll ...string) { + data := make([]*common.KeyStringValuePair, 0, int32(math.Ceil(float64(len(ll))/2.0))) + var kvp *common.KeyStringValuePair + for i, l := range ll { + if i%2 == 0 { + kvp = &common.KeyStringValuePair{} + data = append(data, kvp) + kvp.Key = l + } else { + if kvp != nil { + kvp.Value = l + } + } + } + ds.Logs = append(ds.Logs, &v3.Log{Time: tool.Millisecond(time), Data: data}) +} + +func (ds *defaultSpan) Error(time time.Time, ll ...string) { + ds.IsError = true + ds.Log(time, ll...) +} + +func (ds *defaultSpan) End() { + ds.EndTime = time.Now() +} + +func (ds *defaultSpan) IsEntry() bool { + return ds.SpanType == SpanTypeEntry +} + +func (ds *defaultSpan) IsExit() bool { + return ds.SpanType == SpanTypeExit +} + +// SpanOption allows for functional options to adjust behaviour +// of a Span to be created by CreateLocalSpan +type SpanOption func(s *defaultSpan) + +// Tag are supported by sky-walking engine. +// As default, all Tags will be stored, but these ones have +// particular meanings. +type Tag string + +const ( + TagURL Tag = "url" + TagStatusCode Tag = "status_code" + TagHTTPMethod Tag = "http.method" + TagDBType Tag = "db.type" + TagDBInstance Tag = "db.instance" + TagDBStatement Tag = "db.statement" + TagDBBindVariables Tag = "db.bind_vars" + TagMQQueue Tag = "mq.queue" + TagMQBroker Tag = "mq.broker" + TagMQTopic Tag = "mq.topic" +) + +const ( + ComponentIDHttpServer int32 = 49 +) diff --git a/vendor/github.com/SkyAPM/go2sky/span_opts.go b/vendor/github.com/SkyAPM/go2sky/span_opts.go new file mode 100644 index 0000000..354487b --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/span_opts.go @@ -0,0 +1,37 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +import "github.com/SkyAPM/go2sky/propagation" + +// WithContext setup trace sc from propagation +func WithContext(sc *propagation.SpanContext) SpanOption { + return func(s *defaultSpan) { + if sc == nil { + return + } + s.Refs = append(s.Refs, sc) + } +} + +// WithSpanType setup span type of a span +func WithSpanType(spanType SpanType) SpanOption { + return func(s *defaultSpan) { + s.SpanType = spanType + } +} diff --git a/vendor/github.com/SkyAPM/go2sky/trace.go b/vendor/github.com/SkyAPM/go2sky/trace.go new file mode 100644 index 0000000..3514c42 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/trace.go @@ -0,0 +1,199 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +import ( + "context" + + "github.com/SkyAPM/go2sky/internal/idgen" + "github.com/pkg/errors" + + "github.com/SkyAPM/go2sky/internal/tool" + "github.com/SkyAPM/go2sky/propagation" +) + +const ( + errParameter = tool.Error("parameter are nil") + EmptyTraceID = "N/A" + NoopTraceID = "[Ignored Trace]" +) + +// Tracer is go2sky tracer implementation. +type Tracer struct { + service string + instance string + reporter Reporter + // 0 not init 1 init + initFlag int32 +} + +// TracerOption allows for functional options to adjust behaviour +// of a Tracer to be created by NewTracer +type TracerOption func(t *Tracer) + +// NewTracer return a new go2sky Tracer +func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) { + if service == "" { + return nil, errParameter + } + t := &Tracer{ + service: service, + initFlag: 0, + } + for _, opt := range opts { + opt(t) + } + + if t.reporter != nil { + if t.instance == "" { + id, err := idgen.UUID() + if err != nil { + return nil, err + } + t.instance = id + "@" + tool.IPV4() + } + t.reporter.Boot(t.service, t.instance) + t.initFlag = 1 + } + return t, nil +} + +// CreateEntrySpan creates and starts an entry span for incoming request +func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extractor propagation.Extractor) (s Span, nCtx context.Context, err error) { + if ctx == nil || operationName == "" || extractor == nil { + return nil, nil, errParameter + } + if s, nCtx = t.createNoop(ctx); s != nil { + return + } + header, err := extractor() + if err != nil { + return + } + var refSc *propagation.SpanContext + if header != "" { + refSc = &propagation.SpanContext{} + err = refSc.DecodeSW8(header) + if err != nil { + return + } + } + s, nCtx, err = t.CreateLocalSpan(ctx, WithContext(refSc), WithSpanType(SpanTypeEntry)) + if err != nil { + return + } + s.SetOperationName(operationName) + return +} + +// CreateLocalSpan creates and starts a span for local usage +func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Span, c context.Context, err error) { + if ctx == nil { + return nil, nil, errParameter + } + if s, c = t.createNoop(ctx); s != nil { + return + } + ds := newLocalSpan(t) + for _, opt := range opts { + opt(ds) + } + parentSpan, ok := ctx.Value(ctxKeyInstance).(segmentSpan) + if !ok { + parentSpan = nil + } + s, err = newSegmentSpan(ds, parentSpan) + if err != nil { + return nil, nil, err + } + return s, context.WithValue(ctx, ctxKeyInstance, s), nil +} + +// CreateExitSpan creates and starts an exit span for client +func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer string, injector propagation.Injector) (Span, error) { + if ctx == nil || operationName == "" || peer == "" || injector == nil { + return nil, errParameter + } + if s, _ := t.createNoop(ctx); s != nil { + return s, nil + } + s, _, err := t.CreateLocalSpan(ctx, WithSpanType(SpanTypeExit)) + if err != nil { + return nil, err + } + s.SetOperationName(operationName) + s.SetPeer(peer) + spanContext := &propagation.SpanContext{} + span, ok := s.(ReportedSpan) + if !ok { + return nil, errors.New("span type is wrong") + } + + firstSpan := span.Context().FirstSpan + spanContext.Sample = 1 + spanContext.TraceID = span.Context().TraceID + spanContext.ParentSegmentID = span.Context().SegmentID + spanContext.ParentSpanID = span.Context().SpanID + spanContext.ParentService = t.service + spanContext.ParentServiceInstance = t.instance + spanContext.ParentEndpoint = firstSpan.GetOperationName() + spanContext.AddressUsedAtClient = peer + + err = injector(spanContext.EncodeSW8()) + if err != nil { + return nil, err + } + return s, nil +} + +func (t *Tracer) createNoop(ctx context.Context) (s Span, nCtx context.Context) { + if ns, ok := ctx.Value(ctxKeyInstance).(*NoopSpan); ok { + nCtx = ctx + s = ns + return + } + if t.initFlag == 0 { + s = &NoopSpan{} + nCtx = context.WithValue(ctx, ctxKeyInstance, s) + return + } + return +} + +type ctxKey struct{} + +var ctxKeyInstance = ctxKey{} + +//Reporter is a data transit specification +type Reporter interface { + Boot(service string, serviceInstance string) + Send(spans []ReportedSpan) + Close() +} + +func TraceID(ctx context.Context) string { + activeSpan := ctx.Value(ctxKeyInstance) + if activeSpan == nil { + return EmptyTraceID + } + span, ok := activeSpan.(segmentSpan) + if ok { + return span.context().TraceID + } + return NoopTraceID +} diff --git a/vendor/github.com/SkyAPM/go2sky/trace_opts.go b/vendor/github.com/SkyAPM/go2sky/trace_opts.go new file mode 100644 index 0000000..d845313 --- /dev/null +++ b/vendor/github.com/SkyAPM/go2sky/trace_opts.go @@ -0,0 +1,32 @@ +// Licensed to SkyAPM org under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. SkyAPM org licenses this file to you 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 go2sky + +// WithReporter setup report pipeline for tracer +func WithReporter(reporter Reporter) TracerOption { + return func(t *Tracer) { + t.reporter = reporter + } +} + +// WithInstance setup instance identify +func WithInstance(instance string) TracerOption { + return func(t *Tracer) { + t.instance = instance + } +} diff --git a/vendor/github.com/alexmullins/zip/.gitignore b/vendor/github.com/alexmullins/zip/.gitignore new file mode 100644 index 0000000..72747d6 --- /dev/null +++ b/vendor/github.com/alexmullins/zip/.gitignore @@ -0,0 +1,26 @@ +.DS_Store + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/alexmullins/zip/LICENSE b/vendor/github.com/alexmullins/zip/LICENSE new file mode 100644 index 0000000..5b5f02e --- /dev/null +++ b/vendor/github.com/alexmullins/zip/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (C) 2015 Alex Mullins + +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. diff --git a/vendor/github.com/alexmullins/zip/README.txt b/vendor/github.com/alexmullins/zip/README.txt new file mode 100644 index 0000000..2134a00 --- /dev/null +++ b/vendor/github.com/alexmullins/zip/README.txt @@ -0,0 +1,109 @@ +This is a fork of the Go archive/zip package to add support +for reading/writing password protected .zip files. +Only supports Winzip's AES extension: http://www.winzip.com/aes_info.htm. + +This package DOES NOT intend to implement the encryption methods +mentioned in the original PKWARE spec (sections 6.0 and 7.0): +https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + +Status - Alpha. More tests and code clean up next. + +Documentation - +https://godoc.org/github.com/alexmullins/zip + +Roadmap +======== +Reading - Done. +Writing - Done. +Testing - Needs more. + +The process +============ +1. hello.txt -> compressed -> encrypted -> .zip +2. .zip -> decrypted -> decompressed -> hello.txt + +Example Encrypt zip +========== +``` +package main + +import ( + "bytes" + "log" + "os" + "github.com/alexmullins/zip" +) + +func main() { + contents := []byte("Hello World") + fzip, err := os.Create(`./test.zip`) + if err != nil { + log.Fatalln(err) + } + zipw := zip.NewWriter(fzip) + defer zipw.Close() + w, err := zipw.Encrypt(`test.txt`, `golang`) + if err != nil { + log.Fatal(err) + } + _, err = io.Copy(w, bytes.NewReader(contents)) + if err != nil { + log.Fatal(err) + } + zipw.Flush() +} +``` + +WinZip AES specifies +===================== +1. Encryption-Decryption w/ AES-CTR (128, 192, or 256 bits) +2. Key generation with PBKDF2-HMAC-SHA1 (1000 iteration count) that +generates a master key broken into the following: + a. First m bytes is for the encryption key + b. Next n bytes is for the authentication key + c. Last 2 bytes is the password verification value. +3. Following salt lengths are used w/ password during keygen: + ------------------------------ + AES Key Size | Salt Size + ------------------------------ + 128bit(16bytes) | 8 bytes + 192bit(24bytes) | 12 bytes + 256bit(32bytes) | 16 bytes + ------------------------------- +4. Master key len = AESKeyLen + AuthKeyLen + PWVLen: + a. AES 128 = 16 + 16 + 2 = 34 bytes of key material + b. AES 192 = 24 + 24 + 2 = 50 bytes of key material + c. AES 256 = 32 + 32 + 2 = 66 bytes of key material +5. Authentication Key is same size as AES key. +6. Authentication with HMAC-SHA1-80 (truncated to 80bits). +7. A new master key is generated for every file. +8. The file header and directory header compression method will +be 99 (decimal) indicating Winzip AES encryption. The actual +compression method will be in the extra's payload at the end +of the headers. +9. A extra field will be added to the file header and directory +header identified by the ID 0x9901 and contains the following info: + a. Header ID (2 bytes) + b. Data Size (2 bytes) + c. Vendor Version (2 bytes) + d. Vendor ID (2 bytes) + e. AES Strength (1 byte) + f. Compression Method (2 bytes) +10. The Data Size is always 7. +11. The Vendor Version can be either 0x0001 (AE-1) or +0x0002 (AE-2). +12. Vendor ID is ASCII "AE" +13. AES Strength: + a. 0x01 - AES-128 + b. 0x02 - AES-192 + c. 0x03 - AES-256 +14. Compression Method is the actual compression method +used that was replaced by the encryption process mentioned in #8. +15. AE-1 keeps the CRC and should be verified after decompression. +AE-2 removes the CRC and shouldn't be verified after decompression. +Refer to http://www.winzip.com/aes_info.htm#winzip11 for the reasoning. +16. Storage Format (file data payload totals CompressedSize64 bytes): + a. Salt - 8, 12, or 16 bytes depending on keysize + b. Password Verification Value - 2 bytes + c. Encrypted Data - compressed size - salt - pwv - auth code lengths + d. Authentication code - 10 bytes diff --git a/vendor/github.com/alexmullins/zip/crypto.go b/vendor/github.com/alexmullins/zip/crypto.go new file mode 100644 index 0000000..137b674 --- /dev/null +++ b/vendor/github.com/alexmullins/zip/crypto.go @@ -0,0 +1,462 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "crypto/subtle" + "errors" + "hash" + "io" + + "golang.org/x/crypto/pbkdf2" +) + +const ( + // AES key lengths + aes128 = 16 + aes192 = 24 + aes256 = 32 +) + +func aesKeyLen(strength byte) int { + switch strength { + case 1: + return aes128 + case 2: + return aes192 + case 3: + return aes256 + default: + return 0 + } +} + +// Encryption/Decryption Errors +var ( + ErrDecryption = errors.New("zip: decryption error") + ErrPassword = errors.New("zip: invalid password") + ErrAuthentication = errors.New("zip: authentication failed") +) + +// Counter (CTR) mode. +// CTR converts a block cipher into a stream cipher by +// repeatedly encrypting an incrementing counter and +// xoring the resulting stream of data with the input. + +// This is a re-implementation of Go's CTR mode to allow +// for a little-endian, left-aligned uint32 counter, which +// is required for WinZip AES encryption. Go's cipher.NewCTR +// follows the NIST Standard SP 800-38A, pp 13-15 +// which has a big-endian, right-aligned counter. + +type ctr struct { + b cipher.Block + ctr []byte + out []byte + outUsed int +} + +const streamBufferSize = 512 + +// NewWinZipCTR returns a Stream which encrypts/decrypts using the given Block in +// counter mode. The counter is initially set to 1 per WinZip AES. +func newWinZipCTR(block cipher.Block) cipher.Stream { + bufSize := streamBufferSize + if bufSize < block.BlockSize() { + bufSize = block.BlockSize() + } + // Set the IV (counter) to 1 + iv := make([]byte, block.BlockSize()) + iv[0] = 1 + return &ctr{ + b: block, + ctr: iv, + out: make([]byte, 0, bufSize), + outUsed: 0, + } +} + +func (x *ctr) refill() { + remain := len(x.out) - x.outUsed + if remain > x.outUsed { + return + } + copy(x.out, x.out[x.outUsed:]) + x.out = x.out[:cap(x.out)] + bs := x.b.BlockSize() + for remain < len(x.out)-bs { + x.b.Encrypt(x.out[remain:], x.ctr) + remain += bs + + // Increment counter + // for i := len(x.ctr) - 1; i >= 0; i-- { + // x.ctr[i]++ + // if x.ctr[i] != 0 { + // break + // } + // } + + // Change to allow for little-endian, + // left-aligned counter + for i := 0; i < len(x.ctr); i++ { + x.ctr[i]++ + if x.ctr[i] != 0 { + break + } + } + } + x.out = x.out[:remain] + x.outUsed = 0 +} + +func (x *ctr) XORKeyStream(dst, src []byte) { + for len(src) > 0 { + if x.outUsed >= len(x.out)-x.b.BlockSize() { + x.refill() + } + n := xorBytes(dst, src, x.out[x.outUsed:]) + dst = dst[n:] + src = src[n:] + x.outUsed += n + } +} + +func xorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + for i := 0; i < n; i++ { + dst[i] = a[i] ^ b[i] + } + return n +} + +// newAuthReader returns either a buffered or streaming authentication reader. +// Buffered authentication is recommended. Streaming authentication is only +// recommended if: 1. you buffer the data yourself and wait for authentication +// before streaming to another source such as the network, or 2. you just don't +// care about authenticating unknown ciphertext before use :). +func newAuthReader(akey []byte, data, adata io.Reader, streaming bool) io.Reader { + ar := authReader{ + data: data, + adata: adata, + mac: hmac.New(sha1.New, akey), + err: nil, + auth: false, + } + if streaming { + return &ar + } + return &bufferedAuthReader{ + ar, + new(bytes.Buffer), + } +} + +// Streaming authentication +type authReader struct { + data io.Reader // data to be authenticated + adata io.Reader // the authentication code to read + mac hash.Hash // hmac hash + err error + auth bool +} + +func (a *authReader) Read(p []byte) (int, error) { + if a.err != nil { + return 0, a.err + } + end := false + // read underlying data + n, err := a.data.Read(p) + if err != nil && err != io.EOF { + a.err = err + return n, a.err + } else if err == io.EOF { + // if we are at the end, calculate the mac + end = true + a.err = err + } + // write any data to mac + _, err = a.mac.Write(p[:n]) + if err != nil { + a.err = err + return n, a.err + } + if end { + ab := new(bytes.Buffer) + _, err = io.Copy(ab, a.adata) + if err != nil || ab.Len() != 10 { + a.err = ErrDecryption + return n, a.err + } + if !a.checkAuthentication(ab.Bytes()) { + a.err = ErrAuthentication + return n, a.err + } + } + return n, a.err +} + +// buffered authentication +type bufferedAuthReader struct { + authReader + buf *bytes.Buffer // buffer to store data to authenticate +} + +func (a *bufferedAuthReader) Read(b []byte) (int, error) { + // check for sticky error + if a.err != nil { + return 0, a.err + } + // make sure we have auth'ed before we send any data + if !a.auth { + _, err := io.Copy(a.buf, a.data) + if err != nil { + a.err = err + return 0, a.err + } + ab := new(bytes.Buffer) + nn, err := io.Copy(ab, a.adata) + if err != nil { + a.err = err + return 0, a.err + } else if nn != 10 { + a.err = ErrDecryption + return 0, a.err + } + _, err = a.mac.Write(a.buf.Bytes()) + if err != nil { + a.err = err + return 0, a.err + } + if !a.checkAuthentication(ab.Bytes()) { + a.err = ErrAuthentication + return 0, a.err + } + } + // so we've authenticated the data, now just pass it on. + n, err := a.buf.Read(b) + if err != nil { + a.err = err + } + return n, a.err +} + +func (a *authReader) checkAuthentication(authcode []byte) bool { + expectedAuthCode := a.mac.Sum(nil) + // Truncate at the first 10 bytes + expectedAuthCode = expectedAuthCode[:10] + a.auth = subtle.ConstantTimeCompare(expectedAuthCode, authcode) > 0 + return a.auth +} + +func checkPasswordVerification(pwvv, pwv []byte) bool { + b := subtle.ConstantTimeCompare(pwvv, pwv) > 0 + return b +} + +func generateKeys(password, salt []byte, keySize int) (encKey, authKey, pwv []byte) { + totalSize := (keySize * 2) + 2 // enc + auth + pv sizes + key := pbkdf2.Key(password, salt, 1000, totalSize, sha1.New) + encKey = key[:keySize] + authKey = key[keySize : keySize*2] + pwv = key[keySize*2:] + return +} + +// newDecryptionReader returns an authenticated, decryption reader +func newDecryptionReader(r *io.SectionReader, f *File) (io.Reader, error) { + keyLen := aesKeyLen(f.aesStrength) + saltLen := keyLen / 2 // salt is half of key len + if saltLen == 0 { + return nil, ErrDecryption + } + // grab the salt and pwvv + saltpwvv := make([]byte, saltLen+2) + if _, err := r.Read(saltpwvv); err != nil { + return nil, err + } + salt := saltpwvv[:saltLen] + pwvv := saltpwvv[saltLen : saltLen+2] + // generate keys only if we have a password + if f.password == nil { + return nil, ErrPassword + } + decKey, authKey, pwv := generateKeys(f.password(), salt, keyLen) + if !checkPasswordVerification(pwv, pwvv) { + return nil, ErrPassword + } + dataOff := int64(saltLen + 2) + dataLen := int64(f.CompressedSize64 - uint64(saltLen) - 2 - 10) + // // TODO(alex): Should the compressed sizes be fixed? + // // Not the ideal place to do this. + // f.CompressedSize64 = uint64(dataLen) + // f.CompressedSize = uint32(dataLen) + data := io.NewSectionReader(r, dataOff, dataLen) + authOff := dataOff + dataLen + authcode := io.NewSectionReader(r, authOff, 10) + ar := newAuthReader(authKey, data, authcode, f.DeferAuth) + dr := decryptStream(decKey, ar) + if dr == nil { + return nil, ErrDecryption + } + return dr, nil +} + +func decryptStream(key []byte, ciphertext io.Reader) io.Reader { + block, err := aes.NewCipher(key) + if err != nil { + return nil + } + stream := newWinZipCTR(block) + reader := &cipher.StreamReader{S: stream, R: ciphertext} + return reader +} + +// writes encrypted data to hmac as it passes through +type authWriter struct { + hmac hash.Hash // from fw.hmac + w io.Writer // this will be the compCount writer +} + +func (aw *authWriter) Write(p []byte) (int, error) { + _, err := aw.hmac.Write(p) + if err != nil { + return 0, err + } + return aw.w.Write(p) +} + +// writes out the salt, pwv, and then the encrypted file data +type encryptionWriter struct { + pwv []byte + salt []byte + w io.Writer // where to write the salt + pwv + es io.Writer // where to write plaintext + first bool // first write + err error +} + +func (ew *encryptionWriter) Write(p []byte) (int, error) { + if ew.err != nil { + return 0, ew.err + } + if ew.first { + // if our first time writing + // must write out the salt and pwv first unencrypted + _, err1 := ew.w.Write(ew.salt) + _, err2 := ew.w.Write(ew.pwv) + if err1 != nil || err2 != nil { + ew.err = errors.New("zip: error writing salt or pwv") + return 0, ew.err + } + ew.first = false + } + // now just pass on to the encryption stream + return ew.es.Write(p) +} + +func encryptStream(key []byte, w io.Writer) (io.Writer, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + stream := newWinZipCTR(block) + writer := &cipher.StreamWriter{S: stream, W: w} + return writer, nil +} + +// newEncryptionWriter returns an io.Writer that when written to, 1. writes +// out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted +// data. The authcode will be written out in fileWriter.close(). +func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter) (io.Writer, error) { + var salt [16]byte + _, err := rand.Read(salt[:]) + if err != nil { + return nil, errors.New("zip: unable to generate random salt") + } + ekey, akey, pwv := generateKeys(password(), salt[:], aes256) + fw.hmac = hmac.New(sha1.New, akey) + aw := &authWriter{ + hmac: fw.hmac, + w: w, + } + es, err := encryptStream(ekey, aw) + if err != nil { + return nil, err + } + ew := &encryptionWriter{ + pwv: pwv, + salt: salt[:], + w: w, + es: es, + first: true, + } + return ew, nil +} + +// IsEncrypted indicates whether this file's data is encrypted. +func (h *FileHeader) IsEncrypted() bool { + return h.Flags&0x1 == 1 +} + +// WinZip AE-2 specifies that no CRC value is written and +// should be skipped when reading. +func (h *FileHeader) isAE2() bool { + return h.ae == 2 +} + +func (h *FileHeader) writeWinZipExtra() { + // total size is 11 bytes + var buf [11]byte + eb := writeBuf(buf[:]) + eb.uint16(winzipAesExtraId) // 0x9901 + eb.uint16(7) // following data size is 7 + eb.uint16(2) // ae 2 + eb.uint16(0x4541) // "AE" + eb.uint8(3) // aes256 + eb.uint16(h.Method) // original compression method + h.Extra = append(h.Extra, buf[:]...) +} + +func (h *FileHeader) setEncryptionBit() { + h.Flags |= 0x1 +} + +// SetPassword sets the password used for encryption/decryption. +func (h *FileHeader) SetPassword(password string) { + if !h.IsEncrypted() { + h.setEncryptionBit() + } + h.password = func() []byte { + return []byte(password) + } +} + +// PasswordFn is a function that returns the password +// as a byte slice +type passwordFn func() []byte + +// Encrypt adds a file to the zip file using the provided name. +// It returns a Writer to which the file contents should be written. File +// contents will be encrypted with AES-256 using the given password. The +// file's contents must be written to the io.Writer before the next call +// to Create, CreateHeader, or Close. +func (w *Writer) Encrypt(name string, password string) (io.Writer, error) { + fh := &FileHeader{ + Name: name, + Method: Deflate, + } + fh.SetPassword(password) + return w.CreateHeader(fh) +} diff --git a/vendor/github.com/alexmullins/zip/reader.go b/vendor/github.com/alexmullins/zip/reader.go new file mode 100644 index 0000000..a9e3f6b --- /dev/null +++ b/vendor/github.com/alexmullins/zip/reader.go @@ -0,0 +1,502 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bufio" + "encoding/binary" + "errors" + "fmt" + "hash" + "hash/crc32" + "io" + "os" +) + +var ( + ErrFormat = errors.New("zip: not a valid zip file") + ErrAlgorithm = errors.New("zip: unsupported compression algorithm") + ErrChecksum = errors.New("zip: checksum error") +) + +type Reader struct { + r io.ReaderAt + File []*File + Comment string +} + +type ReadCloser struct { + f *os.File + Reader +} + +type File struct { + FileHeader + zipr io.ReaderAt + zipsize int64 + headerOffset int64 +} + +func (f *File) hasDataDescriptor() bool { + return f.Flags&0x8 != 0 +} + +// OpenReader will open the Zip file specified by name and return a ReadCloser. +func OpenReader(name string) (*ReadCloser, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + fi, err := f.Stat() + if err != nil { + f.Close() + return nil, err + } + r := new(ReadCloser) + if err := r.init(f, fi.Size()); err != nil { + f.Close() + return nil, err + } + r.f = f + return r, nil +} + +// NewReader returns a new Reader reading from r, which is assumed to +// have the given size in bytes. +func NewReader(r io.ReaderAt, size int64) (*Reader, error) { + zr := new(Reader) + if err := zr.init(r, size); err != nil { + return nil, err + } + return zr, nil +} + +func (z *Reader) init(r io.ReaderAt, size int64) error { + end, err := readDirectoryEnd(r, size) + if err != nil { + return err + } + if end.directoryRecords > uint64(size)/fileHeaderLen { + return fmt.Errorf("archive/zip: TOC declares impossible %d files in %d byte zip", end.directoryRecords, size) + } + z.r = r + z.File = make([]*File, 0, end.directoryRecords) + z.Comment = end.comment + rs := io.NewSectionReader(r, 0, size) + if _, err = rs.Seek(int64(end.directoryOffset), os.SEEK_SET); err != nil { + return err + } + buf := bufio.NewReader(rs) + + // The count of files inside a zip is truncated to fit in a uint16. + // Gloss over this by reading headers until we encounter + // a bad one, and then only report a ErrFormat or UnexpectedEOF if + // the file count modulo 65536 is incorrect. + for { + f := &File{zipr: r, zipsize: size} + err = readDirectoryHeader(f, buf) + if err == ErrFormat || err == io.ErrUnexpectedEOF { + break + } + if err != nil { + return err + } + z.File = append(z.File, f) + } + if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here + // Return the readDirectoryHeader error if we read + // the wrong number of directory entries. + return err + } + return nil +} + +// Close closes the Zip file, rendering it unusable for I/O. +func (rc *ReadCloser) Close() error { + return rc.f.Close() +} + +// DataOffset returns the offset of the file's possibly-compressed +// data, relative to the beginning of the zip file. +// +// Most callers should instead use Open, which transparently +// decompresses data and verifies checksums. +func (f *File) DataOffset() (offset int64, err error) { + bodyOffset, err := f.findBodyOffset() + if err != nil { + return + } + return f.headerOffset + bodyOffset, nil +} + +// Open returns a ReadCloser that provides access to the File's contents. +// Multiple files may be read concurrently. +func (f *File) Open() (rc io.ReadCloser, err error) { + bodyOffset, err := f.findBodyOffset() + if err != nil { + return + } + // If f is encrypted, CompressedSize64 includes salt, pwvv, encrypted data, + // and auth code lengths + size := int64(f.CompressedSize64) + var r io.Reader + rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) + // check for encryption + if f.IsEncrypted() { + if r, err = newDecryptionReader(rr, f); err != nil { + return + } + } else { + r = rr + } + dcomp := decompressor(f.Method) + if dcomp == nil { + err = ErrAlgorithm + return + } + rc = dcomp(r) + // If AE-2, skip CRC and possible dataDescriptor + if f.isAE2() { + return + } + var desr io.Reader + if f.hasDataDescriptor() { + desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) + } + rc = &checksumReader{ + rc: rc, + hash: crc32.NewIEEE(), + f: f, + desr: desr, + } + return +} + +type checksumReader struct { + rc io.ReadCloser + hash hash.Hash32 + nread uint64 // number of bytes read so far + f *File + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error +} + +func (r *checksumReader) Read(b []byte) (n int, err error) { + if r.err != nil { + return 0, r.err + } + n, err = r.rc.Read(b) + r.hash.Write(b[:n]) + r.nread += uint64(n) + if err == nil { + return + } + if err == io.EOF { + if r.nread != r.f.UncompressedSize64 { + return 0, io.ErrUnexpectedEOF + } + if r.desr != nil { + if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { + if err1 == io.EOF { + err = io.ErrUnexpectedEOF + } else { + err = err1 + } + } else if r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } + } else { + // If there's not a data descriptor, we still compare + // the CRC32 of what we've read against the file header + // or TOC's CRC32, if it seems like it was set. + if r.f.CRC32 != 0 && r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } + } + } + r.err = err + return +} + +func (r *checksumReader) Close() error { return r.rc.Close() } + +// findBodyOffset does the minimum work to verify the file has a header +// and returns the file body offset. +func (f *File) findBodyOffset() (int64, error) { + var buf [fileHeaderLen]byte + if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil { + return 0, err + } + b := readBuf(buf[:]) + if sig := b.uint32(); sig != fileHeaderSignature { + return 0, ErrFormat + } + b = b[22:] // skip over most of the header + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) + return int64(fileHeaderLen + filenameLen + extraLen), nil +} + +// readDirectoryHeader attempts to read a directory header from r. +// It returns io.ErrUnexpectedEOF if it cannot read a complete header, +// and ErrFormat if it doesn't find a valid header signature. +func readDirectoryHeader(f *File, r io.Reader) error { + var buf [directoryHeaderLen]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { + return err + } + b := readBuf(buf[:]) + if sig := b.uint32(); sig != directoryHeaderSignature { + return ErrFormat + } + f.CreatorVersion = b.uint16() + f.ReaderVersion = b.uint16() + f.Flags = b.uint16() + f.Method = b.uint16() + f.ModifiedTime = b.uint16() + f.ModifiedDate = b.uint16() + f.CRC32 = b.uint32() + f.CompressedSize = b.uint32() + f.UncompressedSize = b.uint32() + f.CompressedSize64 = uint64(f.CompressedSize) + f.UncompressedSize64 = uint64(f.UncompressedSize) + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) + commentLen := int(b.uint16()) + b = b[4:] // skipped start disk number and internal attributes (2x uint16) + f.ExternalAttrs = b.uint32() + f.headerOffset = int64(b.uint32()) + d := make([]byte, filenameLen+extraLen+commentLen) + if _, err := io.ReadFull(r, d); err != nil { + return err + } + f.Name = string(d[:filenameLen]) + f.Extra = d[filenameLen : filenameLen+extraLen] + f.Comment = string(d[filenameLen+extraLen:]) + + if len(f.Extra) > 0 { + b := readBuf(f.Extra) + for len(b) >= 4 { // need at least tag and size + tag := b.uint16() + size := b.uint16() + if int(size) > len(b) { + return ErrFormat + } + eb := readBuf(b[:size]) + switch tag { + case zip64ExtraId: + // update directory values from the zip64 extra block + if len(eb) >= 8 { + f.UncompressedSize64 = eb.uint64() + } + if len(eb) >= 8 { + f.CompressedSize64 = eb.uint64() + } + if len(eb) >= 8 { + f.headerOffset = int64(eb.uint64()) + } + case winzipAesExtraId: + // grab the AE version + f.ae = eb.uint16() + // skip vendor ID + _ = eb.uint16() + // AES strength + f.aesStrength = eb.uint8() + // set the actual compression method. + f.Method = eb.uint16() + } + b = b[size:] + } + // Should have consumed the whole header. + // But popular zip & JAR creation tools are broken and + // may pad extra zeros at the end, so accept those + // too. See golang.org/issue/8186. + for _, v := range b { + if v != 0 { + return ErrFormat + } + } + } + return nil +} + +func readDataDescriptor(r io.Reader, f *File) error { + var buf [dataDescriptorLen]byte + + // The spec says: "Although not originally assigned a + // signature, the value 0x08074b50 has commonly been adopted + // as a signature value for the data descriptor record. + // Implementers should be aware that ZIP files may be + // encountered with or without this signature marking data + // descriptors and should account for either case when reading + // ZIP files to ensure compatibility." + // + // dataDescriptorLen includes the size of the signature but + // first read just those 4 bytes to see if it exists. + if _, err := io.ReadFull(r, buf[:4]); err != nil { + return err + } + off := 0 + maybeSig := readBuf(buf[:4]) + if maybeSig.uint32() != dataDescriptorSignature { + // No data descriptor signature. Keep these four + // bytes. + off += 4 + } + if _, err := io.ReadFull(r, buf[off:12]); err != nil { + return err + } + b := readBuf(buf[:12]) + if b.uint32() != f.CRC32 { + return ErrChecksum + } + + // The two sizes that follow here can be either 32 bits or 64 bits + // but the spec is not very clear on this and different + // interpretations has been made causing incompatibilities. We + // already have the sizes from the central directory so we can + // just ignore these. + + return nil +} + +func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { + // look for directoryEndSignature in the last 1k, then in the last 65k + var buf []byte + var directoryEndOffset int64 + for i, bLen := range []int64{1024, 65 * 1024} { + if bLen > size { + bLen = size + } + buf = make([]byte, int(bLen)) + if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { + return nil, err + } + if p := findSignatureInBlock(buf); p >= 0 { + buf = buf[p:] + directoryEndOffset = size - bLen + int64(p) + break + } + if i == 1 || bLen == size { + return nil, ErrFormat + } + } + + // read header into struct + b := readBuf(buf[4:]) // skip signature + d := &directoryEnd{ + diskNbr: uint32(b.uint16()), + dirDiskNbr: uint32(b.uint16()), + dirRecordsThisDisk: uint64(b.uint16()), + directoryRecords: uint64(b.uint16()), + directorySize: uint64(b.uint32()), + directoryOffset: uint64(b.uint32()), + commentLen: b.uint16(), + } + l := int(d.commentLen) + if l > len(b) { + return nil, errors.New("zip: invalid comment length") + } + d.comment = string(b[:l]) + + p, err := findDirectory64End(r, directoryEndOffset) + if err == nil && p >= 0 { + err = readDirectory64End(r, p, d) + } + if err != nil { + return nil, err + } + + // Make sure directoryOffset points to somewhere in our file. + if o := int64(d.directoryOffset); o < 0 || o >= size { + return nil, ErrFormat + } + return d, nil +} + +// findDirectory64End tries to read the zip64 locator just before the +// directory end and returns the offset of the zip64 directory end if +// found. +func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) { + locOffset := directoryEndOffset - directory64LocLen + if locOffset < 0 { + return -1, nil // no need to look for a header outside the file + } + buf := make([]byte, directory64LocLen) + if _, err := r.ReadAt(buf, locOffset); err != nil { + return -1, err + } + b := readBuf(buf) + if sig := b.uint32(); sig != directory64LocSignature { + return -1, nil + } + b = b[4:] // skip number of the disk with the start of the zip64 end of central directory + p := b.uint64() // relative offset of the zip64 end of central directory record + return int64(p), nil +} + +// readDirectory64End reads the zip64 directory end and updates the +// directory end with the zip64 directory end values. +func readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) { + buf := make([]byte, directory64EndLen) + if _, err := r.ReadAt(buf, offset); err != nil { + return err + } + + b := readBuf(buf) + if sig := b.uint32(); sig != directory64EndSignature { + return ErrFormat + } + + b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16) + d.diskNbr = b.uint32() // number of this disk + d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory + d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk + d.directoryRecords = b.uint64() // total number of entries in the central directory + d.directorySize = b.uint64() // size of the central directory + d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number + + return nil +} + +func findSignatureInBlock(b []byte) int { + for i := len(b) - directoryEndLen; i >= 0; i-- { + // defined from directoryEndSignature in struct.go + if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 { + // n is length of comment + n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8 + if n+directoryEndLen+i <= len(b) { + return i + } + } + } + return -1 +} + +type readBuf []byte + +func (b *readBuf) uint8() byte { + v := (*b)[0] + *b = (*b)[1:] + return v +} + +func (b *readBuf) uint16() uint16 { + v := binary.LittleEndian.Uint16(*b) + *b = (*b)[2:] + return v +} + +func (b *readBuf) uint32() uint32 { + v := binary.LittleEndian.Uint32(*b) + *b = (*b)[4:] + return v +} + +func (b *readBuf) uint64() uint64 { + v := binary.LittleEndian.Uint64(*b) + *b = (*b)[8:] + return v +} diff --git a/vendor/github.com/alexmullins/zip/register.go b/vendor/github.com/alexmullins/zip/register.go new file mode 100644 index 0000000..4211ec7 --- /dev/null +++ b/vendor/github.com/alexmullins/zip/register.go @@ -0,0 +1,110 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "compress/flate" + "errors" + "io" + "io/ioutil" + "sync" +) + +// A Compressor returns a compressing writer, writing to the +// provided writer. On Close, any pending data should be flushed. +type Compressor func(io.Writer) (io.WriteCloser, error) + +// Decompressor is a function that wraps a Reader with a decompressing Reader. +// The decompressed ReadCloser is returned to callers who open files from +// within the archive. These callers are responsible for closing this reader +// when they're finished reading. +type Decompressor func(io.Reader) io.ReadCloser + +var flateWriterPool sync.Pool + +func newFlateWriter(w io.Writer) io.WriteCloser { + fw, ok := flateWriterPool.Get().(*flate.Writer) + if ok { + fw.Reset(w) + } else { + fw, _ = flate.NewWriter(w, 5) + } + return &pooledFlateWriter{fw: fw} +} + +type pooledFlateWriter struct { + mu sync.Mutex // guards Close and Write + fw *flate.Writer +} + +func (w *pooledFlateWriter) Write(p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + if w.fw == nil { + return 0, errors.New("Write after Close") + } + return w.fw.Write(p) +} + +func (w *pooledFlateWriter) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + var err error + if w.fw != nil { + err = w.fw.Close() + flateWriterPool.Put(w.fw) + w.fw = nil + } + return err +} + +var ( + mu sync.RWMutex // guards compressor and decompressor maps + + compressors = map[uint16]Compressor{ + Store: func(w io.Writer) (io.WriteCloser, error) { return &nopCloser{w}, nil }, + Deflate: func(w io.Writer) (io.WriteCloser, error) { return newFlateWriter(w), nil }, + } + + decompressors = map[uint16]Decompressor{ + Store: ioutil.NopCloser, + Deflate: flate.NewReader, + } +) + +// RegisterDecompressor allows custom decompressors for a specified method ID. +func RegisterDecompressor(method uint16, d Decompressor) { + mu.Lock() + defer mu.Unlock() + + if _, ok := decompressors[method]; ok { + panic("decompressor already registered") + } + decompressors[method] = d +} + +// RegisterCompressor registers custom compressors for a specified method ID. +// The common methods Store and Deflate are built in. +func RegisterCompressor(method uint16, comp Compressor) { + mu.Lock() + defer mu.Unlock() + + if _, ok := compressors[method]; ok { + panic("compressor already registered") + } + compressors[method] = comp +} + +func compressor(method uint16) Compressor { + mu.RLock() + defer mu.RUnlock() + return compressors[method] +} + +func decompressor(method uint16) Decompressor { + mu.RLock() + defer mu.RUnlock() + return decompressors[method] +} diff --git a/vendor/github.com/alexmullins/zip/struct.go b/vendor/github.com/alexmullins/zip/struct.go new file mode 100644 index 0000000..1e0cba2 --- /dev/null +++ b/vendor/github.com/alexmullins/zip/struct.go @@ -0,0 +1,329 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package zip provides support for reading and writing password protected ZIP archives. + +See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT + +This package does not support disk spanning. + +A note about ZIP64: + +To be backwards compatible the FileHeader has both 32 and 64 bit Size +fields. The 64 bit fields will always contain the correct value and +for normal archives both fields will be the same. For files requiring +the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit +fields must be used instead. + +Can read/write password protected files that use Winzip's AES encryption method. +See: http://www.winzip.com/aes_info.htm +*/ +package zip + +import ( + "os" + "path" + "time" +) + +// Compression methods. +const ( + Store uint16 = 0 + Deflate uint16 = 8 +) + +const ( + fileHeaderSignature = 0x04034b50 + directoryHeaderSignature = 0x02014b50 + directoryEndSignature = 0x06054b50 + directory64LocSignature = 0x07064b50 + directory64EndSignature = 0x06064b50 + dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder + fileHeaderLen = 30 // + filename + extra + directoryHeaderLen = 46 // + filename + extra + comment + directoryEndLen = 22 // + comment + dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size + dataDescriptor64Len = 24 // descriptor with 8 byte sizes + directory64LocLen = 20 // + directory64EndLen = 56 // + extra + + // Constants for the first byte in CreatorVersion + creatorFAT = 0 + creatorUnix = 3 + creatorNTFS = 11 + creatorVFAT = 14 + creatorMacOSX = 19 + + // version numbers + zipVersion20 = 20 // 2.0 + zipVersion45 = 45 // 4.5 (reads and writes zip64 archives) + + // limits for non zip64 files + uint16max = (1 << 16) - 1 + uint32max = (1 << 32) - 1 + + // extra header id's + zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field + winzipAesExtraId = 0x9901 // winzip AES Extra Field +) + +// FileHeader describes a file within a zip file. +// See the zip spec for details. +type FileHeader struct { + // Name is the name of the file. + // It must be a relative path: it must not start with a drive + // letter (e.g. C:) or leading slash, and only forward slashes + // are allowed. + Name string + + CreatorVersion uint16 + ReaderVersion uint16 + Flags uint16 + Method uint16 + ModifiedTime uint16 // MS-DOS time + ModifiedDate uint16 // MS-DOS date + CRC32 uint32 + CompressedSize uint32 // Deprecated: Use CompressedSize64 instead. + UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead. + CompressedSize64 uint64 + UncompressedSize64 uint64 + Extra []byte + ExternalAttrs uint32 // Meaning depends on CreatorVersion + Comment string + + // DeferAuth being set to true will delay hmac auth/integrity + // checks when decrypting a file meaning the reader will be + // getting unauthenticated plaintext. It is recommended to leave + // this set to false. For more detail: + // https://www.imperialviolet.org/2014/06/27/streamingencryption.html + // https://www.imperialviolet.org/2015/05/16/aeads.html + DeferAuth bool + + password passwordFn // Returns the password to use when reading/writing + ae uint16 + aesStrength byte +} + +// FileInfo returns an os.FileInfo for the FileHeader. +func (h *FileHeader) FileInfo() os.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements os.FileInfo. +type headerFileInfo struct { + fh *FileHeader +} + +func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } +func (fi headerFileInfo) Size() int64 { + if fi.fh.UncompressedSize64 > 0 { + return int64(fi.fh.UncompressedSize64) + } + return int64(fi.fh.UncompressedSize) +} +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } +func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } +func (fi headerFileInfo) Sys() interface{} { return fi.fh } + +// FileInfoHeader creates a partially-populated FileHeader from an +// os.FileInfo. +// Because os.FileInfo's Name method returns only the base name of +// the file it describes, it may be necessary to modify the Name field +// of the returned header to provide the full path name of the file. +func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { + size := fi.Size() + fh := &FileHeader{ + Name: fi.Name(), + UncompressedSize64: uint64(size), + } + fh.SetModTime(fi.ModTime()) + fh.SetMode(fi.Mode()) + if fh.UncompressedSize64 > uint32max { + fh.UncompressedSize = uint32max + } else { + fh.UncompressedSize = uint32(fh.UncompressedSize64) + } + return fh, nil +} + +type directoryEnd struct { + diskNbr uint32 // unused + dirDiskNbr uint32 // unused + dirRecordsThisDisk uint64 // unused + directoryRecords uint64 + directorySize uint64 + directoryOffset uint64 // relative to file + commentLen uint16 + comment string +} + +// msDosTimeToTime converts an MS-DOS date and time into a time.Time. +// The resolution is 2s. +// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx +func msDosTimeToTime(dosDate, dosTime uint16) time.Time { + return time.Date( + // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 + int(dosDate>>9+1980), + time.Month(dosDate>>5&0xf), + int(dosDate&0x1f), + + // time bits 0-4: second/2; 5-10: minute; 11-15: hour + int(dosTime>>11), + int(dosTime>>5&0x3f), + int(dosTime&0x1f*2), + 0, // nanoseconds + + time.UTC, + ) +} + +// timeToMsDosTime converts a time.Time to an MS-DOS date and time. +// The resolution is 2s. +// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx +func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { + t = t.In(time.UTC) + fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) + fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) + return +} + +// ModTime returns the modification time in UTC. +// The resolution is 2s. +func (h *FileHeader) ModTime() time.Time { + return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) +} + +// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC. +// The resolution is 2s. +func (h *FileHeader) SetModTime(t time.Time) { + h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) +} + +const ( + // Unix constants. The specification doesn't mention them, + // but these seem to be the values agreed on by tools. + s_IFMT = 0xf000 + s_IFSOCK = 0xc000 + s_IFLNK = 0xa000 + s_IFREG = 0x8000 + s_IFBLK = 0x6000 + s_IFDIR = 0x4000 + s_IFCHR = 0x2000 + s_IFIFO = 0x1000 + s_ISUID = 0x800 + s_ISGID = 0x400 + s_ISVTX = 0x200 + + msdosDir = 0x10 + msdosReadOnly = 0x01 +) + +// Mode returns the permission and mode bits for the FileHeader. +func (h *FileHeader) Mode() (mode os.FileMode) { + switch h.CreatorVersion >> 8 { + case creatorUnix, creatorMacOSX: + mode = unixModeToFileMode(h.ExternalAttrs >> 16) + case creatorNTFS, creatorVFAT, creatorFAT: + mode = msdosModeToFileMode(h.ExternalAttrs) + } + if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { + mode |= os.ModeDir + } + return mode +} + +// SetMode changes the permission and mode bits for the FileHeader. +func (h *FileHeader) SetMode(mode os.FileMode) { + h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 + h.ExternalAttrs = fileModeToUnixMode(mode) << 16 + + // set MSDOS attributes too, as the original zip does. + if mode&os.ModeDir != 0 { + h.ExternalAttrs |= msdosDir + } + if mode&0200 == 0 { + h.ExternalAttrs |= msdosReadOnly + } +} + +// isZip64 reports whether the file size exceeds the 32 bit limit +func (fh *FileHeader) isZip64() bool { + return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max +} + +func msdosModeToFileMode(m uint32) (mode os.FileMode) { + if m&msdosDir != 0 { + mode = os.ModeDir | 0777 + } else { + mode = 0666 + } + if m&msdosReadOnly != 0 { + mode &^= 0222 + } + return mode +} + +func fileModeToUnixMode(mode os.FileMode) uint32 { + var m uint32 + switch mode & os.ModeType { + default: + m = s_IFREG + case os.ModeDir: + m = s_IFDIR + case os.ModeSymlink: + m = s_IFLNK + case os.ModeNamedPipe: + m = s_IFIFO + case os.ModeSocket: + m = s_IFSOCK + case os.ModeDevice: + if mode&os.ModeCharDevice != 0 { + m = s_IFCHR + } else { + m = s_IFBLK + } + } + if mode&os.ModeSetuid != 0 { + m |= s_ISUID + } + if mode&os.ModeSetgid != 0 { + m |= s_ISGID + } + if mode&os.ModeSticky != 0 { + m |= s_ISVTX + } + return m | uint32(mode&0777) +} + +func unixModeToFileMode(m uint32) os.FileMode { + mode := os.FileMode(m & 0777) + switch m & s_IFMT { + case s_IFBLK: + mode |= os.ModeDevice + case s_IFCHR: + mode |= os.ModeDevice | os.ModeCharDevice + case s_IFDIR: + mode |= os.ModeDir + case s_IFIFO: + mode |= os.ModeNamedPipe + case s_IFLNK: + mode |= os.ModeSymlink + case s_IFREG: + // nothing to do + case s_IFSOCK: + mode |= os.ModeSocket + } + if m&s_ISGID != 0 { + mode |= os.ModeSetgid + } + if m&s_ISUID != 0 { + mode |= os.ModeSetuid + } + if m&s_ISVTX != 0 { + mode |= os.ModeSticky + } + return mode +} diff --git a/vendor/github.com/alexmullins/zip/writer.go b/vendor/github.com/alexmullins/zip/writer.go new file mode 100644 index 0000000..27d125e --- /dev/null +++ b/vendor/github.com/alexmullins/zip/writer.go @@ -0,0 +1,406 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bufio" + "encoding/binary" + "errors" + "hash" + "hash/crc32" + "io" +) + +// TODO(adg): support zip file comments +// TODO(adg): support specifying deflate level + +// Writer implements a zip file writer. +type Writer struct { + cw *countWriter + dir []*header + last *fileWriter + closed bool +} + +type header struct { + *FileHeader + offset uint64 +} + +// NewWriter returns a new Writer writing a zip file to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} +} + +// SetOffset sets the offset of the beginning of the zip data within the +// underlying writer. It should be used when the zip data is appended to an +// existing file, such as a binary executable. +// It must be called before any data is written. +func (w *Writer) SetOffset(n int64) { + if w.cw.count != 0 { + panic("zip: SetOffset called after data was written") + } + w.cw.count = n +} + +// Flush flushes any buffered data to the underlying writer. +// Calling Flush is not normally necessary; calling Close is sufficient. +func (w *Writer) Flush() error { + return w.cw.w.(*bufio.Writer).Flush() +} + +// Close finishes writing the zip file by writing the central directory. +// It does not (and can not) close the underlying writer. +func (w *Writer) Close() error { + if w.last != nil && !w.last.closed { + if err := w.last.close(); err != nil { + return err + } + w.last = nil + } + if w.closed { + return errors.New("zip: writer closed twice") + } + w.closed = true + + // write central directory + start := w.cw.count + for _, h := range w.dir { + var buf [directoryHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(directoryHeaderSignature)) + b.uint16(h.CreatorVersion) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + b.uint16(h.Method) + b.uint16(h.ModifiedTime) + b.uint16(h.ModifiedDate) + b.uint32(h.CRC32) + if h.isZip64() || h.offset > uint32max { + // the file needs a zip64 header. store maxint in both + // 32 bit size fields (and offset later) to signal that the + // zip64 extra header should be used. + b.uint32(uint32max) // compressed size + b.uint32(uint32max) // uncompressed size + + // append a zip64 extra block to Extra + var buf [28]byte // 2x uint16 + 3x uint64 + eb := writeBuf(buf[:]) + eb.uint16(zip64ExtraId) + eb.uint16(24) // size = 3x uint64 + eb.uint64(h.UncompressedSize64) + eb.uint64(h.CompressedSize64) + eb.uint64(h.offset) + h.Extra = append(h.Extra, buf[:]...) + } else { + b.uint32(h.CompressedSize) + b.uint32(h.UncompressedSize) + } + b.uint16(uint16(len(h.Name))) + b.uint16(uint16(len(h.Extra))) + b.uint16(uint16(len(h.Comment))) + b = b[4:] // skip disk number start and internal file attr (2x uint16) + b.uint32(h.ExternalAttrs) + if h.offset > uint32max { + b.uint32(uint32max) + } else { + b.uint32(uint32(h.offset)) + } + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Name); err != nil { + return err + } + if _, err := w.cw.Write(h.Extra); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Comment); err != nil { + return err + } + } + end := w.cw.count + + records := uint64(len(w.dir)) + size := uint64(end - start) + offset := uint64(start) + + if records > uint16max || size > uint32max || offset > uint32max { + var buf [directory64EndLen + directory64LocLen]byte + b := writeBuf(buf[:]) + + // zip64 end of central directory record + b.uint32(directory64EndSignature) + b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64) + b.uint16(zipVersion45) // version made by + b.uint16(zipVersion45) // version needed to extract + b.uint32(0) // number of this disk + b.uint32(0) // number of the disk with the start of the central directory + b.uint64(records) // total number of entries in the central directory on this disk + b.uint64(records) // total number of entries in the central directory + b.uint64(size) // size of the central directory + b.uint64(offset) // offset of start of central directory with respect to the starting disk number + + // zip64 end of central directory locator + b.uint32(directory64LocSignature) + b.uint32(0) // number of the disk with the start of the zip64 end of central directory + b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record + b.uint32(1) // total number of disks + + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } + + // store max values in the regular end record to signal that + // that the zip64 values should be used instead + records = uint16max + size = uint32max + offset = uint32max + } + + // write end record + var buf [directoryEndLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(directoryEndSignature)) + b = b[4:] // skip over disk number and first disk number (2x uint16) + b.uint16(uint16(records)) // number of entries this disk + b.uint16(uint16(records)) // number of entries total + b.uint32(uint32(size)) // size of directory + b.uint32(uint32(offset)) // start of directory + // skipped size of comment (always zero) + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } + + return w.cw.w.(*bufio.Writer).Flush() +} + +// Create adds a file to the zip file using the provided name. +// It returns a Writer to which the file contents should be written. +// The name must be a relative path: it must not start with a drive +// letter (e.g. C:) or leading slash, and only forward slashes are +// allowed. +// The file's contents must be written to the io.Writer before the next +// call to Create, CreateHeader, or Close. +func (w *Writer) Create(name string) (io.Writer, error) { + header := &FileHeader{ + Name: name, + Method: Deflate, + } + return w.CreateHeader(header) +} + +// CreateHeader adds a file to the zip file using the provided FileHeader +// for the file metadata. +// It returns a Writer to which the file contents should be written. +// +// The file's contents must be written to the io.Writer before the next +// call to Create, CreateHeader, or Close. The provided FileHeader fh +// must not be modified after a call to CreateHeader. +func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { + if w.last != nil && !w.last.closed { + if err := w.last.close(); err != nil { + return nil, err + } + } + if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh { + // See https://golang.org/issue/11144 confusion. + return nil, errors.New("archive/zip: invalid duplicate FileHeader") + } + + fh.Flags |= 0x8 // we will write a data descriptor + // TODO(alex): Look at spec and see if these need to be changed + // when using encryption. + fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte + fh.ReaderVersion = zipVersion20 + + fw := &fileWriter{ + zipw: w.cw, + compCount: &countWriter{w: w.cw}, + crc32: crc32.NewIEEE(), + } + // Get the compressor before possibly changing Method to 99 due to password + comp := compressor(fh.Method) + if comp == nil { + return nil, ErrAlgorithm + } + // check for password + var sw io.Writer = fw.compCount + if fh.password != nil { + // we have a password and need to encrypt. + fh.writeWinZipExtra() + fh.Method = 99 // ok to change, we've gotten the comp and wrote extra + ew, err := newEncryptionWriter(sw, fh.password, fw) + if err != nil { + return nil, err + } + sw = ew + } + var err error + fw.comp, err = comp(sw) + if err != nil { + return nil, err + } + fw.rawCount = &countWriter{w: fw.comp} + + h := &header{ + FileHeader: fh, + offset: uint64(w.cw.count), + } + w.dir = append(w.dir, h) + fw.header = h + + if err := writeHeader(w.cw, fh); err != nil { + return nil, err + } + + w.last = fw + return fw, nil +} + +func writeHeader(w io.Writer, h *FileHeader) error { + var buf [fileHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(fileHeaderSignature)) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + b.uint16(h.Method) + b.uint16(h.ModifiedTime) + b.uint16(h.ModifiedDate) + b.uint32(0) // since we are writing a data descriptor crc32, + b.uint32(0) // compressed size, + b.uint32(0) // and uncompressed size should be zero + b.uint16(uint16(len(h.Name))) + b.uint16(uint16(len(h.Extra))) + if _, err := w.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w, h.Name); err != nil { + return err + } + _, err := w.Write(h.Extra) + return err +} + +type fileWriter struct { + *header + zipw io.Writer + rawCount *countWriter + comp io.WriteCloser + compCount *countWriter + crc32 hash.Hash32 + closed bool + + hmac hash.Hash // possible hmac used for authentication when encrypting +} + +func (w *fileWriter) Write(p []byte) (int, error) { + if w.closed { + return 0, errors.New("zip: write to closed file") + } + w.crc32.Write(p) + return w.rawCount.Write(p) +} + +func (w *fileWriter) close() error { + if w.closed { + return errors.New("zip: file closed twice") + } + w.closed = true + if err := w.comp.Close(); err != nil { + return err + } + // if encrypted grab the hmac and write it out + if w.header.IsEncrypted() { + authCode := w.hmac.Sum(nil) + authCode = authCode[:10] + _, err := w.compCount.Write(authCode) + if err != nil { + return errors.New("zip: error writing authcode") + } + } + // update FileHeader + fh := w.header.FileHeader + // ae-2 we don't write out CRC + if !fh.IsEncrypted() { + fh.CRC32 = w.crc32.Sum32() + } + fh.CompressedSize64 = uint64(w.compCount.count) + fh.UncompressedSize64 = uint64(w.rawCount.count) + + if fh.isZip64() { + fh.CompressedSize = uint32max + fh.UncompressedSize = uint32max + fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions + } else { + fh.CompressedSize = uint32(fh.CompressedSize64) + fh.UncompressedSize = uint32(fh.UncompressedSize64) + } + + // Write data descriptor. This is more complicated than one would + // think, see e.g. comments in zipfile.c:putextended() and + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588. + // The approach here is to write 8 byte sizes if needed without + // adding a zip64 extra in the local header (too late anyway). + var buf []byte + if fh.isZip64() { + buf = make([]byte, dataDescriptor64Len) + } else { + buf = make([]byte, dataDescriptorLen) + } + b := writeBuf(buf) + b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X + b.uint32(fh.CRC32) + if fh.isZip64() { + b.uint64(fh.CompressedSize64) + b.uint64(fh.UncompressedSize64) + } else { + b.uint32(fh.CompressedSize) + b.uint32(fh.UncompressedSize) + } + _, err := w.zipw.Write(buf) + return err +} + +type countWriter struct { + w io.Writer + count int64 +} + +func (w *countWriter) Write(p []byte) (int, error) { + n, err := w.w.Write(p) + w.count += int64(n) + return n, err +} + +type nopCloser struct { + io.Writer +} + +func (w nopCloser) Close() error { + return nil +} + +type writeBuf []byte + +func (b *writeBuf) uint8(v uint8) { + (*b)[0] = v + *b = (*b)[1:] +} + +func (b *writeBuf) uint16(v uint16) { + binary.LittleEndian.PutUint16(*b, v) + *b = (*b)[2:] +} + +func (b *writeBuf) uint32(v uint32) { + binary.LittleEndian.PutUint32(*b, v) + *b = (*b)[4:] +} + +func (b *writeBuf) uint64(v uint64) { + binary.LittleEndian.PutUint64(*b, v) + *b = (*b)[8:] +} diff --git a/vendor/github.com/alexmullins/zip/zipwriters.png b/vendor/github.com/alexmullins/zip/zipwriters.png new file mode 100644 index 0000000..54c409e Binary files /dev/null and b/vendor/github.com/alexmullins/zip/zipwriters.png differ diff --git a/vendor/github.com/armon/go-metrics/.gitignore b/vendor/github.com/armon/go-metrics/.gitignore new file mode 100644 index 0000000..8c03ec1 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +/metrics.out diff --git a/vendor/github.com/armon/go-metrics/.travis.yml b/vendor/github.com/armon/go-metrics/.travis.yml new file mode 100644 index 0000000..87d230c --- /dev/null +++ b/vendor/github.com/armon/go-metrics/.travis.yml @@ -0,0 +1,13 @@ +language: go + +go: + - "1.x" + +env: + - GO111MODULE=on + +install: + - go get ./... + +script: + - go test ./... diff --git a/vendor/github.com/armon/go-metrics/LICENSE b/vendor/github.com/armon/go-metrics/LICENSE new file mode 100644 index 0000000..106569e --- /dev/null +++ b/vendor/github.com/armon/go-metrics/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Armon Dadgar + +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. diff --git a/vendor/github.com/armon/go-metrics/README.md b/vendor/github.com/armon/go-metrics/README.md new file mode 100644 index 0000000..aa73348 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/README.md @@ -0,0 +1,91 @@ +go-metrics +========== + +This library provides a `metrics` package which can be used to instrument code, +expose application metrics, and profile runtime performance in a flexible manner. + +Current API: [![GoDoc](https://godoc.org/github.com/armon/go-metrics?status.svg)](https://godoc.org/github.com/armon/go-metrics) + +Sinks +----- + +The `metrics` package makes use of a `MetricSink` interface to support delivery +to any type of backend. Currently the following sinks are provided: + +* StatsiteSink : Sinks to a [statsite](https://github.com/armon/statsite/) instance (TCP) +* StatsdSink: Sinks to a [StatsD](https://github.com/etsy/statsd/) / statsite instance (UDP) +* PrometheusSink: Sinks to a [Prometheus](http://prometheus.io/) metrics endpoint (exposed via HTTP for scrapes) +* InmemSink : Provides in-memory aggregation, can be used to export stats +* FanoutSink : Sinks to multiple sinks. Enables writing to multiple statsite instances for example. +* BlackholeSink : Sinks to nowhere + +In addition to the sinks, the `InmemSignal` can be used to catch a signal, +and dump a formatted output of recent metrics. For example, when a process gets +a SIGUSR1, it can dump to stderr recent performance metrics for debugging. + +Labels +------ + +Most metrics do have an equivalent ending with `WithLabels`, such methods +allow to push metrics with labels and use some features of underlying Sinks +(ex: translated into Prometheus labels). + +Since some of these labels may increase greatly cardinality of metrics, the +library allow to filter labels using a blacklist/whitelist filtering system +which is global to all metrics. + +* If `Config.AllowedLabels` is not nil, then only labels specified in this value will be sent to underlying Sink, otherwise, all labels are sent by default. +* If `Config.BlockedLabels` is not nil, any label specified in this value will not be sent to underlying Sinks. + +By default, both `Config.AllowedLabels` and `Config.BlockedLabels` are nil, meaning that +no tags are filetered at all, but it allow to a user to globally block some tags with high +cardinality at application level. + +Examples +-------- + +Here is an example of using the package: + +```go +func SlowMethod() { + // Profiling the runtime of a method + defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now()) +} + +// Configure a statsite sink as the global metrics sink +sink, _ := metrics.NewStatsiteSink("statsite:8125") +metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink) + +// Emit a Key/Value pair +metrics.EmitKey([]string{"questions", "meaning of life"}, 42) +``` + +Here is an example of setting up a signal handler: + +```go +// Setup the inmem sink and signal handler +inm := metrics.NewInmemSink(10*time.Second, time.Minute) +sig := metrics.DefaultInmemSignal(inm) +metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm) + +// Run some code +inm.SetGauge([]string{"foo"}, 42) +inm.EmitKey([]string{"bar"}, 30) + +inm.IncrCounter([]string{"baz"}, 42) +inm.IncrCounter([]string{"baz"}, 1) +inm.IncrCounter([]string{"baz"}, 80) + +inm.AddSample([]string{"method", "wow"}, 42) +inm.AddSample([]string{"method", "wow"}, 100) +inm.AddSample([]string{"method", "wow"}, 22) + +.... +``` + +When a signal comes in, output like the following will be dumped to stderr: + + [2014-01-28 14:57:33.04 -0800 PST][G] 'foo': 42.000 + [2014-01-28 14:57:33.04 -0800 PST][P] 'bar': 30.000 + [2014-01-28 14:57:33.04 -0800 PST][C] 'baz': Count: 3 Min: 1.000 Mean: 41.000 Max: 80.000 Stddev: 39.509 + [2014-01-28 14:57:33.04 -0800 PST][S] 'method.wow': Count: 3 Min: 22.000 Mean: 54.667 Max: 100.000 Stddev: 40.513 \ No newline at end of file diff --git a/vendor/github.com/armon/go-metrics/const_unix.go b/vendor/github.com/armon/go-metrics/const_unix.go new file mode 100644 index 0000000..31098dd --- /dev/null +++ b/vendor/github.com/armon/go-metrics/const_unix.go @@ -0,0 +1,12 @@ +// +build !windows + +package metrics + +import ( + "syscall" +) + +const ( + // DefaultSignal is used with DefaultInmemSignal + DefaultSignal = syscall.SIGUSR1 +) diff --git a/vendor/github.com/armon/go-metrics/const_windows.go b/vendor/github.com/armon/go-metrics/const_windows.go new file mode 100644 index 0000000..38136af --- /dev/null +++ b/vendor/github.com/armon/go-metrics/const_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package metrics + +import ( + "syscall" +) + +const ( + // DefaultSignal is used with DefaultInmemSignal + // Windows has no SIGUSR1, use SIGBREAK + DefaultSignal = syscall.Signal(21) +) diff --git a/vendor/github.com/armon/go-metrics/go.mod b/vendor/github.com/armon/go-metrics/go.mod new file mode 100644 index 0000000..e3a656e --- /dev/null +++ b/vendor/github.com/armon/go-metrics/go.mod @@ -0,0 +1,17 @@ +module github.com/armon/go-metrics + +go 1.12 + +require ( + github.com/DataDog/datadog-go v3.2.0+incompatible + github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible + github.com/circonus-labs/circonusllhist v0.1.3 // indirect + github.com/golang/protobuf v1.3.2 + github.com/hashicorp/go-immutable-radix v1.0.0 + github.com/hashicorp/go-retryablehttp v0.5.3 // indirect + github.com/pascaldekloe/goe v0.1.0 + github.com/prometheus/client_golang v1.4.0 + github.com/prometheus/client_model v0.2.0 + github.com/prometheus/common v0.9.1 + github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect +) diff --git a/vendor/github.com/armon/go-metrics/go.sum b/vendor/github.com/armon/go-metrics/go.sum new file mode 100644 index 0000000..519481e --- /dev/null +++ b/vendor/github.com/armon/go-metrics/go.sum @@ -0,0 +1,125 @@ +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/armon/go-metrics/inmem.go b/vendor/github.com/armon/go-metrics/inmem.go new file mode 100644 index 0000000..e8206da --- /dev/null +++ b/vendor/github.com/armon/go-metrics/inmem.go @@ -0,0 +1,335 @@ +package metrics + +import ( + "bytes" + "fmt" + "math" + "net/url" + "strings" + "sync" + "time" +) + +var spaceReplacer = strings.NewReplacer(" ", "_") + +// InmemSink provides a MetricSink that does in-memory aggregation +// without sending metrics over a network. It can be embedded within +// an application to provide profiling information. +type InmemSink struct { + // How long is each aggregation interval + interval time.Duration + + // Retain controls how many metrics interval we keep + retain time.Duration + + // maxIntervals is the maximum length of intervals. + // It is retain / interval. + maxIntervals int + + // intervals is a slice of the retained intervals + intervals []*IntervalMetrics + intervalLock sync.RWMutex + + rateDenom float64 +} + +// IntervalMetrics stores the aggregated metrics +// for a specific interval +type IntervalMetrics struct { + sync.RWMutex + + // The start time of the interval + Interval time.Time + + // Gauges maps the key to the last set value + Gauges map[string]GaugeValue + + // Points maps the string to the list of emitted values + // from EmitKey + Points map[string][]float32 + + // Counters maps the string key to a sum of the counter + // values + Counters map[string]SampledValue + + // Samples maps the key to an AggregateSample, + // which has the rolled up view of a sample + Samples map[string]SampledValue +} + +// NewIntervalMetrics creates a new IntervalMetrics for a given interval +func NewIntervalMetrics(intv time.Time) *IntervalMetrics { + return &IntervalMetrics{ + Interval: intv, + Gauges: make(map[string]GaugeValue), + Points: make(map[string][]float32), + Counters: make(map[string]SampledValue), + Samples: make(map[string]SampledValue), + } +} + +// AggregateSample is used to hold aggregate metrics +// about a sample +type AggregateSample struct { + Count int // The count of emitted pairs + Rate float64 // The values rate per time unit (usually 1 second) + Sum float64 // The sum of values + SumSq float64 `json:"-"` // The sum of squared values + Min float64 // Minimum value + Max float64 // Maximum value + LastUpdated time.Time `json:"-"` // When value was last updated +} + +// Computes a Stddev of the values +func (a *AggregateSample) Stddev() float64 { + num := (float64(a.Count) * a.SumSq) - math.Pow(a.Sum, 2) + div := float64(a.Count * (a.Count - 1)) + if div == 0 { + return 0 + } + return math.Sqrt(num / div) +} + +// Computes a mean of the values +func (a *AggregateSample) Mean() float64 { + if a.Count == 0 { + return 0 + } + return a.Sum / float64(a.Count) +} + +// Ingest is used to update a sample +func (a *AggregateSample) Ingest(v float64, rateDenom float64) { + a.Count++ + a.Sum += v + a.SumSq += (v * v) + if v < a.Min || a.Count == 1 { + a.Min = v + } + if v > a.Max || a.Count == 1 { + a.Max = v + } + a.Rate = float64(a.Sum) / rateDenom + a.LastUpdated = time.Now() +} + +func (a *AggregateSample) String() string { + if a.Count == 0 { + return "Count: 0" + } else if a.Stddev() == 0 { + return fmt.Sprintf("Count: %d Sum: %0.3f LastUpdated: %s", a.Count, a.Sum, a.LastUpdated) + } else { + return fmt.Sprintf("Count: %d Min: %0.3f Mean: %0.3f Max: %0.3f Stddev: %0.3f Sum: %0.3f LastUpdated: %s", + a.Count, a.Min, a.Mean(), a.Max, a.Stddev(), a.Sum, a.LastUpdated) + } +} + +// NewInmemSinkFromURL creates an InmemSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewInmemSinkFromURL(u *url.URL) (MetricSink, error) { + params := u.Query() + + interval, err := time.ParseDuration(params.Get("interval")) + if err != nil { + return nil, fmt.Errorf("Bad 'interval' param: %s", err) + } + + retain, err := time.ParseDuration(params.Get("retain")) + if err != nil { + return nil, fmt.Errorf("Bad 'retain' param: %s", err) + } + + return NewInmemSink(interval, retain), nil +} + +// NewInmemSink is used to construct a new in-memory sink. +// Uses an aggregation interval and maximum retention period. +func NewInmemSink(interval, retain time.Duration) *InmemSink { + rateTimeUnit := time.Second + i := &InmemSink{ + interval: interval, + retain: retain, + maxIntervals: int(retain / interval), + rateDenom: float64(interval.Nanoseconds()) / float64(rateTimeUnit.Nanoseconds()), + } + i.intervals = make([]*IntervalMetrics, 0, i.maxIntervals) + return i +} + +func (i *InmemSink) SetGauge(key []string, val float32) { + i.SetGaugeWithLabels(key, val, nil) +} + +func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) + intv := i.getInterval() + + intv.Lock() + defer intv.Unlock() + intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels} +} + +func (i *InmemSink) EmitKey(key []string, val float32) { + k := i.flattenKey(key) + intv := i.getInterval() + + intv.Lock() + defer intv.Unlock() + vals := intv.Points[k] + intv.Points[k] = append(vals, val) +} + +func (i *InmemSink) IncrCounter(key []string, val float32) { + i.IncrCounterWithLabels(key, val, nil) +} + +func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) + intv := i.getInterval() + + intv.Lock() + defer intv.Unlock() + + agg, ok := intv.Counters[k] + if !ok { + agg = SampledValue{ + Name: name, + AggregateSample: &AggregateSample{}, + Labels: labels, + } + intv.Counters[k] = agg + } + agg.Ingest(float64(val), i.rateDenom) +} + +func (i *InmemSink) AddSample(key []string, val float32) { + i.AddSampleWithLabels(key, val, nil) +} + +func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) + intv := i.getInterval() + + intv.Lock() + defer intv.Unlock() + + agg, ok := intv.Samples[k] + if !ok { + agg = SampledValue{ + Name: name, + AggregateSample: &AggregateSample{}, + Labels: labels, + } + intv.Samples[k] = agg + } + agg.Ingest(float64(val), i.rateDenom) +} + +// Data is used to retrieve all the aggregated metrics +// Intervals may be in use, and a read lock should be acquired +func (i *InmemSink) Data() []*IntervalMetrics { + // Get the current interval, forces creation + i.getInterval() + + i.intervalLock.RLock() + defer i.intervalLock.RUnlock() + + n := len(i.intervals) + intervals := make([]*IntervalMetrics, n) + + copy(intervals[:n-1], i.intervals[:n-1]) + current := i.intervals[n-1] + + // make its own copy for current interval + intervals[n-1] = &IntervalMetrics{} + copyCurrent := intervals[n-1] + current.RLock() + *copyCurrent = *current + + copyCurrent.Gauges = make(map[string]GaugeValue, len(current.Gauges)) + for k, v := range current.Gauges { + copyCurrent.Gauges[k] = v + } + // saved values will be not change, just copy its link + copyCurrent.Points = make(map[string][]float32, len(current.Points)) + for k, v := range current.Points { + copyCurrent.Points[k] = v + } + copyCurrent.Counters = make(map[string]SampledValue, len(current.Counters)) + for k, v := range current.Counters { + copyCurrent.Counters[k] = v.deepCopy() + } + copyCurrent.Samples = make(map[string]SampledValue, len(current.Samples)) + for k, v := range current.Samples { + copyCurrent.Samples[k] = v.deepCopy() + } + current.RUnlock() + + return intervals +} + +func (i *InmemSink) getExistingInterval(intv time.Time) *IntervalMetrics { + i.intervalLock.RLock() + defer i.intervalLock.RUnlock() + + n := len(i.intervals) + if n > 0 && i.intervals[n-1].Interval == intv { + return i.intervals[n-1] + } + return nil +} + +func (i *InmemSink) createInterval(intv time.Time) *IntervalMetrics { + i.intervalLock.Lock() + defer i.intervalLock.Unlock() + + // Check for an existing interval + n := len(i.intervals) + if n > 0 && i.intervals[n-1].Interval == intv { + return i.intervals[n-1] + } + + // Add the current interval + current := NewIntervalMetrics(intv) + i.intervals = append(i.intervals, current) + n++ + + // Truncate the intervals if they are too long + if n >= i.maxIntervals { + copy(i.intervals[0:], i.intervals[n-i.maxIntervals:]) + i.intervals = i.intervals[:i.maxIntervals] + } + return current +} + +// getInterval returns the current interval to write to +func (i *InmemSink) getInterval() *IntervalMetrics { + intv := time.Now().Truncate(i.interval) + if m := i.getExistingInterval(intv); m != nil { + return m + } + return i.createInterval(intv) +} + +// Flattens the key for formatting, removes spaces +func (i *InmemSink) flattenKey(parts []string) string { + buf := &bytes.Buffer{} + + joined := strings.Join(parts, ".") + + spaceReplacer.WriteString(buf, joined) + + return buf.String() +} + +// Flattens the key for formatting along with its labels, removes spaces +func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) { + key := i.flattenKey(parts) + buf := bytes.NewBufferString(key) + + for _, label := range labels { + spaceReplacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value)) + } + + return buf.String(), key +} diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint.go b/vendor/github.com/armon/go-metrics/inmem_endpoint.go new file mode 100644 index 0000000..5fac958 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/inmem_endpoint.go @@ -0,0 +1,131 @@ +package metrics + +import ( + "fmt" + "net/http" + "sort" + "time" +) + +// MetricsSummary holds a roll-up of metrics info for a given interval +type MetricsSummary struct { + Timestamp string + Gauges []GaugeValue + Points []PointValue + Counters []SampledValue + Samples []SampledValue +} + +type GaugeValue struct { + Name string + Hash string `json:"-"` + Value float32 + + Labels []Label `json:"-"` + DisplayLabels map[string]string `json:"Labels"` +} + +type PointValue struct { + Name string + Points []float32 +} + +type SampledValue struct { + Name string + Hash string `json:"-"` + *AggregateSample + Mean float64 + Stddev float64 + + Labels []Label `json:"-"` + DisplayLabels map[string]string `json:"Labels"` +} + +// deepCopy allocates a new instance of AggregateSample +func (source *SampledValue) deepCopy() SampledValue { + dest := *source + if source.AggregateSample != nil { + dest.AggregateSample = &AggregateSample{} + *dest.AggregateSample = *source.AggregateSample + } + return dest +} + +// DisplayMetrics returns a summary of the metrics from the most recent finished interval. +func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + data := i.Data() + + var interval *IntervalMetrics + n := len(data) + switch { + case n == 0: + return nil, fmt.Errorf("no metric intervals have been initialized yet") + case n == 1: + // Show the current interval if it's all we have + interval = data[0] + default: + // Show the most recent finished interval if we have one + interval = data[n-2] + } + + interval.RLock() + defer interval.RUnlock() + + summary := MetricsSummary{ + Timestamp: interval.Interval.Round(time.Second).UTC().String(), + Gauges: make([]GaugeValue, 0, len(interval.Gauges)), + Points: make([]PointValue, 0, len(interval.Points)), + } + + // Format and sort the output of each metric type, so it gets displayed in a + // deterministic order. + for name, points := range interval.Points { + summary.Points = append(summary.Points, PointValue{name, points}) + } + sort.Slice(summary.Points, func(i, j int) bool { + return summary.Points[i].Name < summary.Points[j].Name + }) + + for hash, value := range interval.Gauges { + value.Hash = hash + value.DisplayLabels = make(map[string]string) + for _, label := range value.Labels { + value.DisplayLabels[label.Name] = label.Value + } + value.Labels = nil + + summary.Gauges = append(summary.Gauges, value) + } + sort.Slice(summary.Gauges, func(i, j int) bool { + return summary.Gauges[i].Hash < summary.Gauges[j].Hash + }) + + summary.Counters = formatSamples(interval.Counters) + summary.Samples = formatSamples(interval.Samples) + + return summary, nil +} + +func formatSamples(source map[string]SampledValue) []SampledValue { + output := make([]SampledValue, 0, len(source)) + for hash, sample := range source { + displayLabels := make(map[string]string) + for _, label := range sample.Labels { + displayLabels[label.Name] = label.Value + } + + output = append(output, SampledValue{ + Name: sample.Name, + Hash: hash, + AggregateSample: sample.AggregateSample, + Mean: sample.AggregateSample.Mean(), + Stddev: sample.AggregateSample.Stddev(), + DisplayLabels: displayLabels, + }) + } + sort.Slice(output, func(i, j int) bool { + return output[i].Hash < output[j].Hash + }) + + return output +} diff --git a/vendor/github.com/armon/go-metrics/inmem_signal.go b/vendor/github.com/armon/go-metrics/inmem_signal.go new file mode 100644 index 0000000..0937f4a --- /dev/null +++ b/vendor/github.com/armon/go-metrics/inmem_signal.go @@ -0,0 +1,117 @@ +package metrics + +import ( + "bytes" + "fmt" + "io" + "os" + "os/signal" + "strings" + "sync" + "syscall" +) + +// InmemSignal is used to listen for a given signal, and when received, +// to dump the current metrics from the InmemSink to an io.Writer +type InmemSignal struct { + signal syscall.Signal + inm *InmemSink + w io.Writer + sigCh chan os.Signal + + stop bool + stopCh chan struct{} + stopLock sync.Mutex +} + +// NewInmemSignal creates a new InmemSignal which listens for a given signal, +// and dumps the current metrics out to a writer +func NewInmemSignal(inmem *InmemSink, sig syscall.Signal, w io.Writer) *InmemSignal { + i := &InmemSignal{ + signal: sig, + inm: inmem, + w: w, + sigCh: make(chan os.Signal, 1), + stopCh: make(chan struct{}), + } + signal.Notify(i.sigCh, sig) + go i.run() + return i +} + +// DefaultInmemSignal returns a new InmemSignal that responds to SIGUSR1 +// and writes output to stderr. Windows uses SIGBREAK +func DefaultInmemSignal(inmem *InmemSink) *InmemSignal { + return NewInmemSignal(inmem, DefaultSignal, os.Stderr) +} + +// Stop is used to stop the InmemSignal from listening +func (i *InmemSignal) Stop() { + i.stopLock.Lock() + defer i.stopLock.Unlock() + + if i.stop { + return + } + i.stop = true + close(i.stopCh) + signal.Stop(i.sigCh) +} + +// run is a long running routine that handles signals +func (i *InmemSignal) run() { + for { + select { + case <-i.sigCh: + i.dumpStats() + case <-i.stopCh: + return + } + } +} + +// dumpStats is used to dump the data to output writer +func (i *InmemSignal) dumpStats() { + buf := bytes.NewBuffer(nil) + + data := i.inm.Data() + // Skip the last period which is still being aggregated + for j := 0; j < len(data)-1; j++ { + intv := data[j] + intv.RLock() + for _, val := range intv.Gauges { + name := i.flattenLabels(val.Name, val.Labels) + fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value) + } + for name, vals := range intv.Points { + for _, val := range vals { + fmt.Fprintf(buf, "[%v][P] '%s': %0.3f\n", intv.Interval, name, val) + } + } + for _, agg := range intv.Counters { + name := i.flattenLabels(agg.Name, agg.Labels) + fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg.AggregateSample) + } + for _, agg := range intv.Samples { + name := i.flattenLabels(agg.Name, agg.Labels) + fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg.AggregateSample) + } + intv.RUnlock() + } + + // Write out the bytes + i.w.Write(buf.Bytes()) +} + +// Flattens the key for formatting along with its labels, removes spaces +func (i *InmemSignal) flattenLabels(name string, labels []Label) string { + buf := bytes.NewBufferString(name) + replacer := strings.NewReplacer(" ", "_", ":", "_") + + for _, label := range labels { + replacer.WriteString(buf, ".") + replacer.WriteString(buf, label.Value) + } + + return buf.String() +} diff --git a/vendor/github.com/armon/go-metrics/metrics.go b/vendor/github.com/armon/go-metrics/metrics.go new file mode 100644 index 0000000..457b74b --- /dev/null +++ b/vendor/github.com/armon/go-metrics/metrics.go @@ -0,0 +1,293 @@ +package metrics + +import ( + "runtime" + "strings" + "time" + + "github.com/hashicorp/go-immutable-radix" +) + +type Label struct { + Name string + Value string +} + +func (m *Metrics) SetGauge(key []string, val float32) { + m.SetGaugeWithLabels(key, val, nil) +} + +func (m *Metrics) SetGaugeWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" { + if m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } else if m.EnableHostname { + key = insert(0, m.HostName, key) + } + } + if m.EnableTypePrefix { + key = insert(0, "gauge", key) + } + if m.ServiceName != "" { + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + allowed, labelsFiltered := m.allowMetric(key, labels) + if !allowed { + return + } + m.sink.SetGaugeWithLabels(key, val, labelsFiltered) +} + +func (m *Metrics) EmitKey(key []string, val float32) { + if m.EnableTypePrefix { + key = insert(0, "kv", key) + } + if m.ServiceName != "" { + key = insert(0, m.ServiceName, key) + } + allowed, _ := m.allowMetric(key, nil) + if !allowed { + return + } + m.sink.EmitKey(key, val) +} + +func (m *Metrics) IncrCounter(key []string, val float32) { + m.IncrCounterWithLabels(key, val, nil) +} + +func (m *Metrics) IncrCounterWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } + if m.EnableTypePrefix { + key = insert(0, "counter", key) + } + if m.ServiceName != "" { + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + allowed, labelsFiltered := m.allowMetric(key, labels) + if !allowed { + return + } + m.sink.IncrCounterWithLabels(key, val, labelsFiltered) +} + +func (m *Metrics) AddSample(key []string, val float32) { + m.AddSampleWithLabels(key, val, nil) +} + +func (m *Metrics) AddSampleWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } + if m.EnableTypePrefix { + key = insert(0, "sample", key) + } + if m.ServiceName != "" { + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + allowed, labelsFiltered := m.allowMetric(key, labels) + if !allowed { + return + } + m.sink.AddSampleWithLabels(key, val, labelsFiltered) +} + +func (m *Metrics) MeasureSince(key []string, start time.Time) { + m.MeasureSinceWithLabels(key, start, nil) +} + +func (m *Metrics) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } + if m.EnableTypePrefix { + key = insert(0, "timer", key) + } + if m.ServiceName != "" { + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + allowed, labelsFiltered := m.allowMetric(key, labels) + if !allowed { + return + } + now := time.Now() + elapsed := now.Sub(start) + msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity) + m.sink.AddSampleWithLabels(key, msec, labelsFiltered) +} + +// UpdateFilter overwrites the existing filter with the given rules. +func (m *Metrics) UpdateFilter(allow, block []string) { + m.UpdateFilterAndLabels(allow, block, m.AllowedLabels, m.BlockedLabels) +} + +// UpdateFilterAndLabels overwrites the existing filter with the given rules. +func (m *Metrics) UpdateFilterAndLabels(allow, block, allowedLabels, blockedLabels []string) { + m.filterLock.Lock() + defer m.filterLock.Unlock() + + m.AllowedPrefixes = allow + m.BlockedPrefixes = block + + if allowedLabels == nil { + // Having a white list means we take only elements from it + m.allowedLabels = nil + } else { + m.allowedLabels = make(map[string]bool) + for _, v := range allowedLabels { + m.allowedLabels[v] = true + } + } + m.blockedLabels = make(map[string]bool) + for _, v := range blockedLabels { + m.blockedLabels[v] = true + } + m.AllowedLabels = allowedLabels + m.BlockedLabels = blockedLabels + + m.filter = iradix.New() + for _, prefix := range m.AllowedPrefixes { + m.filter, _, _ = m.filter.Insert([]byte(prefix), true) + } + for _, prefix := range m.BlockedPrefixes { + m.filter, _, _ = m.filter.Insert([]byte(prefix), false) + } +} + +// labelIsAllowed return true if a should be included in metric +// the caller should lock m.filterLock while calling this method +func (m *Metrics) labelIsAllowed(label *Label) bool { + labelName := (*label).Name + if m.blockedLabels != nil { + _, ok := m.blockedLabels[labelName] + if ok { + // If present, let's remove this label + return false + } + } + if m.allowedLabels != nil { + _, ok := m.allowedLabels[labelName] + return ok + } + // Allow by default + return true +} + +// filterLabels return only allowed labels +// the caller should lock m.filterLock while calling this method +func (m *Metrics) filterLabels(labels []Label) []Label { + if labels == nil { + return nil + } + toReturn := []Label{} + for _, label := range labels { + if m.labelIsAllowed(&label) { + toReturn = append(toReturn, label) + } + } + return toReturn +} + +// Returns whether the metric should be allowed based on configured prefix filters +// Also return the applicable labels +func (m *Metrics) allowMetric(key []string, labels []Label) (bool, []Label) { + m.filterLock.RLock() + defer m.filterLock.RUnlock() + + if m.filter == nil || m.filter.Len() == 0 { + return m.Config.FilterDefault, m.filterLabels(labels) + } + + _, allowed, ok := m.filter.Root().LongestPrefix([]byte(strings.Join(key, "."))) + if !ok { + return m.Config.FilterDefault, m.filterLabels(labels) + } + + return allowed.(bool), m.filterLabels(labels) +} + +// Periodically collects runtime stats to publish +func (m *Metrics) collectStats() { + for { + time.Sleep(m.ProfileInterval) + m.emitRuntimeStats() + } +} + +// Emits various runtime statsitics +func (m *Metrics) emitRuntimeStats() { + // Export number of Goroutines + numRoutines := runtime.NumGoroutine() + m.SetGauge([]string{"runtime", "num_goroutines"}, float32(numRoutines)) + + // Export memory stats + var stats runtime.MemStats + runtime.ReadMemStats(&stats) + m.SetGauge([]string{"runtime", "alloc_bytes"}, float32(stats.Alloc)) + m.SetGauge([]string{"runtime", "sys_bytes"}, float32(stats.Sys)) + m.SetGauge([]string{"runtime", "malloc_count"}, float32(stats.Mallocs)) + m.SetGauge([]string{"runtime", "free_count"}, float32(stats.Frees)) + m.SetGauge([]string{"runtime", "heap_objects"}, float32(stats.HeapObjects)) + m.SetGauge([]string{"runtime", "total_gc_pause_ns"}, float32(stats.PauseTotalNs)) + m.SetGauge([]string{"runtime", "total_gc_runs"}, float32(stats.NumGC)) + + // Export info about the last few GC runs + num := stats.NumGC + + // Handle wrap around + if num < m.lastNumGC { + m.lastNumGC = 0 + } + + // Ensure we don't scan more than 256 + if num-m.lastNumGC >= 256 { + m.lastNumGC = num - 255 + } + + for i := m.lastNumGC; i < num; i++ { + pause := stats.PauseNs[i%256] + m.AddSample([]string{"runtime", "gc_pause_ns"}, float32(pause)) + } + m.lastNumGC = num +} + +// Creates a new slice with the provided string value as the first element +// and the provided slice values as the remaining values. +// Ordering of the values in the provided input slice is kept in tact in the output slice. +func insert(i int, v string, s []string) []string { + // Allocate new slice to avoid modifying the input slice + newS := make([]string, len(s)+1) + + // Copy s[0, i-1] into newS + for j := 0; j < i; j++ { + newS[j] = s[j] + } + + // Insert provided element at index i + newS[i] = v + + // Copy s[i, len(s)-1] into newS starting at newS[i+1] + for j := i; j < len(s); j++ { + newS[j+1] = s[j] + } + + return newS +} diff --git a/vendor/github.com/armon/go-metrics/sink.go b/vendor/github.com/armon/go-metrics/sink.go new file mode 100644 index 0000000..0b7d6e4 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/sink.go @@ -0,0 +1,115 @@ +package metrics + +import ( + "fmt" + "net/url" +) + +// The MetricSink interface is used to transmit metrics information +// to an external system +type MetricSink interface { + // A Gauge should retain the last value it is set to + SetGauge(key []string, val float32) + SetGaugeWithLabels(key []string, val float32, labels []Label) + + // Should emit a Key/Value pair for each call + EmitKey(key []string, val float32) + + // Counters should accumulate values + IncrCounter(key []string, val float32) + IncrCounterWithLabels(key []string, val float32, labels []Label) + + // Samples are for timing information, where quantiles are used + AddSample(key []string, val float32) + AddSampleWithLabels(key []string, val float32, labels []Label) +} + +// BlackholeSink is used to just blackhole messages +type BlackholeSink struct{} + +func (*BlackholeSink) SetGauge(key []string, val float32) {} +func (*BlackholeSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {} +func (*BlackholeSink) EmitKey(key []string, val float32) {} +func (*BlackholeSink) IncrCounter(key []string, val float32) {} +func (*BlackholeSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {} +func (*BlackholeSink) AddSample(key []string, val float32) {} +func (*BlackholeSink) AddSampleWithLabels(key []string, val float32, labels []Label) {} + +// FanoutSink is used to sink to fanout values to multiple sinks +type FanoutSink []MetricSink + +func (fh FanoutSink) SetGauge(key []string, val float32) { + fh.SetGaugeWithLabels(key, val, nil) +} + +func (fh FanoutSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + for _, s := range fh { + s.SetGaugeWithLabels(key, val, labels) + } +} + +func (fh FanoutSink) EmitKey(key []string, val float32) { + for _, s := range fh { + s.EmitKey(key, val) + } +} + +func (fh FanoutSink) IncrCounter(key []string, val float32) { + fh.IncrCounterWithLabels(key, val, nil) +} + +func (fh FanoutSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + for _, s := range fh { + s.IncrCounterWithLabels(key, val, labels) + } +} + +func (fh FanoutSink) AddSample(key []string, val float32) { + fh.AddSampleWithLabels(key, val, nil) +} + +func (fh FanoutSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + for _, s := range fh { + s.AddSampleWithLabels(key, val, labels) + } +} + +// sinkURLFactoryFunc is an generic interface around the *SinkFromURL() function provided +// by each sink type +type sinkURLFactoryFunc func(*url.URL) (MetricSink, error) + +// sinkRegistry supports the generic NewMetricSink function by mapping URL +// schemes to metric sink factory functions +var sinkRegistry = map[string]sinkURLFactoryFunc{ + "statsd": NewStatsdSinkFromURL, + "statsite": NewStatsiteSinkFromURL, + "inmem": NewInmemSinkFromURL, +} + +// NewMetricSinkFromURL allows a generic URL input to configure any of the +// supported sinks. The scheme of the URL identifies the type of the sink, the +// and query parameters are used to set options. +// +// "statsd://" - Initializes a StatsdSink. The host and port are passed through +// as the "addr" of the sink +// +// "statsite://" - Initializes a StatsiteSink. The host and port become the +// "addr" of the sink +// +// "inmem://" - Initializes an InmemSink. The host and port are ignored. The +// "interval" and "duration" query parameters must be specified with valid +// durations, see NewInmemSink for details. +func NewMetricSinkFromURL(urlStr string) (MetricSink, error) { + u, err := url.Parse(urlStr) + if err != nil { + return nil, err + } + + sinkURLFactoryFunc := sinkRegistry[u.Scheme] + if sinkURLFactoryFunc == nil { + return nil, fmt.Errorf( + "cannot create metric sink, unrecognized sink name: %q", u.Scheme) + } + + return sinkURLFactoryFunc(u) +} diff --git a/vendor/github.com/armon/go-metrics/start.go b/vendor/github.com/armon/go-metrics/start.go new file mode 100644 index 0000000..32a28c4 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/start.go @@ -0,0 +1,141 @@ +package metrics + +import ( + "os" + "sync" + "sync/atomic" + "time" + + "github.com/hashicorp/go-immutable-radix" +) + +// Config is used to configure metrics settings +type Config struct { + ServiceName string // Prefixed with keys to separate services + HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname + EnableHostname bool // Enable prefixing gauge values with hostname + EnableHostnameLabel bool // Enable adding hostname to labels + EnableServiceLabel bool // Enable adding service to labels + EnableRuntimeMetrics bool // Enables profiling of runtime metrics (GC, Goroutines, Memory) + EnableTypePrefix bool // Prefixes key with a type ("counter", "gauge", "timer") + TimerGranularity time.Duration // Granularity of timers. + ProfileInterval time.Duration // Interval to profile runtime metrics + + AllowedPrefixes []string // A list of metric prefixes to allow, with '.' as the separator + BlockedPrefixes []string // A list of metric prefixes to block, with '.' as the separator + AllowedLabels []string // A list of metric labels to allow, with '.' as the separator + BlockedLabels []string // A list of metric labels to block, with '.' as the separator + FilterDefault bool // Whether to allow metrics by default +} + +// Metrics represents an instance of a metrics sink that can +// be used to emit +type Metrics struct { + Config + lastNumGC uint32 + sink MetricSink + filter *iradix.Tree + allowedLabels map[string]bool + blockedLabels map[string]bool + filterLock sync.RWMutex // Lock filters and allowedLabels/blockedLabels access +} + +// Shared global metrics instance +var globalMetrics atomic.Value // *Metrics + +func init() { + // Initialize to a blackhole sink to avoid errors + globalMetrics.Store(&Metrics{sink: &BlackholeSink{}}) +} + +// DefaultConfig provides a sane default configuration +func DefaultConfig(serviceName string) *Config { + c := &Config{ + ServiceName: serviceName, // Use client provided service + HostName: "", + EnableHostname: true, // Enable hostname prefix + EnableRuntimeMetrics: true, // Enable runtime profiling + EnableTypePrefix: false, // Disable type prefix + TimerGranularity: time.Millisecond, // Timers are in milliseconds + ProfileInterval: time.Second, // Poll runtime every second + FilterDefault: true, // Don't filter metrics by default + } + + // Try to get the hostname + name, _ := os.Hostname() + c.HostName = name + return c +} + +// New is used to create a new instance of Metrics +func New(conf *Config, sink MetricSink) (*Metrics, error) { + met := &Metrics{} + met.Config = *conf + met.sink = sink + met.UpdateFilterAndLabels(conf.AllowedPrefixes, conf.BlockedPrefixes, conf.AllowedLabels, conf.BlockedLabels) + + // Start the runtime collector + if conf.EnableRuntimeMetrics { + go met.collectStats() + } + return met, nil +} + +// NewGlobal is the same as New, but it assigns the metrics object to be +// used globally as well as returning it. +func NewGlobal(conf *Config, sink MetricSink) (*Metrics, error) { + metrics, err := New(conf, sink) + if err == nil { + globalMetrics.Store(metrics) + } + return metrics, err +} + +// Proxy all the methods to the globalMetrics instance +func SetGauge(key []string, val float32) { + globalMetrics.Load().(*Metrics).SetGauge(key, val) +} + +func SetGaugeWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).SetGaugeWithLabels(key, val, labels) +} + +func EmitKey(key []string, val float32) { + globalMetrics.Load().(*Metrics).EmitKey(key, val) +} + +func IncrCounter(key []string, val float32) { + globalMetrics.Load().(*Metrics).IncrCounter(key, val) +} + +func IncrCounterWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).IncrCounterWithLabels(key, val, labels) +} + +func AddSample(key []string, val float32) { + globalMetrics.Load().(*Metrics).AddSample(key, val) +} + +func AddSampleWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).AddSampleWithLabels(key, val, labels) +} + +func MeasureSince(key []string, start time.Time) { + globalMetrics.Load().(*Metrics).MeasureSince(key, start) +} + +func MeasureSinceWithLabels(key []string, start time.Time, labels []Label) { + globalMetrics.Load().(*Metrics).MeasureSinceWithLabels(key, start, labels) +} + +func UpdateFilter(allow, block []string) { + globalMetrics.Load().(*Metrics).UpdateFilter(allow, block) +} + +// UpdateFilterAndLabels set allow/block prefixes of metrics while allowedLabels +// and blockedLabels - when not nil - allow filtering of labels in order to +// block/allow globally labels (especially useful when having large number of +// values for a given label). See README.md for more information about usage. +func UpdateFilterAndLabels(allow, block, allowedLabels, blockedLabels []string) { + globalMetrics.Load().(*Metrics).UpdateFilterAndLabels(allow, block, allowedLabels, blockedLabels) +} diff --git a/vendor/github.com/armon/go-metrics/statsd.go b/vendor/github.com/armon/go-metrics/statsd.go new file mode 100644 index 0000000..1bfffce --- /dev/null +++ b/vendor/github.com/armon/go-metrics/statsd.go @@ -0,0 +1,184 @@ +package metrics + +import ( + "bytes" + "fmt" + "log" + "net" + "net/url" + "strings" + "time" +) + +const ( + // statsdMaxLen is the maximum size of a packet + // to send to statsd + statsdMaxLen = 1400 +) + +// StatsdSink provides a MetricSink that can be used +// with a statsite or statsd metrics server. It uses +// only UDP packets, while StatsiteSink uses TCP. +type StatsdSink struct { + addr string + metricQueue chan string +} + +// NewStatsdSinkFromURL creates an StatsdSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewStatsdSinkFromURL(u *url.URL) (MetricSink, error) { + return NewStatsdSink(u.Host) +} + +// NewStatsdSink is used to create a new StatsdSink +func NewStatsdSink(addr string) (*StatsdSink, error) { + s := &StatsdSink{ + addr: addr, + metricQueue: make(chan string, 4096), + } + go s.flushMetrics() + return s, nil +} + +// Close is used to stop flushing to statsd +func (s *StatsdSink) Shutdown() { + close(s.metricQueue) +} + +func (s *StatsdSink) SetGauge(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + +func (s *StatsdSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + +func (s *StatsdSink) EmitKey(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) +} + +func (s *StatsdSink) IncrCounter(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + +func (s *StatsdSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + +func (s *StatsdSink) AddSample(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + +func (s *StatsdSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + +// Flattens the key for formatting, removes spaces +func (s *StatsdSink) flattenKey(parts []string) string { + joined := strings.Join(parts, ".") + return strings.Map(func(r rune) rune { + switch r { + case ':': + fallthrough + case ' ': + return '_' + default: + return r + } + }, joined) +} + +// Flattens the key along with labels for formatting, removes spaces +func (s *StatsdSink) flattenKeyLabels(parts []string, labels []Label) string { + for _, label := range labels { + parts = append(parts, label.Value) + } + return s.flattenKey(parts) +} + +// Does a non-blocking push to the metrics queue +func (s *StatsdSink) pushMetric(m string) { + select { + case s.metricQueue <- m: + default: + } +} + +// Flushes metrics +func (s *StatsdSink) flushMetrics() { + var sock net.Conn + var err error + var wait <-chan time.Time + ticker := time.NewTicker(flushInterval) + defer ticker.Stop() + +CONNECT: + // Create a buffer + buf := bytes.NewBuffer(nil) + + // Attempt to connect + sock, err = net.Dial("udp", s.addr) + if err != nil { + log.Printf("[ERR] Error connecting to statsd! Err: %s", err) + goto WAIT + } + + for { + select { + case metric, ok := <-s.metricQueue: + // Get a metric from the queue + if !ok { + goto QUIT + } + + // Check if this would overflow the packet size + if len(metric)+buf.Len() > statsdMaxLen { + _, err := sock.Write(buf.Bytes()) + buf.Reset() + if err != nil { + log.Printf("[ERR] Error writing to statsd! Err: %s", err) + goto WAIT + } + } + + // Append to the buffer + buf.WriteString(metric) + + case <-ticker.C: + if buf.Len() == 0 { + continue + } + + _, err := sock.Write(buf.Bytes()) + buf.Reset() + if err != nil { + log.Printf("[ERR] Error flushing to statsd! Err: %s", err) + goto WAIT + } + } + } + +WAIT: + // Wait for a while + wait = time.After(time.Duration(5) * time.Second) + for { + select { + // Dequeue the messages to avoid backlog + case _, ok := <-s.metricQueue: + if !ok { + goto QUIT + } + case <-wait: + goto CONNECT + } + } +QUIT: + s.metricQueue = nil +} diff --git a/vendor/github.com/armon/go-metrics/statsite.go b/vendor/github.com/armon/go-metrics/statsite.go new file mode 100644 index 0000000..6c0d284 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/statsite.go @@ -0,0 +1,172 @@ +package metrics + +import ( + "bufio" + "fmt" + "log" + "net" + "net/url" + "strings" + "time" +) + +const ( + // We force flush the statsite metrics after this period of + // inactivity. Prevents stats from getting stuck in a buffer + // forever. + flushInterval = 100 * time.Millisecond +) + +// NewStatsiteSinkFromURL creates an StatsiteSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewStatsiteSinkFromURL(u *url.URL) (MetricSink, error) { + return NewStatsiteSink(u.Host) +} + +// StatsiteSink provides a MetricSink that can be used with a +// statsite metrics server +type StatsiteSink struct { + addr string + metricQueue chan string +} + +// NewStatsiteSink is used to create a new StatsiteSink +func NewStatsiteSink(addr string) (*StatsiteSink, error) { + s := &StatsiteSink{ + addr: addr, + metricQueue: make(chan string, 4096), + } + go s.flushMetrics() + return s, nil +} + +// Close is used to stop flushing to statsite +func (s *StatsiteSink) Shutdown() { + close(s.metricQueue) +} + +func (s *StatsiteSink) SetGauge(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + +func (s *StatsiteSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + +func (s *StatsiteSink) EmitKey(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) +} + +func (s *StatsiteSink) IncrCounter(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + +func (s *StatsiteSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + +func (s *StatsiteSink) AddSample(key []string, val float32) { + flatKey := s.flattenKey(key) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + +func (s *StatsiteSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + +// Flattens the key for formatting, removes spaces +func (s *StatsiteSink) flattenKey(parts []string) string { + joined := strings.Join(parts, ".") + return strings.Map(func(r rune) rune { + switch r { + case ':': + fallthrough + case ' ': + return '_' + default: + return r + } + }, joined) +} + +// Flattens the key along with labels for formatting, removes spaces +func (s *StatsiteSink) flattenKeyLabels(parts []string, labels []Label) string { + for _, label := range labels { + parts = append(parts, label.Value) + } + return s.flattenKey(parts) +} + +// Does a non-blocking push to the metrics queue +func (s *StatsiteSink) pushMetric(m string) { + select { + case s.metricQueue <- m: + default: + } +} + +// Flushes metrics +func (s *StatsiteSink) flushMetrics() { + var sock net.Conn + var err error + var wait <-chan time.Time + var buffered *bufio.Writer + ticker := time.NewTicker(flushInterval) + defer ticker.Stop() + +CONNECT: + // Attempt to connect + sock, err = net.Dial("tcp", s.addr) + if err != nil { + log.Printf("[ERR] Error connecting to statsite! Err: %s", err) + goto WAIT + } + + // Create a buffered writer + buffered = bufio.NewWriter(sock) + + for { + select { + case metric, ok := <-s.metricQueue: + // Get a metric from the queue + if !ok { + goto QUIT + } + + // Try to send to statsite + _, err := buffered.Write([]byte(metric)) + if err != nil { + log.Printf("[ERR] Error writing to statsite! Err: %s", err) + goto WAIT + } + case <-ticker.C: + if err := buffered.Flush(); err != nil { + log.Printf("[ERR] Error flushing to statsite! Err: %s", err) + goto WAIT + } + } + } + +WAIT: + // Wait for a while + wait = time.After(time.Duration(5) * time.Second) + for { + select { + // Dequeue the messages to avoid backlog + case _, ok := <-s.metricQueue: + if !ok { + goto QUIT + } + case <-wait: + goto CONNECT + } + } +QUIT: + s.metricQueue = nil +} diff --git a/vendor/github.com/beorn7/perks/LICENSE b/vendor/github.com/beorn7/perks/LICENSE new file mode 100644 index 0000000..339177b --- /dev/null +++ b/vendor/github.com/beorn7/perks/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013 Blake Mizerany + +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. diff --git a/vendor/github.com/beorn7/perks/quantile/exampledata.txt b/vendor/github.com/beorn7/perks/quantile/exampledata.txt new file mode 100644 index 0000000..1602287 --- /dev/null +++ b/vendor/github.com/beorn7/perks/quantile/exampledata.txt @@ -0,0 +1,2388 @@ +8 +5 +26 +12 +5 +235 +13 +6 +28 +30 +3 +3 +3 +3 +5 +2 +33 +7 +2 +4 +7 +12 +14 +5 +8 +3 +10 +4 +5 +3 +6 +6 +209 +20 +3 +10 +14 +3 +4 +6 +8 +5 +11 +7 +3 +2 +3 +3 +212 +5 +222 +4 +10 +10 +5 +6 +3 +8 +3 +10 +254 +220 +2 +3 +5 +24 +5 +4 +222 +7 +3 +3 +223 +8 +15 +12 +14 +14 +3 +2 +2 +3 +13 +3 +11 +4 +4 +6 +5 +7 +13 +5 +3 +5 +2 +5 +3 +5 +2 +7 +15 +17 +14 +3 +6 +6 +3 +17 +5 +4 +7 +6 +4 +4 +8 +6 +8 +3 +9 +3 +6 +3 +4 +5 +3 +3 +660 +4 +6 +10 +3 +6 +3 +2 +5 +13 +2 +4 +4 +10 +4 +8 +4 +3 +7 +9 +9 +3 +10 +37 +3 +13 +4 +12 +3 +6 +10 +8 +5 +21 +2 +3 +8 +3 +2 +3 +3 +4 +12 +2 +4 +8 +8 +4 +3 +2 +20 +1 +6 +32 +2 +11 +6 +18 +3 +8 +11 +3 +212 +3 +4 +2 +6 +7 +12 +11 +3 +2 +16 +10 +6 +4 +6 +3 +2 +7 +3 +2 +2 +2 +2 +5 +6 +4 +3 +10 +3 +4 +6 +5 +3 +4 +4 +5 +6 +4 +3 +4 +4 +5 +7 +5 +5 +3 +2 +7 +2 +4 +12 +4 +5 +6 +2 +4 +4 +8 +4 +15 +13 +7 +16 +5 +3 +23 +5 +5 +7 +3 +2 +9 +8 +7 +5 +8 +11 +4 +10 +76 +4 +47 +4 +3 +2 +7 +4 +2 +3 +37 +10 +4 +2 +20 +5 +4 +4 +10 +10 +4 +3 +7 +23 +240 +7 +13 +5 +5 +3 +3 +2 +5 +4 +2 +8 +7 +19 +2 +23 +8 +7 +2 +5 +3 +8 +3 +8 +13 +5 +5 +5 +2 +3 +23 +4 +9 +8 +4 +3 +3 +5 +220 +2 +3 +4 +6 +14 +3 +53 +6 +2 +5 +18 +6 +3 +219 +6 +5 +2 +5 +3 +6 +5 +15 +4 +3 +17 +3 +2 +4 +7 +2 +3 +3 +4 +4 +3 +2 +664 +6 +3 +23 +5 +5 +16 +5 +8 +2 +4 +2 +24 +12 +3 +2 +3 +5 +8 +3 +5 +4 +3 +14 +3 +5 +8 +2 +3 +7 +9 +4 +2 +3 +6 +8 +4 +3 +4 +6 +5 +3 +3 +6 +3 +19 +4 +4 +6 +3 +6 +3 +5 +22 +5 +4 +4 +3 +8 +11 +4 +9 +7 +6 +13 +4 +4 +4 +6 +17 +9 +3 +3 +3 +4 +3 +221 +5 +11 +3 +4 +2 +12 +6 +3 +5 +7 +5 +7 +4 +9 +7 +14 +37 +19 +217 +16 +3 +5 +2 +2 +7 +19 +7 +6 +7 +4 +24 +5 +11 +4 +7 +7 +9 +13 +3 +4 +3 +6 +28 +4 +4 +5 +5 +2 +5 +6 +4 +4 +6 +10 +5 +4 +3 +2 +3 +3 +6 +5 +5 +4 +3 +2 +3 +7 +4 +6 +18 +16 +8 +16 +4 +5 +8 +6 +9 +13 +1545 +6 +215 +6 +5 +6 +3 +45 +31 +5 +2 +2 +4 +3 +3 +2 +5 +4 +3 +5 +7 +7 +4 +5 +8 +5 +4 +749 +2 +31 +9 +11 +2 +11 +5 +4 +4 +7 +9 +11 +4 +5 +4 +7 +3 +4 +6 +2 +15 +3 +4 +3 +4 +3 +5 +2 +13 +5 +5 +3 +3 +23 +4 +4 +5 +7 +4 +13 +2 +4 +3 +4 +2 +6 +2 +7 +3 +5 +5 +3 +29 +5 +4 +4 +3 +10 +2 +3 +79 +16 +6 +6 +7 +7 +3 +5 +5 +7 +4 +3 +7 +9 +5 +6 +5 +9 +6 +3 +6 +4 +17 +2 +10 +9 +3 +6 +2 +3 +21 +22 +5 +11 +4 +2 +17 +2 +224 +2 +14 +3 +4 +4 +2 +4 +4 +4 +4 +5 +3 +4 +4 +10 +2 +6 +3 +3 +5 +7 +2 +7 +5 +6 +3 +218 +2 +2 +5 +2 +6 +3 +5 +222 +14 +6 +33 +3 +2 +5 +3 +3 +3 +9 +5 +3 +3 +2 +7 +4 +3 +4 +3 +5 +6 +5 +26 +4 +13 +9 +7 +3 +221 +3 +3 +4 +4 +4 +4 +2 +18 +5 +3 +7 +9 +6 +8 +3 +10 +3 +11 +9 +5 +4 +17 +5 +5 +6 +6 +3 +2 +4 +12 +17 +6 +7 +218 +4 +2 +4 +10 +3 +5 +15 +3 +9 +4 +3 +3 +6 +29 +3 +3 +4 +5 +5 +3 +8 +5 +6 +6 +7 +5 +3 +5 +3 +29 +2 +31 +5 +15 +24 +16 +5 +207 +4 +3 +3 +2 +15 +4 +4 +13 +5 +5 +4 +6 +10 +2 +7 +8 +4 +6 +20 +5 +3 +4 +3 +12 +12 +5 +17 +7 +3 +3 +3 +6 +10 +3 +5 +25 +80 +4 +9 +3 +2 +11 +3 +3 +2 +3 +8 +7 +5 +5 +19 +5 +3 +3 +12 +11 +2 +6 +5 +5 +5 +3 +3 +3 +4 +209 +14 +3 +2 +5 +19 +4 +4 +3 +4 +14 +5 +6 +4 +13 +9 +7 +4 +7 +10 +2 +9 +5 +7 +2 +8 +4 +6 +5 +5 +222 +8 +7 +12 +5 +216 +3 +4 +4 +6 +3 +14 +8 +7 +13 +4 +3 +3 +3 +3 +17 +5 +4 +3 +33 +6 +6 +33 +7 +5 +3 +8 +7 +5 +2 +9 +4 +2 +233 +24 +7 +4 +8 +10 +3 +4 +15 +2 +16 +3 +3 +13 +12 +7 +5 +4 +207 +4 +2 +4 +27 +15 +2 +5 +2 +25 +6 +5 +5 +6 +13 +6 +18 +6 +4 +12 +225 +10 +7 +5 +2 +2 +11 +4 +14 +21 +8 +10 +3 +5 +4 +232 +2 +5 +5 +3 +7 +17 +11 +6 +6 +23 +4 +6 +3 +5 +4 +2 +17 +3 +6 +5 +8 +3 +2 +2 +14 +9 +4 +4 +2 +5 +5 +3 +7 +6 +12 +6 +10 +3 +6 +2 +2 +19 +5 +4 +4 +9 +2 +4 +13 +3 +5 +6 +3 +6 +5 +4 +9 +6 +3 +5 +7 +3 +6 +6 +4 +3 +10 +6 +3 +221 +3 +5 +3 +6 +4 +8 +5 +3 +6 +4 +4 +2 +54 +5 +6 +11 +3 +3 +4 +4 +4 +3 +7 +3 +11 +11 +7 +10 +6 +13 +223 +213 +15 +231 +7 +3 +7 +228 +2 +3 +4 +4 +5 +6 +7 +4 +13 +3 +4 +5 +3 +6 +4 +6 +7 +2 +4 +3 +4 +3 +3 +6 +3 +7 +3 +5 +18 +5 +6 +8 +10 +3 +3 +3 +2 +4 +2 +4 +4 +5 +6 +6 +4 +10 +13 +3 +12 +5 +12 +16 +8 +4 +19 +11 +2 +4 +5 +6 +8 +5 +6 +4 +18 +10 +4 +2 +216 +6 +6 +6 +2 +4 +12 +8 +3 +11 +5 +6 +14 +5 +3 +13 +4 +5 +4 +5 +3 +28 +6 +3 +7 +219 +3 +9 +7 +3 +10 +6 +3 +4 +19 +5 +7 +11 +6 +15 +19 +4 +13 +11 +3 +7 +5 +10 +2 +8 +11 +2 +6 +4 +6 +24 +6 +3 +3 +3 +3 +6 +18 +4 +11 +4 +2 +5 +10 +8 +3 +9 +5 +3 +4 +5 +6 +2 +5 +7 +4 +4 +14 +6 +4 +4 +5 +5 +7 +2 +4 +3 +7 +3 +3 +6 +4 +5 +4 +4 +4 +3 +3 +3 +3 +8 +14 +2 +3 +5 +3 +2 +4 +5 +3 +7 +3 +3 +18 +3 +4 +4 +5 +7 +3 +3 +3 +13 +5 +4 +8 +211 +5 +5 +3 +5 +2 +5 +4 +2 +655 +6 +3 +5 +11 +2 +5 +3 +12 +9 +15 +11 +5 +12 +217 +2 +6 +17 +3 +3 +207 +5 +5 +4 +5 +9 +3 +2 +8 +5 +4 +3 +2 +5 +12 +4 +14 +5 +4 +2 +13 +5 +8 +4 +225 +4 +3 +4 +5 +4 +3 +3 +6 +23 +9 +2 +6 +7 +233 +4 +4 +6 +18 +3 +4 +6 +3 +4 +4 +2 +3 +7 +4 +13 +227 +4 +3 +5 +4 +2 +12 +9 +17 +3 +7 +14 +6 +4 +5 +21 +4 +8 +9 +2 +9 +25 +16 +3 +6 +4 +7 +8 +5 +2 +3 +5 +4 +3 +3 +5 +3 +3 +3 +2 +3 +19 +2 +4 +3 +4 +2 +3 +4 +4 +2 +4 +3 +3 +3 +2 +6 +3 +17 +5 +6 +4 +3 +13 +5 +3 +3 +3 +4 +9 +4 +2 +14 +12 +4 +5 +24 +4 +3 +37 +12 +11 +21 +3 +4 +3 +13 +4 +2 +3 +15 +4 +11 +4 +4 +3 +8 +3 +4 +4 +12 +8 +5 +3 +3 +4 +2 +220 +3 +5 +223 +3 +3 +3 +10 +3 +15 +4 +241 +9 +7 +3 +6 +6 +23 +4 +13 +7 +3 +4 +7 +4 +9 +3 +3 +4 +10 +5 +5 +1 +5 +24 +2 +4 +5 +5 +6 +14 +3 +8 +2 +3 +5 +13 +13 +3 +5 +2 +3 +15 +3 +4 +2 +10 +4 +4 +4 +5 +5 +3 +5 +3 +4 +7 +4 +27 +3 +6 +4 +15 +3 +5 +6 +6 +5 +4 +8 +3 +9 +2 +6 +3 +4 +3 +7 +4 +18 +3 +11 +3 +3 +8 +9 +7 +24 +3 +219 +7 +10 +4 +5 +9 +12 +2 +5 +4 +4 +4 +3 +3 +19 +5 +8 +16 +8 +6 +22 +3 +23 +3 +242 +9 +4 +3 +3 +5 +7 +3 +3 +5 +8 +3 +7 +5 +14 +8 +10 +3 +4 +3 +7 +4 +6 +7 +4 +10 +4 +3 +11 +3 +7 +10 +3 +13 +6 +8 +12 +10 +5 +7 +9 +3 +4 +7 +7 +10 +8 +30 +9 +19 +4 +3 +19 +15 +4 +13 +3 +215 +223 +4 +7 +4 +8 +17 +16 +3 +7 +6 +5 +5 +4 +12 +3 +7 +4 +4 +13 +4 +5 +2 +5 +6 +5 +6 +6 +7 +10 +18 +23 +9 +3 +3 +6 +5 +2 +4 +2 +7 +3 +3 +2 +5 +5 +14 +10 +224 +6 +3 +4 +3 +7 +5 +9 +3 +6 +4 +2 +5 +11 +4 +3 +3 +2 +8 +4 +7 +4 +10 +7 +3 +3 +18 +18 +17 +3 +3 +3 +4 +5 +3 +3 +4 +12 +7 +3 +11 +13 +5 +4 +7 +13 +5 +4 +11 +3 +12 +3 +6 +4 +4 +21 +4 +6 +9 +5 +3 +10 +8 +4 +6 +4 +4 +6 +5 +4 +8 +6 +4 +6 +4 +4 +5 +9 +6 +3 +4 +2 +9 +3 +18 +2 +4 +3 +13 +3 +6 +6 +8 +7 +9 +3 +2 +16 +3 +4 +6 +3 +2 +33 +22 +14 +4 +9 +12 +4 +5 +6 +3 +23 +9 +4 +3 +5 +5 +3 +4 +5 +3 +5 +3 +10 +4 +5 +5 +8 +4 +4 +6 +8 +5 +4 +3 +4 +6 +3 +3 +3 +5 +9 +12 +6 +5 +9 +3 +5 +3 +2 +2 +2 +18 +3 +2 +21 +2 +5 +4 +6 +4 +5 +10 +3 +9 +3 +2 +10 +7 +3 +6 +6 +4 +4 +8 +12 +7 +3 +7 +3 +3 +9 +3 +4 +5 +4 +4 +5 +5 +10 +15 +4 +4 +14 +6 +227 +3 +14 +5 +216 +22 +5 +4 +2 +2 +6 +3 +4 +2 +9 +9 +4 +3 +28 +13 +11 +4 +5 +3 +3 +2 +3 +3 +5 +3 +4 +3 +5 +23 +26 +3 +4 +5 +6 +4 +6 +3 +5 +5 +3 +4 +3 +2 +2 +2 +7 +14 +3 +6 +7 +17 +2 +2 +15 +14 +16 +4 +6 +7 +13 +6 +4 +5 +6 +16 +3 +3 +28 +3 +6 +15 +3 +9 +2 +4 +6 +3 +3 +22 +4 +12 +6 +7 +2 +5 +4 +10 +3 +16 +6 +9 +2 +5 +12 +7 +5 +5 +5 +5 +2 +11 +9 +17 +4 +3 +11 +7 +3 +5 +15 +4 +3 +4 +211 +8 +7 +5 +4 +7 +6 +7 +6 +3 +6 +5 +6 +5 +3 +4 +4 +26 +4 +6 +10 +4 +4 +3 +2 +3 +3 +4 +5 +9 +3 +9 +4 +4 +5 +5 +8 +2 +4 +2 +3 +8 +4 +11 +19 +5 +8 +6 +3 +5 +6 +12 +3 +2 +4 +16 +12 +3 +4 +4 +8 +6 +5 +6 +6 +219 +8 +222 +6 +16 +3 +13 +19 +5 +4 +3 +11 +6 +10 +4 +7 +7 +12 +5 +3 +3 +5 +6 +10 +3 +8 +2 +5 +4 +7 +2 +4 +4 +2 +12 +9 +6 +4 +2 +40 +2 +4 +10 +4 +223 +4 +2 +20 +6 +7 +24 +5 +4 +5 +2 +20 +16 +6 +5 +13 +2 +3 +3 +19 +3 +2 +4 +5 +6 +7 +11 +12 +5 +6 +7 +7 +3 +5 +3 +5 +3 +14 +3 +4 +4 +2 +11 +1 +7 +3 +9 +6 +11 +12 +5 +8 +6 +221 +4 +2 +12 +4 +3 +15 +4 +5 +226 +7 +218 +7 +5 +4 +5 +18 +4 +5 +9 +4 +4 +2 +9 +18 +18 +9 +5 +6 +6 +3 +3 +7 +3 +5 +4 +4 +4 +12 +3 +6 +31 +5 +4 +7 +3 +6 +5 +6 +5 +11 +2 +2 +11 +11 +6 +7 +5 +8 +7 +10 +5 +23 +7 +4 +3 +5 +34 +2 +5 +23 +7 +3 +6 +8 +4 +4 +4 +2 +5 +3 +8 +5 +4 +8 +25 +2 +3 +17 +8 +3 +4 +8 +7 +3 +15 +6 +5 +7 +21 +9 +5 +6 +6 +5 +3 +2 +3 +10 +3 +6 +3 +14 +7 +4 +4 +8 +7 +8 +2 +6 +12 +4 +213 +6 +5 +21 +8 +2 +5 +23 +3 +11 +2 +3 +6 +25 +2 +3 +6 +7 +6 +6 +4 +4 +6 +3 +17 +9 +7 +6 +4 +3 +10 +7 +2 +3 +3 +3 +11 +8 +3 +7 +6 +4 +14 +36 +3 +4 +3 +3 +22 +13 +21 +4 +2 +7 +4 +4 +17 +15 +3 +7 +11 +2 +4 +7 +6 +209 +6 +3 +2 +2 +24 +4 +9 +4 +3 +3 +3 +29 +2 +2 +4 +3 +3 +5 +4 +6 +3 +3 +2 +4 diff --git a/vendor/github.com/beorn7/perks/quantile/stream.go b/vendor/github.com/beorn7/perks/quantile/stream.go new file mode 100644 index 0000000..d7d14f8 --- /dev/null +++ b/vendor/github.com/beorn7/perks/quantile/stream.go @@ -0,0 +1,316 @@ +// Package quantile computes approximate quantiles over an unbounded data +// stream within low memory and CPU bounds. +// +// A small amount of accuracy is traded to achieve the above properties. +// +// Multiple streams can be merged before calling Query to generate a single set +// of results. This is meaningful when the streams represent the same type of +// data. See Merge and Samples. +// +// For more detailed information about the algorithm used, see: +// +// Effective Computation of Biased Quantiles over Data Streams +// +// http://www.cs.rutgers.edu/~muthu/bquant.pdf +package quantile + +import ( + "math" + "sort" +) + +// Sample holds an observed value and meta information for compression. JSON +// tags have been added for convenience. +type Sample struct { + Value float64 `json:",string"` + Width float64 `json:",string"` + Delta float64 `json:",string"` +} + +// Samples represents a slice of samples. It implements sort.Interface. +type Samples []Sample + +func (a Samples) Len() int { return len(a) } +func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value } +func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type invariant func(s *stream, r float64) float64 + +// NewLowBiased returns an initialized Stream for low-biased quantiles +// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but +// error guarantees can still be given even for the lower ranks of the data +// distribution. +// +// The provided epsilon is a relative error, i.e. the true quantile of a value +// returned by a query is guaranteed to be within (1±Epsilon)*Quantile. +// +// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error +// properties. +func NewLowBiased(epsilon float64) *Stream { + ƒ := func(s *stream, r float64) float64 { + return 2 * epsilon * r + } + return newStream(ƒ) +} + +// NewHighBiased returns an initialized Stream for high-biased quantiles +// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but +// error guarantees can still be given even for the higher ranks of the data +// distribution. +// +// The provided epsilon is a relative error, i.e. the true quantile of a value +// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile). +// +// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error +// properties. +func NewHighBiased(epsilon float64) *Stream { + ƒ := func(s *stream, r float64) float64 { + return 2 * epsilon * (s.n - r) + } + return newStream(ƒ) +} + +// NewTargeted returns an initialized Stream concerned with a particular set of +// quantile values that are supplied a priori. Knowing these a priori reduces +// space and computation time. The targets map maps the desired quantiles to +// their absolute errors, i.e. the true quantile of a value returned by a query +// is guaranteed to be within (Quantile±Epsilon). +// +// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties. +func NewTargeted(targetMap map[float64]float64) *Stream { + // Convert map to slice to avoid slow iterations on a map. + // ƒ is called on the hot path, so converting the map to a slice + // beforehand results in significant CPU savings. + targets := targetMapToSlice(targetMap) + + ƒ := func(s *stream, r float64) float64 { + var m = math.MaxFloat64 + var f float64 + for _, t := range targets { + if t.quantile*s.n <= r { + f = (2 * t.epsilon * r) / t.quantile + } else { + f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile) + } + if f < m { + m = f + } + } + return m + } + return newStream(ƒ) +} + +type target struct { + quantile float64 + epsilon float64 +} + +func targetMapToSlice(targetMap map[float64]float64) []target { + targets := make([]target, 0, len(targetMap)) + + for quantile, epsilon := range targetMap { + t := target{ + quantile: quantile, + epsilon: epsilon, + } + targets = append(targets, t) + } + + return targets +} + +// Stream computes quantiles for a stream of float64s. It is not thread-safe by +// design. Take care when using across multiple goroutines. +type Stream struct { + *stream + b Samples + sorted bool +} + +func newStream(ƒ invariant) *Stream { + x := &stream{ƒ: ƒ} + return &Stream{x, make(Samples, 0, 500), true} +} + +// Insert inserts v into the stream. +func (s *Stream) Insert(v float64) { + s.insert(Sample{Value: v, Width: 1}) +} + +func (s *Stream) insert(sample Sample) { + s.b = append(s.b, sample) + s.sorted = false + if len(s.b) == cap(s.b) { + s.flush() + } +} + +// Query returns the computed qth percentiles value. If s was created with +// NewTargeted, and q is not in the set of quantiles provided a priori, Query +// will return an unspecified result. +func (s *Stream) Query(q float64) float64 { + if !s.flushed() { + // Fast path when there hasn't been enough data for a flush; + // this also yields better accuracy for small sets of data. + l := len(s.b) + if l == 0 { + return 0 + } + i := int(math.Ceil(float64(l) * q)) + if i > 0 { + i -= 1 + } + s.maybeSort() + return s.b[i].Value + } + s.flush() + return s.stream.query(q) +} + +// Merge merges samples into the underlying streams samples. This is handy when +// merging multiple streams from separate threads, database shards, etc. +// +// ATTENTION: This method is broken and does not yield correct results. The +// underlying algorithm is not capable of merging streams correctly. +func (s *Stream) Merge(samples Samples) { + sort.Sort(samples) + s.stream.merge(samples) +} + +// Reset reinitializes and clears the list reusing the samples buffer memory. +func (s *Stream) Reset() { + s.stream.reset() + s.b = s.b[:0] +} + +// Samples returns stream samples held by s. +func (s *Stream) Samples() Samples { + if !s.flushed() { + return s.b + } + s.flush() + return s.stream.samples() +} + +// Count returns the total number of samples observed in the stream +// since initialization. +func (s *Stream) Count() int { + return len(s.b) + s.stream.count() +} + +func (s *Stream) flush() { + s.maybeSort() + s.stream.merge(s.b) + s.b = s.b[:0] +} + +func (s *Stream) maybeSort() { + if !s.sorted { + s.sorted = true + sort.Sort(s.b) + } +} + +func (s *Stream) flushed() bool { + return len(s.stream.l) > 0 +} + +type stream struct { + n float64 + l []Sample + ƒ invariant +} + +func (s *stream) reset() { + s.l = s.l[:0] + s.n = 0 +} + +func (s *stream) insert(v float64) { + s.merge(Samples{{v, 1, 0}}) +} + +func (s *stream) merge(samples Samples) { + // TODO(beorn7): This tries to merge not only individual samples, but + // whole summaries. The paper doesn't mention merging summaries at + // all. Unittests show that the merging is inaccurate. Find out how to + // do merges properly. + var r float64 + i := 0 + for _, sample := range samples { + for ; i < len(s.l); i++ { + c := s.l[i] + if c.Value > sample.Value { + // Insert at position i. + s.l = append(s.l, Sample{}) + copy(s.l[i+1:], s.l[i:]) + s.l[i] = Sample{ + sample.Value, + sample.Width, + math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1), + // TODO(beorn7): How to calculate delta correctly? + } + i++ + goto inserted + } + r += c.Width + } + s.l = append(s.l, Sample{sample.Value, sample.Width, 0}) + i++ + inserted: + s.n += sample.Width + r += sample.Width + } + s.compress() +} + +func (s *stream) count() int { + return int(s.n) +} + +func (s *stream) query(q float64) float64 { + t := math.Ceil(q * s.n) + t += math.Ceil(s.ƒ(s, t) / 2) + p := s.l[0] + var r float64 + for _, c := range s.l[1:] { + r += p.Width + if r+c.Width+c.Delta > t { + return p.Value + } + p = c + } + return p.Value +} + +func (s *stream) compress() { + if len(s.l) < 2 { + return + } + x := s.l[len(s.l)-1] + xi := len(s.l) - 1 + r := s.n - 1 - x.Width + + for i := len(s.l) - 2; i >= 0; i-- { + c := s.l[i] + if c.Width+x.Width+x.Delta <= s.ƒ(s, r) { + x.Width += c.Width + s.l[xi] = x + // Remove element at i. + copy(s.l[i:], s.l[i+1:]) + s.l = s.l[:len(s.l)-1] + xi -= 1 + } else { + x = c + xi = i + } + r -= c.Width + } +} + +func (s *stream) samples() Samples { + samples := make(Samples, len(s.l)) + copy(samples, s.l) + return samples +} diff --git a/vendor/github.com/bitly/go-simplejson/.travis.yml b/vendor/github.com/bitly/go-simplejson/.travis.yml new file mode 100644 index 0000000..55accb9 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.0.3 + - 1.1.2 + - 1.2 + - tip +install: + - go get github.com/bmizerany/assert +notifications: + email: false diff --git a/vendor/github.com/bitly/go-simplejson/LICENSE b/vendor/github.com/bitly/go-simplejson/LICENSE new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/LICENSE @@ -0,0 +1,17 @@ +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. diff --git a/vendor/github.com/bitly/go-simplejson/README.md b/vendor/github.com/bitly/go-simplejson/README.md new file mode 100644 index 0000000..8c6c101 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/README.md @@ -0,0 +1,13 @@ +### go-simplejson + +a Go package to interact with arbitrary JSON + +[![Build Status](https://secure.travis-ci.org/bitly/go-simplejson.png)](http://travis-ci.org/bitly/go-simplejson) + +### Importing + + import github.com/bitly/go-simplejson + +### Documentation + +Visit the docs on [gopkgdoc](http://godoc.org/github.com/bitly/go-simplejson) diff --git a/vendor/github.com/bitly/go-simplejson/simplejson.go b/vendor/github.com/bitly/go-simplejson/simplejson.go new file mode 100644 index 0000000..27ce986 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/simplejson.go @@ -0,0 +1,446 @@ +package simplejson + +import ( + "encoding/json" + "errors" + "log" +) + +// returns the current implementation version +func Version() string { + return "0.5.0" +} + +type Json struct { + data interface{} +} + +// NewJson returns a pointer to a new `Json` object +// after unmarshaling `body` bytes +func NewJson(body []byte) (*Json, error) { + j := new(Json) + err := j.UnmarshalJSON(body) + if err != nil { + return nil, err + } + return j, nil +} + +// New returns a pointer to a new, empty `Json` object +func New() *Json { + return &Json{ + data: make(map[string]interface{}), + } +} + +// Interface returns the underlying data +func (j *Json) Interface() interface{} { + return j.data +} + +// Encode returns its marshaled data as `[]byte` +func (j *Json) Encode() ([]byte, error) { + return j.MarshalJSON() +} + +// EncodePretty returns its marshaled data as `[]byte` with indentation +func (j *Json) EncodePretty() ([]byte, error) { + return json.MarshalIndent(&j.data, "", " ") +} + +// Implements the json.Marshaler interface. +func (j *Json) MarshalJSON() ([]byte, error) { + return json.Marshal(&j.data) +} + +// Set modifies `Json` map by `key` and `value` +// Useful for changing single key/value in a `Json` object easily. +func (j *Json) Set(key string, val interface{}) { + m, err := j.Map() + if err != nil { + return + } + m[key] = val +} + +// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path, +// and then finally writing in the value +func (j *Json) SetPath(branch []string, val interface{}) { + if len(branch) == 0 { + j.data = val + return + } + + // in order to insert our branch, we need map[string]interface{} + if _, ok := (j.data).(map[string]interface{}); !ok { + // have to replace with something suitable + j.data = make(map[string]interface{}) + } + curr := j.data.(map[string]interface{}) + + for i := 0; i < len(branch)-1; i++ { + b := branch[i] + // key exists? + if _, ok := curr[b]; !ok { + n := make(map[string]interface{}) + curr[b] = n + curr = n + continue + } + + // make sure the value is the right sort of thing + if _, ok := curr[b].(map[string]interface{}); !ok { + // have to replace with something suitable + n := make(map[string]interface{}) + curr[b] = n + } + + curr = curr[b].(map[string]interface{}) + } + + // add remaining k/v + curr[branch[len(branch)-1]] = val +} + +// Del modifies `Json` map by deleting `key` if it is present. +func (j *Json) Del(key string) { + m, err := j.Map() + if err != nil { + return + } + delete(m, key) +} + +// Get returns a pointer to a new `Json` object +// for `key` in its `map` representation +// +// useful for chaining operations (to traverse a nested JSON): +// js.Get("top_level").Get("dict").Get("value").Int() +func (j *Json) Get(key string) *Json { + m, err := j.Map() + if err == nil { + if val, ok := m[key]; ok { + return &Json{val} + } + } + return &Json{nil} +} + +// GetPath searches for the item as specified by the branch +// without the need to deep dive using Get()'s. +// +// js.GetPath("top_level", "dict") +func (j *Json) GetPath(branch ...string) *Json { + jin := j + for _, p := range branch { + jin = jin.Get(p) + } + return jin +} + +// GetIndex returns a pointer to a new `Json` object +// for `index` in its `array` representation +// +// this is the analog to Get when accessing elements of +// a json array instead of a json object: +// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int() +func (j *Json) GetIndex(index int) *Json { + a, err := j.Array() + if err == nil { + if len(a) > index { + return &Json{a[index]} + } + } + return &Json{nil} +} + +// CheckGet returns a pointer to a new `Json` object and +// a `bool` identifying success or failure +// +// useful for chained operations when success is important: +// if data, ok := js.Get("top_level").CheckGet("inner"); ok { +// log.Println(data) +// } +func (j *Json) CheckGet(key string) (*Json, bool) { + m, err := j.Map() + if err == nil { + if val, ok := m[key]; ok { + return &Json{val}, true + } + } + return nil, false +} + +// Map type asserts to `map` +func (j *Json) Map() (map[string]interface{}, error) { + if m, ok := (j.data).(map[string]interface{}); ok { + return m, nil + } + return nil, errors.New("type assertion to map[string]interface{} failed") +} + +// Array type asserts to an `array` +func (j *Json) Array() ([]interface{}, error) { + if a, ok := (j.data).([]interface{}); ok { + return a, nil + } + return nil, errors.New("type assertion to []interface{} failed") +} + +// Bool type asserts to `bool` +func (j *Json) Bool() (bool, error) { + if s, ok := (j.data).(bool); ok { + return s, nil + } + return false, errors.New("type assertion to bool failed") +} + +// String type asserts to `string` +func (j *Json) String() (string, error) { + if s, ok := (j.data).(string); ok { + return s, nil + } + return "", errors.New("type assertion to string failed") +} + +// Bytes type asserts to `[]byte` +func (j *Json) Bytes() ([]byte, error) { + if s, ok := (j.data).(string); ok { + return []byte(s), nil + } + return nil, errors.New("type assertion to []byte failed") +} + +// StringArray type asserts to an `array` of `string` +func (j *Json) StringArray() ([]string, error) { + arr, err := j.Array() + if err != nil { + return nil, err + } + retArr := make([]string, 0, len(arr)) + for _, a := range arr { + if a == nil { + retArr = append(retArr, "") + continue + } + s, ok := a.(string) + if !ok { + return nil, err + } + retArr = append(retArr, s) + } + return retArr, nil +} + +// MustArray guarantees the return of a `[]interface{}` (with optional default) +// +// useful when you want to interate over array values in a succinct manner: +// for i, v := range js.Get("results").MustArray() { +// fmt.Println(i, v) +// } +func (j *Json) MustArray(args ...[]interface{}) []interface{} { + var def []interface{} + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustArray() received too many arguments %d", len(args)) + } + + a, err := j.Array() + if err == nil { + return a + } + + return def +} + +// MustMap guarantees the return of a `map[string]interface{}` (with optional default) +// +// useful when you want to interate over map values in a succinct manner: +// for k, v := range js.Get("dictionary").MustMap() { +// fmt.Println(k, v) +// } +func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} { + var def map[string]interface{} + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustMap() received too many arguments %d", len(args)) + } + + a, err := j.Map() + if err == nil { + return a + } + + return def +} + +// MustString guarantees the return of a `string` (with optional default) +// +// useful when you explicitly want a `string` in a single value return context: +// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default")) +func (j *Json) MustString(args ...string) string { + var def string + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustString() received too many arguments %d", len(args)) + } + + s, err := j.String() + if err == nil { + return s + } + + return def +} + +// MustStringArray guarantees the return of a `[]string` (with optional default) +// +// useful when you want to interate over array values in a succinct manner: +// for i, s := range js.Get("results").MustStringArray() { +// fmt.Println(i, s) +// } +func (j *Json) MustStringArray(args ...[]string) []string { + var def []string + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustStringArray() received too many arguments %d", len(args)) + } + + a, err := j.StringArray() + if err == nil { + return a + } + + return def +} + +// MustInt guarantees the return of an `int` (with optional default) +// +// useful when you explicitly want an `int` in a single value return context: +// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150)) +func (j *Json) MustInt(args ...int) int { + var def int + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustInt() received too many arguments %d", len(args)) + } + + i, err := j.Int() + if err == nil { + return i + } + + return def +} + +// MustFloat64 guarantees the return of a `float64` (with optional default) +// +// useful when you explicitly want a `float64` in a single value return context: +// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150)) +func (j *Json) MustFloat64(args ...float64) float64 { + var def float64 + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustFloat64() received too many arguments %d", len(args)) + } + + f, err := j.Float64() + if err == nil { + return f + } + + return def +} + +// MustBool guarantees the return of a `bool` (with optional default) +// +// useful when you explicitly want a `bool` in a single value return context: +// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true)) +func (j *Json) MustBool(args ...bool) bool { + var def bool + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustBool() received too many arguments %d", len(args)) + } + + b, err := j.Bool() + if err == nil { + return b + } + + return def +} + +// MustInt64 guarantees the return of an `int64` (with optional default) +// +// useful when you explicitly want an `int64` in a single value return context: +// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150)) +func (j *Json) MustInt64(args ...int64) int64 { + var def int64 + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustInt64() received too many arguments %d", len(args)) + } + + i, err := j.Int64() + if err == nil { + return i + } + + return def +} + +// MustUInt64 guarantees the return of an `uint64` (with optional default) +// +// useful when you explicitly want an `uint64` in a single value return context: +// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150)) +func (j *Json) MustUint64(args ...uint64) uint64 { + var def uint64 + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("MustUint64() received too many arguments %d", len(args)) + } + + i, err := j.Uint64() + if err == nil { + return i + } + + return def +} diff --git a/vendor/github.com/bitly/go-simplejson/simplejson_go10.go b/vendor/github.com/bitly/go-simplejson/simplejson_go10.go new file mode 100644 index 0000000..c9151e9 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/simplejson_go10.go @@ -0,0 +1,75 @@ +// +build !go1.1 + +package simplejson + +import ( + "encoding/json" + "errors" + "io" + "reflect" +) + +// NewFromReader returns a *Json by decoding from an io.Reader +func NewFromReader(r io.Reader) (*Json, error) { + j := new(Json) + dec := json.NewDecoder(r) + err := dec.Decode(&j.data) + return j, err +} + +// Implements the json.Unmarshaler interface. +func (j *Json) UnmarshalJSON(p []byte) error { + return json.Unmarshal(p, &j.data) +} + +// Float64 coerces into a float64 +func (j *Json) Float64() (float64, error) { + switch j.data.(type) { + case float32, float64: + return reflect.ValueOf(j.data).Float(), nil + case int, int8, int16, int32, int64: + return float64(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return float64(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Int coerces into an int +func (j *Json) Int() (int, error) { + switch j.data.(type) { + case float32, float64: + return int(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return int(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return int(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Int64 coerces into an int64 +func (j *Json) Int64() (int64, error) { + switch j.data.(type) { + case float32, float64: + return int64(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return reflect.ValueOf(j.data).Int(), nil + case uint, uint8, uint16, uint32, uint64: + return int64(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Uint64 coerces into an uint64 +func (j *Json) Uint64() (uint64, error) { + switch j.data.(type) { + case float32, float64: + return uint64(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return uint64(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return reflect.ValueOf(j.data).Uint(), nil + } + return 0, errors.New("invalid value type") +} diff --git a/vendor/github.com/bitly/go-simplejson/simplejson_go11.go b/vendor/github.com/bitly/go-simplejson/simplejson_go11.go new file mode 100644 index 0000000..1c47953 --- /dev/null +++ b/vendor/github.com/bitly/go-simplejson/simplejson_go11.go @@ -0,0 +1,89 @@ +// +build go1.1 + +package simplejson + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "reflect" + "strconv" +) + +// Implements the json.Unmarshaler interface. +func (j *Json) UnmarshalJSON(p []byte) error { + dec := json.NewDecoder(bytes.NewBuffer(p)) + dec.UseNumber() + return dec.Decode(&j.data) +} + +// NewFromReader returns a *Json by decoding from an io.Reader +func NewFromReader(r io.Reader) (*Json, error) { + j := new(Json) + dec := json.NewDecoder(r) + dec.UseNumber() + err := dec.Decode(&j.data) + return j, err +} + +// Float64 coerces into a float64 +func (j *Json) Float64() (float64, error) { + switch j.data.(type) { + case json.Number: + return j.data.(json.Number).Float64() + case float32, float64: + return reflect.ValueOf(j.data).Float(), nil + case int, int8, int16, int32, int64: + return float64(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return float64(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Int coerces into an int +func (j *Json) Int() (int, error) { + switch j.data.(type) { + case json.Number: + i, err := j.data.(json.Number).Int64() + return int(i), err + case float32, float64: + return int(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return int(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return int(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Int64 coerces into an int64 +func (j *Json) Int64() (int64, error) { + switch j.data.(type) { + case json.Number: + return j.data.(json.Number).Int64() + case float32, float64: + return int64(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return reflect.ValueOf(j.data).Int(), nil + case uint, uint8, uint16, uint32, uint64: + return int64(reflect.ValueOf(j.data).Uint()), nil + } + return 0, errors.New("invalid value type") +} + +// Uint64 coerces into an uint64 +func (j *Json) Uint64() (uint64, error) { + switch j.data.(type) { + case json.Number: + return strconv.ParseUint(j.data.(json.Number).String(), 10, 64) + case float32, float64: + return uint64(reflect.ValueOf(j.data).Float()), nil + case int, int8, int16, int32, int64: + return uint64(reflect.ValueOf(j.data).Int()), nil + case uint, uint8, uint16, uint32, uint64: + return reflect.ValueOf(j.data).Uint(), nil + } + return 0, errors.New("invalid value type") +} diff --git a/vendor/github.com/bluele/gcache/LICENSE b/vendor/github.com/bluele/gcache/LICENSE new file mode 100644 index 0000000..d1e7b03 --- /dev/null +++ b/vendor/github.com/bluele/gcache/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Jun Kimura + +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. diff --git a/vendor/github.com/bluele/gcache/README.md b/vendor/github.com/bluele/gcache/README.md new file mode 100644 index 0000000..b8f124b --- /dev/null +++ b/vendor/github.com/bluele/gcache/README.md @@ -0,0 +1,320 @@ +# GCache + +![Test](https://github.com/bluele/gcache/workflows/Test/badge.svg) +[![GoDoc](https://godoc.org/github.com/bluele/gcache?status.svg)](https://pkg.go.dev/github.com/bluele/gcache?tab=doc) + +Cache library for golang. It supports expirable Cache, LFU, LRU and ARC. + +## Features + +* Supports expirable Cache, LFU, LRU and ARC. + +* Goroutine safe. + +* Supports event handlers which evict, purge, and add entry. (Optional) + +* Automatically load cache if it doesn't exists. (Optional) + +## Install + +``` +$ go get github.com/bluele/gcache +``` + +## Example + +### Manually set a key-value pair. + +```go +package main + +import ( + "github.com/bluele/gcache" + "fmt" +) + +func main() { + gc := gcache.New(20). + LRU(). + Build() + gc.Set("key", "ok") + value, err := gc.Get("key") + if err != nil { + panic(err) + } + fmt.Println("Get:", value) +} +``` + +``` +Get: ok +``` + +### Manually set a key-value pair, with an expiration time. + +```go +package main + +import ( + "github.com/bluele/gcache" + "fmt" + "time" +) + +func main() { + gc := gcache.New(20). + LRU(). + Build() + gc.SetWithExpire("key", "ok", time.Second*10) + value, _ := gc.Get("key") + fmt.Println("Get:", value) + + // Wait for value to expire + time.Sleep(time.Second*10) + + value, err = gc.Get("key") + if err != nil { + panic(err) + } + fmt.Println("Get:", value) +} +``` + +``` +Get: ok +// 10 seconds later, new attempt: +panic: ErrKeyNotFound +``` + + +### Automatically load value + +```go +package main + +import ( + "github.com/bluele/gcache" + "fmt" +) + +func main() { + gc := gcache.New(20). + LRU(). + LoaderFunc(func(key interface{}) (interface{}, error) { + return "ok", nil + }). + Build() + value, err := gc.Get("key") + if err != nil { + panic(err) + } + fmt.Println("Get:", value) +} +``` + +``` +Get: ok +``` + +### Automatically load value with expiration + +```go +package main + +import ( + "fmt" + "time" + + "github.com/bluele/gcache" +) + +func main() { + var evictCounter, loaderCounter, purgeCounter int + gc := gcache.New(20). + LRU(). + LoaderExpireFunc(func(key interface{}) (interface{}, *time.Duration, error) { + loaderCounter++ + expire := 1 * time.Second + return "ok", &expire, nil + }). + EvictedFunc(func(key, value interface{}) { + evictCounter++ + fmt.Println("evicted key:", key) + }). + PurgeVisitorFunc(func(key, value interface{}) { + purgeCounter++ + fmt.Println("purged key:", key) + }). + Build() + value, err := gc.Get("key") + if err != nil { + panic(err) + } + fmt.Println("Get:", value) + time.Sleep(1 * time.Second) + value, err = gc.Get("key") + if err != nil { + panic(err) + } + fmt.Println("Get:", value) + gc.Purge() + if loaderCounter != evictCounter+purgeCounter { + panic("bad") + } +} +``` + +``` +Get: ok +evicted key: key +Get: ok +purged key: key +``` + + +## Cache Algorithm + + * Least-Frequently Used (LFU) + + Discards the least frequently used items first. + + ```go + func main() { + // size: 10 + gc := gcache.New(10). + LFU(). + Build() + gc.Set("key", "value") + } + ``` + + * Least Recently Used (LRU) + + Discards the least recently used items first. + + ```go + func main() { + // size: 10 + gc := gcache.New(10). + LRU(). + Build() + gc.Set("key", "value") + } + ``` + + * Adaptive Replacement Cache (ARC) + + Constantly balances between LRU and LFU, to improve the combined result. + + detail: http://en.wikipedia.org/wiki/Adaptive_replacement_cache + + ```go + func main() { + // size: 10 + gc := gcache.New(10). + ARC(). + Build() + gc.Set("key", "value") + } + ``` + + * SimpleCache (Default) + + SimpleCache has no clear priority for evict cache. It depends on key-value map order. + + ```go + func main() { + // size: 10 + gc := gcache.New(10).Build() + gc.Set("key", "value") + v, err := gc.Get("key") + if err != nil { + panic(err) + } + } + ``` + +## Loading Cache + +If specified `LoaderFunc`, values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated. + +```go +func main() { + gc := gcache.New(10). + LRU(). + LoaderFunc(func(key interface{}) (interface{}, error) { + return "value", nil + }). + Build() + v, _ := gc.Get("key") + // output: "value" + fmt.Println(v) +} +``` + +GCache coordinates cache fills such that only one load in one process of an entire replicated set of processes populates the cache, then multiplexes the loaded value to all callers. + +## Expirable cache + +```go +func main() { + // LRU cache, size: 10, expiration: after a hour + gc := gcache.New(10). + LRU(). + Expiration(time.Hour). + Build() +} +``` + +## Event handlers + +### Evicted handler + +Event handler for evict the entry. + +```go +func main() { + gc := gcache.New(2). + EvictedFunc(func(key, value interface{}) { + fmt.Println("evicted key:", key) + }). + Build() + for i := 0; i < 3; i++ { + gc.Set(i, i*i) + } +} +``` + +``` +evicted key: 0 +``` + +### Added handler + +Event handler for add the entry. + +```go +func main() { + gc := gcache.New(2). + AddedFunc(func(key, value interface{}) { + fmt.Println("added key:", key) + }). + Build() + for i := 0; i < 3; i++ { + gc.Set(i, i*i) + } +} +``` + +``` +added key: 0 +added key: 1 +added key: 2 +``` + +# Author + +**Jun Kimura** + +* +* diff --git a/vendor/github.com/bluele/gcache/arc.go b/vendor/github.com/bluele/gcache/arc.go new file mode 100644 index 0000000..e2015e9 --- /dev/null +++ b/vendor/github.com/bluele/gcache/arc.go @@ -0,0 +1,456 @@ +package gcache + +import ( + "container/list" + "time" +) + +// Constantly balances between LRU and LFU, to improve the combined result. +type ARC struct { + baseCache + items map[interface{}]*arcItem + + part int + t1 *arcList + t2 *arcList + b1 *arcList + b2 *arcList +} + +func newARC(cb *CacheBuilder) *ARC { + c := &ARC{} + buildCache(&c.baseCache, cb) + + c.init() + c.loadGroup.cache = c + return c +} + +func (c *ARC) init() { + c.items = make(map[interface{}]*arcItem) + c.t1 = newARCList() + c.t2 = newARCList() + c.b1 = newARCList() + c.b2 = newARCList() +} + +func (c *ARC) replace(key interface{}) { + if !c.isCacheFull() { + return + } + var old interface{} + if c.t1.Len() > 0 && ((c.b2.Has(key) && c.t1.Len() == c.part) || (c.t1.Len() > c.part)) { + old = c.t1.RemoveTail() + c.b1.PushFront(old) + } else if c.t2.Len() > 0 { + old = c.t2.RemoveTail() + c.b2.PushFront(old) + } else { + old = c.t1.RemoveTail() + c.b1.PushFront(old) + } + item, ok := c.items[old] + if ok { + delete(c.items, old) + if c.evictedFunc != nil { + c.evictedFunc(item.key, item.value) + } + } +} + +func (c *ARC) Set(key, value interface{}) error { + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.set(key, value) + return err +} + +// Set a new key-value pair with an expiration time +func (c *ARC) SetWithExpire(key, value interface{}, expiration time.Duration) error { + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, value) + if err != nil { + return err + } + + t := c.clock.Now().Add(expiration) + item.(*arcItem).expiration = &t + return nil +} + +func (c *ARC) set(key, value interface{}) (interface{}, error) { + var err error + if c.serializeFunc != nil { + value, err = c.serializeFunc(key, value) + if err != nil { + return nil, err + } + } + + item, ok := c.items[key] + if ok { + item.value = value + } else { + item = &arcItem{ + clock: c.clock, + key: key, + value: value, + } + c.items[key] = item + } + + if c.expiration != nil { + t := c.clock.Now().Add(*c.expiration) + item.expiration = &t + } + + defer func() { + if c.addedFunc != nil { + c.addedFunc(key, value) + } + }() + + if c.t1.Has(key) || c.t2.Has(key) { + return item, nil + } + + if elt := c.b1.Lookup(key); elt != nil { + c.setPart(minInt(c.size, c.part+maxInt(c.b2.Len()/c.b1.Len(), 1))) + c.replace(key) + c.b1.Remove(key, elt) + c.t2.PushFront(key) + return item, nil + } + + if elt := c.b2.Lookup(key); elt != nil { + c.setPart(maxInt(0, c.part-maxInt(c.b1.Len()/c.b2.Len(), 1))) + c.replace(key) + c.b2.Remove(key, elt) + c.t2.PushFront(key) + return item, nil + } + + if c.isCacheFull() && c.t1.Len()+c.b1.Len() == c.size { + if c.t1.Len() < c.size { + c.b1.RemoveTail() + c.replace(key) + } else { + pop := c.t1.RemoveTail() + item, ok := c.items[pop] + if ok { + delete(c.items, pop) + if c.evictedFunc != nil { + c.evictedFunc(item.key, item.value) + } + } + } + } else { + total := c.t1.Len() + c.b1.Len() + c.t2.Len() + c.b2.Len() + if total >= c.size { + if total == (2 * c.size) { + if c.b2.Len() > 0 { + c.b2.RemoveTail() + } else { + c.b1.RemoveTail() + } + } + c.replace(key) + } + } + c.t1.PushFront(key) + return item, nil +} + +// Get a value from cache pool using key if it exists. If not exists and it has LoaderFunc, it will generate the value using you have specified LoaderFunc method returns value. +func (c *ARC) Get(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, true) + } + return v, err +} + +// GetIFPresent gets a value from cache pool using key if it exists. +// If it dose not exists key, returns KeyNotFoundError. +// And send a request which refresh value for specified key if cache object has LoaderFunc. +func (c *ARC) GetIFPresent(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, false) + } + return v, err +} + +func (c *ARC) get(key interface{}, onLoad bool) (interface{}, error) { + v, err := c.getValue(key, onLoad) + if err != nil { + return nil, err + } + if c.deserializeFunc != nil { + return c.deserializeFunc(key, v) + } + return v, nil +} + +func (c *ARC) getValue(key interface{}, onLoad bool) (interface{}, error) { + c.mu.Lock() + defer c.mu.Unlock() + if elt := c.t1.Lookup(key); elt != nil { + c.t1.Remove(key, elt) + item := c.items[key] + if !item.IsExpired(nil) { + c.t2.PushFront(key) + if !onLoad { + c.stats.IncrHitCount() + } + return item.value, nil + } else { + delete(c.items, key) + c.b1.PushFront(key) + if c.evictedFunc != nil { + c.evictedFunc(item.key, item.value) + } + } + } + if elt := c.t2.Lookup(key); elt != nil { + item := c.items[key] + if !item.IsExpired(nil) { + c.t2.MoveToFront(elt) + if !onLoad { + c.stats.IncrHitCount() + } + return item.value, nil + } else { + delete(c.items, key) + c.t2.Remove(key, elt) + c.b2.PushFront(key) + if c.evictedFunc != nil { + c.evictedFunc(item.key, item.value) + } + } + } + + if !onLoad { + c.stats.IncrMissCount() + } + return nil, KeyNotFoundError +} + +func (c *ARC) getWithLoader(key interface{}, isWait bool) (interface{}, error) { + if c.loaderExpireFunc == nil { + return nil, KeyNotFoundError + } + value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) { + if e != nil { + return nil, e + } + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, v) + if err != nil { + return nil, err + } + if expiration != nil { + t := c.clock.Now().Add(*expiration) + item.(*arcItem).expiration = &t + } + return v, nil + }, isWait) + if err != nil { + return nil, err + } + return value, nil +} + +// Has checks if key exists in cache +func (c *ARC) Has(key interface{}) bool { + c.mu.RLock() + defer c.mu.RUnlock() + now := time.Now() + return c.has(key, &now) +} + +func (c *ARC) has(key interface{}, now *time.Time) bool { + item, ok := c.items[key] + if !ok { + return false + } + return !item.IsExpired(now) +} + +// Remove removes the provided key from the cache. +func (c *ARC) Remove(key interface{}) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.remove(key) +} + +func (c *ARC) remove(key interface{}) bool { + if elt := c.t1.Lookup(key); elt != nil { + c.t1.Remove(key, elt) + item := c.items[key] + delete(c.items, key) + c.b1.PushFront(key) + if c.evictedFunc != nil { + c.evictedFunc(key, item.value) + } + return true + } + + if elt := c.t2.Lookup(key); elt != nil { + c.t2.Remove(key, elt) + item := c.items[key] + delete(c.items, key) + c.b2.PushFront(key) + if c.evictedFunc != nil { + c.evictedFunc(key, item.value) + } + return true + } + + return false +} + +// GetALL returns all key-value pairs in the cache. +func (c *ARC) GetALL(checkExpired bool) map[interface{}]interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + items := make(map[interface{}]interface{}, len(c.items)) + now := time.Now() + for k, item := range c.items { + if !checkExpired || c.has(k, &now) { + items[k] = item.value + } + } + return items +} + +// Keys returns a slice of the keys in the cache. +func (c *ARC) Keys(checkExpired bool) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, 0, len(c.items)) + now := time.Now() + for k := range c.items { + if !checkExpired || c.has(k, &now) { + keys = append(keys, k) + } + } + return keys +} + +// Len returns the number of items in the cache. +func (c *ARC) Len(checkExpired bool) int { + c.mu.RLock() + defer c.mu.RUnlock() + if !checkExpired { + return len(c.items) + } + var length int + now := time.Now() + for k := range c.items { + if c.has(k, &now) { + length++ + } + } + return length +} + +// Purge is used to completely clear the cache +func (c *ARC) Purge() { + c.mu.Lock() + defer c.mu.Unlock() + + if c.purgeVisitorFunc != nil { + for _, item := range c.items { + c.purgeVisitorFunc(item.key, item.value) + } + } + + c.init() +} + +func (c *ARC) setPart(p int) { + if c.isCacheFull() { + c.part = p + } +} + +func (c *ARC) isCacheFull() bool { + return (c.t1.Len() + c.t2.Len()) == c.size +} + +// IsExpired returns boolean value whether this item is expired or not. +func (it *arcItem) IsExpired(now *time.Time) bool { + if it.expiration == nil { + return false + } + if now == nil { + t := it.clock.Now() + now = &t + } + return it.expiration.Before(*now) +} + +type arcList struct { + l *list.List + keys map[interface{}]*list.Element +} + +type arcItem struct { + clock Clock + key interface{} + value interface{} + expiration *time.Time +} + +func newARCList() *arcList { + return &arcList{ + l: list.New(), + keys: make(map[interface{}]*list.Element), + } +} + +func (al *arcList) Has(key interface{}) bool { + _, ok := al.keys[key] + return ok +} + +func (al *arcList) Lookup(key interface{}) *list.Element { + elt := al.keys[key] + return elt +} + +func (al *arcList) MoveToFront(elt *list.Element) { + al.l.MoveToFront(elt) +} + +func (al *arcList) PushFront(key interface{}) { + if elt, ok := al.keys[key]; ok { + al.l.MoveToFront(elt) + return + } + elt := al.l.PushFront(key) + al.keys[key] = elt +} + +func (al *arcList) Remove(key interface{}, elt *list.Element) { + delete(al.keys, key) + al.l.Remove(elt) +} + +func (al *arcList) RemoveTail() interface{} { + elt := al.l.Back() + al.l.Remove(elt) + + key := elt.Value + delete(al.keys, key) + + return key +} + +func (al *arcList) Len() int { + return al.l.Len() +} diff --git a/vendor/github.com/bluele/gcache/cache.go b/vendor/github.com/bluele/gcache/cache.go new file mode 100644 index 0000000..e13e6f1 --- /dev/null +++ b/vendor/github.com/bluele/gcache/cache.go @@ -0,0 +1,205 @@ +package gcache + +import ( + "errors" + "fmt" + "sync" + "time" +) + +const ( + TYPE_SIMPLE = "simple" + TYPE_LRU = "lru" + TYPE_LFU = "lfu" + TYPE_ARC = "arc" +) + +var KeyNotFoundError = errors.New("Key not found.") + +type Cache interface { + Set(key, value interface{}) error + SetWithExpire(key, value interface{}, expiration time.Duration) error + Get(key interface{}) (interface{}, error) + GetIFPresent(key interface{}) (interface{}, error) + GetALL(checkExpired bool) map[interface{}]interface{} + get(key interface{}, onLoad bool) (interface{}, error) + Remove(key interface{}) bool + Purge() + Keys(checkExpired bool) []interface{} + Len(checkExpired bool) int + Has(key interface{}) bool + + statsAccessor +} + +type baseCache struct { + clock Clock + size int + loaderExpireFunc LoaderExpireFunc + evictedFunc EvictedFunc + purgeVisitorFunc PurgeVisitorFunc + addedFunc AddedFunc + deserializeFunc DeserializeFunc + serializeFunc SerializeFunc + expiration *time.Duration + mu sync.RWMutex + loadGroup Group + *stats +} + +type ( + LoaderFunc func(interface{}) (interface{}, error) + LoaderExpireFunc func(interface{}) (interface{}, *time.Duration, error) + EvictedFunc func(interface{}, interface{}) + PurgeVisitorFunc func(interface{}, interface{}) + AddedFunc func(interface{}, interface{}) + DeserializeFunc func(interface{}, interface{}) (interface{}, error) + SerializeFunc func(interface{}, interface{}) (interface{}, error) +) + +type CacheBuilder struct { + clock Clock + tp string + size int + loaderExpireFunc LoaderExpireFunc + evictedFunc EvictedFunc + purgeVisitorFunc PurgeVisitorFunc + addedFunc AddedFunc + expiration *time.Duration + deserializeFunc DeserializeFunc + serializeFunc SerializeFunc +} + +func New(size int) *CacheBuilder { + return &CacheBuilder{ + clock: NewRealClock(), + tp: TYPE_SIMPLE, + size: size, + } +} + +func (cb *CacheBuilder) Clock(clock Clock) *CacheBuilder { + cb.clock = clock + return cb +} + +// Set a loader function. +// loaderFunc: create a new value with this function if cached value is expired. +func (cb *CacheBuilder) LoaderFunc(loaderFunc LoaderFunc) *CacheBuilder { + cb.loaderExpireFunc = func(k interface{}) (interface{}, *time.Duration, error) { + v, err := loaderFunc(k) + return v, nil, err + } + return cb +} + +// Set a loader function with expiration. +// loaderExpireFunc: create a new value with this function if cached value is expired. +// If nil returned instead of time.Duration from loaderExpireFunc than value will never expire. +func (cb *CacheBuilder) LoaderExpireFunc(loaderExpireFunc LoaderExpireFunc) *CacheBuilder { + cb.loaderExpireFunc = loaderExpireFunc + return cb +} + +func (cb *CacheBuilder) EvictType(tp string) *CacheBuilder { + cb.tp = tp + return cb +} + +func (cb *CacheBuilder) Simple() *CacheBuilder { + return cb.EvictType(TYPE_SIMPLE) +} + +func (cb *CacheBuilder) LRU() *CacheBuilder { + return cb.EvictType(TYPE_LRU) +} + +func (cb *CacheBuilder) LFU() *CacheBuilder { + return cb.EvictType(TYPE_LFU) +} + +func (cb *CacheBuilder) ARC() *CacheBuilder { + return cb.EvictType(TYPE_ARC) +} + +func (cb *CacheBuilder) EvictedFunc(evictedFunc EvictedFunc) *CacheBuilder { + cb.evictedFunc = evictedFunc + return cb +} + +func (cb *CacheBuilder) PurgeVisitorFunc(purgeVisitorFunc PurgeVisitorFunc) *CacheBuilder { + cb.purgeVisitorFunc = purgeVisitorFunc + return cb +} + +func (cb *CacheBuilder) AddedFunc(addedFunc AddedFunc) *CacheBuilder { + cb.addedFunc = addedFunc + return cb +} + +func (cb *CacheBuilder) DeserializeFunc(deserializeFunc DeserializeFunc) *CacheBuilder { + cb.deserializeFunc = deserializeFunc + return cb +} + +func (cb *CacheBuilder) SerializeFunc(serializeFunc SerializeFunc) *CacheBuilder { + cb.serializeFunc = serializeFunc + return cb +} + +func (cb *CacheBuilder) Expiration(expiration time.Duration) *CacheBuilder { + cb.expiration = &expiration + return cb +} + +func (cb *CacheBuilder) Build() Cache { + if cb.size <= 0 && cb.tp != TYPE_SIMPLE { + panic("gcache: Cache size <= 0") + } + + return cb.build() +} + +func (cb *CacheBuilder) build() Cache { + switch cb.tp { + case TYPE_SIMPLE: + return newSimpleCache(cb) + case TYPE_LRU: + return newLRUCache(cb) + case TYPE_LFU: + return newLFUCache(cb) + case TYPE_ARC: + return newARC(cb) + default: + panic("gcache: Unknown type " + cb.tp) + } +} + +func buildCache(c *baseCache, cb *CacheBuilder) { + c.clock = cb.clock + c.size = cb.size + c.loaderExpireFunc = cb.loaderExpireFunc + c.expiration = cb.expiration + c.addedFunc = cb.addedFunc + c.deserializeFunc = cb.deserializeFunc + c.serializeFunc = cb.serializeFunc + c.evictedFunc = cb.evictedFunc + c.purgeVisitorFunc = cb.purgeVisitorFunc + c.stats = &stats{} +} + +// load a new value using by specified key. +func (c *baseCache) load(key interface{}, cb func(interface{}, *time.Duration, error) (interface{}, error), isWait bool) (interface{}, bool, error) { + v, called, err := c.loadGroup.Do(key, func() (v interface{}, e error) { + defer func() { + if r := recover(); r != nil { + e = fmt.Errorf("Loader panics: %v", r) + } + }() + return cb(c.loaderExpireFunc(key)) + }, isWait) + if err != nil { + return nil, called, err + } + return v, called, nil +} diff --git a/vendor/github.com/bluele/gcache/clock.go b/vendor/github.com/bluele/gcache/clock.go new file mode 100644 index 0000000..3acc3f0 --- /dev/null +++ b/vendor/github.com/bluele/gcache/clock.go @@ -0,0 +1,53 @@ +package gcache + +import ( + "sync" + "time" +) + +type Clock interface { + Now() time.Time +} + +type RealClock struct{} + +func NewRealClock() Clock { + return RealClock{} +} + +func (rc RealClock) Now() time.Time { + t := time.Now() + return t +} + +type FakeClock interface { + Clock + + Advance(d time.Duration) +} + +func NewFakeClock() FakeClock { + return &fakeclock{ + // Taken from github.com/jonboulle/clockwork: use a fixture that does not fulfill Time.IsZero() + now: time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC), + } +} + +type fakeclock struct { + now time.Time + + mutex sync.RWMutex +} + +func (fc *fakeclock) Now() time.Time { + fc.mutex.RLock() + defer fc.mutex.RUnlock() + t := fc.now + return t +} + +func (fc *fakeclock) Advance(d time.Duration) { + fc.mutex.Lock() + defer fc.mutex.Unlock() + fc.now = fc.now.Add(d) +} diff --git a/vendor/github.com/bluele/gcache/go.mod b/vendor/github.com/bluele/gcache/go.mod new file mode 100644 index 0000000..8c6f20f --- /dev/null +++ b/vendor/github.com/bluele/gcache/go.mod @@ -0,0 +1,3 @@ +module github.com/bluele/gcache + +go 1.15 diff --git a/vendor/github.com/bluele/gcache/lfu.go b/vendor/github.com/bluele/gcache/lfu.go new file mode 100644 index 0000000..9a4e3df --- /dev/null +++ b/vendor/github.com/bluele/gcache/lfu.go @@ -0,0 +1,377 @@ +package gcache + +import ( + "container/list" + "time" +) + +// Discards the least frequently used items first. +type LFUCache struct { + baseCache + items map[interface{}]*lfuItem + freqList *list.List // list for freqEntry +} + +var _ Cache = (*LFUCache)(nil) + +type lfuItem struct { + clock Clock + key interface{} + value interface{} + freqElement *list.Element + expiration *time.Time +} + +type freqEntry struct { + freq uint + items map[*lfuItem]struct{} +} + +func newLFUCache(cb *CacheBuilder) *LFUCache { + c := &LFUCache{} + buildCache(&c.baseCache, cb) + + c.init() + c.loadGroup.cache = c + return c +} + +func (c *LFUCache) init() { + c.freqList = list.New() + c.items = make(map[interface{}]*lfuItem, c.size) + c.freqList.PushFront(&freqEntry{ + freq: 0, + items: make(map[*lfuItem]struct{}), + }) +} + +// Set a new key-value pair +func (c *LFUCache) Set(key, value interface{}) error { + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.set(key, value) + return err +} + +// Set a new key-value pair with an expiration time +func (c *LFUCache) SetWithExpire(key, value interface{}, expiration time.Duration) error { + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, value) + if err != nil { + return err + } + + t := c.clock.Now().Add(expiration) + item.(*lfuItem).expiration = &t + return nil +} + +func (c *LFUCache) set(key, value interface{}) (interface{}, error) { + var err error + if c.serializeFunc != nil { + value, err = c.serializeFunc(key, value) + if err != nil { + return nil, err + } + } + + // Check for existing item + item, ok := c.items[key] + if ok { + item.value = value + } else { + // Verify size not exceeded + if len(c.items) >= c.size { + c.evict(1) + } + item = &lfuItem{ + clock: c.clock, + key: key, + value: value, + freqElement: nil, + } + el := c.freqList.Front() + fe := el.Value.(*freqEntry) + fe.items[item] = struct{}{} + + item.freqElement = el + c.items[key] = item + } + + if c.expiration != nil { + t := c.clock.Now().Add(*c.expiration) + item.expiration = &t + } + + if c.addedFunc != nil { + c.addedFunc(key, value) + } + + return item, nil +} + +// Get a value from cache pool using key if it exists. +// If it dose not exists key and has LoaderFunc, +// generate a value using `LoaderFunc` method returns value. +func (c *LFUCache) Get(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, true) + } + return v, err +} + +// GetIFPresent gets a value from cache pool using key if it exists. +// If it dose not exists key, returns KeyNotFoundError. +// And send a request which refresh value for specified key if cache object has LoaderFunc. +func (c *LFUCache) GetIFPresent(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, false) + } + return v, err +} + +func (c *LFUCache) get(key interface{}, onLoad bool) (interface{}, error) { + v, err := c.getValue(key, onLoad) + if err != nil { + return nil, err + } + if c.deserializeFunc != nil { + return c.deserializeFunc(key, v) + } + return v, nil +} + +func (c *LFUCache) getValue(key interface{}, onLoad bool) (interface{}, error) { + c.mu.Lock() + item, ok := c.items[key] + if ok { + if !item.IsExpired(nil) { + c.increment(item) + v := item.value + c.mu.Unlock() + if !onLoad { + c.stats.IncrHitCount() + } + return v, nil + } + c.removeItem(item) + } + c.mu.Unlock() + if !onLoad { + c.stats.IncrMissCount() + } + return nil, KeyNotFoundError +} + +func (c *LFUCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) { + if c.loaderExpireFunc == nil { + return nil, KeyNotFoundError + } + value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) { + if e != nil { + return nil, e + } + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, v) + if err != nil { + return nil, err + } + if expiration != nil { + t := c.clock.Now().Add(*expiration) + item.(*lfuItem).expiration = &t + } + return v, nil + }, isWait) + if err != nil { + return nil, err + } + return value, nil +} + +func (c *LFUCache) increment(item *lfuItem) { + currentFreqElement := item.freqElement + currentFreqEntry := currentFreqElement.Value.(*freqEntry) + nextFreq := currentFreqEntry.freq + 1 + delete(currentFreqEntry.items, item) + + // a boolean whether reuse the empty current entry + removable := isRemovableFreqEntry(currentFreqEntry) + + // insert item into a valid entry + nextFreqElement := currentFreqElement.Next() + switch { + case nextFreqElement == nil || nextFreqElement.Value.(*freqEntry).freq > nextFreq: + if removable { + currentFreqEntry.freq = nextFreq + nextFreqElement = currentFreqElement + } else { + nextFreqElement = c.freqList.InsertAfter(&freqEntry{ + freq: nextFreq, + items: make(map[*lfuItem]struct{}), + }, currentFreqElement) + } + case nextFreqElement.Value.(*freqEntry).freq == nextFreq: + if removable { + c.freqList.Remove(currentFreqElement) + } + default: + panic("unreachable") + } + nextFreqElement.Value.(*freqEntry).items[item] = struct{}{} + item.freqElement = nextFreqElement +} + +// evict removes the least frequence item from the cache. +func (c *LFUCache) evict(count int) { + entry := c.freqList.Front() + for i := 0; i < count; { + if entry == nil { + return + } else { + for item := range entry.Value.(*freqEntry).items { + if i >= count { + return + } + c.removeItem(item) + i++ + } + entry = entry.Next() + } + } +} + +// Has checks if key exists in cache +func (c *LFUCache) Has(key interface{}) bool { + c.mu.RLock() + defer c.mu.RUnlock() + now := time.Now() + return c.has(key, &now) +} + +func (c *LFUCache) has(key interface{}, now *time.Time) bool { + item, ok := c.items[key] + if !ok { + return false + } + return !item.IsExpired(now) +} + +// Remove removes the provided key from the cache. +func (c *LFUCache) Remove(key interface{}) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.remove(key) +} + +func (c *LFUCache) remove(key interface{}) bool { + if item, ok := c.items[key]; ok { + c.removeItem(item) + return true + } + return false +} + +// removeElement is used to remove a given list element from the cache +func (c *LFUCache) removeItem(item *lfuItem) { + entry := item.freqElement.Value.(*freqEntry) + delete(c.items, item.key) + delete(entry.items, item) + if isRemovableFreqEntry(entry) { + c.freqList.Remove(item.freqElement) + } + if c.evictedFunc != nil { + c.evictedFunc(item.key, item.value) + } +} + +func (c *LFUCache) keys() []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, len(c.items)) + var i = 0 + for k := range c.items { + keys[i] = k + i++ + } + return keys +} + +// GetALL returns all key-value pairs in the cache. +func (c *LFUCache) GetALL(checkExpired bool) map[interface{}]interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + items := make(map[interface{}]interface{}, len(c.items)) + now := time.Now() + for k, item := range c.items { + if !checkExpired || c.has(k, &now) { + items[k] = item.value + } + } + return items +} + +// Keys returns a slice of the keys in the cache. +func (c *LFUCache) Keys(checkExpired bool) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, 0, len(c.items)) + now := time.Now() + for k := range c.items { + if !checkExpired || c.has(k, &now) { + keys = append(keys, k) + } + } + return keys +} + +// Len returns the number of items in the cache. +func (c *LFUCache) Len(checkExpired bool) int { + c.mu.RLock() + defer c.mu.RUnlock() + if !checkExpired { + return len(c.items) + } + var length int + now := time.Now() + for k := range c.items { + if c.has(k, &now) { + length++ + } + } + return length +} + +// Completely clear the cache +func (c *LFUCache) Purge() { + c.mu.Lock() + defer c.mu.Unlock() + + if c.purgeVisitorFunc != nil { + for key, item := range c.items { + c.purgeVisitorFunc(key, item.value) + } + } + + c.init() +} + +// IsExpired returns boolean value whether this item is expired or not. +func (it *lfuItem) IsExpired(now *time.Time) bool { + if it.expiration == nil { + return false + } + if now == nil { + t := it.clock.Now() + now = &t + } + return it.expiration.Before(*now) +} + +func isRemovableFreqEntry(entry *freqEntry) bool { + return entry.freq != 0 && len(entry.items) == 0 +} diff --git a/vendor/github.com/bluele/gcache/lru.go b/vendor/github.com/bluele/gcache/lru.go new file mode 100644 index 0000000..a85d660 --- /dev/null +++ b/vendor/github.com/bluele/gcache/lru.go @@ -0,0 +1,317 @@ +package gcache + +import ( + "container/list" + "time" +) + +// Discards the least recently used items first. +type LRUCache struct { + baseCache + items map[interface{}]*list.Element + evictList *list.List +} + +func newLRUCache(cb *CacheBuilder) *LRUCache { + c := &LRUCache{} + buildCache(&c.baseCache, cb) + + c.init() + c.loadGroup.cache = c + return c +} + +func (c *LRUCache) init() { + c.evictList = list.New() + c.items = make(map[interface{}]*list.Element, c.size+1) +} + +func (c *LRUCache) set(key, value interface{}) (interface{}, error) { + var err error + if c.serializeFunc != nil { + value, err = c.serializeFunc(key, value) + if err != nil { + return nil, err + } + } + + // Check for existing item + var item *lruItem + if it, ok := c.items[key]; ok { + c.evictList.MoveToFront(it) + item = it.Value.(*lruItem) + item.value = value + } else { + // Verify size not exceeded + if c.evictList.Len() >= c.size { + c.evict(1) + } + item = &lruItem{ + clock: c.clock, + key: key, + value: value, + } + c.items[key] = c.evictList.PushFront(item) + } + + if c.expiration != nil { + t := c.clock.Now().Add(*c.expiration) + item.expiration = &t + } + + if c.addedFunc != nil { + c.addedFunc(key, value) + } + + return item, nil +} + +// set a new key-value pair +func (c *LRUCache) Set(key, value interface{}) error { + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.set(key, value) + return err +} + +// Set a new key-value pair with an expiration time +func (c *LRUCache) SetWithExpire(key, value interface{}, expiration time.Duration) error { + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, value) + if err != nil { + return err + } + + t := c.clock.Now().Add(expiration) + item.(*lruItem).expiration = &t + return nil +} + +// Get a value from cache pool using key if it exists. +// If it dose not exists key and has LoaderFunc, +// generate a value using `LoaderFunc` method returns value. +func (c *LRUCache) Get(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, true) + } + return v, err +} + +// GetIFPresent gets a value from cache pool using key if it exists. +// If it dose not exists key, returns KeyNotFoundError. +// And send a request which refresh value for specified key if cache object has LoaderFunc. +func (c *LRUCache) GetIFPresent(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, false) + } + return v, err +} + +func (c *LRUCache) get(key interface{}, onLoad bool) (interface{}, error) { + v, err := c.getValue(key, onLoad) + if err != nil { + return nil, err + } + if c.deserializeFunc != nil { + return c.deserializeFunc(key, v) + } + return v, nil +} + +func (c *LRUCache) getValue(key interface{}, onLoad bool) (interface{}, error) { + c.mu.Lock() + item, ok := c.items[key] + if ok { + it := item.Value.(*lruItem) + if !it.IsExpired(nil) { + c.evictList.MoveToFront(item) + v := it.value + c.mu.Unlock() + if !onLoad { + c.stats.IncrHitCount() + } + return v, nil + } + c.removeElement(item) + } + c.mu.Unlock() + if !onLoad { + c.stats.IncrMissCount() + } + return nil, KeyNotFoundError +} + +func (c *LRUCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) { + if c.loaderExpireFunc == nil { + return nil, KeyNotFoundError + } + value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) { + if e != nil { + return nil, e + } + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, v) + if err != nil { + return nil, err + } + if expiration != nil { + t := c.clock.Now().Add(*expiration) + item.(*lruItem).expiration = &t + } + return v, nil + }, isWait) + if err != nil { + return nil, err + } + return value, nil +} + +// evict removes the oldest item from the cache. +func (c *LRUCache) evict(count int) { + for i := 0; i < count; i++ { + ent := c.evictList.Back() + if ent == nil { + return + } else { + c.removeElement(ent) + } + } +} + +// Has checks if key exists in cache +func (c *LRUCache) Has(key interface{}) bool { + c.mu.RLock() + defer c.mu.RUnlock() + now := time.Now() + return c.has(key, &now) +} + +func (c *LRUCache) has(key interface{}, now *time.Time) bool { + item, ok := c.items[key] + if !ok { + return false + } + return !item.Value.(*lruItem).IsExpired(now) +} + +// Remove removes the provided key from the cache. +func (c *LRUCache) Remove(key interface{}) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.remove(key) +} + +func (c *LRUCache) remove(key interface{}) bool { + if ent, ok := c.items[key]; ok { + c.removeElement(ent) + return true + } + return false +} + +func (c *LRUCache) removeElement(e *list.Element) { + c.evictList.Remove(e) + entry := e.Value.(*lruItem) + delete(c.items, entry.key) + if c.evictedFunc != nil { + entry := e.Value.(*lruItem) + c.evictedFunc(entry.key, entry.value) + } +} + +func (c *LRUCache) keys() []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, len(c.items)) + var i = 0 + for k := range c.items { + keys[i] = k + i++ + } + return keys +} + +// GetALL returns all key-value pairs in the cache. +func (c *LRUCache) GetALL(checkExpired bool) map[interface{}]interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + items := make(map[interface{}]interface{}, len(c.items)) + now := time.Now() + for k, item := range c.items { + if !checkExpired || c.has(k, &now) { + items[k] = item.Value.(*lruItem).value + } + } + return items +} + +// Keys returns a slice of the keys in the cache. +func (c *LRUCache) Keys(checkExpired bool) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, 0, len(c.items)) + now := time.Now() + for k := range c.items { + if !checkExpired || c.has(k, &now) { + keys = append(keys, k) + } + } + return keys +} + +// Len returns the number of items in the cache. +func (c *LRUCache) Len(checkExpired bool) int { + c.mu.RLock() + defer c.mu.RUnlock() + if !checkExpired { + return len(c.items) + } + var length int + now := time.Now() + for k := range c.items { + if c.has(k, &now) { + length++ + } + } + return length +} + +// Completely clear the cache +func (c *LRUCache) Purge() { + c.mu.Lock() + defer c.mu.Unlock() + + if c.purgeVisitorFunc != nil { + for key, item := range c.items { + it := item.Value.(*lruItem) + v := it.value + c.purgeVisitorFunc(key, v) + } + } + + c.init() +} + +type lruItem struct { + clock Clock + key interface{} + value interface{} + expiration *time.Time +} + +// IsExpired returns boolean value whether this item is expired or not. +func (it *lruItem) IsExpired(now *time.Time) bool { + if it.expiration == nil { + return false + } + if now == nil { + t := it.clock.Now() + now = &t + } + return it.expiration.Before(*now) +} diff --git a/vendor/github.com/bluele/gcache/simple.go b/vendor/github.com/bluele/gcache/simple.go new file mode 100644 index 0000000..7310af1 --- /dev/null +++ b/vendor/github.com/bluele/gcache/simple.go @@ -0,0 +1,307 @@ +package gcache + +import ( + "time" +) + +// SimpleCache has no clear priority for evict cache. It depends on key-value map order. +type SimpleCache struct { + baseCache + items map[interface{}]*simpleItem +} + +func newSimpleCache(cb *CacheBuilder) *SimpleCache { + c := &SimpleCache{} + buildCache(&c.baseCache, cb) + + c.init() + c.loadGroup.cache = c + return c +} + +func (c *SimpleCache) init() { + if c.size <= 0 { + c.items = make(map[interface{}]*simpleItem) + } else { + c.items = make(map[interface{}]*simpleItem, c.size) + } +} + +// Set a new key-value pair +func (c *SimpleCache) Set(key, value interface{}) error { + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.set(key, value) + return err +} + +// Set a new key-value pair with an expiration time +func (c *SimpleCache) SetWithExpire(key, value interface{}, expiration time.Duration) error { + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, value) + if err != nil { + return err + } + + t := c.clock.Now().Add(expiration) + item.(*simpleItem).expiration = &t + return nil +} + +func (c *SimpleCache) set(key, value interface{}) (interface{}, error) { + var err error + if c.serializeFunc != nil { + value, err = c.serializeFunc(key, value) + if err != nil { + return nil, err + } + } + + // Check for existing item + item, ok := c.items[key] + if ok { + item.value = value + } else { + // Verify size not exceeded + if (len(c.items) >= c.size) && c.size > 0 { + c.evict(1) + } + item = &simpleItem{ + clock: c.clock, + value: value, + } + c.items[key] = item + } + + if c.expiration != nil { + t := c.clock.Now().Add(*c.expiration) + item.expiration = &t + } + + if c.addedFunc != nil { + c.addedFunc(key, value) + } + + return item, nil +} + +// Get a value from cache pool using key if it exists. +// If it dose not exists key and has LoaderFunc, +// generate a value using `LoaderFunc` method returns value. +func (c *SimpleCache) Get(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, true) + } + return v, err +} + +// GetIFPresent gets a value from cache pool using key if it exists. +// If it dose not exists key, returns KeyNotFoundError. +// And send a request which refresh value for specified key if cache object has LoaderFunc. +func (c *SimpleCache) GetIFPresent(key interface{}) (interface{}, error) { + v, err := c.get(key, false) + if err == KeyNotFoundError { + return c.getWithLoader(key, false) + } + return v, nil +} + +func (c *SimpleCache) get(key interface{}, onLoad bool) (interface{}, error) { + v, err := c.getValue(key, onLoad) + if err != nil { + return nil, err + } + if c.deserializeFunc != nil { + return c.deserializeFunc(key, v) + } + return v, nil +} + +func (c *SimpleCache) getValue(key interface{}, onLoad bool) (interface{}, error) { + c.mu.Lock() + item, ok := c.items[key] + if ok { + if !item.IsExpired(nil) { + v := item.value + c.mu.Unlock() + if !onLoad { + c.stats.IncrHitCount() + } + return v, nil + } + c.remove(key) + } + c.mu.Unlock() + if !onLoad { + c.stats.IncrMissCount() + } + return nil, KeyNotFoundError +} + +func (c *SimpleCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) { + if c.loaderExpireFunc == nil { + return nil, KeyNotFoundError + } + value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) { + if e != nil { + return nil, e + } + c.mu.Lock() + defer c.mu.Unlock() + item, err := c.set(key, v) + if err != nil { + return nil, err + } + if expiration != nil { + t := c.clock.Now().Add(*expiration) + item.(*simpleItem).expiration = &t + } + return v, nil + }, isWait) + if err != nil { + return nil, err + } + return value, nil +} + +func (c *SimpleCache) evict(count int) { + now := c.clock.Now() + current := 0 + for key, item := range c.items { + if current >= count { + return + } + if item.expiration == nil || now.After(*item.expiration) { + defer c.remove(key) + current++ + } + } +} + +// Has checks if key exists in cache +func (c *SimpleCache) Has(key interface{}) bool { + c.mu.RLock() + defer c.mu.RUnlock() + now := time.Now() + return c.has(key, &now) +} + +func (c *SimpleCache) has(key interface{}, now *time.Time) bool { + item, ok := c.items[key] + if !ok { + return false + } + return !item.IsExpired(now) +} + +// Remove removes the provided key from the cache. +func (c *SimpleCache) Remove(key interface{}) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.remove(key) +} + +func (c *SimpleCache) remove(key interface{}) bool { + item, ok := c.items[key] + if ok { + delete(c.items, key) + if c.evictedFunc != nil { + c.evictedFunc(key, item.value) + } + return true + } + return false +} + +// Returns a slice of the keys in the cache. +func (c *SimpleCache) keys() []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, len(c.items)) + var i = 0 + for k := range c.items { + keys[i] = k + i++ + } + return keys +} + +// GetALL returns all key-value pairs in the cache. +func (c *SimpleCache) GetALL(checkExpired bool) map[interface{}]interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + items := make(map[interface{}]interface{}, len(c.items)) + now := time.Now() + for k, item := range c.items { + if !checkExpired || c.has(k, &now) { + items[k] = item.value + } + } + return items +} + +// Keys returns a slice of the keys in the cache. +func (c *SimpleCache) Keys(checkExpired bool) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + keys := make([]interface{}, 0, len(c.items)) + now := time.Now() + for k := range c.items { + if !checkExpired || c.has(k, &now) { + keys = append(keys, k) + } + } + return keys +} + +// Len returns the number of items in the cache. +func (c *SimpleCache) Len(checkExpired bool) int { + c.mu.RLock() + defer c.mu.RUnlock() + if !checkExpired { + return len(c.items) + } + var length int + now := time.Now() + for k := range c.items { + if c.has(k, &now) { + length++ + } + } + return length +} + +// Completely clear the cache +func (c *SimpleCache) Purge() { + c.mu.Lock() + defer c.mu.Unlock() + + if c.purgeVisitorFunc != nil { + for key, item := range c.items { + c.purgeVisitorFunc(key, item.value) + } + } + + c.init() +} + +type simpleItem struct { + clock Clock + value interface{} + expiration *time.Time +} + +// IsExpired returns boolean value whether this item is expired or not. +func (si *simpleItem) IsExpired(now *time.Time) bool { + if si.expiration == nil { + return false + } + if now == nil { + t := si.clock.Now() + now = &t + } + return si.expiration.Before(*now) +} diff --git a/vendor/github.com/bluele/gcache/singleflight.go b/vendor/github.com/bluele/gcache/singleflight.go new file mode 100644 index 0000000..2c6285e --- /dev/null +++ b/vendor/github.com/bluele/gcache/singleflight.go @@ -0,0 +1,82 @@ +package gcache + +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This module provides a duplicate function call suppression +// mechanism. + +import "sync" + +// call is an in-flight or completed Do call +type call struct { + wg sync.WaitGroup + val interface{} + err error +} + +// Group represents a class of work and forms a namespace in which +// units of work can be executed with duplicate suppression. +type Group struct { + cache Cache + mu sync.Mutex // protects m + m map[interface{}]*call // lazily initialized +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +func (g *Group) Do(key interface{}, fn func() (interface{}, error), isWait bool) (interface{}, bool, error) { + g.mu.Lock() + v, err := g.cache.get(key, true) + if err == nil { + g.mu.Unlock() + return v, false, nil + } + if g.m == nil { + g.m = make(map[interface{}]*call) + } + if c, ok := g.m[key]; ok { + g.mu.Unlock() + if !isWait { + return nil, false, KeyNotFoundError + } + c.wg.Wait() + return c.val, false, c.err + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + if !isWait { + go g.call(c, key, fn) + return nil, false, KeyNotFoundError + } + v, err = g.call(c, key, fn) + return v, true, err +} + +func (g *Group) call(c *call, key interface{}, fn func() (interface{}, error)) (interface{}, error) { + c.val, c.err = fn() + c.wg.Done() + + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + + return c.val, c.err +} diff --git a/vendor/github.com/bluele/gcache/stats.go b/vendor/github.com/bluele/gcache/stats.go new file mode 100644 index 0000000..ca0bf31 --- /dev/null +++ b/vendor/github.com/bluele/gcache/stats.go @@ -0,0 +1,53 @@ +package gcache + +import ( + "sync/atomic" +) + +type statsAccessor interface { + HitCount() uint64 + MissCount() uint64 + LookupCount() uint64 + HitRate() float64 +} + +// statistics +type stats struct { + hitCount uint64 + missCount uint64 +} + +// increment hit count +func (st *stats) IncrHitCount() uint64 { + return atomic.AddUint64(&st.hitCount, 1) +} + +// increment miss count +func (st *stats) IncrMissCount() uint64 { + return atomic.AddUint64(&st.missCount, 1) +} + +// HitCount returns hit count +func (st *stats) HitCount() uint64 { + return atomic.LoadUint64(&st.hitCount) +} + +// MissCount returns miss count +func (st *stats) MissCount() uint64 { + return atomic.LoadUint64(&st.missCount) +} + +// LookupCount returns lookup count +func (st *stats) LookupCount() uint64 { + return st.HitCount() + st.MissCount() +} + +// HitRate returns rate for cache hitting +func (st *stats) HitRate() float64 { + hc, mc := st.HitCount(), st.MissCount() + total := hc + mc + if total == 0 { + return 0.0 + } + return float64(hc) / float64(total) +} diff --git a/vendor/github.com/bluele/gcache/utils.go b/vendor/github.com/bluele/gcache/utils.go new file mode 100644 index 0000000..1f784e4 --- /dev/null +++ b/vendor/github.com/bluele/gcache/utils.go @@ -0,0 +1,15 @@ +package gcache + +func minInt(x, y int) int { + if x < y { + return x + } + return y +} + +func maxInt(x, y int) int { + if x > y { + return x + } + return y +} diff --git a/vendor/github.com/bsm/sarama-cluster/.gitignore b/vendor/github.com/bsm/sarama-cluster/.gitignore new file mode 100644 index 0000000..88113c5 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/.gitignore @@ -0,0 +1,4 @@ +*.log +*.pid +kafka*/ +vendor/ diff --git a/vendor/github.com/bsm/sarama-cluster/.travis.yml b/vendor/github.com/bsm/sarama-cluster/.travis.yml new file mode 100644 index 0000000..07c7c10 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/.travis.yml @@ -0,0 +1,18 @@ +sudo: false +language: go +go: + - 1.10.x + - 1.9.x +install: + - go get -u github.com/golang/dep/cmd/dep + - dep ensure +env: + - SCALA_VERSION=2.12 KAFKA_VERSION=0.11.0.1 + - SCALA_VERSION=2.12 KAFKA_VERSION=1.0.1 + - SCALA_VERSION=2.12 KAFKA_VERSION=1.1.0 +script: + - make default test-race +addons: + apt: + packages: + - oracle-java8-set-default diff --git a/vendor/github.com/bsm/sarama-cluster/Gopkg.lock b/vendor/github.com/bsm/sarama-cluster/Gopkg.lock new file mode 100644 index 0000000..e1bc110 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/Gopkg.lock @@ -0,0 +1,151 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/Shopify/sarama" + packages = ["."] + revision = "35324cf48e33d8260e1c7c18854465a904ade249" + version = "v1.17.0" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + name = "github.com/eapache/go-resiliency" + packages = ["breaker"] + revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/eapache/go-xerial-snappy" + packages = ["."] + revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" + +[[projects]] + name = "github.com/eapache/queue" + packages = ["."] + revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/golang/snappy" + packages = ["."] + revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" + +[[projects]] + name = "github.com/onsi/ginkgo" + packages = [ + ".", + "config", + "extensions/table", + "internal/codelocation", + "internal/containernode", + "internal/failer", + "internal/leafnodes", + "internal/remote", + "internal/spec", + "internal/spec_iterator", + "internal/specrunner", + "internal/suite", + "internal/testingtproxy", + "internal/writer", + "reporters", + "reporters/stenographer", + "reporters/stenographer/support/go-colorable", + "reporters/stenographer/support/go-isatty", + "types" + ] + revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf" + version = "v1.5.0" + +[[projects]] + name = "github.com/onsi/gomega" + packages = [ + ".", + "format", + "internal/assertion", + "internal/asyncassertion", + "internal/oraclematcher", + "internal/testingtsupport", + "matchers", + "matchers/support/goraph/bipartitegraph", + "matchers/support/goraph/edge", + "matchers/support/goraph/node", + "matchers/support/goraph/util", + "types" + ] + revision = "62bff4df71bdbc266561a0caee19f0594b17c240" + version = "v1.4.0" + +[[projects]] + name = "github.com/pierrec/lz4" + packages = [ + ".", + "internal/xxh32" + ] + revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" + version = "v2.0.2" + +[[projects]] + branch = "master" + name = "github.com/rcrowley/go-metrics" + packages = ["."] + revision = "e2704e165165ec55d062f5919b4b29494e9fa790" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = [ + "html", + "html/atom", + "html/charset" + ] + revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9" + +[[projects]] + branch = "master" + name = "golang.org/x/sys" + packages = ["unix"] + revision = "63fc586f45fe72d95d5240a5d5eb95e6503907d3" + +[[projects]] + name = "golang.org/x/text" + packages = [ + "encoding", + "encoding/charmap", + "encoding/htmlindex", + "encoding/internal", + "encoding/internal/identifier", + "encoding/japanese", + "encoding/korean", + "encoding/simplifiedchinese", + "encoding/traditionalchinese", + "encoding/unicode", + "internal/gen", + "internal/tag", + "internal/utf8internal", + "language", + "runes", + "transform", + "unicode/cldr" + ] + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "2fa33a2d1ae87e0905ef09332bb4b3fda29179f6bcd48fd3b94070774b9e458b" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/bsm/sarama-cluster/Gopkg.toml b/vendor/github.com/bsm/sarama-cluster/Gopkg.toml new file mode 100644 index 0000000..1eecfef --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/Gopkg.toml @@ -0,0 +1,26 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "github.com/Shopify/sarama" + version = "^1.14.0" diff --git a/vendor/github.com/bsm/sarama-cluster/LICENSE b/vendor/github.com/bsm/sarama-cluster/LICENSE new file mode 100644 index 0000000..127751c --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2017 Black Square Media Ltd + +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. diff --git a/vendor/github.com/bsm/sarama-cluster/Makefile b/vendor/github.com/bsm/sarama-cluster/Makefile new file mode 100644 index 0000000..25c5bc2 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/Makefile @@ -0,0 +1,35 @@ +SCALA_VERSION?= 2.12 +KAFKA_VERSION?= 1.1.0 +KAFKA_DIR= kafka_$(SCALA_VERSION)-$(KAFKA_VERSION) +KAFKA_SRC= https://archive.apache.org/dist/kafka/$(KAFKA_VERSION)/$(KAFKA_DIR).tgz +KAFKA_ROOT= testdata/$(KAFKA_DIR) +PKG=$(shell go list ./... | grep -v vendor) + +default: vet test + +vet: + go vet $(PKG) + +test: testdeps + KAFKA_DIR=$(KAFKA_DIR) go test $(PKG) -ginkgo.slowSpecThreshold=60 + +test-verbose: testdeps + KAFKA_DIR=$(KAFKA_DIR) go test $(PKG) -ginkgo.slowSpecThreshold=60 -v + +test-race: testdeps + KAFKA_DIR=$(KAFKA_DIR) go test $(PKG) -ginkgo.slowSpecThreshold=60 -v -race + +testdeps: $(KAFKA_ROOT) + +doc: README.md + +.PHONY: test testdeps vet doc + +# --------------------------------------------------------------------- + +$(KAFKA_ROOT): + @mkdir -p $(dir $@) + cd $(dir $@) && curl -sSL $(KAFKA_SRC) | tar xz + +README.md: README.md.tpl $(wildcard *.go) + becca -package $(subst $(GOPATH)/src/,,$(PWD)) diff --git a/vendor/github.com/bsm/sarama-cluster/README.md b/vendor/github.com/bsm/sarama-cluster/README.md new file mode 100644 index 0000000..ebcd755 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/README.md @@ -0,0 +1,151 @@ +# Sarama Cluster + +[![GoDoc](https://godoc.org/github.com/bsm/sarama-cluster?status.svg)](https://godoc.org/github.com/bsm/sarama-cluster) +[![Build Status](https://travis-ci.org/bsm/sarama-cluster.svg?branch=master)](https://travis-ci.org/bsm/sarama-cluster) +[![Go Report Card](https://goreportcard.com/badge/github.com/bsm/sarama-cluster)](https://goreportcard.com/report/github.com/bsm/sarama-cluster) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +Cluster extensions for [Sarama](https://github.com/Shopify/sarama), the Go client library for Apache Kafka 0.9 (and later). + +## Documentation + +Documentation and example are available via godoc at http://godoc.org/github.com/bsm/sarama-cluster + +## Examples + +Consumers have two modes of operation. In the default multiplexed mode messages (and errors) of multiple +topics and partitions are all passed to the single channel: + +```go +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + + cluster "github.com/bsm/sarama-cluster" +) + +func main() { + + // init (custom) config, enable errors and notifications + config := cluster.NewConfig() + config.Consumer.Return.Errors = true + config.Group.Return.Notifications = true + + // init consumer + brokers := []string{"127.0.0.1:9092"} + topics := []string{"my_topic", "other_topic"} + consumer, err := cluster.NewConsumer(brokers, "my-consumer-group", topics, config) + if err != nil { + panic(err) + } + defer consumer.Close() + + // trap SIGINT to trigger a shutdown. + signals := make(chan os.Signal, 1) + signal.Notify(signals, os.Interrupt) + + // consume errors + go func() { + for err := range consumer.Errors() { + log.Printf("Error: %s\n", err.Error()) + } + }() + + // consume notifications + go func() { + for ntf := range consumer.Notifications() { + log.Printf("Rebalanced: %+v\n", ntf) + } + }() + + // consume messages, watch signals + for { + select { + case msg, ok := <-consumer.Messages(): + if ok { + fmt.Fprintf(os.Stdout, "%s/%d/%d\t%s\t%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value) + consumer.MarkOffset(msg, "") // mark message as processed + } + case <-signals: + return + } + } +} +``` + +Users who require access to individual partitions can use the partitioned mode which exposes access to partition-level +consumers: + +```go +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + + cluster "github.com/bsm/sarama-cluster" +) + +func main() { + + // init (custom) config, set mode to ConsumerModePartitions + config := cluster.NewConfig() + config.Group.Mode = cluster.ConsumerModePartitions + + // init consumer + brokers := []string{"127.0.0.1:9092"} + topics := []string{"my_topic", "other_topic"} + consumer, err := cluster.NewConsumer(brokers, "my-consumer-group", topics, config) + if err != nil { + panic(err) + } + defer consumer.Close() + + // trap SIGINT to trigger a shutdown. + signals := make(chan os.Signal, 1) + signal.Notify(signals, os.Interrupt) + + // consume partitions + for { + select { + case part, ok := <-consumer.Partitions(): + if !ok { + return + } + + // start a separate goroutine to consume messages + go func(pc cluster.PartitionConsumer) { + for msg := range pc.Messages() { + fmt.Fprintf(os.Stdout, "%s/%d/%d\t%s\t%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value) + consumer.MarkOffset(msg, "") // mark message as processed + } + }(part) + case <-signals: + return + } + } +} +``` + +## Running tests + +You need to install Ginkgo & Gomega to run tests. Please see +http://onsi.github.io/ginkgo for more details. + +To run tests, call: + + $ make test + +## Troubleshooting + +### Consumer not receiving any messages? + +By default, sarama's `Config.Consumer.Offsets.Initial` is set to `sarama.OffsetNewest`. This means that in the event that a brand new consumer is created, and it has never committed any offsets to kafka, it will only receive messages starting from the message after the current one that was written. + +If you wish to receive all messages (from the start of all messages in the topic) in the event that a consumer does not have any offsets committed to kafka, you need to set `Config.Consumer.Offsets.Initial` to `sarama.OffsetOldest`. diff --git a/vendor/github.com/bsm/sarama-cluster/README.md.tpl b/vendor/github.com/bsm/sarama-cluster/README.md.tpl new file mode 100644 index 0000000..5f63a69 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/README.md.tpl @@ -0,0 +1,67 @@ +# Sarama Cluster + +[![GoDoc](https://godoc.org/github.com/bsm/sarama-cluster?status.svg)](https://godoc.org/github.com/bsm/sarama-cluster) +[![Build Status](https://travis-ci.org/bsm/sarama-cluster.svg?branch=master)](https://travis-ci.org/bsm/sarama-cluster) +[![Go Report Card](https://goreportcard.com/badge/github.com/bsm/sarama-cluster)](https://goreportcard.com/report/github.com/bsm/sarama-cluster) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +Cluster extensions for [Sarama](https://github.com/Shopify/sarama), the Go client library for Apache Kafka 0.9 (and later). + +## Documentation + +Documentation and example are available via godoc at http://godoc.org/github.com/bsm/sarama-cluster + +## Examples + +Consumers have two modes of operation. In the default multiplexed mode messages (and errors) of multiple +topics and partitions are all passed to the single channel: + +```go +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + + cluster "github.com/bsm/sarama-cluster" +) + +func main() {{ "ExampleConsumer" | code }} +``` + +Users who require access to individual partitions can use the partitioned mode which exposes access to partition-level +consumers: + +```go +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + + cluster "github.com/bsm/sarama-cluster" +) + +func main() {{ "ExampleConsumer_Partitions" | code }} +``` + +## Running tests + +You need to install Ginkgo & Gomega to run tests. Please see +http://onsi.github.io/ginkgo for more details. + +To run tests, call: + + $ make test + +## Troubleshooting + +### Consumer not receiving any messages? + +By default, sarama's `Config.Consumer.Offsets.Initial` is set to `sarama.OffsetNewest`. This means that in the event that a brand new consumer is created, and it has never committed any offsets to kafka, it will only receive messages starting from the message after the current one that was written. + +If you wish to receive all messages (from the start of all messages in the topic) in the event that a consumer does not have any offsets committed to kafka, you need to set `Config.Consumer.Offsets.Initial` to `sarama.OffsetOldest`. diff --git a/vendor/github.com/bsm/sarama-cluster/balancer.go b/vendor/github.com/bsm/sarama-cluster/balancer.go new file mode 100644 index 0000000..3aeaece --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/balancer.go @@ -0,0 +1,170 @@ +package cluster + +import ( + "math" + "sort" + + "github.com/Shopify/sarama" +) + +// NotificationType defines the type of notification +type NotificationType uint8 + +// String describes the notification type +func (t NotificationType) String() string { + switch t { + case RebalanceStart: + return "rebalance start" + case RebalanceOK: + return "rebalance OK" + case RebalanceError: + return "rebalance error" + } + return "unknown" +} + +const ( + UnknownNotification NotificationType = iota + RebalanceStart + RebalanceOK + RebalanceError +) + +// Notification are state events emitted by the consumers on rebalance +type Notification struct { + // Type exposes the notification type + Type NotificationType + + // Claimed contains topic/partitions that were claimed by this rebalance cycle + Claimed map[string][]int32 + + // Released contains topic/partitions that were released as part of this rebalance cycle + Released map[string][]int32 + + // Current are topic/partitions that are currently claimed to the consumer + Current map[string][]int32 +} + +func newNotification(current map[string][]int32) *Notification { + return &Notification{ + Type: RebalanceStart, + Current: current, + } +} + +func (n *Notification) success(current map[string][]int32) *Notification { + o := &Notification{ + Type: RebalanceOK, + Claimed: make(map[string][]int32), + Released: make(map[string][]int32), + Current: current, + } + for topic, partitions := range current { + o.Claimed[topic] = int32Slice(partitions).Diff(int32Slice(n.Current[topic])) + } + for topic, partitions := range n.Current { + o.Released[topic] = int32Slice(partitions).Diff(int32Slice(current[topic])) + } + return o +} + +// -------------------------------------------------------------------- + +type topicInfo struct { + Partitions []int32 + MemberIDs []string +} + +func (info topicInfo) Perform(s Strategy) map[string][]int32 { + if s == StrategyRoundRobin { + return info.RoundRobin() + } + return info.Ranges() +} + +func (info topicInfo) Ranges() map[string][]int32 { + sort.Strings(info.MemberIDs) + + mlen := len(info.MemberIDs) + plen := len(info.Partitions) + res := make(map[string][]int32, mlen) + + for pos, memberID := range info.MemberIDs { + n, i := float64(plen)/float64(mlen), float64(pos) + min := int(math.Floor(i*n + 0.5)) + max := int(math.Floor((i+1)*n + 0.5)) + sub := info.Partitions[min:max] + if len(sub) > 0 { + res[memberID] = sub + } + } + return res +} + +func (info topicInfo) RoundRobin() map[string][]int32 { + sort.Strings(info.MemberIDs) + + mlen := len(info.MemberIDs) + res := make(map[string][]int32, mlen) + for i, pnum := range info.Partitions { + memberID := info.MemberIDs[i%mlen] + res[memberID] = append(res[memberID], pnum) + } + return res +} + +// -------------------------------------------------------------------- + +type balancer struct { + client sarama.Client + topics map[string]topicInfo +} + +func newBalancerFromMeta(client sarama.Client, members map[string]sarama.ConsumerGroupMemberMetadata) (*balancer, error) { + balancer := newBalancer(client) + for memberID, meta := range members { + for _, topic := range meta.Topics { + if err := balancer.Topic(topic, memberID); err != nil { + return nil, err + } + } + } + return balancer, nil +} + +func newBalancer(client sarama.Client) *balancer { + return &balancer{ + client: client, + topics: make(map[string]topicInfo), + } +} + +func (r *balancer) Topic(name string, memberID string) error { + topic, ok := r.topics[name] + if !ok { + nums, err := r.client.Partitions(name) + if err != nil { + return err + } + topic = topicInfo{ + Partitions: nums, + MemberIDs: make([]string, 0, 1), + } + } + topic.MemberIDs = append(topic.MemberIDs, memberID) + r.topics[name] = topic + return nil +} + +func (r *balancer) Perform(s Strategy) map[string]map[string][]int32 { + res := make(map[string]map[string][]int32, 1) + for topic, info := range r.topics { + for memberID, partitions := range info.Perform(s) { + if _, ok := res[memberID]; !ok { + res[memberID] = make(map[string][]int32, 1) + } + res[memberID][topic] = partitions + } + } + return res +} diff --git a/vendor/github.com/bsm/sarama-cluster/client.go b/vendor/github.com/bsm/sarama-cluster/client.go new file mode 100644 index 0000000..42ffb30 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/client.go @@ -0,0 +1,50 @@ +package cluster + +import ( + "errors" + "sync/atomic" + + "github.com/Shopify/sarama" +) + +var errClientInUse = errors.New("cluster: client is already used by another consumer") + +// Client is a group client +type Client struct { + sarama.Client + config Config + + inUse uint32 +} + +// NewClient creates a new client instance +func NewClient(addrs []string, config *Config) (*Client, error) { + if config == nil { + config = NewConfig() + } + + if err := config.Validate(); err != nil { + return nil, err + } + + client, err := sarama.NewClient(addrs, &config.Config) + if err != nil { + return nil, err + } + + return &Client{Client: client, config: *config}, nil +} + +// ClusterConfig returns the cluster configuration. +func (c *Client) ClusterConfig() *Config { + cfg := c.config + return &cfg +} + +func (c *Client) claim() bool { + return atomic.CompareAndSwapUint32(&c.inUse, 0, 1) +} + +func (c *Client) release() { + atomic.CompareAndSwapUint32(&c.inUse, 1, 0) +} diff --git a/vendor/github.com/bsm/sarama-cluster/cluster.go b/vendor/github.com/bsm/sarama-cluster/cluster.go new file mode 100644 index 0000000..adcf0e9 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/cluster.go @@ -0,0 +1,25 @@ +package cluster + +// Strategy for partition to consumer assignement +type Strategy string + +const ( + // StrategyRange is the default and assigns partition ranges to consumers. + // Example with six partitions and two consumers: + // C1: [0, 1, 2] + // C2: [3, 4, 5] + StrategyRange Strategy = "range" + + // StrategyRoundRobin assigns partitions by alternating over consumers. + // Example with six partitions and two consumers: + // C1: [0, 2, 4] + // C2: [1, 3, 5] + StrategyRoundRobin Strategy = "roundrobin" +) + +// Error instances are wrappers for internal errors with a context and +// may be returned through the consumer's Errors() channel +type Error struct { + Ctx string + error +} diff --git a/vendor/github.com/bsm/sarama-cluster/config.go b/vendor/github.com/bsm/sarama-cluster/config.go new file mode 100644 index 0000000..084b835 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/config.go @@ -0,0 +1,146 @@ +package cluster + +import ( + "regexp" + "time" + + "github.com/Shopify/sarama" +) + +var minVersion = sarama.V0_9_0_0 + +type ConsumerMode uint8 + +const ( + ConsumerModeMultiplex ConsumerMode = iota + ConsumerModePartitions +) + +// Config extends sarama.Config with Group specific namespace +type Config struct { + sarama.Config + + // Group is the namespace for group management properties + Group struct { + + // The strategy to use for the allocation of partitions to consumers (defaults to StrategyRange) + PartitionStrategy Strategy + + // By default, messages and errors from the subscribed topics and partitions are all multiplexed and + // made available through the consumer's Messages() and Errors() channels. + // + // Users who require low-level access can enable ConsumerModePartitions where individual partitions + // are exposed on the Partitions() channel. Messages and errors must then be consumed on the partitions + // themselves. + Mode ConsumerMode + + Offsets struct { + Retry struct { + // The numer retries when committing offsets (defaults to 3). + Max int + } + Synchronization struct { + // The duration allowed for other clients to commit their offsets before resumption in this client, e.g. during a rebalance + // NewConfig sets this to the Consumer.MaxProcessingTime duration of the Sarama configuration + DwellTime time.Duration + } + } + + Session struct { + // The allowed session timeout for registered consumers (defaults to 30s). + // Must be within the allowed server range. + Timeout time.Duration + } + + Heartbeat struct { + // Interval between each heartbeat (defaults to 3s). It should be no more + // than 1/3rd of the Group.Session.Timout setting + Interval time.Duration + } + + // Return specifies which group channels will be populated. If they are set to true, + // you must read from the respective channels to prevent deadlock. + Return struct { + // If enabled, rebalance notification will be returned on the + // Notifications channel (default disabled). + Notifications bool + } + + Topics struct { + // An additional whitelist of topics to subscribe to. + Whitelist *regexp.Regexp + // An additional blacklist of topics to avoid. If set, this will precede over + // the Whitelist setting. + Blacklist *regexp.Regexp + } + + Member struct { + // Custom metadata to include when joining the group. The user data for all joined members + // can be retrieved by sending a DescribeGroupRequest to the broker that is the + // coordinator for the group. + UserData []byte + } + } +} + +// NewConfig returns a new configuration instance with sane defaults. +func NewConfig() *Config { + c := &Config{ + Config: *sarama.NewConfig(), + } + c.Group.PartitionStrategy = StrategyRange + c.Group.Offsets.Retry.Max = 3 + c.Group.Offsets.Synchronization.DwellTime = c.Consumer.MaxProcessingTime + c.Group.Session.Timeout = 30 * time.Second + c.Group.Heartbeat.Interval = 3 * time.Second + c.Config.Version = minVersion + return c +} + +// Validate checks a Config instance. It will return a +// sarama.ConfigurationError if the specified values don't make sense. +func (c *Config) Validate() error { + if c.Group.Heartbeat.Interval%time.Millisecond != 0 { + sarama.Logger.Println("Group.Heartbeat.Interval only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Group.Session.Timeout%time.Millisecond != 0 { + sarama.Logger.Println("Group.Session.Timeout only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Group.PartitionStrategy != StrategyRange && c.Group.PartitionStrategy != StrategyRoundRobin { + sarama.Logger.Println("Group.PartitionStrategy is not supported; range will be assumed.") + } + if !c.Version.IsAtLeast(minVersion) { + sarama.Logger.Println("Version is not supported; 0.9. will be assumed.") + c.Version = minVersion + } + if err := c.Config.Validate(); err != nil { + return err + } + + // validate the Group values + switch { + case c.Group.Offsets.Retry.Max < 0: + return sarama.ConfigurationError("Group.Offsets.Retry.Max must be >= 0") + case c.Group.Offsets.Synchronization.DwellTime <= 0: + return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be > 0") + case c.Group.Offsets.Synchronization.DwellTime > 10*time.Minute: + return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be <= 10m") + case c.Group.Heartbeat.Interval <= 0: + return sarama.ConfigurationError("Group.Heartbeat.Interval must be > 0") + case c.Group.Session.Timeout <= 0: + return sarama.ConfigurationError("Group.Session.Timeout must be > 0") + case !c.Metadata.Full && c.Group.Topics.Whitelist != nil: + return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Whitelist is used") + case !c.Metadata.Full && c.Group.Topics.Blacklist != nil: + return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Blacklist is used") + } + + // ensure offset is correct + switch c.Consumer.Offsets.Initial { + case sarama.OffsetOldest, sarama.OffsetNewest: + default: + return sarama.ConfigurationError("Consumer.Offsets.Initial must be either OffsetOldest or OffsetNewest") + } + + return nil +} diff --git a/vendor/github.com/bsm/sarama-cluster/consumer.go b/vendor/github.com/bsm/sarama-cluster/consumer.go new file mode 100644 index 0000000..e7a67da --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/consumer.go @@ -0,0 +1,919 @@ +package cluster + +import ( + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/Shopify/sarama" +) + +// Consumer is a cluster group consumer +type Consumer struct { + client *Client + ownClient bool + + consumer sarama.Consumer + subs *partitionMap + + consumerID string + groupID string + + memberID string + generationID int32 + membershipMu sync.RWMutex + + coreTopics []string + extraTopics []string + + dying, dead chan none + closeOnce sync.Once + + consuming int32 + messages chan *sarama.ConsumerMessage + errors chan error + partitions chan PartitionConsumer + notifications chan *Notification + + commitMu sync.Mutex +} + +// NewConsumer initializes a new consumer +func NewConsumer(addrs []string, groupID string, topics []string, config *Config) (*Consumer, error) { + client, err := NewClient(addrs, config) + if err != nil { + return nil, err + } + + consumer, err := NewConsumerFromClient(client, groupID, topics) + if err != nil { + return nil, err + } + consumer.ownClient = true + return consumer, nil +} + +// NewConsumerFromClient initializes a new consumer from an existing client. +// +// Please note that clients cannot be shared between consumers (due to Kafka internals), +// they can only be re-used which requires the user to call Close() on the first consumer +// before using this method again to initialize another one. Attempts to use a client with +// more than one consumer at a time will return errors. +func NewConsumerFromClient(client *Client, groupID string, topics []string) (*Consumer, error) { + if !client.claim() { + return nil, errClientInUse + } + + consumer, err := sarama.NewConsumerFromClient(client.Client) + if err != nil { + client.release() + return nil, err + } + + sort.Strings(topics) + c := &Consumer{ + client: client, + consumer: consumer, + subs: newPartitionMap(), + groupID: groupID, + + coreTopics: topics, + + dying: make(chan none), + dead: make(chan none), + + messages: make(chan *sarama.ConsumerMessage), + errors: make(chan error, client.config.ChannelBufferSize), + partitions: make(chan PartitionConsumer, 1), + notifications: make(chan *Notification), + } + if err := c.client.RefreshCoordinator(groupID); err != nil { + client.release() + return nil, err + } + + go c.mainLoop() + return c, nil +} + +// Messages returns the read channel for the messages that are returned by +// the broker. +// +// This channel will only return if Config.Group.Mode option is set to +// ConsumerModeMultiplex (default). +func (c *Consumer) Messages() <-chan *sarama.ConsumerMessage { return c.messages } + +// Partitions returns the read channels for individual partitions of this broker. +// +// This will channel will only return if Config.Group.Mode option is set to +// ConsumerModePartitions. +// +// The Partitions() channel must be listened to for the life of this consumer; +// when a rebalance happens old partitions will be closed (naturally come to +// completion) and new ones will be emitted. The returned channel will only close +// when the consumer is completely shut down. +func (c *Consumer) Partitions() <-chan PartitionConsumer { return c.partitions } + +// Errors returns a read channel of errors that occur during offset management, if +// enabled. By default, errors are logged and not returned over this channel. If +// you want to implement any custom error handling, set your config's +// Consumer.Return.Errors setting to true, and read from this channel. +func (c *Consumer) Errors() <-chan error { return c.errors } + +// Notifications returns a channel of Notifications that occur during consumer +// rebalancing. Notifications will only be emitted over this channel, if your config's +// Group.Return.Notifications setting to true. +func (c *Consumer) Notifications() <-chan *Notification { return c.notifications } + +// HighWaterMarks returns the current high water marks for each topic and partition +// Consistency between partitions is not guaranteed since high water marks are updated separately. +func (c *Consumer) HighWaterMarks() map[string]map[int32]int64 { return c.consumer.HighWaterMarks() } + +// MarkOffset marks the provided message as processed, alongside a metadata string +// that represents the state of the partition consumer at that point in time. The +// metadata string can be used by another consumer to restore that state, so it +// can resume consumption. +// +// Note: calling MarkOffset does not necessarily commit the offset to the backend +// store immediately for efficiency reasons, and it may never be committed if +// your application crashes. This means that you may end up processing the same +// message twice, and your processing should ideally be idempotent. +func (c *Consumer) MarkOffset(msg *sarama.ConsumerMessage, metadata string) { + if sub := c.subs.Fetch(msg.Topic, msg.Partition); sub != nil { + sub.MarkOffset(msg.Offset, metadata) + } +} + +// MarkPartitionOffset marks an offset of the provided topic/partition as processed. +// See MarkOffset for additional explanation. +func (c *Consumer) MarkPartitionOffset(topic string, partition int32, offset int64, metadata string) { + if sub := c.subs.Fetch(topic, partition); sub != nil { + sub.MarkOffset(offset, metadata) + } +} + +// MarkOffsets marks stashed offsets as processed. +// See MarkOffset for additional explanation. +func (c *Consumer) MarkOffsets(s *OffsetStash) { + s.mu.Lock() + defer s.mu.Unlock() + + for tp, info := range s.offsets { + if sub := c.subs.Fetch(tp.Topic, tp.Partition); sub != nil { + sub.MarkOffset(info.Offset, info.Metadata) + } + delete(s.offsets, tp) + } +} + +// ResetOffsets marks the provided message as processed, alongside a metadata string +// that represents the state of the partition consumer at that point in time. The +// metadata string can be used by another consumer to restore that state, so it +// can resume consumption. +// +// Difference between ResetOffset and MarkOffset is that it allows to rewind to an earlier offset +func (c *Consumer) ResetOffset(msg *sarama.ConsumerMessage, metadata string) { + if sub := c.subs.Fetch(msg.Topic, msg.Partition); sub != nil { + sub.ResetOffset(msg.Offset, metadata) + } +} + +// ResetPartitionOffset marks an offset of the provided topic/partition as processed. +// See ResetOffset for additional explanation. +func (c *Consumer) ResetPartitionOffset(topic string, partition int32, offset int64, metadata string) { + sub := c.subs.Fetch(topic, partition) + if sub != nil { + sub.ResetOffset(offset, metadata) + } +} + +// ResetOffsets marks stashed offsets as processed. +// See ResetOffset for additional explanation. +func (c *Consumer) ResetOffsets(s *OffsetStash) { + s.mu.Lock() + defer s.mu.Unlock() + + for tp, info := range s.offsets { + if sub := c.subs.Fetch(tp.Topic, tp.Partition); sub != nil { + sub.ResetOffset(info.Offset, info.Metadata) + } + delete(s.offsets, tp) + } +} + +// Subscriptions returns the consumed topics and partitions +func (c *Consumer) Subscriptions() map[string][]int32 { + return c.subs.Info() +} + +// CommitOffsets allows to manually commit previously marked offsets. By default there is no +// need to call this function as the consumer will commit offsets automatically +// using the Config.Consumer.Offsets.CommitInterval setting. +// +// Please be aware that calling this function during an internal rebalance cycle may return +// broker errors (e.g. sarama.ErrUnknownMemberId or sarama.ErrIllegalGeneration). +func (c *Consumer) CommitOffsets() error { + c.commitMu.Lock() + defer c.commitMu.Unlock() + + memberID, generationID := c.membership() + req := &sarama.OffsetCommitRequest{ + Version: 2, + ConsumerGroup: c.groupID, + ConsumerGroupGeneration: generationID, + ConsumerID: memberID, + RetentionTime: -1, + } + + if ns := c.client.config.Consumer.Offsets.Retention; ns != 0 { + req.RetentionTime = int64(ns / time.Millisecond) + } + + snap := c.subs.Snapshot() + dirty := false + for tp, state := range snap { + if state.Dirty { + dirty = true + req.AddBlock(tp.Topic, tp.Partition, state.Info.Offset, 0, state.Info.Metadata) + } + } + if !dirty { + return nil + } + + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return err + } + + resp, err := broker.CommitOffset(req) + if err != nil { + c.closeCoordinator(broker, err) + return err + } + + for topic, errs := range resp.Errors { + for partition, kerr := range errs { + if kerr != sarama.ErrNoError { + err = kerr + } else if state, ok := snap[topicPartition{topic, partition}]; ok { + if sub := c.subs.Fetch(topic, partition); sub != nil { + sub.markCommitted(state.Info.Offset) + } + } + } + } + return err +} + +// Close safely closes the consumer and releases all resources +func (c *Consumer) Close() (err error) { + c.closeOnce.Do(func() { + close(c.dying) + <-c.dead + + if e := c.release(); e != nil { + err = e + } + if e := c.consumer.Close(); e != nil { + err = e + } + close(c.messages) + close(c.errors) + + if e := c.leaveGroup(); e != nil { + err = e + } + close(c.partitions) + close(c.notifications) + + // drain + for range c.messages { + } + for range c.errors { + } + for p := range c.partitions { + _ = p.Close() + } + for range c.notifications { + } + + c.client.release() + if c.ownClient { + if e := c.client.Close(); e != nil { + err = e + } + } + }) + return +} + +func (c *Consumer) mainLoop() { + defer close(c.dead) + defer atomic.StoreInt32(&c.consuming, 0) + + for { + atomic.StoreInt32(&c.consuming, 0) + + // Check if close was requested + select { + case <-c.dying: + return + default: + } + + // Start next consume cycle + c.nextTick() + } +} + +func (c *Consumer) nextTick() { + // Remember previous subscriptions + var notification *Notification + if c.client.config.Group.Return.Notifications { + notification = newNotification(c.subs.Info()) + } + + // Refresh coordinator + if err := c.refreshCoordinator(); err != nil { + c.rebalanceError(err, nil) + return + } + + // Release subscriptions + if err := c.release(); err != nil { + c.rebalanceError(err, nil) + return + } + + // Issue rebalance start notification + if c.client.config.Group.Return.Notifications { + c.handleNotification(notification) + } + + // Rebalance, fetch new subscriptions + subs, err := c.rebalance() + if err != nil { + c.rebalanceError(err, notification) + return + } + + // Coordinate loops, make sure everything is + // stopped on exit + tomb := newLoopTomb() + defer tomb.Close() + + // Start the heartbeat + tomb.Go(c.hbLoop) + + // Subscribe to topic/partitions + if err := c.subscribe(tomb, subs); err != nil { + c.rebalanceError(err, notification) + return + } + + // Update/issue notification with new claims + if c.client.config.Group.Return.Notifications { + notification = notification.success(subs) + c.handleNotification(notification) + } + + // Start topic watcher loop + tomb.Go(c.twLoop) + + // Start consuming and committing offsets + tomb.Go(c.cmLoop) + atomic.StoreInt32(&c.consuming, 1) + + // Wait for signals + select { + case <-tomb.Dying(): + case <-c.dying: + } +} + +// heartbeat loop, triggered by the mainLoop +func (c *Consumer) hbLoop(stopped <-chan none) { + ticker := time.NewTicker(c.client.config.Group.Heartbeat.Interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + switch err := c.heartbeat(); err { + case nil, sarama.ErrNoError: + case sarama.ErrNotCoordinatorForConsumer, sarama.ErrRebalanceInProgress: + return + default: + c.handleError(&Error{Ctx: "heartbeat", error: err}) + return + } + case <-stopped: + return + case <-c.dying: + return + } + } +} + +// topic watcher loop, triggered by the mainLoop +func (c *Consumer) twLoop(stopped <-chan none) { + ticker := time.NewTicker(c.client.config.Metadata.RefreshFrequency / 2) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + topics, err := c.client.Topics() + if err != nil { + c.handleError(&Error{Ctx: "topics", error: err}) + return + } + + for _, topic := range topics { + if !c.isKnownCoreTopic(topic) && + !c.isKnownExtraTopic(topic) && + c.isPotentialExtraTopic(topic) { + return + } + } + case <-stopped: + return + case <-c.dying: + return + } + } +} + +// commit loop, triggered by the mainLoop +func (c *Consumer) cmLoop(stopped <-chan none) { + ticker := time.NewTicker(c.client.config.Consumer.Offsets.CommitInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := c.commitOffsetsWithRetry(c.client.config.Group.Offsets.Retry.Max); err != nil { + c.handleError(&Error{Ctx: "commit", error: err}) + return + } + case <-stopped: + return + case <-c.dying: + return + } + } +} + +func (c *Consumer) rebalanceError(err error, n *Notification) { + if n != nil { + n.Type = RebalanceError + c.handleNotification(n) + } + + switch err { + case sarama.ErrRebalanceInProgress: + default: + c.handleError(&Error{Ctx: "rebalance", error: err}) + } + + select { + case <-c.dying: + case <-time.After(c.client.config.Metadata.Retry.Backoff): + } +} + +func (c *Consumer) handleNotification(n *Notification) { + if c.client.config.Group.Return.Notifications { + select { + case c.notifications <- n: + case <-c.dying: + return + } + } +} + +func (c *Consumer) handleError(e *Error) { + if c.client.config.Consumer.Return.Errors { + select { + case c.errors <- e: + case <-c.dying: + return + } + } else { + sarama.Logger.Printf("%s error: %s\n", e.Ctx, e.Error()) + } +} + +// Releases the consumer and commits offsets, called from rebalance() and Close() +func (c *Consumer) release() (err error) { + // Stop all consumers + c.subs.Stop() + + // Clear subscriptions on exit + defer c.subs.Clear() + + // Wait for messages to be processed + timeout := time.NewTimer(c.client.config.Group.Offsets.Synchronization.DwellTime) + defer timeout.Stop() + + select { + case <-c.dying: + case <-timeout.C: + } + + // Commit offsets, continue on errors + if e := c.commitOffsetsWithRetry(c.client.config.Group.Offsets.Retry.Max); e != nil { + err = e + } + + return +} + +// -------------------------------------------------------------------- + +// Performs a heartbeat, part of the mainLoop() +func (c *Consumer) heartbeat() error { + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return err + } + + memberID, generationID := c.membership() + resp, err := broker.Heartbeat(&sarama.HeartbeatRequest{ + GroupId: c.groupID, + MemberId: memberID, + GenerationId: generationID, + }) + if err != nil { + c.closeCoordinator(broker, err) + return err + } + return resp.Err +} + +// Performs a rebalance, part of the mainLoop() +func (c *Consumer) rebalance() (map[string][]int32, error) { + memberID, _ := c.membership() + sarama.Logger.Printf("cluster/consumer %s rebalance\n", memberID) + + allTopics, err := c.client.Topics() + if err != nil { + return nil, err + } + c.extraTopics = c.selectExtraTopics(allTopics) + sort.Strings(c.extraTopics) + + // Re-join consumer group + strategy, err := c.joinGroup() + switch { + case err == sarama.ErrUnknownMemberId: + c.membershipMu.Lock() + c.memberID = "" + c.membershipMu.Unlock() + return nil, err + case err != nil: + return nil, err + } + + // Sync consumer group state, fetch subscriptions + subs, err := c.syncGroup(strategy) + switch { + case err == sarama.ErrRebalanceInProgress: + return nil, err + case err != nil: + _ = c.leaveGroup() + return nil, err + } + return subs, nil +} + +// Performs the subscription, part of the mainLoop() +func (c *Consumer) subscribe(tomb *loopTomb, subs map[string][]int32) error { + // fetch offsets + offsets, err := c.fetchOffsets(subs) + if err != nil { + _ = c.leaveGroup() + return err + } + + // create consumers in parallel + var mu sync.Mutex + var wg sync.WaitGroup + + for topic, partitions := range subs { + for _, partition := range partitions { + wg.Add(1) + + info := offsets[topic][partition] + go func(topic string, partition int32) { + if e := c.createConsumer(tomb, topic, partition, info); e != nil { + mu.Lock() + err = e + mu.Unlock() + } + wg.Done() + }(topic, partition) + } + } + wg.Wait() + + if err != nil { + _ = c.release() + _ = c.leaveGroup() + } + return err +} + +// -------------------------------------------------------------------- + +// Send a request to the broker to join group on rebalance() +func (c *Consumer) joinGroup() (*balancer, error) { + memberID, _ := c.membership() + req := &sarama.JoinGroupRequest{ + GroupId: c.groupID, + MemberId: memberID, + SessionTimeout: int32(c.client.config.Group.Session.Timeout / time.Millisecond), + ProtocolType: "consumer", + } + + meta := &sarama.ConsumerGroupMemberMetadata{ + Version: 1, + Topics: append(c.coreTopics, c.extraTopics...), + UserData: c.client.config.Group.Member.UserData, + } + err := req.AddGroupProtocolMetadata(string(StrategyRange), meta) + if err != nil { + return nil, err + } + err = req.AddGroupProtocolMetadata(string(StrategyRoundRobin), meta) + if err != nil { + return nil, err + } + + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } + + resp, err := broker.JoinGroup(req) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } else if resp.Err != sarama.ErrNoError { + c.closeCoordinator(broker, resp.Err) + return nil, resp.Err + } + + var strategy *balancer + if resp.LeaderId == resp.MemberId { + members, err := resp.GetMembers() + if err != nil { + return nil, err + } + + strategy, err = newBalancerFromMeta(c.client, members) + if err != nil { + return nil, err + } + } + + c.membershipMu.Lock() + c.memberID = resp.MemberId + c.generationID = resp.GenerationId + c.membershipMu.Unlock() + + return strategy, nil +} + +// Send a request to the broker to sync the group on rebalance(). +// Returns a list of topics and partitions to consume. +func (c *Consumer) syncGroup(strategy *balancer) (map[string][]int32, error) { + memberID, generationID := c.membership() + req := &sarama.SyncGroupRequest{ + GroupId: c.groupID, + MemberId: memberID, + GenerationId: generationID, + } + + if strategy != nil { + for memberID, topics := range strategy.Perform(c.client.config.Group.PartitionStrategy) { + if err := req.AddGroupAssignmentMember(memberID, &sarama.ConsumerGroupMemberAssignment{ + Topics: topics, + }); err != nil { + return nil, err + } + } + } + + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } + + resp, err := broker.SyncGroup(req) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } else if resp.Err != sarama.ErrNoError { + c.closeCoordinator(broker, resp.Err) + return nil, resp.Err + } + + // Return if there is nothing to subscribe to + if len(resp.MemberAssignment) == 0 { + return nil, nil + } + + // Get assigned subscriptions + members, err := resp.GetMemberAssignment() + if err != nil { + return nil, err + } + + // Sort partitions, for each topic + for topic := range members.Topics { + sort.Sort(int32Slice(members.Topics[topic])) + } + return members.Topics, nil +} + +// Fetches latest committed offsets for all subscriptions +func (c *Consumer) fetchOffsets(subs map[string][]int32) (map[string]map[int32]offsetInfo, error) { + offsets := make(map[string]map[int32]offsetInfo, len(subs)) + req := &sarama.OffsetFetchRequest{ + Version: 1, + ConsumerGroup: c.groupID, + } + + for topic, partitions := range subs { + offsets[topic] = make(map[int32]offsetInfo, len(partitions)) + for _, partition := range partitions { + offsets[topic][partition] = offsetInfo{Offset: -1} + req.AddPartition(topic, partition) + } + } + + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } + + resp, err := broker.FetchOffset(req) + if err != nil { + c.closeCoordinator(broker, err) + return nil, err + } + + for topic, partitions := range subs { + for _, partition := range partitions { + block := resp.GetBlock(topic, partition) + if block == nil { + return nil, sarama.ErrIncompleteResponse + } + + if block.Err == sarama.ErrNoError { + offsets[topic][partition] = offsetInfo{Offset: block.Offset, Metadata: block.Metadata} + } else { + return nil, block.Err + } + } + } + return offsets, nil +} + +// Send a request to the broker to leave the group on failes rebalance() and on Close() +func (c *Consumer) leaveGroup() error { + broker, err := c.client.Coordinator(c.groupID) + if err != nil { + c.closeCoordinator(broker, err) + return err + } + + memberID, _ := c.membership() + if _, err = broker.LeaveGroup(&sarama.LeaveGroupRequest{ + GroupId: c.groupID, + MemberId: memberID, + }); err != nil { + c.closeCoordinator(broker, err) + } + return err +} + +// -------------------------------------------------------------------- + +func (c *Consumer) createConsumer(tomb *loopTomb, topic string, partition int32, info offsetInfo) error { + memberID, _ := c.membership() + sarama.Logger.Printf("cluster/consumer %s consume %s/%d from %d\n", memberID, topic, partition, info.NextOffset(c.client.config.Consumer.Offsets.Initial)) + + // Create partitionConsumer + pc, err := newPartitionConsumer(c.consumer, topic, partition, info, c.client.config.Consumer.Offsets.Initial) + if err != nil { + return err + } + + // Store in subscriptions + c.subs.Store(topic, partition, pc) + + // Start partition consumer goroutine + tomb.Go(func(stopper <-chan none) { + if c.client.config.Group.Mode == ConsumerModePartitions { + pc.waitFor(stopper, c.errors) + } else { + pc.multiplex(stopper, c.messages, c.errors) + } + }) + + if c.client.config.Group.Mode == ConsumerModePartitions { + c.partitions <- pc + } + return nil +} + +func (c *Consumer) commitOffsetsWithRetry(retries int) error { + err := c.CommitOffsets() + if err != nil && retries > 0 { + return c.commitOffsetsWithRetry(retries - 1) + } + return err +} + +func (c *Consumer) closeCoordinator(broker *sarama.Broker, err error) { + if broker != nil { + _ = broker.Close() + } + + switch err { + case sarama.ErrConsumerCoordinatorNotAvailable, sarama.ErrNotCoordinatorForConsumer: + _ = c.client.RefreshCoordinator(c.groupID) + } +} + +func (c *Consumer) selectExtraTopics(allTopics []string) []string { + extra := allTopics[:0] + for _, topic := range allTopics { + if !c.isKnownCoreTopic(topic) && c.isPotentialExtraTopic(topic) { + extra = append(extra, topic) + } + } + return extra +} + +func (c *Consumer) isKnownCoreTopic(topic string) bool { + pos := sort.SearchStrings(c.coreTopics, topic) + return pos < len(c.coreTopics) && c.coreTopics[pos] == topic +} + +func (c *Consumer) isKnownExtraTopic(topic string) bool { + pos := sort.SearchStrings(c.extraTopics, topic) + return pos < len(c.extraTopics) && c.extraTopics[pos] == topic +} + +func (c *Consumer) isPotentialExtraTopic(topic string) bool { + rx := c.client.config.Group.Topics + if rx.Blacklist != nil && rx.Blacklist.MatchString(topic) { + return false + } + if rx.Whitelist != nil && rx.Whitelist.MatchString(topic) { + return true + } + return false +} + +func (c *Consumer) refreshCoordinator() error { + if err := c.refreshMetadata(); err != nil { + return err + } + return c.client.RefreshCoordinator(c.groupID) +} + +func (c *Consumer) refreshMetadata() (err error) { + if c.client.config.Metadata.Full { + err = c.client.RefreshMetadata() + } else { + var topics []string + if topics, err = c.client.Topics(); err == nil && len(topics) != 0 { + err = c.client.RefreshMetadata(topics...) + } + } + + // maybe we didn't have authorization to describe all topics + switch err { + case sarama.ErrTopicAuthorizationFailed: + err = c.client.RefreshMetadata(c.coreTopics...) + } + return +} + +func (c *Consumer) membership() (memberID string, generationID int32) { + c.membershipMu.RLock() + memberID, generationID = c.memberID, c.generationID + c.membershipMu.RUnlock() + return +} diff --git a/vendor/github.com/bsm/sarama-cluster/doc.go b/vendor/github.com/bsm/sarama-cluster/doc.go new file mode 100644 index 0000000..9c8ff16 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/doc.go @@ -0,0 +1,8 @@ +/* +Package cluster provides cluster extensions for Sarama, enabing users +to consume topics across from multiple, balanced nodes. + +It requires Kafka v0.9+ and follows the steps guide, described in: +https://cwiki.apache.org/confluence/display/KAFKA/Kafka+0.9+Consumer+Rewrite+Design +*/ +package cluster diff --git a/vendor/github.com/bsm/sarama-cluster/offsets.go b/vendor/github.com/bsm/sarama-cluster/offsets.go new file mode 100644 index 0000000..4223ac5 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/offsets.go @@ -0,0 +1,69 @@ +package cluster + +import ( + "sync" + + "github.com/Shopify/sarama" +) + +// OffsetStash allows to accumulate offsets and +// mark them as processed in a bulk +type OffsetStash struct { + offsets map[topicPartition]offsetInfo + mu sync.Mutex +} + +// NewOffsetStash inits a blank stash +func NewOffsetStash() *OffsetStash { + return &OffsetStash{offsets: make(map[topicPartition]offsetInfo)} +} + +// MarkOffset stashes the provided message offset +func (s *OffsetStash) MarkOffset(msg *sarama.ConsumerMessage, metadata string) { + s.MarkPartitionOffset(msg.Topic, msg.Partition, msg.Offset, metadata) +} + +// MarkPartitionOffset stashes the offset for the provided topic/partition combination +func (s *OffsetStash) MarkPartitionOffset(topic string, partition int32, offset int64, metadata string) { + s.mu.Lock() + defer s.mu.Unlock() + + key := topicPartition{Topic: topic, Partition: partition} + if info := s.offsets[key]; offset >= info.Offset { + info.Offset = offset + info.Metadata = metadata + s.offsets[key] = info + } +} + +// ResetPartitionOffset stashes the offset for the provided topic/partition combination. +// Difference between ResetPartitionOffset and MarkPartitionOffset is that, ResetPartitionOffset supports earlier offsets +func (s *OffsetStash) ResetPartitionOffset(topic string, partition int32, offset int64, metadata string) { + s.mu.Lock() + defer s.mu.Unlock() + + key := topicPartition{Topic: topic, Partition: partition} + if info := s.offsets[key]; offset <= info.Offset { + info.Offset = offset + info.Metadata = metadata + s.offsets[key] = info + } +} + +// ResetOffset stashes the provided message offset +// See ResetPartitionOffset for explanation +func (s *OffsetStash) ResetOffset(msg *sarama.ConsumerMessage, metadata string) { + s.ResetPartitionOffset(msg.Topic, msg.Partition, msg.Offset, metadata) +} + +// Offsets returns the latest stashed offsets by topic-partition +func (s *OffsetStash) Offsets() map[string]int64 { + s.mu.Lock() + defer s.mu.Unlock() + + res := make(map[string]int64, len(s.offsets)) + for tp, info := range s.offsets { + res[tp.String()] = info.Offset + } + return res +} diff --git a/vendor/github.com/bsm/sarama-cluster/partitions.go b/vendor/github.com/bsm/sarama-cluster/partitions.go new file mode 100644 index 0000000..bfaa587 --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/partitions.go @@ -0,0 +1,290 @@ +package cluster + +import ( + "sort" + "sync" + "time" + + "github.com/Shopify/sarama" +) + +// PartitionConsumer allows code to consume individual partitions from the cluster. +// +// See docs for Consumer.Partitions() for more on how to implement this. +type PartitionConsumer interface { + sarama.PartitionConsumer + + // Topic returns the consumed topic name + Topic() string + + // Partition returns the consumed partition + Partition() int32 + + // InitialOffset returns the offset used for creating the PartitionConsumer instance. + // The returned offset can be a literal offset, or OffsetNewest, or OffsetOldest + InitialOffset() int64 + + // MarkOffset marks the offset of a message as preocessed. + MarkOffset(offset int64, metadata string) + + // ResetOffset resets the offset to a previously processed message. + ResetOffset(offset int64, metadata string) +} + +type partitionConsumer struct { + sarama.PartitionConsumer + + state partitionState + mu sync.Mutex + + topic string + partition int32 + initialOffset int64 + + closeOnce sync.Once + closeErr error + + dying, dead chan none +} + +func newPartitionConsumer(manager sarama.Consumer, topic string, partition int32, info offsetInfo, defaultOffset int64) (*partitionConsumer, error) { + offset := info.NextOffset(defaultOffset) + pcm, err := manager.ConsumePartition(topic, partition, offset) + + // Resume from default offset, if requested offset is out-of-range + if err == sarama.ErrOffsetOutOfRange { + info.Offset = -1 + offset = defaultOffset + pcm, err = manager.ConsumePartition(topic, partition, offset) + } + if err != nil { + return nil, err + } + + return &partitionConsumer{ + PartitionConsumer: pcm, + state: partitionState{Info: info}, + + topic: topic, + partition: partition, + initialOffset: offset, + + dying: make(chan none), + dead: make(chan none), + }, nil +} + +// Topic implements PartitionConsumer +func (c *partitionConsumer) Topic() string { return c.topic } + +// Partition implements PartitionConsumer +func (c *partitionConsumer) Partition() int32 { return c.partition } + +// InitialOffset implements PartitionConsumer +func (c *partitionConsumer) InitialOffset() int64 { return c.initialOffset } + +// AsyncClose implements PartitionConsumer +func (c *partitionConsumer) AsyncClose() { + c.closeOnce.Do(func() { + c.closeErr = c.PartitionConsumer.Close() + close(c.dying) + }) +} + +// Close implements PartitionConsumer +func (c *partitionConsumer) Close() error { + c.AsyncClose() + <-c.dead + return c.closeErr +} + +func (c *partitionConsumer) waitFor(stopper <-chan none, errors chan<- error) { + defer close(c.dead) + + for { + select { + case err, ok := <-c.Errors(): + if !ok { + return + } + select { + case errors <- err: + case <-stopper: + return + case <-c.dying: + return + } + case <-stopper: + return + case <-c.dying: + return + } + } +} + +func (c *partitionConsumer) multiplex(stopper <-chan none, messages chan<- *sarama.ConsumerMessage, errors chan<- error) { + defer close(c.dead) + + for { + select { + case msg, ok := <-c.Messages(): + if !ok { + return + } + select { + case messages <- msg: + case <-stopper: + return + case <-c.dying: + return + } + case err, ok := <-c.Errors(): + if !ok { + return + } + select { + case errors <- err: + case <-stopper: + return + case <-c.dying: + return + } + case <-stopper: + return + case <-c.dying: + return + } + } +} + +func (c *partitionConsumer) getState() partitionState { + c.mu.Lock() + state := c.state + c.mu.Unlock() + + return state +} + +func (c *partitionConsumer) markCommitted(offset int64) { + c.mu.Lock() + if offset == c.state.Info.Offset { + c.state.Dirty = false + } + c.mu.Unlock() +} + +// MarkOffset implements PartitionConsumer +func (c *partitionConsumer) MarkOffset(offset int64, metadata string) { + c.mu.Lock() + if next := offset + 1; next > c.state.Info.Offset { + c.state.Info.Offset = next + c.state.Info.Metadata = metadata + c.state.Dirty = true + } + c.mu.Unlock() +} + +// ResetOffset implements PartitionConsumer +func (c *partitionConsumer) ResetOffset(offset int64, metadata string) { + c.mu.Lock() + if next := offset + 1; next <= c.state.Info.Offset { + c.state.Info.Offset = next + c.state.Info.Metadata = metadata + c.state.Dirty = true + } + c.mu.Unlock() +} + +// -------------------------------------------------------------------- + +type partitionState struct { + Info offsetInfo + Dirty bool + LastCommit time.Time +} + +// -------------------------------------------------------------------- + +type partitionMap struct { + data map[topicPartition]*partitionConsumer + mu sync.RWMutex +} + +func newPartitionMap() *partitionMap { + return &partitionMap{ + data: make(map[topicPartition]*partitionConsumer), + } +} + +func (m *partitionMap) IsSubscribedTo(topic string) bool { + m.mu.RLock() + defer m.mu.RUnlock() + + for tp := range m.data { + if tp.Topic == topic { + return true + } + } + return false +} + +func (m *partitionMap) Fetch(topic string, partition int32) *partitionConsumer { + m.mu.RLock() + pc, _ := m.data[topicPartition{topic, partition}] + m.mu.RUnlock() + return pc +} + +func (m *partitionMap) Store(topic string, partition int32, pc *partitionConsumer) { + m.mu.Lock() + m.data[topicPartition{topic, partition}] = pc + m.mu.Unlock() +} + +func (m *partitionMap) Snapshot() map[topicPartition]partitionState { + m.mu.RLock() + defer m.mu.RUnlock() + + snap := make(map[topicPartition]partitionState, len(m.data)) + for tp, pc := range m.data { + snap[tp] = pc.getState() + } + return snap +} + +func (m *partitionMap) Stop() { + m.mu.RLock() + defer m.mu.RUnlock() + + var wg sync.WaitGroup + for tp := range m.data { + wg.Add(1) + go func(p *partitionConsumer) { + _ = p.Close() + wg.Done() + }(m.data[tp]) + } + wg.Wait() +} + +func (m *partitionMap) Clear() { + m.mu.Lock() + for tp := range m.data { + delete(m.data, tp) + } + m.mu.Unlock() +} + +func (m *partitionMap) Info() map[string][]int32 { + info := make(map[string][]int32) + m.mu.RLock() + for tp := range m.data { + info[tp.Topic] = append(info[tp.Topic], tp.Partition) + } + m.mu.RUnlock() + + for topic := range info { + sort.Sort(int32Slice(info[topic])) + } + return info +} diff --git a/vendor/github.com/bsm/sarama-cluster/util.go b/vendor/github.com/bsm/sarama-cluster/util.go new file mode 100644 index 0000000..e7cb5dd --- /dev/null +++ b/vendor/github.com/bsm/sarama-cluster/util.go @@ -0,0 +1,75 @@ +package cluster + +import ( + "fmt" + "sort" + "sync" +) + +type none struct{} + +type topicPartition struct { + Topic string + Partition int32 +} + +func (tp *topicPartition) String() string { + return fmt.Sprintf("%s-%d", tp.Topic, tp.Partition) +} + +type offsetInfo struct { + Offset int64 + Metadata string +} + +func (i offsetInfo) NextOffset(fallback int64) int64 { + if i.Offset > -1 { + return i.Offset + } + return fallback +} + +type int32Slice []int32 + +func (p int32Slice) Len() int { return len(p) } +func (p int32Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p int32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func (p int32Slice) Diff(o int32Slice) (res []int32) { + on := len(o) + for _, x := range p { + n := sort.Search(on, func(i int) bool { return o[i] >= x }) + if n < on && o[n] == x { + continue + } + res = append(res, x) + } + return +} + +// -------------------------------------------------------------------- + +type loopTomb struct { + c chan none + o sync.Once + w sync.WaitGroup +} + +func newLoopTomb() *loopTomb { + return &loopTomb{c: make(chan none)} +} + +func (t *loopTomb) stop() { t.o.Do(func() { close(t.c) }) } +func (t *loopTomb) Close() { t.stop(); t.w.Wait() } + +func (t *loopTomb) Dying() <-chan none { return t.c } +func (t *loopTomb) Go(f func(<-chan none)) { + t.w.Add(1) + + go func() { + defer t.stop() + defer t.w.Done() + + f(t.c) + }() +} diff --git a/vendor/github.com/caarlos0/env/v6/.gitignore b/vendor/github.com/caarlos0/env/v6/.gitignore new file mode 100644 index 0000000..ca6a0ff --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/.gitignore @@ -0,0 +1,4 @@ +coverage.txt +bin +card.png +dist diff --git a/vendor/github.com/caarlos0/env/v6/.gorelangci.yml b/vendor/github.com/caarlos0/env/v6/.gorelangci.yml new file mode 100644 index 0000000..496be3e --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/.gorelangci.yml @@ -0,0 +1,7 @@ +linters: + enable-all: true + disable: + - wsl + - lll + - funlen + - godox diff --git a/vendor/github.com/caarlos0/env/v6/.goreleaser.yml b/vendor/github.com/caarlos0/env/v6/.goreleaser.yml new file mode 100644 index 0000000..461ca3f --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/.goreleaser.yml @@ -0,0 +1,11 @@ +before: + hooks: + - go mod tidy +builds: +- skip: true +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/vendor/github.com/caarlos0/env/v6/.hound.yml b/vendor/github.com/caarlos0/env/v6/.hound.yml new file mode 100644 index 0000000..e5c719d --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/.hound.yml @@ -0,0 +1,2 @@ +go: + enabled: true diff --git a/vendor/github.com/caarlos0/env/v6/LICENSE.md b/vendor/github.com/caarlos0/env/v6/LICENSE.md new file mode 100644 index 0000000..a05f258 --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2019 Carlos Alexandro Becker + +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. diff --git a/vendor/github.com/caarlos0/env/v6/Makefile b/vendor/github.com/caarlos0/env/v6/Makefile new file mode 100644 index 0000000..3e25d1b --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/Makefile @@ -0,0 +1,38 @@ +SOURCE_FILES?=./... +TEST_PATTERN?=. + +export GO111MODULE := on + +setup: + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh + go mod download +.PHONY: setup + +build: + go build +.PHONY: build + +test: + go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m +.PHONY: test + +cover: test + go tool cover -html=coverage.txt +.PHONY: cover + +fmt: + find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done +.PHONY: fmt + +lint: + ./bin/golangci-lint run ./... +.PHONY: lint + +ci: build test lint +.PHONY: ci + +card: + wget -O card.png -c "https://og.caarlos0.dev/**env**: parse envs to structs.png?theme=light&md=1&fontSize=100px&images=https://github.com/caarlos0.png" +.PHONY: card + +.DEFAULT_GOAL := ci diff --git a/vendor/github.com/caarlos0/env/v6/README.md b/vendor/github.com/caarlos0/env/v6/README.md new file mode 100644 index 0000000..a759776 --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/README.md @@ -0,0 +1,181 @@ +# env + +[![Build Status](https://img.shields.io/github/workflow/status/caarlos0/env/build?style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build) +[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env) +[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](http://godoc.org/github.com/caarlos0/env) + +Simple lib to parse envs to structs in Go. + +## Example + +A very basic example: + +```go +package main + +import ( + "fmt" + "time" + + // if using go modules + "github.com/caarlos0/env/v6" + + // if using dep/others + "github.com/caarlos0/env" +) + +type config struct { + Home string `env:"HOME"` + Port int `env:"PORT" envDefault:"3000"` + IsProduction bool `env:"PRODUCTION"` + Hosts []string `env:"HOSTS" envSeparator:":"` + Duration time.Duration `env:"DURATION"` + TempFolder string `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"` +} + +func main() { + cfg := config{} + if err := env.Parse(&cfg); err != nil { + fmt.Printf("%+v\n", err) + } + + fmt.Printf("%+v\n", cfg) +} +``` + +You can run it like this: + +```sh +$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go +{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s} +``` + +## Supported types and defaults + +Out of the box all built-in types are supported, plus a few others that +are commonly used. + +Complete list: + +- `string` +- `bool` +- `int` +- `int8` +- `int16` +- `int32` +- `int64` +- `uint` +- `uint8` +- `uint16` +- `uint32` +- `uint64` +- `float32` +- `float64` +- `string` +- `time.Duration` +- `encoding.TextUnmarshaler` +- `url.URL` + +Pointers, slices and slices of pointers of those types are also supported. + +You can also use/define a [custom parser func](#custom-parser-funcs) for any +other type you want. + +If you set the `envDefault` tag for something, this value will be used in the +case of absence of it in the environment. + +By default, slice types will split the environment value on `,`; you can change +this behavior by setting the `envSeparator` tag. + +If you set the `envExpand` tag, environment variables (either in `${var}` or +`$var` format) in the string will be replaced according with the actual value +of the variable. + +Unexported fields are ignored. + +## Custom Parser Funcs + +If you have a type that is not supported out of the box by the lib, you are able +to use (or define) and pass custom parsers (and their associated `reflect.Type`) +to the `env.ParseWithFuncs()` function. + +In addition to accepting a struct pointer (same as `Parse()`), this function +also accepts a `map[reflect.Type]env.ParserFunc`. + +`env` also ships with some pre-built custom parser funcs for common types. You +can check them out [here](parsers/). + +If you add a custom parser for, say `Foo`, it will also be used to parse +`*Foo` and `[]Foo` types. + +This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs` +to facilitate the parsing of envs that are not basic types. + +Check the example in the [go doc](http://godoc.org/github.com/caarlos0/env) +for more info. + +## Required fields + +The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added +to ensure that some environment variable is set. In the example above, +an error is returned if the `config` struct is changed to: + + +```go +type config struct { + Home string `env:"HOME"` + Port int `env:"PORT" envDefault:"3000"` + IsProduction bool `env:"PRODUCTION"` + Hosts []string `env:"HOSTS" envSeparator:":"` + SecretKey string `env:"SECRET_KEY,required"` +} +``` + + +## From file + +The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added +to in order to indicate that the value of the variable shall be loaded from a file. The path of that file is given +by the environment variable associated with it +Example below + +```go +package main + +import ( + "fmt" + "time" + "github.com/caarlos0/env" +) + +type config struct { + Secret string `env:"SECRET,file"` + Password string `env:"PASSWORD,file" envDefault:"/tmp/password"` + Certificate string `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"` +} + +func main() { + cfg := config{} + if err := env.Parse(&cfg); err != nil { + fmt.Printf("%+v\n", err) + } + + fmt.Printf("%+v\n", cfg) +} +``` + +```sh +$ echo qwerty > /tmp/secret +$ echo dvorak > /tmp/password +$ echo coleman > /tmp/certificate + +$ SECRET=/tmp/secret \ + CERTIFICATE_FILE=/tmp/certificate \ + go run main.go +{Secret:qwerty Password:dvorak Certificate:coleman} +``` + +## Stargazers over time + +[![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env) + diff --git a/vendor/github.com/caarlos0/env/v6/env.go b/vendor/github.com/caarlos0/env/v6/env.go new file mode 100644 index 0000000..9016d50 --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/env.go @@ -0,0 +1,373 @@ +package env + +import ( + "encoding" + "errors" + "fmt" + "io/ioutil" + "net/url" + "os" + "reflect" + "strconv" + "strings" + "time" +) + +// nolint: gochecknoglobals +var ( + // ErrNotAStructPtr is returned if you pass something that is not a pointer to a + // Struct to Parse + ErrNotAStructPtr = errors.New("env: expected a pointer to a Struct") + + defaultBuiltInParsers = map[reflect.Kind]ParserFunc{ + reflect.Bool: func(v string) (interface{}, error) { + return strconv.ParseBool(v) + }, + reflect.String: func(v string) (interface{}, error) { + return v, nil + }, + reflect.Int: func(v string) (interface{}, error) { + i, err := strconv.ParseInt(v, 10, 32) + return int(i), err + }, + reflect.Int16: func(v string) (interface{}, error) { + i, err := strconv.ParseInt(v, 10, 16) + return int16(i), err + }, + reflect.Int32: func(v string) (interface{}, error) { + i, err := strconv.ParseInt(v, 10, 32) + return int32(i), err + }, + reflect.Int64: func(v string) (interface{}, error) { + return strconv.ParseInt(v, 10, 64) + }, + reflect.Int8: func(v string) (interface{}, error) { + i, err := strconv.ParseInt(v, 10, 8) + return int8(i), err + }, + reflect.Uint: func(v string) (interface{}, error) { + i, err := strconv.ParseUint(v, 10, 32) + return uint(i), err + }, + reflect.Uint16: func(v string) (interface{}, error) { + i, err := strconv.ParseUint(v, 10, 16) + return uint16(i), err + }, + reflect.Uint32: func(v string) (interface{}, error) { + i, err := strconv.ParseUint(v, 10, 32) + return uint32(i), err + }, + reflect.Uint64: func(v string) (interface{}, error) { + i, err := strconv.ParseUint(v, 10, 64) + return i, err + }, + reflect.Uint8: func(v string) (interface{}, error) { + i, err := strconv.ParseUint(v, 10, 8) + return uint8(i), err + }, + reflect.Float64: func(v string) (interface{}, error) { + return strconv.ParseFloat(v, 64) + }, + reflect.Float32: func(v string) (interface{}, error) { + f, err := strconv.ParseFloat(v, 32) + return float32(f), err + }, + } + + defaultTypeParsers = map[reflect.Type]ParserFunc{ + reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) { + u, err := url.Parse(v) + if err != nil { + return nil, fmt.Errorf("unable to parse URL: %v", err) + } + return *u, nil + }, + reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) { + s, err := time.ParseDuration(v) + if err != nil { + return nil, fmt.Errorf("unable to parse duration: %v", err) + } + return s, err + }, + } +) + +// ParserFunc defines the signature of a function that can be used within `CustomParsers` +type ParserFunc func(v string) (interface{}, error) + +// Parse parses a struct containing `env` tags and loads its values from +// environment variables. +func Parse(v interface{}) error { + return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}) +} + +// ParseWithFuncs is the same as `Parse` except it also allows the user to pass +// in custom parsers. +func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc) error { + ptrRef := reflect.ValueOf(v) + if ptrRef.Kind() != reflect.Ptr { + return ErrNotAStructPtr + } + ref := ptrRef.Elem() + if ref.Kind() != reflect.Struct { + return ErrNotAStructPtr + } + var parsers = defaultTypeParsers + for k, v := range funcMap { + parsers[k] = v + } + return doParse(ref, parsers) +} + +func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc) error { + var refType = ref.Type() + + for i := 0; i < refType.NumField(); i++ { + refField := ref.Field(i) + if !refField.CanSet() { + continue + } + if reflect.Ptr == refField.Kind() && !refField.IsNil() { + err := ParseWithFuncs(refField.Interface(), funcMap) + if err != nil { + return err + } + continue + } + if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" { + err := Parse(refField.Addr().Interface()) + if err != nil { + return err + } + continue + } + refTypeField := refType.Field(i) + value, err := get(refTypeField) + if err != nil { + return err + } + if value == "" { + if reflect.Struct == refField.Kind() { + if err := doParse(refField, funcMap); err != nil { + return err + } + } + continue + } + if err := set(refField, refTypeField, value, funcMap); err != nil { + return err + } + } + return nil +} + +func get(field reflect.StructField) (val string, err error) { + var required bool + var exists bool + var loadFile bool + var expand = strings.EqualFold(field.Tag.Get("envExpand"), "true") + + key, opts := parseKeyForOption(field.Tag.Get("env")) + + for _, opt := range opts { + switch opt { + case "": + break + case "file": + loadFile = true + case "required": + required = true + default: + return "", fmt.Errorf("env: tag option %q not supported", opt) + } + } + + defaultValue := field.Tag.Get("envDefault") + val, exists = getOr(key, defaultValue) + + if expand { + val = os.ExpandEnv(val) + } + + if required && !exists { + return "", fmt.Errorf(`env: required environment variable %q is not set`, key) + } + + if loadFile && val != "" { + filename := val + val, err = getFromFile(filename) + if err != nil { + return "", fmt.Errorf(`env: could not load content of file "%s" from variable %s: %v`, filename, key, err) + } + } + + return val, err +} + +// split the env tag's key into the expected key and desired option, if any. +func parseKeyForOption(key string) (string, []string) { + opts := strings.Split(key, ",") + return opts[0], opts[1:] +} + +func getFromFile(filename string) (value string, err error) { + b, err := ioutil.ReadFile(filename) + return string(b), err +} + +func getOr(key, defaultValue string) (value string, exists bool) { + value, exists = os.LookupEnv(key) + if !exists { + value = defaultValue + } + return value, exists +} + +func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[reflect.Type]ParserFunc) error { + if field.Kind() == reflect.Slice { + return handleSlice(field, value, sf, funcMap) + } + + var tm = asTextUnmarshaler(field) + if tm != nil { + var err = tm.UnmarshalText([]byte(value)) + return newParseError(sf, err) + } + + var typee = sf.Type + var fieldee = field + if typee.Kind() == reflect.Ptr { + typee = typee.Elem() + fieldee = field.Elem() + } + + parserFunc, ok := funcMap[typee] + if ok { + val, err := parserFunc(value) + if err != nil { + return newParseError(sf, err) + } + + fieldee.Set(reflect.ValueOf(val)) + return nil + } + + parserFunc, ok = defaultBuiltInParsers[typee.Kind()] + if ok { + val, err := parserFunc(value) + if err != nil { + return newParseError(sf, err) + } + + fieldee.Set(reflect.ValueOf(val).Convert(typee)) + return nil + } + + return newNoParserError(sf) +} + +func handleSlice(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error { + var separator = sf.Tag.Get("envSeparator") + if separator == "" { + separator = "," + } + var parts = strings.Split(value, separator) + + var typee = sf.Type.Elem() + if typee.Kind() == reflect.Ptr { + typee = typee.Elem() + } + + if _, ok := reflect.New(typee).Interface().(encoding.TextUnmarshaler); ok { + return parseTextUnmarshalers(field, parts, sf) + } + + parserFunc, ok := funcMap[typee] + if !ok { + parserFunc, ok = defaultBuiltInParsers[typee.Kind()] + if !ok { + return newNoParserError(sf) + } + } + + var result = reflect.MakeSlice(sf.Type, 0, len(parts)) + for _, part := range parts { + r, err := parserFunc(part) + if err != nil { + return newParseError(sf, err) + } + var v = reflect.ValueOf(r).Convert(typee) + if sf.Type.Elem().Kind() == reflect.Ptr { + v = reflect.New(typee) + v.Elem().Set(reflect.ValueOf(r).Convert(typee)) + } + result = reflect.Append(result, v) + } + field.Set(result) + return nil +} + +func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler { + if reflect.Ptr == field.Kind() { + if field.IsNil() { + field.Set(reflect.New(field.Type().Elem())) + } + } else if field.CanAddr() { + field = field.Addr() + } + + tm, ok := field.Interface().(encoding.TextUnmarshaler) + if !ok { + return nil + } + return tm +} + +func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.StructField) error { + s := len(data) + elemType := field.Type().Elem() + slice := reflect.MakeSlice(reflect.SliceOf(elemType), s, s) + for i, v := range data { + sv := slice.Index(i) + kind := sv.Kind() + if kind == reflect.Ptr { + sv = reflect.New(elemType.Elem()) + } else { + sv = sv.Addr() + } + tm := sv.Interface().(encoding.TextUnmarshaler) + if err := tm.UnmarshalText([]byte(v)); err != nil { + return newParseError(sf, err) + } + if kind == reflect.Ptr { + slice.Index(i).Set(sv) + } + } + + field.Set(slice) + + return nil +} + +func newParseError(sf reflect.StructField, err error) error { + if err == nil { + return nil + } + return parseError{ + sf: sf, + err: err, + } +} + +type parseError struct { + sf reflect.StructField + err error +} + +func (e parseError) Error() string { + return fmt.Sprintf(`env: parse error on field "%s" of type "%s": %v`, e.sf.Name, e.sf.Type, e.err) +} + +func newNoParserError(sf reflect.StructField) error { + return fmt.Errorf(`env: no parser found for field "%s" of type "%s"`, sf.Name, sf.Type) +} diff --git a/vendor/github.com/caarlos0/env/v6/go.mod b/vendor/github.com/caarlos0/env/v6/go.mod new file mode 100644 index 0000000..67ed02f --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/go.mod @@ -0,0 +1,5 @@ +module github.com/caarlos0/env/v6 + +require github.com/stretchr/testify v1.5.1 + +go 1.14 diff --git a/vendor/github.com/caarlos0/env/v6/go.sum b/vendor/github.com/caarlos0/env/v6/go.sum new file mode 100644 index 0000000..c0565d7 --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/caarlos0/env/v6/renovate.json b/vendor/github.com/caarlos0/env/v6/renovate.json new file mode 100644 index 0000000..f45d8f1 --- /dev/null +++ b/vendor/github.com/caarlos0/env/v6/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} diff --git a/vendor/github.com/cespare/xxhash/v2/.travis.yml b/vendor/github.com/cespare/xxhash/v2/.travis.yml new file mode 100644 index 0000000..c516ea8 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - "1.x" + - master +env: + - TAGS="" + - TAGS="-tags purego" +script: go test $TAGS -v ./... diff --git a/vendor/github.com/cespare/xxhash/v2/LICENSE.txt b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt new file mode 100644 index 0000000..24b5306 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2016 Caleb Spare + +MIT License + +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. diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md new file mode 100644 index 0000000..2fd8693 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -0,0 +1,67 @@ +# xxhash + +[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash) +[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash) + +xxhash is a Go implementation of the 64-bit +[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a +high-quality hashing algorithm that is much faster than anything in the Go +standard library. + +This package provides a straightforward API: + +``` +func Sum64(b []byte) uint64 +func Sum64String(s string) uint64 +type Digest struct{ ... } + func New() *Digest +``` + +The `Digest` type implements hash.Hash64. Its key methods are: + +``` +func (*Digest) Write([]byte) (int, error) +func (*Digest) WriteString(string) (int, error) +func (*Digest) Sum64() uint64 +``` + +This implementation provides a fast pure-Go implementation and an even faster +assembly implementation for amd64. + +## Compatibility + +This package is in a module and the latest code is in version 2 of the module. +You need a version of Go with at least "minimal module compatibility" to use +github.com/cespare/xxhash/v2: + +* 1.9.7+ for Go 1.9 +* 1.10.3+ for Go 1.10 +* Go 1.11 or later + +I recommend using the latest release of Go. + +## Benchmarks + +Here are some quick benchmarks comparing the pure-Go and assembly +implementations of Sum64. + +| input size | purego | asm | +| --- | --- | --- | +| 5 B | 979.66 MB/s | 1291.17 MB/s | +| 100 B | 7475.26 MB/s | 7973.40 MB/s | +| 4 KB | 17573.46 MB/s | 17602.65 MB/s | +| 10 MB | 17131.46 MB/s | 17142.16 MB/s | + +These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using +the following commands under Go 1.11.2: + +``` +$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes' +$ go test -benchtime 10s -bench '/xxhash,direct,bytes' +``` + +## Projects using this package + +- [InfluxDB](https://github.com/influxdata/influxdb) +- [Prometheus](https://github.com/prometheus/prometheus) +- [FreeCache](https://github.com/coocood/freecache) diff --git a/vendor/github.com/cespare/xxhash/v2/go.mod b/vendor/github.com/cespare/xxhash/v2/go.mod new file mode 100644 index 0000000..49f6760 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/cespare/xxhash/v2 + +go 1.11 diff --git a/vendor/github.com/cespare/xxhash/v2/go.sum b/vendor/github.com/cespare/xxhash/v2/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go new file mode 100644 index 0000000..db0b35f --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -0,0 +1,236 @@ +// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described +// at http://cyan4973.github.io/xxHash/. +package xxhash + +import ( + "encoding/binary" + "errors" + "math/bits" +) + +const ( + prime1 uint64 = 11400714785074694791 + prime2 uint64 = 14029467366897019727 + prime3 uint64 = 1609587929392839161 + prime4 uint64 = 9650029242287828579 + prime5 uint64 = 2870177450012600261 +) + +// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where +// possible in the Go code is worth a small (but measurable) performance boost +// by avoiding some MOVQs. Vars are needed for the asm and also are useful for +// convenience in the Go code in a few places where we need to intentionally +// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the +// result overflows a uint64). +var ( + prime1v = prime1 + prime2v = prime2 + prime3v = prime3 + prime4v = prime4 + prime5v = prime5 +) + +// Digest implements hash.Hash64. +type Digest struct { + v1 uint64 + v2 uint64 + v3 uint64 + v4 uint64 + total uint64 + mem [32]byte + n int // how much of mem is used +} + +// New creates a new Digest that computes the 64-bit xxHash algorithm. +func New() *Digest { + var d Digest + d.Reset() + return &d +} + +// Reset clears the Digest's state so that it can be reused. +func (d *Digest) Reset() { + d.v1 = prime1v + prime2 + d.v2 = prime2 + d.v3 = 0 + d.v4 = -prime1v + d.total = 0 + d.n = 0 +} + +// Size always returns 8 bytes. +func (d *Digest) Size() int { return 8 } + +// BlockSize always returns 32 bytes. +func (d *Digest) BlockSize() int { return 32 } + +// Write adds more data to d. It always returns len(b), nil. +func (d *Digest) Write(b []byte) (n int, err error) { + n = len(b) + d.total += uint64(n) + + if d.n+n < 32 { + // This new data doesn't even fill the current block. + copy(d.mem[d.n:], b) + d.n += n + return + } + + if d.n > 0 { + // Finish off the partial block. + copy(d.mem[d.n:], b) + d.v1 = round(d.v1, u64(d.mem[0:8])) + d.v2 = round(d.v2, u64(d.mem[8:16])) + d.v3 = round(d.v3, u64(d.mem[16:24])) + d.v4 = round(d.v4, u64(d.mem[24:32])) + b = b[32-d.n:] + d.n = 0 + } + + if len(b) >= 32 { + // One or more full blocks left. + nw := writeBlocks(d, b) + b = b[nw:] + } + + // Store any remaining partial block. + copy(d.mem[:], b) + d.n = len(b) + + return +} + +// Sum appends the current hash to b and returns the resulting slice. +func (d *Digest) Sum(b []byte) []byte { + s := d.Sum64() + return append( + b, + byte(s>>56), + byte(s>>48), + byte(s>>40), + byte(s>>32), + byte(s>>24), + byte(s>>16), + byte(s>>8), + byte(s), + ) +} + +// Sum64 returns the current hash. +func (d *Digest) Sum64() uint64 { + var h uint64 + + if d.total >= 32 { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = d.v3 + prime5 + } + + h += d.total + + i, end := 0, d.n + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(d.mem[i:i+8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(d.mem[i:i+4])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for i < end { + h ^= uint64(d.mem[i]) * prime5 + h = rol11(h) * prime1 + i++ + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +const ( + magic = "xxh\x06" + marshaledSize = len(magic) + 8*5 + 32 +) + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d *Digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + b = appendUint64(b, d.v1) + b = appendUint64(b, d.v2) + b = appendUint64(b, d.v3) + b = appendUint64(b, d.v4) + b = appendUint64(b, d.total) + b = append(b, d.mem[:d.n]...) + b = b[:len(b)+len(d.mem)-d.n] + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("xxhash: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("xxhash: invalid hash state size") + } + b = b[len(magic):] + b, d.v1 = consumeUint64(b) + b, d.v2 = consumeUint64(b) + b, d.v3 = consumeUint64(b) + b, d.v4 = consumeUint64(b) + b, d.total = consumeUint64(b) + copy(d.mem[:], b) + b = b[len(d.mem):] + d.n = int(d.total % uint64(len(d.mem))) + return nil +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + binary.LittleEndian.PutUint64(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + x := u64(b) + return b[8:], x +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go new file mode 100644 index 0000000..ad14b80 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go @@ -0,0 +1,13 @@ +// +build !appengine +// +build gc +// +build !purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +// +//go:noescape +func Sum64(b []byte) uint64 + +//go:noescape +func writeBlocks(d *Digest, b []byte) int diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s new file mode 100644 index 0000000..d580e32 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s @@ -0,0 +1,215 @@ +// +build !appengine +// +build gc +// +build !purego + +#include "textflag.h" + +// Register allocation: +// AX h +// CX pointer to advance through b +// DX n +// BX loop end +// R8 v1, k1 +// R9 v2 +// R10 v3 +// R11 v4 +// R12 tmp +// R13 prime1v +// R14 prime2v +// R15 prime4v + +// round reads from and advances the buffer pointer in CX. +// It assumes that R13 has prime1v and R14 has prime2v. +#define round(r) \ + MOVQ (CX), R12 \ + ADDQ $8, CX \ + IMULQ R14, R12 \ + ADDQ R12, r \ + ROLQ $31, r \ + IMULQ R13, r + +// mergeRound applies a merge round on the two registers acc and val. +// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v. +#define mergeRound(acc, val) \ + IMULQ R14, val \ + ROLQ $31, val \ + IMULQ R13, val \ + XORQ val, acc \ + IMULQ R13, acc \ + ADDQ R15, acc + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT, $0-32 + // Load fixed primes. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + MOVQ ·prime4v(SB), R15 + + // Load slice. + MOVQ b_base+0(FP), CX + MOVQ b_len+8(FP), DX + LEAQ (CX)(DX*1), BX + + // The first loop limit will be len(b)-32. + SUBQ $32, BX + + // Check whether we have at least one block. + CMPQ DX, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ R13, R8 + ADDQ R14, R8 + MOVQ R14, R9 + XORQ R10, R10 + XORQ R11, R11 + SUBQ R13, R11 + + // Loop until CX > BX. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + MOVQ R8, AX + ROLQ $1, AX + MOVQ R9, R12 + ROLQ $7, R12 + ADDQ R12, AX + MOVQ R10, R12 + ROLQ $12, R12 + ADDQ R12, AX + MOVQ R11, R12 + ROLQ $18, R12 + ADDQ R12, AX + + mergeRound(AX, R8) + mergeRound(AX, R9) + mergeRound(AX, R10) + mergeRound(AX, R11) + + JMP afterBlocks + +noBlocks: + MOVQ ·prime5v(SB), AX + +afterBlocks: + ADDQ DX, AX + + // Right now BX has len(b)-32, and we want to loop until CX > len(b)-8. + ADDQ $24, BX + + CMPQ CX, BX + JG fourByte + +wordLoop: + // Calculate k1. + MOVQ (CX), R8 + ADDQ $8, CX + IMULQ R14, R8 + ROLQ $31, R8 + IMULQ R13, R8 + + XORQ R8, AX + ROLQ $27, AX + IMULQ R13, AX + ADDQ R15, AX + + CMPQ CX, BX + JLE wordLoop + +fourByte: + ADDQ $4, BX + CMPQ CX, BX + JG singles + + MOVL (CX), R8 + ADDQ $4, CX + IMULQ R13, R8 + XORQ R8, AX + + ROLQ $23, AX + IMULQ R14, AX + ADDQ ·prime3v(SB), AX + +singles: + ADDQ $4, BX + CMPQ CX, BX + JGE finalize + +singlesLoop: + MOVBQZX (CX), R12 + ADDQ $1, CX + IMULQ ·prime5v(SB), R12 + XORQ R12, AX + + ROLQ $11, AX + IMULQ R13, AX + + CMPQ CX, BX + JL singlesLoop + +finalize: + MOVQ AX, R12 + SHRQ $33, R12 + XORQ R12, AX + IMULQ R14, AX + MOVQ AX, R12 + SHRQ $29, R12 + XORQ R12, AX + IMULQ ·prime3v(SB), AX + MOVQ AX, R12 + SHRQ $32, R12 + XORQ R12, AX + + MOVQ AX, ret+24(FP) + RET + +// writeBlocks uses the same registers as above except that it uses AX to store +// the d pointer. + +// func writeBlocks(d *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT, $0-40 + // Load fixed primes needed for round. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + + // Load slice. + MOVQ b_base+8(FP), CX + MOVQ b_len+16(FP), DX + LEAQ (CX)(DX*1), BX + SUBQ $32, BX + + // Load vN from d. + MOVQ d+0(FP), AX + MOVQ 0(AX), R8 // v1 + MOVQ 8(AX), R9 // v2 + MOVQ 16(AX), R10 // v3 + MOVQ 24(AX), R11 // v4 + + // We don't need to check the loop condition here; this function is + // always called with at least one block of data to process. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + // Copy vN back to d. + MOVQ R8, 0(AX) + MOVQ R9, 8(AX) + MOVQ R10, 16(AX) + MOVQ R11, 24(AX) + + // The number of bytes written is CX minus the old base pointer. + SUBQ b_base+8(FP), CX + MOVQ CX, ret+32(FP) + + RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go new file mode 100644 index 0000000..4a5a821 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go @@ -0,0 +1,76 @@ +// +build !amd64 appengine !gc purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + // A simpler version would be + // d := New() + // d.Write(b) + // return d.Sum64() + // but this is faster, particularly for small inputs. + + n := len(b) + var h uint64 + + if n >= 32 { + v1 := prime1v + prime2 + v2 := prime2 + v3 := uint64(0) + v4 := -prime1v + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + i, end := 0, len(b) + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(b[i:i+8:len(b)])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for ; i < end; i++ { + h ^= uint64(b[i]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func writeBlocks(d *Digest, b []byte) int { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + n := len(b) + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 + return n - len(b) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go new file mode 100644 index 0000000..fc9bea7 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go @@ -0,0 +1,15 @@ +// +build appengine + +// This file contains the safe implementations of otherwise unsafe-using code. + +package xxhash + +// Sum64String computes the 64-bit xxHash digest of s. +func Sum64String(s string) uint64 { + return Sum64([]byte(s)) +} + +// WriteString adds more data to d. It always returns len(s), nil. +func (d *Digest) WriteString(s string) (n int, err error) { + return d.Write([]byte(s)) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go new file mode 100644 index 0000000..53bf76e --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -0,0 +1,46 @@ +// +build !appengine + +// This file encapsulates usage of unsafe. +// xxhash_safe.go contains the safe implementations. + +package xxhash + +import ( + "reflect" + "unsafe" +) + +// Notes: +// +// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ +// for some discussion about these unsafe conversions. +// +// In the future it's possible that compiler optimizations will make these +// unsafe operations unnecessary: https://golang.org/issue/2205. +// +// Both of these wrapper functions still incur function call overhead since they +// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write +// for strings to squeeze out a bit more speed. Mid-stack inlining should +// eventually fix this. + +// Sum64String computes the 64-bit xxHash digest of s. +// It may be faster than Sum64([]byte(s)) by avoiding a copy. +func Sum64String(s string) uint64 { + var b []byte + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + bh.Len = len(s) + bh.Cap = len(s) + return Sum64(b) +} + +// WriteString adds more data to d. It always returns len(s), nil. +// It may be faster than Write([]byte(s)) by avoiding a copy. +func (d *Digest) WriteString(s string) (n int, err error) { + var b []byte + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + bh.Len = len(s) + bh.Cap = len(s) + return d.Write(b) +} diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 0000000..bc52e96 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go new file mode 100644 index 0000000..7929947 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -0,0 +1,145 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +type flag uintptr + +var ( + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag +) + +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) + +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v + } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} + +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } + } + panic("reflect.Value read-only flag has changed semantics") +} diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go new file mode 100644 index 0000000..205c28d --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe !go1.4 + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go new file mode 100644 index 0000000..1be8ce9 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/davecgh/go-spew/spew/config.go new file mode 100644 index 0000000..2e3d22f --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/davecgh/go-spew/spew/doc.go new file mode 100644 index 0000000..aacaac6 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 0000000..f78d89f --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound: + d.w.Write(nilAngleBytes) + + case cycleFound: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go new file mode 100644 index 0000000..b04edb7 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound: + f.fs.Write(nilAngleBytes) + + case cycleFound: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/davecgh/go-spew/spew/spew.go new file mode 100644 index 0000000..32c0e33 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/vendor/github.com/dgryski/go-rendezvous/LICENSE b/vendor/github.com/dgryski/go-rendezvous/LICENSE new file mode 100644 index 0000000..22080f7 --- /dev/null +++ b/vendor/github.com/dgryski/go-rendezvous/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Damian Gryski + +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. diff --git a/vendor/github.com/dgryski/go-rendezvous/rdv.go b/vendor/github.com/dgryski/go-rendezvous/rdv.go new file mode 100644 index 0000000..7a6f820 --- /dev/null +++ b/vendor/github.com/dgryski/go-rendezvous/rdv.go @@ -0,0 +1,79 @@ +package rendezvous + +type Rendezvous struct { + nodes map[string]int + nstr []string + nhash []uint64 + hash Hasher +} + +type Hasher func(s string) uint64 + +func New(nodes []string, hash Hasher) *Rendezvous { + r := &Rendezvous{ + nodes: make(map[string]int, len(nodes)), + nstr: make([]string, len(nodes)), + nhash: make([]uint64, len(nodes)), + hash: hash, + } + + for i, n := range nodes { + r.nodes[n] = i + r.nstr[i] = n + r.nhash[i] = hash(n) + } + + return r +} + +func (r *Rendezvous) Lookup(k string) string { + // short-circuit if we're empty + if len(r.nodes) == 0 { + return "" + } + + khash := r.hash(k) + + var midx int + var mhash = xorshiftMult64(khash ^ r.nhash[0]) + + for i, nhash := range r.nhash[1:] { + if h := xorshiftMult64(khash ^ nhash); h > mhash { + midx = i + 1 + mhash = h + } + } + + return r.nstr[midx] +} + +func (r *Rendezvous) Add(node string) { + r.nodes[node] = len(r.nstr) + r.nstr = append(r.nstr, node) + r.nhash = append(r.nhash, r.hash(node)) +} + +func (r *Rendezvous) Remove(node string) { + // find index of node to remove + nidx := r.nodes[node] + + // remove from the slices + l := len(r.nstr) + r.nstr[nidx] = r.nstr[l] + r.nstr = r.nstr[:l] + + r.nhash[nidx] = r.nhash[l] + r.nhash = r.nhash[:l] + + // update the map + delete(r.nodes, node) + moved := r.nstr[nidx] + r.nodes[moved] = nidx +} + +func xorshiftMult64(x uint64) uint64 { + x ^= x >> 12 // a + x ^= x << 25 // b + x ^= x >> 27 // c + return x * 2685821657736338717 +} diff --git a/vendor/github.com/eapache/go-resiliency/LICENSE b/vendor/github.com/eapache/go-resiliency/LICENSE new file mode 100644 index 0000000..698a3f5 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +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. + diff --git a/vendor/github.com/eapache/go-resiliency/breaker/README.md b/vendor/github.com/eapache/go-resiliency/breaker/README.md new file mode 100644 index 0000000..2d1b3d9 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/breaker/README.md @@ -0,0 +1,34 @@ +circuit-breaker +=============== + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/breaker?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/breaker) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The circuit-breaker resiliency pattern for golang. + +Creating a breaker takes three parameters: +- error threshold (for opening the breaker) +- success threshold (for closing the breaker) +- timeout (how long to keep the breaker open) + +```go +b := breaker.New(3, 1, 5*time.Second) + +for { + result := b.Run(func() error { + // communicate with some external service and + // return an error if the communication failed + return nil + }) + + switch result { + case nil: + // success! + case breaker.ErrBreakerOpen: + // our function wasn't run because the breaker was open + default: + // some other error + } +} +``` diff --git a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go new file mode 100644 index 0000000..f88ca72 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go @@ -0,0 +1,161 @@ +// Package breaker implements the circuit-breaker resiliency pattern for Go. +package breaker + +import ( + "errors" + "sync" + "sync/atomic" + "time" +) + +// ErrBreakerOpen is the error returned from Run() when the function is not executed +// because the breaker is currently open. +var ErrBreakerOpen = errors.New("circuit breaker is open") + +const ( + closed uint32 = iota + open + halfOpen +) + +// Breaker implements the circuit-breaker resiliency pattern +type Breaker struct { + errorThreshold, successThreshold int + timeout time.Duration + + lock sync.Mutex + state uint32 + errors, successes int + lastError time.Time +} + +// New constructs a new circuit-breaker that starts closed. +// From closed, the breaker opens if "errorThreshold" errors are seen +// without an error-free period of at least "timeout". From open, the +// breaker half-closes after "timeout". From half-open, the breaker closes +// after "successThreshold" consecutive successes, or opens on a single error. +func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker { + return &Breaker{ + errorThreshold: errorThreshold, + successThreshold: successThreshold, + timeout: timeout, + } +} + +// Run will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function and pass along its return +// value. It is safe to call Run concurrently on the same Breaker. +func (b *Breaker) Run(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + return b.doWork(state, work) +} + +// Go will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function in a separate goroutine. +// If the function is run, Go will return nil immediately, and will *not* return +// the return value of the function. It is safe to call Go concurrently on the +// same Breaker. +func (b *Breaker) Go(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + // errcheck complains about ignoring the error return value, but + // that's on purpose; if you want an error from a goroutine you have to + // get it over a channel or something + go b.doWork(state, work) + + return nil +} + +func (b *Breaker) doWork(state uint32, work func() error) error { + var panicValue interface{} + + result := func() error { + defer func() { + panicValue = recover() + }() + return work() + }() + + if result == nil && panicValue == nil && state == closed { + // short-circuit the normal, success path without contending + // on the lock + return nil + } + + // oh well, I guess we have to contend on the lock + b.processResult(result, panicValue) + + if panicValue != nil { + // as close as Go lets us come to a "rethrow" although unfortunately + // we lose the original panicing location + panic(panicValue) + } + + return result +} + +func (b *Breaker) processResult(result error, panicValue interface{}) { + b.lock.Lock() + defer b.lock.Unlock() + + if result == nil && panicValue == nil { + if b.state == halfOpen { + b.successes++ + if b.successes == b.successThreshold { + b.closeBreaker() + } + } + } else { + if b.errors > 0 { + expiry := b.lastError.Add(b.timeout) + if time.Now().After(expiry) { + b.errors = 0 + } + } + + switch b.state { + case closed: + b.errors++ + if b.errors == b.errorThreshold { + b.openBreaker() + } else { + b.lastError = time.Now() + } + case halfOpen: + b.openBreaker() + } + } +} + +func (b *Breaker) openBreaker() { + b.changeState(open) + go b.timer() +} + +func (b *Breaker) closeBreaker() { + b.changeState(closed) +} + +func (b *Breaker) timer() { + time.Sleep(b.timeout) + + b.lock.Lock() + defer b.lock.Unlock() + + b.changeState(halfOpen) +} + +func (b *Breaker) changeState(newState uint32) { + b.errors = 0 + b.successes = 0 + atomic.StoreUint32(&b.state, newState) +} diff --git a/vendor/github.com/eapache/go-xerial-snappy/.gitignore b/vendor/github.com/eapache/go-xerial-snappy/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/eapache/go-xerial-snappy/.travis.yml b/vendor/github.com/eapache/go-xerial-snappy/.travis.yml new file mode 100644 index 0000000..d6cf4f1 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: +- 1.5.4 +- 1.6.1 + +sudo: false diff --git a/vendor/github.com/eapache/go-xerial-snappy/LICENSE b/vendor/github.com/eapache/go-xerial-snappy/LICENSE new file mode 100644 index 0000000..5bf3688 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Evan Huus + +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. diff --git a/vendor/github.com/eapache/go-xerial-snappy/README.md b/vendor/github.com/eapache/go-xerial-snappy/README.md new file mode 100644 index 0000000..3f2695c --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/README.md @@ -0,0 +1,13 @@ +# go-xerial-snappy + +[![Build Status](https://travis-ci.org/eapache/go-xerial-snappy.svg?branch=master)](https://travis-ci.org/eapache/go-xerial-snappy) + +Xerial-compatible Snappy framing support for golang. + +Packages using Xerial for snappy encoding use a framing format incompatible with +basically everything else in existence. This package wraps Go's built-in snappy +package to support it. + +Apps that use this format include Apache Kafka (see +https://github.com/dpkp/kafka-python/issues/126#issuecomment-35478921 for +details). diff --git a/vendor/github.com/eapache/go-xerial-snappy/fuzz.go b/vendor/github.com/eapache/go-xerial-snappy/fuzz.go new file mode 100644 index 0000000..6a46f47 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/fuzz.go @@ -0,0 +1,16 @@ +// +build gofuzz + +package snappy + +func Fuzz(data []byte) int { + decode, err := Decode(data) + if decode == nil && err == nil { + panic("nil error with nil result") + } + + if err != nil { + return 0 + } + + return 1 +} diff --git a/vendor/github.com/eapache/go-xerial-snappy/snappy.go b/vendor/github.com/eapache/go-xerial-snappy/snappy.go new file mode 100644 index 0000000..ea8f7af --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/snappy.go @@ -0,0 +1,131 @@ +package snappy + +import ( + "bytes" + "encoding/binary" + "errors" + + master "github.com/golang/snappy" +) + +const ( + sizeOffset = 16 + sizeBytes = 4 +) + +var ( + xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0} + + // This is xerial version 1 and minimally compatible with version 1 + xerialVersionInfo = []byte{0, 0, 0, 1, 0, 0, 0, 1} + + // ErrMalformed is returned by the decoder when the xerial framing + // is malformed + ErrMalformed = errors.New("malformed xerial framing") +) + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +// Encode encodes data as snappy with no framing header. +func Encode(src []byte) []byte { + return master.Encode(nil, src) +} + +// EncodeStream *appends* to the specified 'dst' the compressed +// 'src' in xerial framing format. If 'dst' does not have enough +// capacity, then a new slice will be allocated. If 'dst' has +// non-zero length, then if *must* have been built using this function. +func EncodeStream(dst, src []byte) []byte { + if len(dst) == 0 { + dst = append(dst, xerialHeader...) + dst = append(dst, xerialVersionInfo...) + } + + // Snappy encode in blocks of maximum 32KB + var ( + max = len(src) + blockSize = 32 * 1024 + pos = 0 + chunk []byte + ) + + for pos < max { + newPos := min(pos + blockSize, max) + chunk = master.Encode(chunk[:cap(chunk)], src[pos:newPos]) + + // First encode the compressed size (big-endian) + // Put* panics if the buffer is too small, so pad 4 bytes first + origLen := len(dst) + dst = append(dst, dst[0:4]...) + binary.BigEndian.PutUint32(dst[origLen:], uint32(len(chunk))) + + // And now the compressed data + dst = append(dst, chunk...) + pos = newPos + } + return dst +} + +// Decode decodes snappy data whether it is traditional unframed +// or includes the xerial framing format. +func Decode(src []byte) ([]byte, error) { + return DecodeInto(nil, src) +} + +// DecodeInto decodes snappy data whether it is traditional unframed +// or includes the xerial framing format into the specified `dst`. +// It is assumed that the entirety of `dst` including all capacity is available +// for use by this function. If `dst` is nil *or* insufficiently large to hold +// the decoded `src`, new space will be allocated. +func DecodeInto(dst, src []byte) ([]byte, error) { + var max = len(src) + if max < len(xerialHeader) { + return nil, ErrMalformed + } + + if !bytes.Equal(src[:8], xerialHeader) { + return master.Decode(dst[:cap(dst)], src) + } + + if max < sizeOffset+sizeBytes { + return nil, ErrMalformed + } + + if dst == nil { + dst = make([]byte, 0, len(src)) + } + + dst = dst[:0] + var ( + pos = sizeOffset + chunk []byte + err error + ) + + for pos+sizeBytes <= max { + size := int(binary.BigEndian.Uint32(src[pos : pos+sizeBytes])) + pos += sizeBytes + + nextPos := pos + size + // On architectures where int is 32-bytes wide size + pos could + // overflow so we need to check the low bound as well as the + // high + if nextPos < pos || nextPos > max { + return nil, ErrMalformed + } + + chunk, err = master.Decode(chunk[:cap(chunk)], src[pos:nextPos]) + + if err != nil { + return nil, err + } + pos = nextPos + dst = append(dst, chunk...) + } + return dst, nil +} diff --git a/vendor/github.com/eapache/queue/.gitignore b/vendor/github.com/eapache/queue/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/eapache/queue/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/eapache/queue/.travis.yml b/vendor/github.com/eapache/queue/.travis.yml new file mode 100644 index 0000000..235a40a --- /dev/null +++ b/vendor/github.com/eapache/queue/.travis.yml @@ -0,0 +1,7 @@ +language: go +sudo: false + +go: + - 1.2 + - 1.3 + - 1.4 diff --git a/vendor/github.com/eapache/queue/LICENSE b/vendor/github.com/eapache/queue/LICENSE new file mode 100644 index 0000000..d5f36db --- /dev/null +++ b/vendor/github.com/eapache/queue/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +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. \ No newline at end of file diff --git a/vendor/github.com/eapache/queue/README.md b/vendor/github.com/eapache/queue/README.md new file mode 100644 index 0000000..8e78233 --- /dev/null +++ b/vendor/github.com/eapache/queue/README.md @@ -0,0 +1,16 @@ +Queue +===== + +[![Build Status](https://travis-ci.org/eapache/queue.svg)](https://travis-ci.org/eapache/queue) +[![GoDoc](https://godoc.org/github.com/eapache/queue?status.png)](https://godoc.org/github.com/eapache/queue) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki. +Using this instead of other, simpler, queue implementations (slice+append or linked list) provides +substantial memory and time benefits, and fewer GC pauses. + +The queue implemented here is as fast as it is in part because it is *not* thread-safe. + +Follows semantic versioning using https://gopkg.in/ - import from +[`gopkg.in/eapache/queue.v1`](https://gopkg.in/eapache/queue.v1) +for guaranteed API stability. diff --git a/vendor/github.com/eapache/queue/queue.go b/vendor/github.com/eapache/queue/queue.go new file mode 100644 index 0000000..71d1acd --- /dev/null +++ b/vendor/github.com/eapache/queue/queue.go @@ -0,0 +1,102 @@ +/* +Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki. +Using this instead of other, simpler, queue implementations (slice+append or linked list) provides +substantial memory and time benefits, and fewer GC pauses. + +The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. +*/ +package queue + +// minQueueLen is smallest capacity that queue may have. +// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). +const minQueueLen = 16 + +// Queue represents a single instance of the queue data structure. +type Queue struct { + buf []interface{} + head, tail, count int +} + +// New constructs and returns a new Queue. +func New() *Queue { + return &Queue{ + buf: make([]interface{}, minQueueLen), + } +} + +// Length returns the number of elements currently stored in the queue. +func (q *Queue) Length() int { + return q.count +} + +// resizes the queue to fit exactly twice its current contents +// this can result in shrinking if the queue is less than half-full +func (q *Queue) resize() { + newBuf := make([]interface{}, q.count<<1) + + if q.tail > q.head { + copy(newBuf, q.buf[q.head:q.tail]) + } else { + n := copy(newBuf, q.buf[q.head:]) + copy(newBuf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = newBuf +} + +// Add puts an element on the end of the queue. +func (q *Queue) Add(elem interface{}) { + if q.count == len(q.buf) { + q.resize() + } + + q.buf[q.tail] = elem + // bitwise modulus + q.tail = (q.tail + 1) & (len(q.buf) - 1) + q.count++ +} + +// Peek returns the element at the head of the queue. This call panics +// if the queue is empty. +func (q *Queue) Peek() interface{} { + if q.count <= 0 { + panic("queue: Peek() called on empty queue") + } + return q.buf[q.head] +} + +// Get returns the element at index i in the queue. If the index is +// invalid, the call will panic. This method accepts both positive and +// negative index values. Index 0 refers to the first element, and +// index -1 refers to the last. +func (q *Queue) Get(i int) interface{} { + // If indexing backwards, convert to positive index. + if i < 0 { + i += q.count + } + if i < 0 || i >= q.count { + panic("queue: Get() called with index out of range") + } + // bitwise modulus + return q.buf[(q.head+i)&(len(q.buf)-1)] +} + +// Remove removes and returns the element from the front of the queue. If the +// queue is empty, the call will panic. +func (q *Queue) Remove() interface{} { + if q.count <= 0 { + panic("queue: Remove() called on empty queue") + } + ret := q.buf[q.head] + q.buf[q.head] = nil + // bitwise modulus + q.head = (q.head + 1) & (len(q.buf) - 1) + q.count-- + // Resize down if buffer 1/4 full. + if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) { + q.resize() + } + return ret +} diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000..25fdaf6 --- /dev/null +++ b/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +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. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md new file mode 100644 index 0000000..42d9abc --- /dev/null +++ b/vendor/github.com/fatih/color/README.md @@ -0,0 +1,182 @@ +# Archived project. No maintenance. + +This project is not maintained anymore and is archived. Feel free to fork and +make your own changes if needed. For more detail read my blog post: [Taking an indefinite sabbatical from my projects](https://arslan.io/2018/10/09/taking-an-indefinite-sabbatical-from-my-projects/) + +Thanks to everyone for their valuable feedback and contributions. + + +# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + + +![Color](https://i.imgur.com/c1JI0lA.png) + + +## Install + +```bash +go get github.com/fatih/color +``` + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. the +`go-isatty` package will automatically disable color output for non-tty output streams +(for example if the output were piped directly to `less`) + +`Color` has support to disable/enable colors both globally and for single color +definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You +can easily disable the color output with: + +```go + +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details + diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go new file mode 100644 index 0000000..91c8e9f --- /dev/null +++ b/vendor/github.com/fatih/color/color.go @@ -0,0 +1,603 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. This is a global option and affects all colors. For more control + // over each color block use the methods DisableColor() individually. + NoColor = os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // Error defines a color supporting writer for os.Stderr. + Error = colorable.NewColorableStderr() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{params: make([]Attribute, 0)} + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user setted action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is a convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is a convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is a convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is a convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is a convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is a convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is a convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is a convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is a convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is a convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is a convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is a convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is a convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is a convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is a convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is a convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack is a convenient helper function to print with hi-intensity black foreground. A +// newline is appended to format by default. +func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } + +// HiRed is a convenient helper function to print with hi-intensity red foreground. A +// newline is appended to format by default. +func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } + +// HiGreen is a convenient helper function to print with hi-intensity green foreground. A +// newline is appended to format by default. +func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } + +// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. +// A newline is appended to format by default. +func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } + +// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A +// newline is appended to format by default. +func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } + +// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. +// A newline is appended to format by default. +func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } + +// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A +// newline is appended to format by default. +func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } + +// HiWhite is a convenient helper function to print with hi-intensity white foreground. A +// newline is appended to format by default. +func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } + +// HiBlackString is a convenient helper function to return a string with hi-intensity black +// foreground. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString is a convenient helper function to return a string with hi-intensity red +// foreground. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString is a convenient helper function to return a string with hi-intensity green +// foreground. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString is a convenient helper function to return a string with hi-intensity yellow +// foreground. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString is a convenient helper function to return a string with hi-intensity blue +// foreground. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta +// foreground. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString is a convenient helper function to return a string with hi-intensity cyan +// foreground. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString is a convenient helper function to return a string with hi-intensity white +// foreground. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go new file mode 100644 index 0000000..cf1e965 --- /dev/null +++ b/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,133 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions work as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/fatih/color/go.mod b/vendor/github.com/fatih/color/go.mod new file mode 100644 index 0000000..bc0df75 --- /dev/null +++ b/vendor/github.com/fatih/color/go.mod @@ -0,0 +1,8 @@ +module github.com/fatih/color + +go 1.13 + +require ( + github.com/mattn/go-colorable v0.1.4 + github.com/mattn/go-isatty v0.0.11 +) diff --git a/vendor/github.com/fatih/color/go.sum b/vendor/github.com/fatih/color/go.sum new file mode 100644 index 0000000..44328a8 --- /dev/null +++ b/vendor/github.com/fatih/color/go.sum @@ -0,0 +1,8 @@ +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/garyburd/redigo/LICENSE b/vendor/github.com/garyburd/redigo/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/LICENSE @@ -0,0 +1,175 @@ + + 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. diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo.go b/vendor/github.com/garyburd/redigo/internal/commandinfo.go new file mode 100644 index 0000000..11e5842 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/internal/commandinfo.go @@ -0,0 +1,54 @@ +// 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)] +} diff --git a/vendor/github.com/garyburd/redigo/redis/conn.go b/vendor/github.com/garyburd/redigo/redis/conn.go new file mode 100644 index 0000000..5aa0f32 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/conn.go @@ -0,0 +1,673 @@ +// 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" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "net/url" + "regexp" + "strconv" + "sync" + "time" +) + +var ( + _ ConnWithTimeout = (*conn)(nil) +) + +// 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 + dialer *net.Dialer + dial func(network, addr string) (net.Conn, error) + db int + password string + useTLS bool + skipVerify bool + tlsConfig *tls.Config +} + +// 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 when +// no DialNetDial option is specified. +func DialConnectTimeout(d time.Duration) DialOption { + return DialOption{func(do *dialOptions) { + do.dialer.Timeout = d + }} +} + +// DialKeepAlive specifies the keep-alive period for TCP connections to the Redis server +// when no DialNetDial option is specified. +// If zero, keep-alives are not enabled. If no DialKeepAlive option is specified then +// the default of 5 minutes is used to ensure that half-closed TCP sessions are detected. +func DialKeepAlive(d time.Duration) DialOption { + return DialOption{func(do *dialOptions) { + do.dialer.KeepAlive = d + }} +} + +// DialNetDial specifies a custom dial function for creating TCP +// connections, otherwise a net.Dialer customized via the other options is used. +// DialNetDial overrides DialConnectTimeout and DialKeepAlive. +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 + }} +} + +// DialTLSConfig specifies the config to use when a TLS connection is dialed. +// Has no effect when not dialing a TLS connection. +func DialTLSConfig(c *tls.Config) DialOption { + return DialOption{func(do *dialOptions) { + do.tlsConfig = c + }} +} + +// DialTLSSkipVerify disables server name verification when connecting over +// TLS. Has no effect when not dialing a TLS connection. +func DialTLSSkipVerify(skip bool) DialOption { + return DialOption{func(do *dialOptions) { + do.skipVerify = skip + }} +} + +// DialUseTLS specifies whether TLS should be used when connecting to the +// server. This option is ignore by DialURL. +func DialUseTLS(useTLS bool) DialOption { + return DialOption{func(do *dialOptions) { + do.useTLS = useTLS + }} +} + +// 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{ + dialer: &net.Dialer{ + KeepAlive: time.Minute * 5, + }, + } + for _, option := range options { + option.f(&do) + } + if do.dial == nil { + do.dial = do.dialer.Dial + } + + netConn, err := do.dial(network, address) + if err != nil { + return nil, err + } + + if do.useTLS { + var tlsConfig *tls.Config + if do.tlsConfig == nil { + tlsConfig = &tls.Config{InsecureSkipVerify: do.skipVerify} + } else { + tlsConfig = cloneTLSConfig(do.tlsConfig) + } + if tlsConfig.ServerName == "" { + host, _, err := net.SplitHostPort(address) + if err != nil { + netConn.Close() + return nil, err + } + tlsConfig.ServerName = host + } + + tlsConn := tls.Client(netConn, tlsConfig) + if err := tlsConn.Handshake(); err != nil { + netConn.Close() + return nil, err + } + netConn = tlsConn + } + + 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" && u.Scheme != "rediss" { + 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:]) + } + + options = append(options, DialUseTLS(u.Scheme == "rediss")) + + 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{}) error { + c.writeLen('*', 1+len(args)) + if err := c.writeString(cmd); err != nil { + return err + } + for _, arg := range args { + if err := c.writeArg(arg, true); err != nil { + return err + } + } + return nil +} + +func (c *conn) writeArg(arg interface{}, argumentTypeOK bool) (err error) { + switch arg := arg.(type) { + case string: + return c.writeString(arg) + case []byte: + return c.writeBytes(arg) + case int: + return c.writeInt64(int64(arg)) + case int64: + return c.writeInt64(arg) + case float64: + return c.writeFloat64(arg) + case bool: + if arg { + return c.writeString("1") + } else { + return c.writeString("0") + } + case nil: + return c.writeString("") + case Argument: + if argumentTypeOK { + return c.writeArg(arg.RedisArg(), false) + } + // See comment in default clause below. + var buf bytes.Buffer + fmt.Fprint(&buf, arg) + return c.writeBytes(buf.Bytes()) + default: + // This default clause is intended to handle builtin numeric types. + // The function should return an error for other types, but this is not + // done for compatibility with previous versions of the package. + var buf bytes.Buffer + fmt.Fprint(&buf, arg) + return c.writeBytes(buf.Bytes()) + } +} + +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() (interface{}, error) { + return c.ReceiveWithTimeout(c.readTimeout) +} + +func (c *conn) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) { + var deadline time.Time + if timeout != 0 { + deadline = time.Now().Add(timeout) + } + c.conn.SetReadDeadline(deadline) + + 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) { + return c.DoWithTimeout(c.readTimeout, cmd, args...) +} + +func (c *conn) DoWithTimeout(readTimeout time.Duration, 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) + } + + var deadline time.Time + if readTimeout != 0 { + deadline = time.Now().Add(readTimeout) + } + c.conn.SetReadDeadline(deadline) + + 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 +} diff --git a/vendor/github.com/garyburd/redigo/redis/doc.go b/vendor/github.com/garyburd/redigo/redis/doc.go new file mode 100644 index 0000000..1d19c16 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/doc.go @@ -0,0 +1,177 @@ +// 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 bulk 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.Fprint(w, 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{Conn: 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 +// } +// +// Errors +// +// Connection methods return error replies from the server as type redis.Error. +// +// Call the connection Err() method to determine if the connection encountered +// non-recoverable error such as a network error or protocol parsing error. If +// Err() returns a non-nil value, then the connection is not usable and should +// be closed. +package redis // import "github.com/garyburd/redigo/redis" diff --git a/vendor/github.com/garyburd/redigo/redis/go16.go b/vendor/github.com/garyburd/redigo/redis/go16.go new file mode 100644 index 0000000..f6b1a7c --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/go16.go @@ -0,0 +1,27 @@ +// +build !go1.7 + +package redis + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + } +} diff --git a/vendor/github.com/garyburd/redigo/redis/go17.go b/vendor/github.com/garyburd/redigo/redis/go17.go new file mode 100644 index 0000000..5f36379 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/go17.go @@ -0,0 +1,29 @@ +// +build go1.7,!go1.8 + +package redis + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, + Renegotiation: cfg.Renegotiation, + } +} diff --git a/vendor/github.com/garyburd/redigo/redis/go18.go b/vendor/github.com/garyburd/redigo/redis/go18.go new file mode 100644 index 0000000..558363b --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/go18.go @@ -0,0 +1,9 @@ +// +build go1.8 + +package redis + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + return cfg.Clone() +} diff --git a/vendor/github.com/garyburd/redigo/redis/log.go b/vendor/github.com/garyburd/redigo/redis/log.go new file mode 100644 index 0000000..b299661 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/log.go @@ -0,0 +1,134 @@ +// 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" + "time" +) + +var ( + _ ConnWithTimeout = (*loggingConn)(nil) +) + +// 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) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) { + reply, err := DoWithTimeout(c.Conn, timeout, commandName, args...) + c.print("DoWithTimeout", 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 +} + +func (c *loggingConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) { + reply, err := ReceiveWithTimeout(c.Conn, timeout) + c.print("ReceiveWithTimeout", "", nil, reply, err) + return reply, err +} diff --git a/vendor/github.com/garyburd/redigo/redis/pool.go b/vendor/github.com/garyburd/redigo/redis/pool.go new file mode 100644 index 0000000..3e6f426 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/pool.go @@ -0,0 +1,527 @@ +// 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" + "crypto/rand" + "crypto/sha1" + "errors" + "io" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/garyburd/redigo/internal" +) + +var ( + _ ConnWithTimeout = (*pooledConnection)(nil) + _ ConnWithTimeout = (*errorConnection)(nil) +) + +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 package level variable. The pool configuration used +// here is an example, not a recommendation. +// +// func newPool(addr string) *redis.Pool { +// return &redis.Pool{ +// MaxIdle: 3, +// IdleTimeout: 240 * time.Second, +// Dial: func () (redis.Conn, error) { return redis.Dial("tcp", addr) }, +// } +// } +// +// var ( +// pool *redis.Pool +// redisServer = flag.String("redisServer", ":6379", "") +// ) +// +// func main() { +// flag.Parse() +// pool = newPool(*redisServer) +// ... +// } +// +// 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() +// ... +// } +// +// Use the Dial function to authenticate connections with the AUTH command or +// select a database with the SELECT command: +// +// pool := &redis.Pool{ +// // Other pool configuration not shown in this example. +// 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 +// } +// if _, err := c.Do("SELECT", db); err != nil { +// c.Close() +// return nil, err +// } +// return c, nil +// }, +// } +// +// Use the TestOnBorrow function to check the health of an idle connection +// before the connection is returned to the application. This example PINGs +// connections that have been idle more than a minute: +// +// pool := &redis.Pool{ +// // Other pool configuration not shown in this example. +// TestOnBorrow: func(c redis.Conn, t time.Time) error { +// if time.Since(t) < time.Minute { +// return nil +// } +// _, err := c.Do("PING") +// return err +// }, +// } +// +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 + + chInitialized uint32 // set to 1 when field ch is initialized + + mu sync.Mutex // mu protects the following fields + closed bool // set to true when the pool is closed. + active int // the number of open connections in the pool + ch chan struct{} // limits open connections when p.Wait is true + idle idleList // idle connections +} + +// 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(nil) + if err != nil { + return errorConnection{err} + } + return &pooledConnection{p: p, c: c} +} + +// PoolStats contains pool statistics. +type PoolStats struct { + // ActiveCount is the number of connections in the pool. The count includes + // idle connections and connections in use. + ActiveCount int + // IdleCount is the number of idle connections in the pool. + IdleCount int +} + +// Stats returns pool's statistics. +func (p *Pool) Stats() PoolStats { + p.mu.Lock() + stats := PoolStats{ + ActiveCount: p.active, + IdleCount: p.idle.count, + } + p.mu.Unlock() + + return stats +} + +// ActiveCount returns the number of connections in the pool. The count +// includes idle connections and connections in use. +func (p *Pool) ActiveCount() int { + p.mu.Lock() + active := p.active + p.mu.Unlock() + return active +} + +// IdleCount returns the number of idle connections in the pool. +func (p *Pool) IdleCount() int { + p.mu.Lock() + idle := p.idle.count + p.mu.Unlock() + return idle +} + +// Close releases the resources used by the pool. +func (p *Pool) Close() error { + p.mu.Lock() + if p.closed { + p.mu.Unlock() + return nil + } + p.closed = true + p.active -= p.idle.count + ic := p.idle.front + p.idle.count = 0 + p.idle.front, p.idle.back = nil, nil + if p.ch != nil { + close(p.ch) + } + p.mu.Unlock() + for ; ic != nil; ic = ic.next { + ic.c.Close() + } + return nil +} + +func (p *Pool) lazyInit() { + // Fast path. + if atomic.LoadUint32(&p.chInitialized) == 1 { + return + } + // Slow path. + p.mu.Lock() + if p.chInitialized == 0 { + p.ch = make(chan struct{}, p.MaxActive) + if p.closed { + close(p.ch) + } else { + for i := 0; i < p.MaxActive; i++ { + p.ch <- struct{}{} + } + } + atomic.StoreUint32(&p.chInitialized, 1) + } + p.mu.Unlock() +} + +// get prunes stale connections and returns a connection from the idle list or +// creates a new connection. +func (p *Pool) get(ctx interface { + Done() <-chan struct{} + Err() error +}) (Conn, error) { + + // Handle limit for p.Wait == true. + if p.Wait && p.MaxActive > 0 { + p.lazyInit() + if ctx == nil { + <-p.ch + } else { + select { + case <-p.ch: + case <-ctx.Done(): + return nil, ctx.Err() + } + } + } + + p.mu.Lock() + + // Prune stale connections at the back of the idle list. + if p.IdleTimeout > 0 { + n := p.idle.count + for i := 0; i < n && p.idle.back != nil && p.idle.back.t.Add(p.IdleTimeout).Before(nowFunc()); i++ { + c := p.idle.back.c + p.idle.popBack() + p.mu.Unlock() + c.Close() + p.mu.Lock() + p.active-- + } + } + + // Get idle connection from the front of idle list. + for p.idle.front != nil { + ic := p.idle.front + p.idle.popFront() + p.mu.Unlock() + if p.TestOnBorrow == nil || p.TestOnBorrow(ic.c, ic.t) == nil { + return ic.c, nil + } + ic.c.Close() + p.mu.Lock() + p.active-- + } + + // Check for pool closed before dialing a new connection. + if p.closed { + p.mu.Unlock() + return nil, errors.New("redigo: get on closed pool") + } + + // Handle limit for p.Wait == false. + if !p.Wait && p.MaxActive > 0 && p.active >= p.MaxActive { + p.mu.Unlock() + return nil, ErrPoolExhausted + } + + p.active++ + p.mu.Unlock() + c, err := p.Dial() + if err != nil { + c = nil + p.mu.Lock() + p.active-- + if p.ch != nil && !p.closed { + p.ch <- struct{}{} + } + p.mu.Unlock() + } + return c, err +} + +func (p *Pool) put(c Conn, forceClose bool) error { + p.mu.Lock() + if !p.closed && !forceClose { + p.idle.pushFront(&idleConn{t: nowFunc(), c: c}) + if p.idle.count > p.MaxIdle { + c = p.idle.back.c + p.idle.popBack() + } else { + c = nil + } + } + + if c != nil { + p.mu.Unlock() + c.Close() + p.mu.Lock() + p.active-- + } + + if p.ch != nil && !p.closed { + p.ch <- struct{}{} + } + p.mu.Unlock() + return nil +} + +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 || c.Err() != nil) + 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) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) { + cwt, ok := pc.c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + ci := internal.LookupCommandInfo(commandName) + pc.state = (pc.state | ci.Set) &^ ci.Clear + return cwt.DoWithTimeout(timeout, 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() +} + +func (pc *pooledConnection) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) { + cwt, ok := pc.c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.ReceiveWithTimeout(timeout) +} + +type errorConnection struct{ err error } + +func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err } +func (ec errorConnection) DoWithTimeout(time.Duration, 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 nil } +func (ec errorConnection) Flush() error { return ec.err } +func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err } +func (ec errorConnection) ReceiveWithTimeout(time.Duration) (interface{}, error) { return nil, ec.err } + +type idleList struct { + count int + front, back *idleConn +} + +type idleConn struct { + c Conn + t time.Time + next, prev *idleConn +} + +func (l *idleList) pushFront(ic *idleConn) { + ic.next = l.front + ic.prev = nil + if l.count == 0 { + l.back = ic + } else { + l.front.prev = ic + } + l.front = ic + l.count++ + return +} + +func (l *idleList) popFront() { + ic := l.front + l.count-- + if l.count == 0 { + l.front, l.back = nil, nil + } else { + ic.next.prev = nil + l.front = ic.next + } + ic.next, ic.prev = nil, nil +} + +func (l *idleList) popBack() { + ic := l.back + l.count-- + if l.count == 0 { + l.front, l.back = nil, nil + } else { + ic.prev.next = nil + l.back = ic.prev + } + ic.next, ic.prev = nil, nil +} diff --git a/vendor/github.com/garyburd/redigo/redis/pool17.go b/vendor/github.com/garyburd/redigo/redis/pool17.go new file mode 100644 index 0000000..57a2264 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/pool17.go @@ -0,0 +1,35 @@ +// Copyright 2018 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. + +// +build go1.7 + +package redis + +import "context" + +// GetContext gets a connection using the provided context. +// +// The provided Context must be non-nil. If the context expires before the +// connection is complete, an error is returned. Any expiration on the context +// will not affect the returned connection. +// +// If the function completes without error, then the application must close the +// returned connection. +func (p *Pool) GetContext(ctx context.Context) (Conn, error) { + c, err := p.get(ctx) + if err != nil { + return errorConnection{err}, err + } + return &pooledConnection{p: p, c: c}, nil +} diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub.go b/vendor/github.com/garyburd/redigo/redis/pubsub.go new file mode 100644 index 0000000..f0ac825 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/pubsub.go @@ -0,0 +1,157 @@ +// 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" + "time" +) + +// 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. +// +// The connection must be subscribed to at least one channel or pattern when +// calling this method. +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{} { + return c.receiveInternal(c.Conn.Receive()) +} + +// ReceiveWithTimeout is like Receive, but it allows the application to +// override the connection's default timeout. +func (c PubSubConn) ReceiveWithTimeout(timeout time.Duration) interface{} { + return c.receiveInternal(ReceiveWithTimeout(c.Conn, timeout)) +} + +func (c PubSubConn) receiveInternal(replyArg interface{}, errArg error) interface{} { + reply, err := Values(replyArg, errArg) + 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") +} diff --git a/vendor/github.com/garyburd/redigo/redis/redis.go b/vendor/github.com/garyburd/redigo/redis/redis.go new file mode 100644 index 0000000..141fa4a --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/redis.go @@ -0,0 +1,117 @@ +// 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" + "time" +) + +// 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 when the connection is not usable. + 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) +} + +// Argument is the interface implemented by an object which wants to control how +// the object is converted to Redis bulk strings. +type Argument interface { + // RedisArg returns a value to be encoded as a bulk string per the + // conversions listed in the section 'Executing Commands'. + // Implementations should typically return a []byte or string. + RedisArg() interface{} +} + +// Scanner is implemented by an object which wants to control its value is +// interpreted when read from Redis. +type Scanner interface { + // RedisScan assigns a value from a Redis value. The argument src is one of + // the reply types listed in the section `Executing Commands`. + // + // An error should be returned if the value cannot be stored without + // loss of information. + RedisScan(src interface{}) error +} + +// ConnWithTimeout is an optional interface that allows the caller to override +// a connection's default read timeout. This interface is useful for executing +// the BLPOP, BRPOP, BRPOPLPUSH, XREAD and other commands that block at the +// server. +// +// A connection's default read timeout is set with the DialReadTimeout dial +// option. Applications should rely on the default timeout for commands that do +// not block at the server. +// +// All of the Conn implementations in this package satisfy the ConnWithTimeout +// interface. +// +// Use the DoWithTimeout and ReceiveWithTimeout helper functions to simplify +// use of this interface. +type ConnWithTimeout interface { + Conn + + // Do sends a command to the server and returns the received reply. + // The timeout overrides the read timeout set when dialing the + // connection. + DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) + + // Receive receives a single reply from the Redis server. The timeout + // overrides the read timeout set when dialing the connection. + ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) +} + +var errTimeoutNotSupported = errors.New("redis: connection does not support ConnWithTimeout") + +// DoWithTimeout executes a Redis command with the specified read timeout. If +// the connection does not satisfy the ConnWithTimeout interface, then an error +// is returned. +func DoWithTimeout(c Conn, timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { + cwt, ok := c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.DoWithTimeout(timeout, cmd, args...) +} + +// ReceiveWithTimeout receives a reply with the specified read timeout. If the +// connection does not satisfy the ConnWithTimeout interface, then an error is +// returned. +func ReceiveWithTimeout(c Conn, timeout time.Duration) (interface{}, error) { + cwt, ok := c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.ReceiveWithTimeout(timeout) +} diff --git a/vendor/github.com/garyburd/redigo/redis/reply.go b/vendor/github.com/garyburd/redigo/redis/reply.go new file mode 100644 index 0000000..c2b3b2b --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/reply.go @@ -0,0 +1,479 @@ +// 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) +} + +func sliceHelper(reply interface{}, err error, name string, makeSlice func(int), assign func(int, interface{}) error) error { + if err != nil { + return err + } + switch reply := reply.(type) { + case []interface{}: + makeSlice(len(reply)) + for i := range reply { + if reply[i] == nil { + continue + } + if err := assign(i, reply[i]); err != nil { + return err + } + } + return nil + case nil: + return ErrNil + case Error: + return reply + } + return fmt.Errorf("redigo: unexpected type for %s, got type %T", name, reply) +} + +// Float64s is a helper that converts an array command reply to a []float64. If +// err is not equal to nil, then Float64s returns nil, err. Nil array items are +// converted to 0 in the output slice. Floats64 returns an error if an array +// item is not a bulk string or nil. +func Float64s(reply interface{}, err error) ([]float64, error) { + var result []float64 + err = sliceHelper(reply, err, "Float64s", func(n int) { result = make([]float64, n) }, func(i int, v interface{}) error { + p, ok := v.([]byte) + if !ok { + return fmt.Errorf("redigo: unexpected element type for Floats64, got type %T", v) + } + f, err := strconv.ParseFloat(string(p), 64) + result[i] = f + return err + }) + return result, err +} + +// 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) { + var result []string + err = sliceHelper(reply, err, "Strings", func(n int) { result = make([]string, n) }, func(i int, v interface{}) error { + switch v := v.(type) { + case string: + result[i] = v + return nil + case []byte: + result[i] = string(v) + return nil + default: + return fmt.Errorf("redigo: unexpected element type for Strings, got type %T", v) + } + }) + return result, err +} + +// 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) { + var result [][]byte + err = sliceHelper(reply, err, "ByteSlices", func(n int) { result = make([][]byte, n) }, func(i int, v interface{}) error { + p, ok := v.([]byte) + if !ok { + return fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", v) + } + result[i] = p + return nil + }) + return result, err +} + +// Int64s is a helper that converts an array command reply to a []int64. +// If err is not equal to nil, then Int64s returns nil, err. Nil array +// items are stay nil. Int64s returns an error if an array item is not a +// bulk string or nil. +func Int64s(reply interface{}, err error) ([]int64, error) { + var result []int64 + err = sliceHelper(reply, err, "Int64s", func(n int) { result = make([]int64, n) }, func(i int, v interface{}) error { + switch v := v.(type) { + case int64: + result[i] = v + return nil + case []byte: + n, err := strconv.ParseInt(string(v), 10, 64) + result[i] = n + return err + default: + return fmt.Errorf("redigo: unexpected element type for Int64s, got type %T", v) + } + }) + return result, err +} + +// Ints is a helper that converts an array command reply to a []in. +// If err is not equal to nil, then Ints returns nil, err. Nil array +// items are stay nil. Ints returns an error if an array item is not a +// bulk string or nil. +func Ints(reply interface{}, err error) ([]int, error) { + var result []int + err = sliceHelper(reply, err, "Ints", func(n int) { result = make([]int, n) }, func(i int, v interface{}) error { + switch v := v.(type) { + case int64: + n := int(v) + if int64(n) != v { + return strconv.ErrRange + } + result[i] = n + return nil + case []byte: + n, err := strconv.Atoi(string(v)) + result[i] = n + return err + default: + return fmt.Errorf("redigo: unexpected element type for Ints, got type %T", v) + } + }) + return result, err +} + +// 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: StringMap 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: IntMap 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: Int64Map 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 +} + +// Positions is a helper that converts an array of positions (lat, long) +// into a [][2]float64. The GEOPOS command returns replies in this format. +func Positions(result interface{}, err error) ([]*[2]float64, error) { + values, err := Values(result, err) + if err != nil { + return nil, err + } + positions := make([]*[2]float64, len(values)) + for i := range values { + if values[i] == nil { + continue + } + p, ok := values[i].([]interface{}) + if !ok { + return nil, fmt.Errorf("redigo: unexpected element type for interface slice, got type %T", values[i]) + } + if len(p) != 2 { + return nil, fmt.Errorf("redigo: unexpected number of values for a member position, got %d", len(p)) + } + lat, err := Float64(p[0], nil) + if err != nil { + return nil, err + } + long, err := Float64(p[1], nil) + if err != nil { + return nil, err + } + positions[i] = &[2]float64{lat, long} + } + return positions, nil +} diff --git a/vendor/github.com/garyburd/redigo/redis/scan.go b/vendor/github.com/garyburd/redigo/redis/scan.go new file mode 100644 index 0000000..ef9551b --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/scan.go @@ -0,0 +1,585 @@ +// 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) { + if d.Kind() != reflect.Ptr { + if d.CanAddr() { + d2 := d.Addr() + if d2.CanInterface() { + if scanner, ok := d2.Interface().(Scanner); ok { + return scanner.RedisScan(s) + } + } + } + } else if d.CanInterface() { + // Already a reflect.Ptr + if d.IsNil() { + d.Set(reflect.New(d.Type().Elem())) + } + if scanner, ok := d.Interface().(Scanner); ok { + return scanner.RedisScan(s) + } + } + + 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) { + if scanner, ok := d.(Scanner); ok { + return scanner.RedisScan(s) + } + + // Handle the most common destination types using type switches and + // fall back to reflection for all other types. + switch s := s.(type) { + case nil: + // ignore + 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 = s + case *interface{}: + *d = s + case nil: + // skip value + 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. +// +// Scan uses RedisScan if available otherwise: +// +// 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. +// +// Each field uses RedisScan if available otherwise: +// 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 +} diff --git a/vendor/github.com/garyburd/redigo/redis/script.go b/vendor/github.com/garyburd/redigo/redis/script.go new file mode 100644 index 0000000..0ef1c82 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/script.go @@ -0,0 +1,91 @@ +// 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 +} + +// Hash returns the script hash. +func (s *Script) Hash() string { + return s.hash +} + +// 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 +} diff --git a/vendor/github.com/gin-contrib/sse/.travis.yml b/vendor/github.com/gin-contrib/sse/.travis.yml new file mode 100644 index 0000000..d0e8fcf --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/.travis.yml @@ -0,0 +1,26 @@ +language: go +sudo: false +go: + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - 1.12.x + - master + +git: + depth: 10 + +matrix: + fast_finish: true + include: + - go: 1.11.x + env: GO111MODULE=on + - go: 1.12.x + env: GO111MODULE=on + +script: + - go test -v -covermode=count -coverprofile=coverage.out + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/gin-contrib/sse/LICENSE b/vendor/github.com/gin-contrib/sse/LICENSE new file mode 100644 index 0000000..1ff7f37 --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Manuel Martínez-Almeida + +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. diff --git a/vendor/github.com/gin-contrib/sse/README.md b/vendor/github.com/gin-contrib/sse/README.md new file mode 100644 index 0000000..c9c49cf --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/README.md @@ -0,0 +1,58 @@ +# Server-Sent Events + +[![GoDoc](https://godoc.org/github.com/gin-contrib/sse?status.svg)](https://godoc.org/github.com/gin-contrib/sse) +[![Build Status](https://travis-ci.org/gin-contrib/sse.svg)](https://travis-ci.org/gin-contrib/sse) +[![codecov](https://codecov.io/gh/gin-contrib/sse/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/sse) +[![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/sse)](https://goreportcard.com/report/github.com/gin-contrib/sse) + +Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/). + +- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/) +- [Browser support](http://caniuse.com/#feat=eventsource) + +## Sample code + +```go +import "github.com/gin-contrib/sse" + +func httpHandler(w http.ResponseWriter, req *http.Request) { + // data can be a primitive like a string, an integer or a float + sse.Encode(w, sse.Event{ + Event: "message", + Data: "some data\nmore data", + }) + + // also a complex type, like a map, a struct or a slice + sse.Encode(w, sse.Event{ + Id: "124", + Event: "message", + Data: map[string]interface{}{ + "user": "manu", + "date": time.Now().Unix(), + "content": "hi!", + }, + }) +} +``` +``` +event: message +data: some data\\nmore data + +id: 124 +event: message +data: {"content":"hi!","date":1431540810,"user":"manu"} + +``` + +## Content-Type + +```go +fmt.Println(sse.ContentType) +``` +``` +text/event-stream +``` + +## Decoding support + +There is a client-side implementation of SSE coming soon. diff --git a/vendor/github.com/gin-contrib/sse/go.mod b/vendor/github.com/gin-contrib/sse/go.mod new file mode 100644 index 0000000..b9c03f4 --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/go.mod @@ -0,0 +1,5 @@ +module github.com/gin-contrib/sse + +go 1.12 + +require github.com/stretchr/testify v1.3.0 diff --git a/vendor/github.com/gin-contrib/sse/go.sum b/vendor/github.com/gin-contrib/sse/go.sum new file mode 100644 index 0000000..4347755 --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/go.sum @@ -0,0 +1,7 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/gin-contrib/sse/sse-decoder.go b/vendor/github.com/gin-contrib/sse/sse-decoder.go new file mode 100644 index 0000000..fd49b9c --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/sse-decoder.go @@ -0,0 +1,116 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package sse + +import ( + "bytes" + "io" + "io/ioutil" +) + +type decoder struct { + events []Event +} + +func Decode(r io.Reader) ([]Event, error) { + var dec decoder + return dec.decode(r) +} + +func (d *decoder) dispatchEvent(event Event, data string) { + dataLength := len(data) + if dataLength > 0 { + //If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer. + data = data[:dataLength-1] + dataLength-- + } + if dataLength == 0 && event.Event == "" { + return + } + if event.Event == "" { + event.Event = "message" + } + event.Data = data + d.events = append(d.events, event) +} + +func (d *decoder) decode(r io.Reader) ([]Event, error) { + buf, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + var currentEvent Event + var dataBuffer *bytes.Buffer = new(bytes.Buffer) + // TODO (and unit tests) + // Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, + // a single U+000A LINE FEED (LF) character, + // or a single U+000D CARRIAGE RETURN (CR) character. + lines := bytes.Split(buf, []byte{'\n'}) + for _, line := range lines { + if len(line) == 0 { + // If the line is empty (a blank line). Dispatch the event. + d.dispatchEvent(currentEvent, dataBuffer.String()) + + // reset current event and data buffer + currentEvent = Event{} + dataBuffer.Reset() + continue + } + if line[0] == byte(':') { + // If the line starts with a U+003A COLON character (:), ignore the line. + continue + } + + var field, value []byte + colonIndex := bytes.IndexRune(line, ':') + if colonIndex != -1 { + // If the line contains a U+003A COLON character character (:) + // Collect the characters on the line before the first U+003A COLON character (:), + // and let field be that string. + field = line[:colonIndex] + // Collect the characters on the line after the first U+003A COLON character (:), + // and let value be that string. + value = line[colonIndex+1:] + // If value starts with a single U+0020 SPACE character, remove it from value. + if len(value) > 0 && value[0] == ' ' { + value = value[1:] + } + } else { + // Otherwise, the string is not empty but does not contain a U+003A COLON character character (:) + // Use the whole line as the field name, and the empty string as the field value. + field = line + value = []byte{} + } + // The steps to process the field given a field name and a field value depend on the field name, + // as given in the following list. Field names must be compared literally, + // with no case folding performed. + switch string(field) { + case "event": + // Set the event name buffer to field value. + currentEvent.Event = string(value) + case "id": + // Set the event stream's last event ID to the field value. + currentEvent.Id = string(value) + case "retry": + // If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), + // then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer. + // Otherwise, ignore the field. + currentEvent.Id = string(value) + case "data": + // Append the field value to the data buffer, + dataBuffer.Write(value) + // then append a single U+000A LINE FEED (LF) character to the data buffer. + dataBuffer.WriteString("\n") + default: + //Otherwise. The field is ignored. + continue + } + } + // Once the end of the file is reached, the user agent must dispatch the event one final time. + d.dispatchEvent(currentEvent, dataBuffer.String()) + + return d.events, nil +} diff --git a/vendor/github.com/gin-contrib/sse/sse-encoder.go b/vendor/github.com/gin-contrib/sse/sse-encoder.go new file mode 100644 index 0000000..f9c8087 --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/sse-encoder.go @@ -0,0 +1,110 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package sse + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strconv" + "strings" +) + +// Server-Sent Events +// W3C Working Draft 29 October 2009 +// http://www.w3.org/TR/2009/WD-eventsource-20091029/ + +const ContentType = "text/event-stream" + +var contentType = []string{ContentType} +var noCache = []string{"no-cache"} + +var fieldReplacer = strings.NewReplacer( + "\n", "\\n", + "\r", "\\r") + +var dataReplacer = strings.NewReplacer( + "\n", "\ndata:", + "\r", "\\r") + +type Event struct { + Event string + Id string + Retry uint + Data interface{} +} + +func Encode(writer io.Writer, event Event) error { + w := checkWriter(writer) + writeId(w, event.Id) + writeEvent(w, event.Event) + writeRetry(w, event.Retry) + return writeData(w, event.Data) +} + +func writeId(w stringWriter, id string) { + if len(id) > 0 { + w.WriteString("id:") + fieldReplacer.WriteString(w, id) + w.WriteString("\n") + } +} + +func writeEvent(w stringWriter, event string) { + if len(event) > 0 { + w.WriteString("event:") + fieldReplacer.WriteString(w, event) + w.WriteString("\n") + } +} + +func writeRetry(w stringWriter, retry uint) { + if retry > 0 { + w.WriteString("retry:") + w.WriteString(strconv.FormatUint(uint64(retry), 10)) + w.WriteString("\n") + } +} + +func writeData(w stringWriter, data interface{}) error { + w.WriteString("data:") + switch kindOfData(data) { + case reflect.Struct, reflect.Slice, reflect.Map: + err := json.NewEncoder(w).Encode(data) + if err != nil { + return err + } + w.WriteString("\n") + default: + dataReplacer.WriteString(w, fmt.Sprint(data)) + w.WriteString("\n\n") + } + return nil +} + +func (r Event) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + return Encode(w, r) +} + +func (r Event) WriteContentType(w http.ResponseWriter) { + header := w.Header() + header["Content-Type"] = contentType + + if _, exist := header["Cache-Control"]; !exist { + header["Cache-Control"] = noCache + } +} + +func kindOfData(data interface{}) reflect.Kind { + value := reflect.ValueOf(data) + valueType := value.Kind() + if valueType == reflect.Ptr { + valueType = value.Elem().Kind() + } + return valueType +} diff --git a/vendor/github.com/gin-contrib/sse/writer.go b/vendor/github.com/gin-contrib/sse/writer.go new file mode 100644 index 0000000..6f9806c --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/writer.go @@ -0,0 +1,24 @@ +package sse + +import "io" + +type stringWriter interface { + io.Writer + WriteString(string) (int, error) +} + +type stringWrapper struct { + io.Writer +} + +func (w stringWrapper) WriteString(str string) (int, error) { + return w.Writer.Write([]byte(str)) +} + +func checkWriter(writer io.Writer) stringWriter { + if w, ok := writer.(stringWriter); ok { + return w + } else { + return stringWrapper{writer} + } +} diff --git a/vendor/github.com/gin-gonic/gin/.gitignore b/vendor/github.com/gin-gonic/gin/.gitignore new file mode 100644 index 0000000..bdd50c9 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/.gitignore @@ -0,0 +1,7 @@ +vendor/* +!vendor/vendor.json +coverage.out +count.out +test +profile.out +tmp.out diff --git a/vendor/github.com/gin-gonic/gin/.travis.yml b/vendor/github.com/gin-gonic/gin/.travis.yml new file mode 100644 index 0000000..8ebae71 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/.travis.yml @@ -0,0 +1,50 @@ +language: go + +matrix: + fast_finish: true + include: + - go: 1.12.x + env: GO111MODULE=on + - go: 1.13.x + - go: 1.13.x + env: + - TESTTAGS=nomsgpack + - go: 1.14.x + - go: 1.14.x + env: + - TESTTAGS=nomsgpack + - go: 1.15.x + - go: 1.15.x + env: + - TESTTAGS=nomsgpack + - go: master + +git: + depth: 10 + +before_install: + - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi + +install: + - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi + - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi + - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi + +go_import_path: github.com/gin-gonic/gin + +script: + - make vet + - make fmt-check + - make misspell-check + - make test + +after_success: + - bash <(curl -s https://codecov.io/bash) + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/7f95bf605c4d356372f4 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/vendor/github.com/gin-gonic/gin/AUTHORS.md b/vendor/github.com/gin-gonic/gin/AUTHORS.md new file mode 100644 index 0000000..c634e6b --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/AUTHORS.md @@ -0,0 +1,233 @@ +List of all the awesome people working to make Gin the best Web Framework in Go. + +## gin 1.x series authors + +**Gin Core Team:** Bo-Yi Wu (@appleboy), 田欧 (@thinkerou), Javier Provecho (@javierprovecho) + +## gin 0.x series authors + +**Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho) + +People and companies, who have contributed, in alphabetical order. + +**@858806258 (杰哥)** +- Fix typo in example + + +**@achedeuzot (Klemen Sever)** +- Fix newline debug printing + + +**@adammck (Adam Mckaig)** +- Add MIT license + + +**@AlexanderChen1989 (Alexander)** +- Typos in README + + +**@alexanderdidenko (Aleksandr Didenko)** +- Add support multipart/form-data + + +**@alexandernyquist (Alexander Nyquist)** +- Using template.Must to fix multiple return issue +- ★ Added support for OPTIONS verb +- ★ Setting response headers before calling WriteHeader +- Improved documentation for model binding +- ★ Added Content.Redirect() +- ★ Added tons of Unit tests + + +**@austinheap (Austin Heap)** +- Added travis CI integration + + +**@andredublin (Andre Dublin)** +- Fix typo in comment + + +**@bredov (Ludwig Valda Vasquez)** +- Fix html templating in debug mode + + +**@bluele (Jun Kimura)** +- Fixes code examples in README + + +**@chad-russell** +- ★ Support for serializing gin.H into XML + + +**@dickeyxxx (Jeff Dickey)** +- Typos in README +- Add example about serving static files + + +**@donileo (Adonis)** +- Add NoMethod handler + + +**@dutchcoders (DutchCoders)** +- ★ Fix security bug that allows client to spoof ip +- Fix typo. r.HTMLTemplates -> SetHTMLTemplate + + +**@el3ctro- (Joshua Loper)** +- Fix typo in example + + +**@ethankan (Ethan Kan)** +- Unsigned integers in binding + + +**(Evgeny Persienko)** +- Validate sub structures + + +**@frankbille (Frank Bille)** +- Add support for HTTP Realm Auth + + +**@fmd (Fareed Dudhia)** +- Fix typo. SetHTTPTemplate -> SetHTMLTemplate + + +**@ironiridis (Christopher Harrington)** +- Remove old reference + + +**@jammie-stackhouse (Jamie Stackhouse)** +- Add more shortcuts for router methods + + +**@jasonrhansen** +- Fix spelling and grammar errors in documentation + + +**@JasonSoft (Jason Lee)** +- Fix typo in comment + + +**@joiggama (Ignacio Galindo)** +- Add utf-8 charset header on renders + + +**@julienschmidt (Julien Schmidt)** +- gofmt the code examples + + +**@kelcecil (Kel Cecil)** +- Fix readme typo + + +**@kyledinh (Kyle Dinh)** +- Adds RunTLS() + + +**@LinusU (Linus Unnebäck)** +- Small fixes in README + + +**@loongmxbt (Saint Asky)** +- Fix typo in example + + +**@lucas-clemente (Lucas Clemente)** +- ★ work around path.Join removing trailing slashes from routes + + +**@mattn (Yasuhiro Matsumoto)** +- Improve color logger + + +**@mdigger (Dmitry Sedykh)** +- Fixes Form binding when content-type is x-www-form-urlencoded +- No repeat call c.Writer.Status() in gin.Logger +- Fixes Content-Type for json render + + +**@mirzac (Mirza Ceric)** +- Fix debug printing + + +**@mopemope (Yutaka Matsubara)** +- ★ Adds Godep support (Dependencies Manager) +- Fix variadic parameter in the flexible render API +- Fix Corrupted plain render +- Add Pluggable View Renderer Example + + +**@msemenistyi (Mykyta Semenistyi)** +- update Readme.md. Add code to String method + + +**@msoedov (Sasha Myasoedov)** +- ★ Adds tons of unit tests. + + +**@ngerakines (Nick Gerakines)** +- ★ Improves API, c.GET() doesn't panic +- Adds MustGet() method + + +**@r8k (Rajiv Kilaparti)** +- Fix Port usage in README. + + +**@rayrod2030 (Ray Rodriguez)** +- Fix typo in example + + +**@rns** +- Fix typo in example + + +**@RobAWilkinson (Robert Wilkinson)** +- Add example of forms and params + + +**@rogierlommers (Rogier Lommers)** +- Add updated static serve example + +**@rw-access (Ross Wolf)** +- Added support to mix exact and param routes + +**@se77en (Damon Zhao)** +- Improve color logging + + +**@silasb (Silas Baronda)** +- Fixing quotes in README + + +**@SkuliOskarsson (Skuli Oskarsson)** +- Fixes some texts in README II + + +**@slimmy (Jimmy Pettersson)** +- Added messages for required bindings + + +**@smira (Andrey Smirnov)** +- Add support for ignored/unexported fields in binding + + +**@superalsrk (SRK.Lyu)** +- Update httprouter godeps + + +**@tebeka (Miki Tebeka)** +- Use net/http constants instead of numeric values + + +**@techjanitor** +- Update context.go reserved IPs + + +**@yosssi (Keiji Yoshida)** +- Fix link in README + + +**@yuyabee** +- Fixed README diff --git a/vendor/github.com/gin-gonic/gin/BENCHMARKS.md b/vendor/github.com/gin-gonic/gin/BENCHMARKS.md new file mode 100644 index 0000000..c11ee99 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/BENCHMARKS.md @@ -0,0 +1,666 @@ + +# Benchmark System + +**VM HOST:** Travis +**Machine:** Ubuntu 16.04.6 LTS x64 +**Date:** May 04th, 2020 +**Version:** Gin v1.6.3 +**Go Version:** 1.14.2 linux/amd64 +**Source:** [Go HTTP Router Benchmark](https://github.com/gin-gonic/go-http-routing-benchmark) +**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) or [Travis result](https://travis-ci.org/github/gin-gonic/go-http-routing-benchmark/jobs/682947061) + +## Static Routes: 157 + +```sh +Gin: 34936 Bytes + +HttpServeMux: 14512 Bytes +Ace: 30680 Bytes +Aero: 34536 Bytes +Bear: 30456 Bytes +Beego: 98456 Bytes +Bone: 40224 Bytes +Chi: 83608 Bytes +Denco: 10216 Bytes +Echo: 80328 Bytes +GocraftWeb: 55288 Bytes +Goji: 29744 Bytes +Gojiv2: 105840 Bytes +GoJsonRest: 137496 Bytes +GoRestful: 816936 Bytes +GorillaMux: 585632 Bytes +GowwwRouter: 24968 Bytes +HttpRouter: 21712 Bytes +HttpTreeMux: 73448 Bytes +Kocha: 115472 Bytes +LARS: 30640 Bytes +Macaron: 38592 Bytes +Martini: 310864 Bytes +Pat: 19696 Bytes +Possum: 89920 Bytes +R2router: 23712 Bytes +Rivet: 24608 Bytes +Tango: 28264 Bytes +TigerTonic: 78768 Bytes +Traffic: 538976 Bytes +Vulcan: 369960 Bytes +``` + +## GithubAPI Routes: 203 + +```sh +Gin: 58512 Bytes + +Ace: 48688 Bytes +Aero: 318568 Bytes +Bear: 84248 Bytes +Beego: 150936 Bytes +Bone: 100976 Bytes +Chi: 95112 Bytes +Denco: 36736 Bytes +Echo: 100296 Bytes +GocraftWeb: 95432 Bytes +Goji: 49680 Bytes +Gojiv2: 104704 Bytes +GoJsonRest: 141976 Bytes +GoRestful: 1241656 Bytes +GorillaMux: 1322784 Bytes +GowwwRouter: 80008 Bytes +HttpRouter: 37144 Bytes +HttpTreeMux: 78800 Bytes +Kocha: 785120 Bytes +LARS: 48600 Bytes +Macaron: 92784 Bytes +Martini: 485264 Bytes +Pat: 21200 Bytes +Possum: 85312 Bytes +R2router: 47104 Bytes +Rivet: 42840 Bytes +Tango: 54840 Bytes +TigerTonic: 95264 Bytes +Traffic: 921744 Bytes +Vulcan: 425992 Bytes +``` + +## GPlusAPI Routes: 13 + +```sh +Gin: 4384 Bytes + +Ace: 3712 Bytes +Aero: 26056 Bytes +Bear: 7112 Bytes +Beego: 10272 Bytes +Bone: 6688 Bytes +Chi: 8024 Bytes +Denco: 3264 Bytes +Echo: 9688 Bytes +GocraftWeb: 7496 Bytes +Goji: 3152 Bytes +Gojiv2: 7376 Bytes +GoJsonRest: 11400 Bytes +GoRestful: 74328 Bytes +GorillaMux: 66208 Bytes +GowwwRouter: 5744 Bytes +HttpRouter: 2808 Bytes +HttpTreeMux: 7440 Bytes +Kocha: 128880 Bytes +LARS: 3656 Bytes +Macaron: 8656 Bytes +Martini: 23920 Bytes +Pat: 1856 Bytes +Possum: 7248 Bytes +R2router: 3928 Bytes +Rivet: 3064 Bytes +Tango: 5168 Bytes +TigerTonic: 9408 Bytes +Traffic: 46400 Bytes +Vulcan: 25544 Bytes +``` + +## ParseAPI Routes: 26 + +```sh +Gin: 7776 Bytes + +Ace: 6704 Bytes +Aero: 28488 Bytes +Bear: 12320 Bytes +Beego: 19280 Bytes +Bone: 11440 Bytes +Chi: 9744 Bytes +Denco: 4192 Bytes +Echo: 11664 Bytes +GocraftWeb: 12800 Bytes +Goji: 5680 Bytes +Gojiv2: 14464 Bytes +GoJsonRest: 14072 Bytes +GoRestful: 116264 Bytes +GorillaMux: 105880 Bytes +GowwwRouter: 9344 Bytes +HttpRouter: 5072 Bytes +HttpTreeMux: 7848 Bytes +Kocha: 181712 Bytes +LARS: 6632 Bytes +Macaron: 13648 Bytes +Martini: 45888 Bytes +Pat: 2560 Bytes +Possum: 9200 Bytes +R2router: 7056 Bytes +Rivet: 5680 Bytes +Tango: 8920 Bytes +TigerTonic: 9840 Bytes +Traffic: 79096 Bytes +Vulcan: 44504 Bytes +``` + +## Static Routes + +```sh +BenchmarkGin_StaticAll 62169 19319 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_StaticAll 65428 18313 ns/op 0 B/op 0 allocs/op +BenchmarkAero_StaticAll 121132 9632 ns/op 0 B/op 0 allocs/op +BenchmarkHttpServeMux_StaticAll 52626 22758 ns/op 0 B/op 0 allocs/op +BenchmarkBeego_StaticAll 9962 179058 ns/op 55264 B/op 471 allocs/op +BenchmarkBear_StaticAll 14894 80966 ns/op 20272 B/op 469 allocs/op +BenchmarkBone_StaticAll 18718 64065 ns/op 0 B/op 0 allocs/op +BenchmarkChi_StaticAll 10000 149827 ns/op 67824 B/op 471 allocs/op +BenchmarkDenco_StaticAll 211393 5680 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_StaticAll 49341 24343 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_StaticAll 10000 126209 ns/op 46312 B/op 785 allocs/op +BenchmarkGoji_StaticAll 27956 43174 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_StaticAll 3430 370718 ns/op 205984 B/op 1570 allocs/op +BenchmarkGoJsonRest_StaticAll 9134 188888 ns/op 51653 B/op 1727 allocs/op +BenchmarkGoRestful_StaticAll 706 1703330 ns/op 613280 B/op 2053 allocs/op +BenchmarkGorillaMux_StaticAll 1268 924083 ns/op 153233 B/op 1413 allocs/op +BenchmarkGowwwRouter_StaticAll 63374 18935 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_StaticAll 109938 10902 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_StaticAll 109166 10861 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_StaticAll 92258 12992 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_StaticAll 65200 18387 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_StaticAll 5671 291501 ns/op 115553 B/op 1256 allocs/op +BenchmarkMartini_StaticAll 807 1460498 ns/op 125444 B/op 1717 allocs/op +BenchmarkPat_StaticAll 513 2342396 ns/op 602832 B/op 12559 allocs/op +BenchmarkPossum_StaticAll 10000 128270 ns/op 65312 B/op 471 allocs/op +BenchmarkR2router_StaticAll 16726 71760 ns/op 22608 B/op 628 allocs/op +BenchmarkRivet_StaticAll 41722 28723 ns/op 0 B/op 0 allocs/op +BenchmarkTango_StaticAll 7606 205082 ns/op 39209 B/op 1256 allocs/op +BenchmarkTigerTonic_StaticAll 26247 45806 ns/op 7376 B/op 157 allocs/op +BenchmarkTraffic_StaticAll 550 2284518 ns/op 754864 B/op 14601 allocs/op +BenchmarkVulcan_StaticAll 10000 131343 ns/op 15386 B/op 471 allocs/op +``` + +## Micro Benchmarks + +```sh +BenchmarkGin_Param 18785022 63.9 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_Param 14689765 81.5 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param 23094770 51.2 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param 1417045 845 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_Param 1000000 1080 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param 1000000 1463 ns/op 816 B/op 6 allocs/op +BenchmarkChi_Param 1378756 885 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param 8557899 143 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_Param 16433347 75.5 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param 1000000 1218 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_Param 1921248 617 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param 561848 2156 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_Param 1000000 1358 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_Param 224857 5307 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_Param 498313 2459 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_Param 1864354 654 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param 26269074 47.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param 2109829 557 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_Param 5050216 243 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_Param 19811712 59.9 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param 662746 2329 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param 279902 4260 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_Param 1000000 1382 ns/op 536 B/op 11 allocs/op +BenchmarkPossum_Param 1000000 1014 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param 1712559 707 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param 6648086 182 ns/op 48 B/op 1 allocs/op +BenchmarkTango_Param 1221504 994 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_Param 891661 2261 ns/op 776 B/op 16 allocs/op +BenchmarkTraffic_Param 350059 3598 ns/op 1856 B/op 21 allocs/op +BenchmarkVulcan_Param 2517823 472 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param5 9214365 130 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param5 15369013 77.9 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param5 1000000 1113 ns/op 501 B/op 5 allocs/op +BenchmarkBeego_Param5 1000000 1269 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param5 986820 1873 ns/op 864 B/op 6 allocs/op +BenchmarkChi_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param5 3036331 400 ns/op 160 B/op 1 allocs/op +BenchmarkEcho_Param5 6447133 186 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param5 10786068 110 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param5 844820 1944 ns/op 920 B/op 11 allocs/op +BenchmarkGoji_Param5 1474965 827 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Param5 442820 2516 ns/op 1392 B/op 11 allocs/op +BenchmarkGoJsonRest_Param5 507555 2711 ns/op 1097 B/op 16 allocs/op +BenchmarkGoRestful_Param5 216481 6093 ns/op 4288 B/op 14 allocs/op +BenchmarkGorillaMux_Param5 314402 3628 ns/op 1344 B/op 10 allocs/op +BenchmarkGowwwRouter_Param5 1624660 733 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param5 13167324 92.0 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param5 1000000 1295 ns/op 576 B/op 6 allocs/op +BenchmarkKocha_Param5 1000000 1138 ns/op 440 B/op 10 allocs/op +BenchmarkLARS_Param5 11580613 105 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param5 473596 2755 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Param5 230756 5111 ns/op 1232 B/op 11 allocs/op +BenchmarkPat_Param5 469190 3370 ns/op 888 B/op 29 allocs/op +BenchmarkPossum_Param5 1000000 1002 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param5 1422129 844 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Param5 2263789 539 ns/op 240 B/op 1 allocs/op +BenchmarkTango_Param5 1000000 1256 ns/op 360 B/op 8 allocs/op +BenchmarkTigerTonic_Param5 175500 7492 ns/op 2279 B/op 39 allocs/op +BenchmarkTraffic_Param5 233631 5816 ns/op 2208 B/op 27 allocs/op +BenchmarkVulcan_Param5 1923416 629 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Param20 4321266 281 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Param20 31501641 35.2 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Param20 335204 3489 ns/op 1665 B/op 5 allocs/op +BenchmarkBeego_Param20 503674 2860 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Param20 298922 4741 ns/op 2031 B/op 6 allocs/op +BenchmarkChi_Param20 878181 1957 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Param20 1000000 1360 ns/op 640 B/op 1 allocs/op +BenchmarkEcho_Param20 2104946 580 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Param20 4167204 290 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Param20 173064 7514 ns/op 3796 B/op 15 allocs/op +BenchmarkGoji_Param20 458778 2651 ns/op 1247 B/op 2 allocs/op +BenchmarkGojiv2_Param20 364862 3178 ns/op 1632 B/op 11 allocs/op +BenchmarkGoJsonRest_Param20 125514 9760 ns/op 4485 B/op 20 allocs/op +BenchmarkGoRestful_Param20 101217 11964 ns/op 6715 B/op 18 allocs/op +BenchmarkGorillaMux_Param20 147654 8132 ns/op 3452 B/op 12 allocs/op +BenchmarkGowwwRouter_Param20 1000000 1225 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Param20 4920895 247 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Param20 173202 6605 ns/op 3196 B/op 10 allocs/op +BenchmarkKocha_Param20 345988 3620 ns/op 1808 B/op 27 allocs/op +BenchmarkLARS_Param20 4592326 262 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Param20 166492 7286 ns/op 2924 B/op 12 allocs/op +BenchmarkMartini_Param20 122162 10653 ns/op 3595 B/op 13 allocs/op +BenchmarkPat_Param20 78630 15239 ns/op 4424 B/op 93 allocs/op +BenchmarkPossum_Param20 1000000 1008 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Param20 294981 4587 ns/op 2284 B/op 7 allocs/op +BenchmarkRivet_Param20 691798 2090 ns/op 1024 B/op 1 allocs/op +BenchmarkTango_Param20 842440 2505 ns/op 856 B/op 8 allocs/op +BenchmarkTigerTonic_Param20 38614 31509 ns/op 9870 B/op 119 allocs/op +BenchmarkTraffic_Param20 57633 21107 ns/op 7853 B/op 47 allocs/op +BenchmarkVulcan_Param20 1000000 1178 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParamWrite 7330743 180 ns/op 8 B/op 1 allocs/op +BenchmarkAero_ParamWrite 13833598 86.7 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParamWrite 1363321 867 ns/op 456 B/op 5 allocs/op +BenchmarkBeego_ParamWrite 1000000 1104 ns/op 360 B/op 4 allocs/op +BenchmarkBone_ParamWrite 1000000 1475 ns/op 816 B/op 6 allocs/op +BenchmarkChi_ParamWrite 1320590 892 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParamWrite 7093605 172 ns/op 32 B/op 1 allocs/op +BenchmarkEcho_ParamWrite 8434424 161 ns/op 8 B/op 1 allocs/op +BenchmarkGin_ParamWrite 10377034 118 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParamWrite 1000000 1266 ns/op 656 B/op 9 allocs/op +BenchmarkGoji_ParamWrite 1874168 654 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParamWrite 459032 2352 ns/op 1360 B/op 13 allocs/op +BenchmarkGoJsonRest_ParamWrite 499434 2145 ns/op 1128 B/op 18 allocs/op +BenchmarkGoRestful_ParamWrite 241087 5470 ns/op 4200 B/op 15 allocs/op +BenchmarkGorillaMux_ParamWrite 425686 2522 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParamWrite 922172 1778 ns/op 976 B/op 8 allocs/op +BenchmarkHttpRouter_ParamWrite 15392049 77.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParamWrite 1973385 597 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParamWrite 4262500 281 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParamWrite 10764410 113 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParamWrite 486769 2726 ns/op 1176 B/op 14 allocs/op +BenchmarkMartini_ParamWrite 264804 4842 ns/op 1176 B/op 14 allocs/op +BenchmarkPat_ParamWrite 735116 2047 ns/op 960 B/op 15 allocs/op +BenchmarkPossum_ParamWrite 1000000 1004 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParamWrite 1592136 768 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParamWrite 3582051 339 ns/op 112 B/op 2 allocs/op +BenchmarkTango_ParamWrite 2237337 534 ns/op 136 B/op 4 allocs/op +BenchmarkTigerTonic_ParamWrite 439608 3136 ns/op 1216 B/op 21 allocs/op +BenchmarkTraffic_ParamWrite 306979 4328 ns/op 2280 B/op 25 allocs/op +BenchmarkVulcan_ParamWrite 2529973 472 ns/op 98 B/op 3 allocs/op +``` + +## GitHub + +```sh +BenchmarkGin_GithubStatic 15629472 76.7 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_GithubStatic 15542612 75.9 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubStatic 24777151 48.5 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubStatic 2788894 435 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_GithubStatic 1000000 1064 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubStatic 93507 12838 ns/op 2880 B/op 60 allocs/op +BenchmarkChi_GithubStatic 1387743 860 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GithubStatic 39384996 30.4 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GithubStatic 12076382 99.1 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubStatic 1596495 756 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_GithubStatic 6364876 189 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GithubStatic 550202 2098 ns/op 1312 B/op 10 allocs/op +BenchmarkGoRestful_GithubStatic 102183 12552 ns/op 4256 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubStatic 1000000 1029 ns/op 329 B/op 11 allocs/op +BenchmarkGorillaMux_GithubStatic 255552 5190 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GithubStatic 15531916 77.1 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GithubStatic 27920724 43.1 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubStatic 21448953 55.8 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GithubStatic 21405310 56.0 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GithubStatic 13625156 89.0 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubStatic 1000000 1747 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GithubStatic 187186 7326 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GithubStatic 109143 11563 ns/op 3648 B/op 76 allocs/op +BenchmarkPossum_GithubStatic 1575898 770 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GithubStatic 3046231 404 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GithubStatic 11484826 105 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GithubStatic 1000000 1153 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_GithubStatic 4929780 249 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_GithubStatic 106351 11819 ns/op 4664 B/op 90 allocs/op +BenchmarkVulcan_GithubStatic 1613271 722 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubParam 8386032 143 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubParam 11816200 102 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubParam 1000000 1012 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GithubParam 1000000 1157 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GithubParam 184653 6912 ns/op 1888 B/op 19 allocs/op +BenchmarkChi_GithubParam 1000000 1102 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GithubParam 3484798 352 ns/op 128 B/op 1 allocs/op +BenchmarkEcho_GithubParam 6337380 189 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubParam 9132032 131 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubParam 1000000 1446 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GithubParam 1248640 977 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GithubParam 383233 2784 ns/op 1408 B/op 13 allocs/op +BenchmarkGoJsonRest_GithubParam 1000000 1991 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GithubParam 76414 16015 ns/op 4352 B/op 16 allocs/op +BenchmarkGorillaMux_GithubParam 150026 7663 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GithubParam 1592044 751 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GithubParam 10420628 115 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubParam 1403755 835 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GithubParam 2286170 533 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GithubParam 9540374 129 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubParam 533154 2742 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GithubParam 119397 9638 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_GithubParam 150675 8858 ns/op 2408 B/op 48 allocs/op +BenchmarkPossum_GithubParam 1000000 1001 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GithubParam 1602886 761 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GithubParam 2986579 409 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GithubParam 1000000 1356 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GithubParam 388899 3429 ns/op 1176 B/op 22 allocs/op +BenchmarkTraffic_GithubParam 123160 9734 ns/op 2816 B/op 40 allocs/op +BenchmarkVulcan_GithubParam 1000000 1138 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GithubAll 40543 29670 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GithubAll 57632 20648 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GithubAll 9234 216179 ns/op 86448 B/op 943 allocs/op +BenchmarkBeego_GithubAll 7407 243496 ns/op 71456 B/op 609 allocs/op +BenchmarkBone_GithubAll 420 2922835 ns/op 720160 B/op 8620 allocs/op +BenchmarkChi_GithubAll 7620 238331 ns/op 87696 B/op 609 allocs/op +BenchmarkDenco_GithubAll 18355 64494 ns/op 20224 B/op 167 allocs/op +BenchmarkEcho_GithubAll 31251 38479 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GithubAll 43550 27364 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GithubAll 4117 300062 ns/op 131656 B/op 1686 allocs/op +BenchmarkGoji_GithubAll 3274 416158 ns/op 56112 B/op 334 allocs/op +BenchmarkGojiv2_GithubAll 1402 870518 ns/op 352720 B/op 4321 allocs/op +BenchmarkGoJsonRest_GithubAll 2976 401507 ns/op 134371 B/op 2737 allocs/op +BenchmarkGoRestful_GithubAll 410 2913158 ns/op 910144 B/op 2938 allocs/op +BenchmarkGorillaMux_GithubAll 346 3384987 ns/op 251650 B/op 1994 allocs/op +BenchmarkGowwwRouter_GithubAll 10000 143025 ns/op 72144 B/op 501 allocs/op +BenchmarkHttpRouter_GithubAll 55938 21360 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GithubAll 10000 153944 ns/op 65856 B/op 671 allocs/op +BenchmarkKocha_GithubAll 10000 106315 ns/op 23304 B/op 843 allocs/op +BenchmarkLARS_GithubAll 47779 25084 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GithubAll 3266 371907 ns/op 149409 B/op 1624 allocs/op +BenchmarkMartini_GithubAll 331 3444706 ns/op 226551 B/op 2325 allocs/op +BenchmarkPat_GithubAll 273 4381818 ns/op 1483152 B/op 26963 allocs/op +BenchmarkPossum_GithubAll 10000 164367 ns/op 84448 B/op 609 allocs/op +BenchmarkR2router_GithubAll 10000 160220 ns/op 77328 B/op 979 allocs/op +BenchmarkRivet_GithubAll 14625 82453 ns/op 16272 B/op 167 allocs/op +BenchmarkTango_GithubAll 6255 279611 ns/op 63826 B/op 1618 allocs/op +BenchmarkTigerTonic_GithubAll 2008 687874 ns/op 193856 B/op 4474 allocs/op +BenchmarkTraffic_GithubAll 355 3478508 ns/op 820744 B/op 14114 allocs/op +BenchmarkVulcan_GithubAll 6885 193333 ns/op 19894 B/op 609 allocs/op +``` + +## Google+ + +```sh +BenchmarkGin_GPlusStatic 19247326 62.2 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_GPlusStatic 20235060 59.2 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusStatic 31978935 37.6 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusStatic 3516523 341 ns/op 104 B/op 3 allocs/op +BenchmarkBeego_GPlusStatic 1212036 991 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusStatic 6736242 183 ns/op 32 B/op 1 allocs/op +BenchmarkChi_GPlusStatic 1490640 814 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlusStatic 55006856 21.8 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_GPlusStatic 17688258 67.9 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusStatic 1829181 666 ns/op 280 B/op 5 allocs/op +BenchmarkGoji_GPlusStatic 9147451 130 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_GPlusStatic 594015 2063 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_GPlusStatic 1264906 950 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_GPlusStatic 231558 5341 ns/op 3872 B/op 13 allocs/op +BenchmarkGorillaMux_GPlusStatic 908418 1809 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_GPlusStatic 40684604 29.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_GPlusStatic 46742804 25.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusStatic 32567161 36.9 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_GPlusStatic 33800060 35.3 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_GPlusStatic 20431858 60.0 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusStatic 1000000 1745 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_GPlusStatic 442248 3619 ns/op 768 B/op 9 allocs/op +BenchmarkPat_GPlusStatic 4328004 292 ns/op 96 B/op 2 allocs/op +BenchmarkPossum_GPlusStatic 1570753 763 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_GPlusStatic 3339474 355 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_GPlusStatic 18570961 64.7 ns/op 0 B/op 0 allocs/op +BenchmarkTango_GPlusStatic 1388702 860 ns/op 200 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusStatic 7803543 159 ns/op 32 B/op 1 allocs/op +BenchmarkTraffic_GPlusStatic 878605 2171 ns/op 1112 B/op 16 allocs/op +BenchmarkVulcan_GPlusStatic 2742446 437 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusParam 11626975 105 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusParam 16914322 71.6 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusParam 1405173 832 ns/op 480 B/op 5 allocs/op +BenchmarkBeego_GPlusParam 1000000 1075 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlusParam 1000000 1557 ns/op 816 B/op 6 allocs/op +BenchmarkChi_GPlusParam 1347926 894 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlusParam 5513000 212 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlusParam 11884383 101 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusParam 12898952 93.1 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusParam 1000000 1194 ns/op 648 B/op 8 allocs/op +BenchmarkGoji_GPlusParam 1857229 645 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlusParam 520939 2322 ns/op 1328 B/op 11 allocs/op +BenchmarkGoJsonRest_GPlusParam 1000000 1536 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_GPlusParam 205449 5800 ns/op 4192 B/op 14 allocs/op +BenchmarkGorillaMux_GPlusParam 395310 3188 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlusParam 1851798 667 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlusParam 18420789 65.2 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusParam 1878463 629 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_GPlusParam 4495610 273 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_GPlusParam 14615976 83.2 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusParam 584145 2549 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlusParam 250501 4583 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_GPlusParam 1000000 1645 ns/op 576 B/op 11 allocs/op +BenchmarkPossum_GPlusParam 1000000 1008 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlusParam 1708191 688 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlusParam 5795014 211 ns/op 48 B/op 1 allocs/op +BenchmarkTango_GPlusParam 1000000 1091 ns/op 264 B/op 8 allocs/op +BenchmarkTigerTonic_GPlusParam 760221 2489 ns/op 856 B/op 16 allocs/op +BenchmarkTraffic_GPlusParam 309774 4039 ns/op 1872 B/op 21 allocs/op +BenchmarkVulcan_GPlusParam 1935730 623 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlus2Params 9158314 134 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlus2Params 11300517 107 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlus2Params 1239238 961 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_GPlus2Params 1000000 1202 ns/op 352 B/op 3 allocs/op +BenchmarkBone_GPlus2Params 335576 3725 ns/op 1168 B/op 10 allocs/op +BenchmarkChi_GPlus2Params 1000000 1014 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_GPlus2Params 4394598 280 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_GPlus2Params 7851861 154 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlus2Params 9958588 120 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlus2Params 1000000 1433 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_GPlus2Params 1325134 909 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_GPlus2Params 405955 2870 ns/op 1408 B/op 14 allocs/op +BenchmarkGoJsonRest_GPlus2Params 977038 1987 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_GPlus2Params 205018 6142 ns/op 4384 B/op 16 allocs/op +BenchmarkGorillaMux_GPlus2Params 205641 6015 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_GPlus2Params 1748542 684 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_GPlus2Params 14047102 87.7 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlus2Params 1418673 828 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_GPlus2Params 2334562 520 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_GPlus2Params 11954094 101 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlus2Params 491552 2890 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_GPlus2Params 120532 9545 ns/op 1200 B/op 13 allocs/op +BenchmarkPat_GPlus2Params 194739 6766 ns/op 2168 B/op 33 allocs/op +BenchmarkPossum_GPlus2Params 1201224 1009 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_GPlus2Params 1575535 756 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_GPlus2Params 3698930 325 ns/op 96 B/op 1 allocs/op +BenchmarkTango_GPlus2Params 1000000 1212 ns/op 344 B/op 8 allocs/op +BenchmarkTigerTonic_GPlus2Params 349350 3660 ns/op 1200 B/op 22 allocs/op +BenchmarkTraffic_GPlus2Params 169714 7862 ns/op 2248 B/op 28 allocs/op +BenchmarkVulcan_GPlus2Params 1222288 974 ns/op 98 B/op 3 allocs/op +BenchmarkAce_GPlusAll 845606 1398 ns/op 0 B/op 0 allocs/op +BenchmarkAero_GPlusAll 1000000 1009 ns/op 0 B/op 0 allocs/op +BenchmarkBear_GPlusAll 103830 11386 ns/op 5488 B/op 61 allocs/op +BenchmarkBeego_GPlusAll 82653 14784 ns/op 4576 B/op 39 allocs/op +BenchmarkBone_GPlusAll 36601 33123 ns/op 11744 B/op 109 allocs/op +BenchmarkChi_GPlusAll 95264 12831 ns/op 5616 B/op 39 allocs/op +BenchmarkDenco_GPlusAll 567681 2950 ns/op 672 B/op 11 allocs/op +BenchmarkEcho_GPlusAll 720366 1665 ns/op 0 B/op 0 allocs/op +BenchmarkGin_GPlusAll 1000000 1185 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_GPlusAll 71575 16365 ns/op 8040 B/op 103 allocs/op +BenchmarkGoji_GPlusAll 136352 9191 ns/op 3696 B/op 22 allocs/op +BenchmarkGojiv2_GPlusAll 38006 31802 ns/op 17616 B/op 154 allocs/op +BenchmarkGoJsonRest_GPlusAll 57238 21561 ns/op 8117 B/op 170 allocs/op +BenchmarkGoRestful_GPlusAll 15147 79276 ns/op 55520 B/op 192 allocs/op +BenchmarkGorillaMux_GPlusAll 24446 48410 ns/op 16112 B/op 128 allocs/op +BenchmarkGowwwRouter_GPlusAll 150112 7770 ns/op 4752 B/op 33 allocs/op +BenchmarkHttpRouter_GPlusAll 1367820 878 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_GPlusAll 166628 8004 ns/op 4032 B/op 38 allocs/op +BenchmarkKocha_GPlusAll 265694 4570 ns/op 976 B/op 43 allocs/op +BenchmarkLARS_GPlusAll 1000000 1068 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_GPlusAll 54564 23305 ns/op 9568 B/op 104 allocs/op +BenchmarkMartini_GPlusAll 16274 73845 ns/op 14016 B/op 145 allocs/op +BenchmarkPat_GPlusAll 27181 44478 ns/op 15264 B/op 271 allocs/op +BenchmarkPossum_GPlusAll 122587 10277 ns/op 5408 B/op 39 allocs/op +BenchmarkR2router_GPlusAll 130137 9297 ns/op 5040 B/op 63 allocs/op +BenchmarkRivet_GPlusAll 532438 3323 ns/op 768 B/op 11 allocs/op +BenchmarkTango_GPlusAll 86054 14531 ns/op 3656 B/op 104 allocs/op +BenchmarkTigerTonic_GPlusAll 33936 35356 ns/op 11600 B/op 242 allocs/op +BenchmarkTraffic_GPlusAll 17833 68181 ns/op 26248 B/op 341 allocs/op +BenchmarkVulcan_GPlusAll 120109 9861 ns/op 1274 B/op 39 allocs/op +``` + +## Parse.com + +```sh +BenchmarkGin_ParseStatic 18877833 63.5 ns/op 0 B/op 0 allocs/op + +BenchmarkAce_ParseStatic 19663731 60.8 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseStatic 28967341 41.5 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseStatic 3006984 402 ns/op 120 B/op 3 allocs/op +BenchmarkBeego_ParseStatic 1000000 1031 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseStatic 1782482 675 ns/op 144 B/op 3 allocs/op +BenchmarkChi_ParseStatic 1453261 819 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseStatic 45023595 26.5 ns/op 0 B/op 0 allocs/op +BenchmarkEcho_ParseStatic 17330470 69.3 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseStatic 1644006 731 ns/op 296 B/op 5 allocs/op +BenchmarkGoji_ParseStatic 7026930 170 ns/op 0 B/op 0 allocs/op +BenchmarkGojiv2_ParseStatic 517618 2037 ns/op 1312 B/op 10 allocs/op +BenchmarkGoJsonRest_ParseStatic 1227080 975 ns/op 329 B/op 11 allocs/op +BenchmarkGoRestful_ParseStatic 192458 6659 ns/op 4256 B/op 13 allocs/op +BenchmarkGorillaMux_ParseStatic 744062 2109 ns/op 976 B/op 9 allocs/op +BenchmarkGowwwRouter_ParseStatic 37781062 31.8 ns/op 0 B/op 0 allocs/op +BenchmarkHttpRouter_ParseStatic 45311223 26.5 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseStatic 21383475 56.1 ns/op 0 B/op 0 allocs/op +BenchmarkKocha_ParseStatic 29953290 40.1 ns/op 0 B/op 0 allocs/op +BenchmarkLARS_ParseStatic 20036196 62.7 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseStatic 1000000 1740 ns/op 736 B/op 8 allocs/op +BenchmarkMartini_ParseStatic 404156 3801 ns/op 768 B/op 9 allocs/op +BenchmarkPat_ParseStatic 1547180 772 ns/op 240 B/op 5 allocs/op +BenchmarkPossum_ParseStatic 1608991 757 ns/op 416 B/op 3 allocs/op +BenchmarkR2router_ParseStatic 3177936 385 ns/op 144 B/op 4 allocs/op +BenchmarkRivet_ParseStatic 17783205 67.4 ns/op 0 B/op 0 allocs/op +BenchmarkTango_ParseStatic 1210777 990 ns/op 248 B/op 8 allocs/op +BenchmarkTigerTonic_ParseStatic 5316440 231 ns/op 48 B/op 1 allocs/op +BenchmarkTraffic_ParseStatic 496050 2539 ns/op 1256 B/op 19 allocs/op +BenchmarkVulcan_ParseStatic 2462798 488 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseParam 13393669 89.6 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseParam 19836619 60.4 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseParam 1405954 864 ns/op 467 B/op 5 allocs/op +BenchmarkBeego_ParseParam 1000000 1065 ns/op 352 B/op 3 allocs/op +BenchmarkBone_ParseParam 1000000 1698 ns/op 896 B/op 7 allocs/op +BenchmarkChi_ParseParam 1356037 873 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_ParseParam 6241392 204 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_ParseParam 14088100 85.1 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseParam 17426064 68.9 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseParam 1000000 1254 ns/op 664 B/op 8 allocs/op +BenchmarkGoji_ParseParam 1682574 713 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_ParseParam 502224 2333 ns/op 1360 B/op 12 allocs/op +BenchmarkGoJsonRest_ParseParam 1000000 1401 ns/op 649 B/op 13 allocs/op +BenchmarkGoRestful_ParseParam 182623 7097 ns/op 4576 B/op 14 allocs/op +BenchmarkGorillaMux_ParseParam 482332 2477 ns/op 1280 B/op 10 allocs/op +BenchmarkGowwwRouter_ParseParam 1834873 657 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_ParseParam 23593393 51.0 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseParam 2100160 574 ns/op 352 B/op 3 allocs/op +BenchmarkKocha_ParseParam 4837220 252 ns/op 56 B/op 3 allocs/op +BenchmarkLARS_ParseParam 18411192 66.2 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseParam 571870 2398 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_ParseParam 286262 4268 ns/op 1072 B/op 10 allocs/op +BenchmarkPat_ParseParam 692906 2157 ns/op 992 B/op 15 allocs/op +BenchmarkPossum_ParseParam 1000000 1011 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_ParseParam 1722735 697 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_ParseParam 6058054 203 ns/op 48 B/op 1 allocs/op +BenchmarkTango_ParseParam 1000000 1061 ns/op 280 B/op 8 allocs/op +BenchmarkTigerTonic_ParseParam 890275 2277 ns/op 784 B/op 15 allocs/op +BenchmarkTraffic_ParseParam 351322 3543 ns/op 1896 B/op 21 allocs/op +BenchmarkVulcan_ParseParam 2076544 572 ns/op 98 B/op 3 allocs/op +BenchmarkAce_Parse2Params 11718074 101 ns/op 0 B/op 0 allocs/op +BenchmarkAero_Parse2Params 16264988 73.4 ns/op 0 B/op 0 allocs/op +BenchmarkBear_Parse2Params 1238322 973 ns/op 496 B/op 5 allocs/op +BenchmarkBeego_Parse2Params 1000000 1120 ns/op 352 B/op 3 allocs/op +BenchmarkBone_Parse2Params 1000000 1632 ns/op 848 B/op 6 allocs/op +BenchmarkChi_Parse2Params 1239477 955 ns/op 432 B/op 3 allocs/op +BenchmarkDenco_Parse2Params 4944133 245 ns/op 64 B/op 1 allocs/op +BenchmarkEcho_Parse2Params 10518286 114 ns/op 0 B/op 0 allocs/op +BenchmarkGin_Parse2Params 14505195 82.7 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_Parse2Params 1000000 1437 ns/op 712 B/op 9 allocs/op +BenchmarkGoji_Parse2Params 1689883 707 ns/op 336 B/op 2 allocs/op +BenchmarkGojiv2_Parse2Params 502334 2308 ns/op 1344 B/op 11 allocs/op +BenchmarkGoJsonRest_Parse2Params 1000000 1771 ns/op 713 B/op 14 allocs/op +BenchmarkGoRestful_Parse2Params 159092 7583 ns/op 4928 B/op 14 allocs/op +BenchmarkGorillaMux_Parse2Params 417548 2980 ns/op 1296 B/op 10 allocs/op +BenchmarkGowwwRouter_Parse2Params 1751737 686 ns/op 432 B/op 3 allocs/op +BenchmarkHttpRouter_Parse2Params 18089204 66.3 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_Parse2Params 1556986 777 ns/op 384 B/op 4 allocs/op +BenchmarkKocha_Parse2Params 2493082 485 ns/op 128 B/op 5 allocs/op +BenchmarkLARS_Parse2Params 15350108 78.5 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_Parse2Params 530974 2605 ns/op 1072 B/op 10 allocs/op +BenchmarkMartini_Parse2Params 247069 4673 ns/op 1152 B/op 11 allocs/op +BenchmarkPat_Parse2Params 816295 2126 ns/op 752 B/op 16 allocs/op +BenchmarkPossum_Parse2Params 1000000 1002 ns/op 496 B/op 5 allocs/op +BenchmarkR2router_Parse2Params 1569771 733 ns/op 432 B/op 5 allocs/op +BenchmarkRivet_Parse2Params 4080546 295 ns/op 96 B/op 1 allocs/op +BenchmarkTango_Parse2Params 1000000 1121 ns/op 312 B/op 8 allocs/op +BenchmarkTigerTonic_Parse2Params 399556 3470 ns/op 1168 B/op 22 allocs/op +BenchmarkTraffic_Parse2Params 314194 4159 ns/op 1944 B/op 22 allocs/op +BenchmarkVulcan_Parse2Params 1827559 664 ns/op 98 B/op 3 allocs/op +BenchmarkAce_ParseAll 478395 2503 ns/op 0 B/op 0 allocs/op +BenchmarkAero_ParseAll 715392 1658 ns/op 0 B/op 0 allocs/op +BenchmarkBear_ParseAll 59191 20124 ns/op 8928 B/op 110 allocs/op +BenchmarkBeego_ParseAll 45507 27266 ns/op 9152 B/op 78 allocs/op +BenchmarkBone_ParseAll 29328 41459 ns/op 16208 B/op 147 allocs/op +BenchmarkChi_ParseAll 48531 25053 ns/op 11232 B/op 78 allocs/op +BenchmarkDenco_ParseAll 325532 4284 ns/op 928 B/op 16 allocs/op +BenchmarkEcho_ParseAll 433771 2759 ns/op 0 B/op 0 allocs/op +BenchmarkGin_ParseAll 576316 2082 ns/op 0 B/op 0 allocs/op +BenchmarkGocraftWeb_ParseAll 41500 29692 ns/op 13728 B/op 181 allocs/op +BenchmarkGoji_ParseAll 80833 15563 ns/op 5376 B/op 32 allocs/op +BenchmarkGojiv2_ParseAll 19836 60335 ns/op 34448 B/op 277 allocs/op +BenchmarkGoJsonRest_ParseAll 32210 38027 ns/op 13866 B/op 321 allocs/op +BenchmarkGoRestful_ParseAll 6644 190842 ns/op 117600 B/op 354 allocs/op +BenchmarkGorillaMux_ParseAll 12634 95894 ns/op 30288 B/op 250 allocs/op +BenchmarkGowwwRouter_ParseAll 98152 12159 ns/op 6912 B/op 48 allocs/op +BenchmarkHttpRouter_ParseAll 933208 1273 ns/op 0 B/op 0 allocs/op +BenchmarkHttpTreeMux_ParseAll 107191 11554 ns/op 5728 B/op 51 allocs/op +BenchmarkKocha_ParseAll 184862 6225 ns/op 1112 B/op 54 allocs/op +BenchmarkLARS_ParseAll 644546 1858 ns/op 0 B/op 0 allocs/op +BenchmarkMacaron_ParseAll 26145 46484 ns/op 19136 B/op 208 allocs/op +BenchmarkMartini_ParseAll 10000 121838 ns/op 25072 B/op 253 allocs/op +BenchmarkPat_ParseAll 25417 47196 ns/op 15216 B/op 308 allocs/op +BenchmarkPossum_ParseAll 58550 20735 ns/op 10816 B/op 78 allocs/op +BenchmarkR2router_ParseAll 72732 16584 ns/op 8352 B/op 120 allocs/op +BenchmarkRivet_ParseAll 281365 4968 ns/op 912 B/op 16 allocs/op +BenchmarkTango_ParseAll 42831 28668 ns/op 7168 B/op 208 allocs/op +BenchmarkTigerTonic_ParseAll 23774 49972 ns/op 16048 B/op 332 allocs/op +BenchmarkTraffic_ParseAll 10000 104679 ns/op 45520 B/op 605 allocs/op +BenchmarkVulcan_ParseAll 64810 18108 ns/op 2548 B/op 78 allocs/op +``` diff --git a/vendor/github.com/gin-gonic/gin/CHANGELOG.md b/vendor/github.com/gin-gonic/gin/CHANGELOG.md new file mode 100644 index 0000000..dc2c2f5 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/CHANGELOG.md @@ -0,0 +1,412 @@ +# Gin ChangeLog + +## Gin v1.7.1 + +### BUGFIXES + +* fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675)) + +## Gin v1.7.0 + +### BUGFIXES + +* fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600)) +* fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528)) +* fix(tree): reassign fullpath when register new node ([#2366](https://github.com/gin-gonic/gin/pull/2366)) + +### ENHANCEMENTS + +* Support params and exact routes without creating conflicts ([#2663](https://github.com/gin-gonic/gin/pull/2663)) +* chore: improve render string performance ([#2365](https://github.com/gin-gonic/gin/pull/2365)) +* Sync route tree to httprouter latest code ([#2368](https://github.com/gin-gonic/gin/pull/2368)) +* chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa ([#2375](https://github.com/gin-gonic/gin/pull/2375)) +* chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378)) +* Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387)) +* update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321)) +* remove a unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391)) +* Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389)) +* Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322)) +* binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450)) +* Prevent panic in Context.GetQuery() when there is no Request ([#2412](https://github.com/gin-gonic/gin/pull/2412)) +* Add GetUint and GetUint64 method on gin.context ([#2487](https://github.com/gin-gonic/gin/pull/2487)) +* update content-disposition header to MIME-style ([#2512](https://github.com/gin-gonic/gin/pull/2512)) +* reduce allocs and improve the render `WriteString` ([#2508](https://github.com/gin-gonic/gin/pull/2508)) +* implement ".Unwrap() error" on Error type ([#2525](https://github.com/gin-gonic/gin/pull/2525)) ([#2526](https://github.com/gin-gonic/gin/pull/2526)) +* Allow bind with a map[string]string ([#2484](https://github.com/gin-gonic/gin/pull/2484)) +* chore: update tree ([#2371](https://github.com/gin-gonic/gin/pull/2371)) +* Support binding for slice/array obj [Rewrite] ([#2302](https://github.com/gin-gonic/gin/pull/2302)) +* basic auth: fix timing oracle ([#2609](https://github.com/gin-gonic/gin/pull/2609)) +* Add mixed param and non-param paths (port of httprouter[#329](https://github.com/gin-gonic/gin/pull/329)) ([#2663](https://github.com/gin-gonic/gin/pull/2663)) +* feat(engine): add trustedproxies and remoteIP ([#2632](https://github.com/gin-gonic/gin/pull/2632)) + +## Gin v1.6.3 + +### ENHANCEMENTS + + * Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351) + +## Gin v1.6.2 + +### BUGFIXES + * fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305) +### ENHANCEMENTS + * Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306) + +## Gin v1.6.1 + +### BUGFIXES + * Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294) + +## Gin v1.6.0 + +### BREAKING + * chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159) + * drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148) + * Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615) +### FEATURES + * add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220) + * FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112) +### BUGFIXES + * Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280) + * Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228) + * fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216) + * Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166) + * [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121) + * Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391) +### ENHANCEMENTS + * Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277) + * tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229) + * tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222) + * chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215) + * path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212) + * Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206) + * Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179) + * tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177) + * tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171) + * tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163) + * use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155) + * upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149) + * Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970) + * Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852) +### DOCS + * docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223) + * Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217) + * Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202) + * Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198) + * Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196) + * Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190) + * upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189) + * Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188) + * Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186) + * Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165) + * docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153) + * Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122) +### MISC + * ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262) + * chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231) + * Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147) + * fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129) + +## Gin v1.5.0 + +- [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891) +- [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893) +- [FIX] Some code improvements [#1909](https://github.com/gin-gonic/gin/pull/1909) +- [FIX] Use encode replace json marshal increase json encoder speed [#1546](https://github.com/gin-gonic/gin/pull/1546) +- [NEW] Hold matched route full path in the Context [#1826](https://github.com/gin-gonic/gin/pull/1826) +- [FIX] Fix context.Params race condition on Copy() [#1841](https://github.com/gin-gonic/gin/pull/1841) +- [NEW] Add context param query cache [#1450](https://github.com/gin-gonic/gin/pull/1450) +- [FIX] Improve GetQueryMap performance [#1918](https://github.com/gin-gonic/gin/pull/1918) +- [FIX] Improve get post data [#1920](https://github.com/gin-gonic/gin/pull/1920) +- [FIX] Use context instead of x/net/context [#1922](https://github.com/gin-gonic/gin/pull/1922) +- [FIX] Attempt to fix PostForm cache bug [#1931](https://github.com/gin-gonic/gin/pull/1931) +- [NEW] Add support of multipart multi files [#1949](https://github.com/gin-gonic/gin/pull/1949) +- [NEW] Support bind http header param [#1957](https://github.com/gin-gonic/gin/pull/1957) +- [FIX] Drop support for go1.8 and go1.9 [#1933](https://github.com/gin-gonic/gin/pull/1933) +- [FIX] Bugfix for the FullPath feature [#1919](https://github.com/gin-gonic/gin/pull/1919) +- [FIX] Gin1.5 bytes.Buffer to strings.Builder [#1939](https://github.com/gin-gonic/gin/pull/1939) +- [FIX] Upgrade github.com/ugorji/go/codec [#1969](https://github.com/gin-gonic/gin/pull/1969) +- [NEW] Support bind unix time [#1980](https://github.com/gin-gonic/gin/pull/1980) +- [FIX] Simplify code [#2004](https://github.com/gin-gonic/gin/pull/2004) +- [NEW] Support negative Content-Length in DataFromReader [#1981](https://github.com/gin-gonic/gin/pull/1981) +- [FIX] Identify terminal on a RISC-V architecture for auto-colored logs [#2019](https://github.com/gin-gonic/gin/pull/2019) +- [BREAKING] `Context.JSONP()` now expects a semicolon (`;`) at the end [#2007](https://github.com/gin-gonic/gin/pull/2007) +- [BREAKING] Upgrade default `binding.Validator` to v9 (see [its changelog](https://github.com/go-playground/validator/releases/tag/v9.0.0)) [#1015](https://github.com/gin-gonic/gin/pull/1015) +- [NEW] Add `DisallowUnknownFields()` in `Context.BindJSON()` [#2028](https://github.com/gin-gonic/gin/pull/2028) +- [NEW] Use specific `net.Listener` with `Engine.RunListener()` [#2023](https://github.com/gin-gonic/gin/pull/2023) +- [FIX] Fix some typo [#2079](https://github.com/gin-gonic/gin/pull/2079) [#2080](https://github.com/gin-gonic/gin/pull/2080) +- [FIX] Relocate binding body tests [#2086](https://github.com/gin-gonic/gin/pull/2086) +- [FIX] Use Writer in Context.Status [#1606](https://github.com/gin-gonic/gin/pull/1606) +- [FIX] `Engine.RunUnix()` now returns the error if it can't change the file mode [#2093](https://github.com/gin-gonic/gin/pull/2093) +- [FIX] `RouterGroup.StaticFS()` leaked files. Now it closes them. [#2118](https://github.com/gin-gonic/gin/pull/2118) +- [FIX] `Context.Request.FormFile` leaked file. Now it closes it. [#2114](https://github.com/gin-gonic/gin/pull/2114) +- [FIX] Ignore walking on `form:"-"` mapping [#1943](https://github.com/gin-gonic/gin/pull/1943) + +### Gin v1.4.0 + +- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569) +- [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829) +- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830) +- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802) +- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264) +- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797) +- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789) +- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804) +- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779) +- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791) +- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794) +- [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749) +- [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252) +- [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775) +- [NEW] Extend context.File to allow for the content-disposition attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260) +- [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112) +- [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238) +- [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020) +- [NEW] Add context.HandlerNames() [#1729](https://github.com/gin-gonic/gin/pull/1729) +- [FIX] Change color methods to public in the defaultLogger. [#1771](https://github.com/gin-gonic/gin/pull/1771) +- [FIX] Update writeHeaders method to use http.Header.Set [#1722](https://github.com/gin-gonic/gin/pull/1722) +- [NEW] Add response size to LogFormatterParams [#1752](https://github.com/gin-gonic/gin/pull/1752) +- [NEW] Allow ignoring field on form mapping [#1733](https://github.com/gin-gonic/gin/pull/1733) +- [NEW] Add a function to force color in console output. [#1724](https://github.com/gin-gonic/gin/pull/1724) +- [FIX] Context.Next() - recheck len of handlers on every iteration. [#1745](https://github.com/gin-gonic/gin/pull/1745) +- [FIX] Fix all errcheck warnings [#1739](https://github.com/gin-gonic/gin/pull/1739) [#1653](https://github.com/gin-gonic/gin/pull/1653) +- [NEW] context: inherits context cancellation and deadline from http.Request context for Go>=1.7 [#1690](https://github.com/gin-gonic/gin/pull/1690) +- [NEW] Binding for URL Params [#1694](https://github.com/gin-gonic/gin/pull/1694) +- [NEW] Add LoggerWithFormatter method [#1677](https://github.com/gin-gonic/gin/pull/1677) +- [FIX] CI testing updates [#1671](https://github.com/gin-gonic/gin/pull/1671) [#1670](https://github.com/gin-gonic/gin/pull/1670) [#1682](https://github.com/gin-gonic/gin/pull/1682) [#1669](https://github.com/gin-gonic/gin/pull/1669) +- [FIX] StaticFS(): Send 404 when path does not exist [#1663](https://github.com/gin-gonic/gin/pull/1663) +- [FIX] Handle nil body for JSON binding [#1638](https://github.com/gin-gonic/gin/pull/1638) +- [FIX] Support bind uri param [#1612](https://github.com/gin-gonic/gin/pull/1612) +- [FIX] recovery: fix issue with syscall import on google app engine [#1640](https://github.com/gin-gonic/gin/pull/1640) +- [FIX] Make sure the debug log contains line breaks [#1650](https://github.com/gin-gonic/gin/pull/1650) +- [FIX] Panic stack trace being printed during recovery of broken pipe [#1089](https://github.com/gin-gonic/gin/pull/1089) [#1259](https://github.com/gin-gonic/gin/pull/1259) +- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609) +- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618) +- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600) +- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559) +- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565) +- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571) +- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570) +- [FIX] Remove sensitive request information from panic log. [#1370](https://github.com/gin-gonic/gin/pull/1370) +- [FIX] log.Println() does not print timestamp [#829](https://github.com/gin-gonic/gin/pull/829) [#1560](https://github.com/gin-gonic/gin/pull/1560) +- [NEW] Add PureJSON renderer [#694](https://github.com/gin-gonic/gin/pull/694) +- [FIX] Add missing copyright and update if/else [#1497](https://github.com/gin-gonic/gin/pull/1497) +- [FIX] Update msgpack usage [#1498](https://github.com/gin-gonic/gin/pull/1498) +- [FIX] Use protobuf on render [#1496](https://github.com/gin-gonic/gin/pull/1496) +- [FIX] Add support for Protobuf format response [#1479](https://github.com/gin-gonic/gin/pull/1479) +- [NEW] Set default time format in form binding [#1487](https://github.com/gin-gonic/gin/pull/1487) +- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485) +- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491) + + +## Gin v1.3.0 + +- [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383) +- [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358) +- [NEW] Add `Pusher()` in [`type ResponseWriter`](https://godoc.org/github.com/gin-gonic/gin#ResponseWriter) for supporting http2 push, see [#1273](https://github.com/gin-gonic/gin/pull/1273) +- [NEW] Add [`func (*Context) DataFromReader`](https://godoc.org/github.com/gin-gonic/gin#Context.DataFromReader) for serving dynamic data, see [#1304](https://github.com/gin-gonic/gin/pull/1304) +- [NEW] Add [`func (*Context) ShouldBindBodyWith`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindBodyWith) allowing to call binding multiple times, see [#1341](https://github.com/gin-gonic/gin/pull/1341) +- [NEW] Support pointers in form binding, see [#1336](https://github.com/gin-gonic/gin/pull/1336) +- [NEW] Add [`func (*Context) JSONP`](https://godoc.org/github.com/gin-gonic/gin#Context.JSONP), see [#1333](https://github.com/gin-gonic/gin/pull/1333) +- [NEW] Support default value in form binding, see [#1138](https://github.com/gin-gonic/gin/pull/1138) +- [NEW] Expose validator engine in [`type StructValidator`](https://godoc.org/github.com/gin-gonic/gin/binding#StructValidator), see [#1277](https://github.com/gin-gonic/gin/pull/1277) +- [NEW] Add [`func (*Context) ShouldBind`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBind), [`func (*Context) ShouldBindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindQuery) and [`func (*Context) ShouldBindJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindJSON), see [#1047](https://github.com/gin-gonic/gin/pull/1047) +- [NEW] Add support for `time.Time` location in form binding, see [#1117](https://github.com/gin-gonic/gin/pull/1117) +- [NEW] Add [`func (*Context) BindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.BindQuery), see [#1029](https://github.com/gin-gonic/gin/pull/1029) +- [NEW] Make [jsonite](https://github.com/json-iterator/go) optional with build tags, see [#1026](https://github.com/gin-gonic/gin/pull/1026) +- [NEW] Show query string in logger, see [#999](https://github.com/gin-gonic/gin/pull/999) +- [NEW] Add [`func (*Context) SecureJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.SecureJSON), see [#987](https://github.com/gin-gonic/gin/pull/987) and [#993](https://github.com/gin-gonic/gin/pull/993) +- [DEPRECATE] `func (*Context) GetCookie` for [`func (*Context) Cookie`](https://godoc.org/github.com/gin-gonic/gin#Context.Cookie) +- [FIX] Don't display color tags if [`func DisableConsoleColor`](https://godoc.org/github.com/gin-gonic/gin#DisableConsoleColor) called, see [#1072](https://github.com/gin-gonic/gin/pull/1072) +- [FIX] Gin Mode `""` when calling [`func Mode`](https://godoc.org/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250) +- [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460) + +## Gin 1.2.0 + +- [NEW] Switch from godeps to govendor +- [NEW] Add support for Let's Encrypt via gin-gonic/autotls +- [NEW] Improve README examples and add extra at examples folder +- [NEW] Improved support with App Engine +- [NEW] Add custom template delimiters, see #860 +- [NEW] Add Template Func Maps, see #962 +- [NEW] Add \*context.Handler(), see #928 +- [NEW] Add \*context.GetRawData() +- [NEW] Add \*context.GetHeader() (request) +- [NEW] Add \*context.AbortWithStatusJSON() (JSON content type) +- [NEW] Add \*context.Keys type cast helpers +- [NEW] Add \*context.ShouldBindWith() +- [NEW] Add \*context.MustBindWith() +- [NEW] Add \*engine.SetFuncMap() +- [DEPRECATE] On next release: \*context.BindWith(), see #855 +- [FIX] Refactor render +- [FIX] Reworked tests +- [FIX] logger now supports cygwin +- [FIX] Use X-Forwarded-For before X-Real-Ip +- [FIX] time.Time binding (#904) + +## Gin 1.1.4 + +- [NEW] Support google appengine for IsTerminal func + +## Gin 1.1.3 + +- [FIX] Reverted Logger: skip ANSI color commands + +## Gin 1.1 + +- [NEW] Implement QueryArray and PostArray methods +- [NEW] Refactor GetQuery and GetPostForm +- [NEW] Add contribution guide +- [FIX] Corrected typos in README +- [FIX] Removed additional Iota +- [FIX] Changed imports to gopkg instead of github in README (#733) +- [FIX] Logger: skip ANSI color commands if output is not a tty + +## Gin 1.0rc2 (...) + +- [PERFORMANCE] Fast path for writing Content-Type. +- [PERFORMANCE] Much faster 404 routing +- [PERFORMANCE] Allocation optimizations +- [PERFORMANCE] Faster root tree lookup +- [PERFORMANCE] Zero overhead, String() and JSON() rendering. +- [PERFORMANCE] Faster ClientIP parsing +- [PERFORMANCE] Much faster SSE implementation +- [NEW] Benchmarks suite +- [NEW] Bind validation can be disabled and replaced with custom validators. +- [NEW] More flexible HTML render +- [NEW] Multipart and PostForm bindings +- [NEW] Adds method to return all the registered routes +- [NEW] Context.HandlerName() returns the main handler's name +- [NEW] Adds Error.IsType() helper +- [FIX] Binding multipart form +- [FIX] Integration tests +- [FIX] Crash when binding non struct object in Context. +- [FIX] RunTLS() implementation +- [FIX] Logger() unit tests +- [FIX] Adds SetHTMLTemplate() warning +- [FIX] Context.IsAborted() +- [FIX] More unit tests +- [FIX] JSON, XML, HTML renders accept custom content-types +- [FIX] gin.AbortIndex is unexported +- [FIX] Better approach to avoid directory listing in StaticFS() +- [FIX] Context.ClientIP() always returns the IP with trimmed spaces. +- [FIX] Better warning when running in debug mode. +- [FIX] Google App Engine integration. debugPrint does not use os.Stdout +- [FIX] Fixes integer overflow in error type +- [FIX] Error implements the json.Marshaller interface +- [FIX] MIT license in every file + + +## Gin 1.0rc1 (May 22, 2015) + +- [PERFORMANCE] Zero allocation router +- [PERFORMANCE] Faster JSON, XML and text rendering +- [PERFORMANCE] Custom hand optimized HttpRouter for Gin +- [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations +- [NEW] Built-in support for golang.org/x/net/context +- [NEW] Any(path, handler). Create a route that matches any path +- [NEW] Refactored rendering pipeline (faster and static typed) +- [NEW] Refactored errors API +- [NEW] IndentedJSON() prints pretty JSON +- [NEW] Added gin.DefaultWriter +- [NEW] UNIX socket support +- [NEW] RouterGroup.BasePath is exposed +- [NEW] JSON validation using go-validate-yourself (very powerful options) +- [NEW] Completed suite of unit tests +- [NEW] HTTP streaming with c.Stream() +- [NEW] StaticFile() creates a router for serving just one file. +- [NEW] StaticFS() has an option to disable directory listing. +- [NEW] StaticFS() for serving static files through virtual filesystems +- [NEW] Server-Sent Events native support +- [NEW] WrapF() and WrapH() helpers for wrapping http.HandlerFunc and http.Handler +- [NEW] Added LoggerWithWriter() middleware +- [NEW] Added RecoveryWithWriter() middleware +- [NEW] Added DefaultPostFormValue() +- [NEW] Added DefaultFormValue() +- [NEW] Added DefaultParamValue() +- [FIX] BasicAuth() when using custom realm +- [FIX] Bug when serving static files in nested routing group +- [FIX] Redirect using built-in http.Redirect() +- [FIX] Logger when printing the requested path +- [FIX] Documentation typos +- [FIX] Context.Engine renamed to Context.engine +- [FIX] Better debugging messages +- [FIX] ErrorLogger +- [FIX] Debug HTTP render +- [FIX] Refactored binding and render modules +- [FIX] Refactored Context initialization +- [FIX] Refactored BasicAuth() +- [FIX] NoMethod/NoRoute handlers +- [FIX] Hijacking http +- [FIX] Better support for Google App Engine (using log instead of fmt) + + +## Gin 0.6 (Mar 9, 2015) + +- [NEW] Support multipart/form-data +- [NEW] NoMethod handler +- [NEW] Validate sub structures +- [NEW] Support for HTTP Realm Auth +- [FIX] Unsigned integers in binding +- [FIX] Improve color logger + + +## Gin 0.5 (Feb 7, 2015) + +- [NEW] Content Negotiation +- [FIX] Solved security bug that allow a client to spoof ip +- [FIX] Fix unexported/ignored fields in binding + + +## Gin 0.4 (Aug 21, 2014) + +- [NEW] Development mode +- [NEW] Unit tests +- [NEW] Add Content.Redirect() +- [FIX] Deferring WriteHeader() +- [FIX] Improved documentation for model binding + + +## Gin 0.3 (Jul 18, 2014) + +- [PERFORMANCE] Normal log and error log are printed in the same call. +- [PERFORMANCE] Improve performance of NoRouter() +- [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults. +- [NEW] Flexible rendering API +- [NEW] Add Context.File() +- [NEW] Add shortcut RunTLS() for http.ListenAndServeTLS +- [FIX] Rename NotFound404() to NoRoute() +- [FIX] Errors in context are purged +- [FIX] Adds HEAD method in Static file serving +- [FIX] Refactors Static() file serving +- [FIX] Using keyed initialization to fix app-engine integration +- [FIX] Can't unmarshal JSON array, #63 +- [FIX] Renaming Context.Req to Context.Request +- [FIX] Check application/x-www-form-urlencoded when parsing form + + +## Gin 0.2b (Jul 08, 2014) +- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead +- [NEW] Travis CI integration +- [NEW] Completely new logger +- [NEW] New API for serving static files. gin.Static() +- [NEW] gin.H() can be serialized into XML +- [NEW] Typed errors. Errors can be typed. Internet/external/custom. +- [NEW] Support for Godeps +- [NEW] Travis/Godocs badges in README +- [NEW] New Bind() and BindWith() methods for parsing request body. +- [NEW] Add Content.Copy() +- [NEW] Add context.LastError() +- [NEW] Add shortcut for OPTIONS HTTP method +- [FIX] Tons of README fixes +- [FIX] Header is written before body +- [FIX] BasicAuth() and changes API a little bit +- [FIX] Recovery() middleware only prints panics +- [FIX] Context.Get() does not panic anymore. Use MustGet() instead. +- [FIX] Multiple http.WriteHeader() in NotFound handlers +- [FIX] Engine.Run() panics if http server can't be set up +- [FIX] Crash when route path doesn't start with '/' +- [FIX] Do not update header when status code is negative +- [FIX] Setting response headers before calling WriteHeader in context.String() +- [FIX] Add MIT license +- [FIX] Changes behaviour of ErrorLogger() and Logger() diff --git a/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md b/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4ea14f3 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md b/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md new file mode 100644 index 0000000..97daa80 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md @@ -0,0 +1,13 @@ +## Contributing + +- With issues: + - Use the search tool before opening a new issue. + - Please provide source code and commit sha if you found a bug. + - Review existing issues and provide feedback or react to them. + +- With pull requests: + - Open your pull request against `master` + - Your pull request should have no more than two commits, if not you should squash them. + - It should pass all tests in the available continuous integration systems such as TravisCI. + - You should add/modify tests to cover your proposed code changes. + - If your pull request contains a new feature, please document it on the README. diff --git a/vendor/github.com/gin-gonic/gin/LICENSE b/vendor/github.com/gin-gonic/gin/LICENSE new file mode 100644 index 0000000..1ff7f37 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Manuel Martínez-Almeida + +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. diff --git a/vendor/github.com/gin-gonic/gin/Makefile b/vendor/github.com/gin-gonic/gin/Makefile new file mode 100644 index 0000000..1a99193 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/Makefile @@ -0,0 +1,71 @@ +GO ?= go +GOFMT ?= gofmt "-s" +PACKAGES ?= $(shell $(GO) list ./...) +VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/) +GOFILES := $(shell find . -name "*.go") +TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples) +TESTTAGS ?= "" + +.PHONY: test +test: + echo "mode: count" > coverage.out + for d in $(TESTFOLDER); do \ + $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ + cat tmp.out; \ + if grep -q "^--- FAIL" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + elif grep -q "build failed" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + elif grep -q "setup failed" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + fi; \ + if [ -f profile.out ]; then \ + cat profile.out | grep -v "mode:" >> coverage.out; \ + rm profile.out; \ + fi; \ + done + +.PHONY: fmt +fmt: + $(GOFMT) -w $(GOFILES) + +.PHONY: fmt-check +fmt-check: + @diff=$$($(GOFMT) -d $(GOFILES)); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make fmt' and commit the result:"; \ + echo "$${diff}"; \ + exit 1; \ + fi; + +vet: + $(GO) vet $(VETPACKAGES) + +.PHONY: lint +lint: + @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ + $(GO) get -u golang.org/x/lint/golint; \ + fi + for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; + +.PHONY: misspell-check +misspell-check: + @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ + $(GO) get -u github.com/client9/misspell/cmd/misspell; \ + fi + misspell -error $(GOFILES) + +.PHONY: misspell +misspell: + @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ + $(GO) get -u github.com/client9/misspell/cmd/misspell; \ + fi + misspell -w $(GOFILES) + +.PHONY: tools +tools: + go install golang.org/x/lint/golint; \ + go install github.com/client9/misspell/cmd/misspell; diff --git a/vendor/github.com/gin-gonic/gin/README.md b/vendor/github.com/gin-gonic/gin/README.md new file mode 100644 index 0000000..d4772d7 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/README.md @@ -0,0 +1,2217 @@ +# Gin Web Framework + + + +[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) +[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) +[![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) +[![GoDoc](https://pkg.go.dev/badge/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) +[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) +[![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) +[![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) +[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gin-gonic/gin)](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin) + +Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. + + +## Contents + +- [Gin Web Framework](#gin-web-framework) + - [Contents](#contents) + - [Installation](#installation) + - [Quick start](#quick-start) + - [Benchmarks](#benchmarks) + - [Gin v1. stable](#gin-v1-stable) + - [Build with jsoniter](#build-with-jsoniter) + - [API Examples](#api-examples) + - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) + - [Parameters in path](#parameters-in-path) + - [Querystring parameters](#querystring-parameters) + - [Multipart/Urlencoded Form](#multiparturlencoded-form) + - [Another example: query + post form](#another-example-query--post-form) + - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) + - [Upload files](#upload-files) + - [Single file](#single-file) + - [Multiple files](#multiple-files) + - [Grouping routes](#grouping-routes) + - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) + - [Using middleware](#using-middleware) + - [How to write log file](#how-to-write-log-file) + - [Custom Log Format](#custom-log-format) + - [Controlling Log output coloring](#controlling-log-output-coloring) + - [Model binding and validation](#model-binding-and-validation) + - [Custom Validators](#custom-validators) + - [Only Bind Query String](#only-bind-query-string) + - [Bind Query String or Post Data](#bind-query-string-or-post-data) + - [Bind Uri](#bind-uri) + - [Bind Header](#bind-header) + - [Bind HTML checkboxes](#bind-html-checkboxes) + - [Multipart/Urlencoded binding](#multiparturlencoded-binding) + - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) + - [SecureJSON](#securejson) + - [JSONP](#jsonp) + - [AsciiJSON](#asciijson) + - [PureJSON](#purejson) + - [Serving static files](#serving-static-files) + - [Serving data from file](#serving-data-from-file) + - [Serving data from reader](#serving-data-from-reader) + - [HTML rendering](#html-rendering) + - [Custom Template renderer](#custom-template-renderer) + - [Custom Delimiters](#custom-delimiters) + - [Custom Template Funcs](#custom-template-funcs) + - [Multitemplate](#multitemplate) + - [Redirects](#redirects) + - [Custom Middleware](#custom-middleware) + - [Using BasicAuth() middleware](#using-basicauth-middleware) + - [Goroutines inside a middleware](#goroutines-inside-a-middleware) + - [Custom HTTP configuration](#custom-http-configuration) + - [Support Let's Encrypt](#support-lets-encrypt) + - [Run multiple service using Gin](#run-multiple-service-using-gin) + - [Graceful shutdown or restart](#graceful-shutdown-or-restart) + - [Third-party packages](#third-party-packages) + - [Manually](#manually) + - [Build a single binary with templates](#build-a-single-binary-with-templates) + - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) + - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) + - [http2 server push](#http2-server-push) + - [Define format for the log of routes](#define-format-for-the-log-of-routes) + - [Set and get a cookie](#set-and-get-a-cookie) + - [Testing](#testing) + - [Users](#users) + +## Installation + +To install Gin package, you need to install Go and set your Go workspace first. + +1. The first need [Go](https://golang.org/) installed (**version 1.12+ is required**), then you can use the below Go command to install Gin. + +```sh +$ go get -u github.com/gin-gonic/gin +``` + +2. Import it in your code: + +```go +import "github.com/gin-gonic/gin" +``` + +3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`. + +```go +import "net/http" +``` + +## Quick start + +```sh +# assume the following codes in example.go file +$ cat example.go +``` + +```go +package main + +import "github.com/gin-gonic/gin" + +func main() { + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) + }) + r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") +} +``` + +``` +# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser +$ go run example.go +``` + +## Benchmarks + +Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) + +[See all benchmarks](/BENCHMARKS.md) + +| Benchmark name | (1) | (2) | (3) | (4) | +| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| +| BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** | +| BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op | +| BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op | +| BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op | +| BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op | +| BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op | +| BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op | +| BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op | +| BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op | +| BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op | +| BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op | +| BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op | +| BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op | +| BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op | +| BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op | +| BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op | +| BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op | +| BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op | +| BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op | +| BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op | +| BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op | +| BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op | +| BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op | +| BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op | +| BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op | +| BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op | + +- (1): Total Repetitions achieved in constant time, higher means more confident result +- (2): Single Repetition Duration (ns/op), lower is better +- (3): Heap Memory (B/op), lower is better +- (4): Average Allocations per Repetition (allocs/op), lower is better + +## Gin v1. stable + +- [x] Zero allocation router. +- [x] Still the fastest http router and framework. From routing to writing. +- [x] Complete suite of unit tests. +- [x] Battle tested. +- [x] API frozen, new releases will not break your code. + +## Build with [jsoniter](https://github.com/json-iterator/go) + +Gin uses `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags. + +```sh +$ go build -tags=jsoniter . +``` + +## API Examples + +You can find a number of ready-to-run examples at [Gin examples repository](https://github.com/gin-gonic/examples). + +### Using GET, POST, PUT, PATCH, DELETE and OPTIONS + +```go +func main() { + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/someGet", getting) + router.POST("/somePost", posting) + router.PUT("/somePut", putting) + router.DELETE("/someDelete", deleting) + router.PATCH("/somePatch", patching) + router.HEAD("/someHead", head) + router.OPTIONS("/someOptions", options) + + // By default it serves on :8080 unless a + // PORT environment variable was defined. + router.Run() + // router.Run(":3000") for a hard coded port +} +``` + +### Parameters in path + +```go +func main() { + router := gin.Default() + + // This handler will match /user/john but will not match /user/ or /user + router.GET("/user/:name", func(c *gin.Context) { + name := c.Param("name") + c.String(http.StatusOK, "Hello %s", name) + }) + + // However, this one will match /user/john/ and also /user/john/send + // If no other routers match /user/john, it will redirect to /user/john/ + router.GET("/user/:name/*action", func(c *gin.Context) { + name := c.Param("name") + action := c.Param("action") + message := name + " is " + action + c.String(http.StatusOK, message) + }) + + // For each matched request Context will hold the route definition + router.POST("/user/:name/*action", func(c *gin.Context) { + c.FullPath() == "/user/:name/*action" // true + }) + + // This handler will add a new router for /user/groups. + // Exact routes are resolved before param routes, regardless of the order they were defined. + // Routes starting with /user/groups are never interpreted as /user/:name/... routes + router.GET("/user/groups", func(c *gin.Context) { + c.String(http.StatusOK, "The available groups are [...]", name) + }) + + router.Run(":8080") +} +``` + +### Querystring parameters + +```go +func main() { + router := gin.Default() + + // Query string parameters are parsed using the existing underlying request object. + // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") +} +``` + +### Multipart/Urlencoded Form + +```go +func main() { + router := gin.Default() + + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") + + c.JSON(200, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") +} +``` + +### Another example: query + post form + +``` +POST /post?id=1234&page=1 HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +name=manu&message=this_is_great +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") + + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + }) + router.Run(":8080") +} +``` + +``` +id: 1234; page: 1; name: manu; message: this_is_great +``` + +### Map as querystring or postform parameters + +``` +POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +names[first]=thinkerou&names[second]=tianou +``` + +```go +func main() { + router := gin.Default() + + router.POST("/post", func(c *gin.Context) { + + ids := c.QueryMap("ids") + names := c.PostFormMap("names") + + fmt.Printf("ids: %v; names: %v", ids, names) + }) + router.Run(":8080") +} +``` + +``` +ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou] +``` + +### Upload files + +#### Single file + +References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/single). + +`file.Filename` **SHOULD NOT** be trusted. See [`Content-Disposition` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) and [#1693](https://github.com/gin-gonic/gin/issues/1693) + +> The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. + +```go +func main() { + router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.POST("/upload", func(c *gin.Context) { + // single file + file, _ := c.FormFile("file") + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) + router.Run(":8080") +} +``` + +How to `curl`: + +```bash +curl -X POST http://localhost:8080/upload \ + -F "file=@/Users/appleboy/test.zip" \ + -H "Content-Type: multipart/form-data" +``` + +#### Multiple files + +See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). + +```go +func main() { + router := gin.Default() + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB + router.POST("/upload", func(c *gin.Context) { + // Multipart form + form, _ := c.MultipartForm() + files := form.File["upload[]"] + + for _, file := range files { + log.Println(file.Filename) + + // Upload the file to specific dst. + c.SaveUploadedFile(file, dst) + } + c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) + }) + router.Run(":8080") +} +``` + +How to `curl`: + +```bash +curl -X POST http://localhost:8080/upload \ + -F "upload[]=@/Users/appleboy/test1.zip" \ + -F "upload[]=@/Users/appleboy/test2.zip" \ + -H "Content-Type: multipart/form-data" +``` + +### Grouping routes + +```go +func main() { + router := gin.Default() + + // Simple group: v1 + v1 := router.Group("/v1") + { + v1.POST("/login", loginEndpoint) + v1.POST("/submit", submitEndpoint) + v1.POST("/read", readEndpoint) + } + + // Simple group: v2 + v2 := router.Group("/v2") + { + v2.POST("/login", loginEndpoint) + v2.POST("/submit", submitEndpoint) + v2.POST("/read", readEndpoint) + } + + router.Run(":8080") +} +``` + +### Blank Gin without middleware by default + +Use + +```go +r := gin.New() +``` + +instead of + +```go +// Default With the Logger and Recovery middleware already attached +r := gin.Default() +``` + + +### Using middleware +```go +func main() { + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.Recovery()) + + // Per route middleware, you can add as many as you desire. + r.GET("/benchmark", MyBenchLogger(), benchEndpoint) + + // Authorization group + // authorized := r.Group("/", AuthRequired()) + // exactly the same as: + authorized := r.Group("/") + // per group middleware! in this case we use the custom created + // AuthRequired() middleware just in the "authorized" group. + authorized.Use(AuthRequired()) + { + authorized.POST("/login", loginEndpoint) + authorized.POST("/submit", submitEndpoint) + authorized.POST("/read", readEndpoint) + + // nested group + testing := authorized.Group("testing") + testing.GET("/analytics", analyticsEndpoint) + } + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Custom Recovery behavior +```go +func main() { + // Creates a router without any middleware by default + r := gin.New() + + // Global middleware + // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. + // By default gin.DefaultWriter = os.Stdout + r.Use(gin.Logger()) + + // Recovery middleware recovers from any panics and writes a 500 if there was one. + r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { + if err, ok := recovered.(string); ok { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + c.AbortWithStatus(http.StatusInternalServerError) + })) + + r.GET("/panic", func(c *gin.Context) { + // panic with a string -- the custom middleware could save this to a database or report it to the user + panic("foo") + }) + + r.GET("/", func(c *gin.Context) { + c.String(http.StatusOK, "ohai") + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### How to write log file +```go +func main() { + // Disable Console Color, you don't need console color when writing the logs to file. + gin.DisableConsoleColor() + + // Logging to a file. + f, _ := os.Create("gin.log") + gin.DefaultWriter = io.MultiWriter(f) + + // Use the following code if you need to write the logs to file and console at the same time. + // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) + + router := gin.Default() + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + +    router.Run(":8080") +} +``` + +### Custom Log Format +```go +func main() { + router := gin.New() + + // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter + // By default gin.DefaultWriter = os.Stdout + router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { + + // your custom format + return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", + param.ClientIP, + param.TimeStamp.Format(time.RFC1123), + param.Method, + param.Path, + param.Request.Proto, + param.StatusCode, + param.Latency, + param.Request.UserAgent(), + param.ErrorMessage, + ) + })) + router.Use(gin.Recovery()) + + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + router.Run(":8080") +} +``` + +**Sample Output** +``` +::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " +``` + +### Controlling Log output coloring + +By default, logs output on console should be colorized depending on the detected TTY. + +Never colorize logs: + +```go +func main() { + // Disable log's color + gin.DisableConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + router.Run(":8080") +} +``` + +Always colorize logs: + +```go +func main() { + // Force log's color + gin.ForceConsoleColor() + + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + router.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + router.Run(":8080") +} +``` + +### Model binding and validation + +To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz). + +Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). + +Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. + +Also, Gin provides two sets of methods for binding: +- **Type** - Must bind + - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader` + - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. +- **Type** - Should bind + - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader` + - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. + +When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. + +You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, an error will be returned. + +```go +// Binding from JSON +type Login struct { + User string `form:"user" json:"user" xml:"user" binding:"required"` + Password string `form:"password" json:"password" xml:"password" binding:"required"` +} + +func main() { + router := gin.Default() + + // Example for binding JSON ({"user": "manu", "password": "123"}) + router.POST("/loginJSON", func(c *gin.Context) { + var json Login + if err := c.ShouldBindJSON(&json); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if json.User != "manu" || json.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding XML ( + // + // + // user + // 123 + // ) + router.POST("/loginXML", func(c *gin.Context) { + var xml Login + if err := c.ShouldBindXML(&xml); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if xml.User != "manu" || xml.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if err := c.ShouldBind(&form); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if form.User != "manu" || form.Password != "123" { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + }) + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +**Sample request** +```shell +$ curl -v -X POST \ + http://localhost:8080/loginJSON \ + -H 'content-type: application/json' \ + -d '{ "user": "manu" }' +> POST /loginJSON HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.51.0 +> Accept: */* +> content-type: application/json +> Content-Length: 18 +> +* upload completely sent off: 18 out of 18 bytes +< HTTP/1.1 400 Bad Request +< Content-Type: application/json; charset=utf-8 +< Date: Fri, 04 Aug 2017 03:51:31 GMT +< Content-Length: 100 +< +{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} +``` + +**Skip validate** + +When running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. + +### Custom Validators + +It is also possible to register custom validators. See the [example code](https://github.com/gin-gonic/examples/tree/master/custom-validation/server.go). + +```go +package main + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" +) + +// Booking contains binded and validated data. +type Booking struct { + CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` + CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` +} + +var bookableDate validator.Func = func(fl validator.FieldLevel) bool { + date, ok := fl.Field().Interface().(time.Time) + if ok { + today := time.Now() + if today.After(date) { + return false + } + } + return true +} + +func main() { + route := gin.Default() + + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("bookabledate", bookableDate) + } + + route.GET("/bookable", getBookable) + route.Run(":8085") +} + +func getBookable(c *gin.Context) { + var b Booking + if err := c.ShouldBindWith(&b, binding.Query); err == nil { + c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + } +} +``` + +```console +$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" +{"message":"Booking dates are valid!"} + +$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" +{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} + +$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" +{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}% +``` + +[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way. +See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations) to learn more. + +### Only Bind Query String + +`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017). + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` +} + +func main() { + route := gin.Default() + route.Any("/testing", startPage) + route.Run(":8085") +} + +func startPage(c *gin.Context) { + var person Person + if c.ShouldBindQuery(&person) == nil { + log.Println("====== Only Bind By Query String ======") + log.Println(person.Name) + log.Println(person.Address) + } + c.String(200, "Success") +} + +``` + +### Bind Query String or Post Data + +See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). + +```go +package main + +import ( + "log" + "time" + + "github.com/gin-gonic/gin" +) + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` + Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` + CreateTime time.Time `form:"createTime" time_format:"unixNano"` + UnixTime time.Time `form:"unixTime" time_format:"unix"` +} + +func main() { + route := gin.Default() + route.GET("/testing", startPage) + route.Run(":8085") +} + +func startPage(c *gin.Context) { + var person Person + // If `GET`, only `Form` binding engine (`query`) used. + // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). + // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 + if c.ShouldBind(&person) == nil { + log.Println(person.Name) + log.Println(person.Address) + log.Println(person.Birthday) + log.Println(person.CreateTime) + log.Println(person.UnixTime) + } + + c.String(200, "Success") +} +``` + +Test it with: +```sh +$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" +``` + +### Bind Uri + +See the [detail information](https://github.com/gin-gonic/gin/issues/846). + +```go +package main + +import "github.com/gin-gonic/gin" + +type Person struct { + ID string `uri:"id" binding:"required,uuid"` + Name string `uri:"name" binding:"required"` +} + +func main() { + route := gin.Default() + route.GET("/:name/:id", func(c *gin.Context) { + var person Person + if err := c.ShouldBindUri(&person); err != nil { + c.JSON(400, gin.H{"msg": err}) + return + } + c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID}) + }) + route.Run(":8088") +} +``` + +Test it with: +```sh +$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 +$ curl -v localhost:8088/thinkerou/not-uuid +``` + +### Bind Header + +```go +package main + +import ( + "fmt" + "github.com/gin-gonic/gin" +) + +type testHeader struct { + Rate int `header:"Rate"` + Domain string `header:"Domain"` +} + +func main() { + r := gin.Default() + r.GET("/", func(c *gin.Context) { + h := testHeader{} + + if err := c.ShouldBindHeader(&h); err != nil { + c.JSON(200, err) + } + + fmt.Printf("%#v\n", h) + c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + }) + + r.Run() + +// client +// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ +// output +// {"Domain":"music","Rate":300} +} +``` + +### Bind HTML checkboxes + +See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) + +main.go + +```go +... + +type myForm struct { + Colors []string `form:"colors[]"` +} + +... + +func formHandler(c *gin.Context) { + var fakeForm myForm + c.ShouldBind(&fakeForm) + c.JSON(200, gin.H{"color": fakeForm.Colors}) +} + +... + +``` + +form.html + +```html +
+

Check some colors

+ + + + + + + +
+``` + +result: + +``` +{"color":["red","green","blue"]} +``` + +### Multipart/Urlencoded binding + +```go +type ProfileForm struct { + Name string `form:"name" binding:"required"` + Avatar *multipart.FileHeader `form:"avatar" binding:"required"` + + // or for multiple files + // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` +} + +func main() { + router := gin.Default() + router.POST("/profile", func(c *gin.Context) { + // you can bind multipart form with explicit binding declaration: + // c.ShouldBindWith(&form, binding.Form) + // or you can simply use autobinding with ShouldBind method: + var form ProfileForm + // in this case proper binding will be automatically selected + if err := c.ShouldBind(&form); err != nil { + c.String(http.StatusBadRequest, "bad request") + return + } + + err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) + if err != nil { + c.String(http.StatusInternalServerError, "unknown error") + return + } + + // db.Save(&form) + + c.String(http.StatusOK, "ok") + }) + router.Run(":8080") +} +``` + +Test it with: +```sh +$ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile +``` + +### XML, JSON, YAML and ProtoBuf rendering + +```go +func main() { + r := gin.Default() + + // gin.H is a shortcut for map[string]interface{} + r.GET("/someJSON", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/moreJSON", func(c *gin.Context) { + // You also can use a struct + var msg struct { + Name string `json:"user"` + Message string + Number int + } + msg.Name = "Lena" + msg.Message = "hey" + msg.Number = 123 + // Note that msg.Name becomes "user" in the JSON + // Will output : {"user": "Lena", "Message": "hey", "Number": 123} + c.JSON(http.StatusOK, msg) + }) + + r.GET("/someXML", func(c *gin.Context) { + c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someYAML", func(c *gin.Context) { + c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + + r.GET("/someProtoBuf", func(c *gin.Context) { + reps := []int64{int64(1), int64(2)} + label := "test" + // The specific definition of protobuf is written in the testdata/protoexample file. + data := &protoexample.Test{ + Label: &label, + Reps: reps, + } + // Note that data becomes binary data in the response + // Will output protoexample.Test protobuf serialized data + c.ProtoBuf(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### SecureJSON + +Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values. + +```go +func main() { + r := gin.Default() + + // You can also use your own secure json prefix + // r.SecureJsonPrefix(")]}',\n") + + r.GET("/someJSON", func(c *gin.Context) { + names := []string{"lena", "austin", "foo"} + + // Will output : while(1);["lena","austin","foo"] + c.SecureJSON(http.StatusOK, names) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` +#### JSONP + +Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. + +```go +func main() { + r := gin.Default() + + r.GET("/JSONP", func(c *gin.Context) { + data := gin.H{ + "foo": "bar", + } + + //callback is x + // Will output : x({\"foo\":\"bar\"}) + c.JSONP(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") + + // client + // curl http://127.0.0.1:8080/JSONP?callback=x +} +``` + +#### AsciiJSON + +Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. + +```go +func main() { + r := gin.Default() + + r.GET("/someJSON", func(c *gin.Context) { + data := gin.H{ + "lang": "GO语言", + "tag": "
", + } + + // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} + c.AsciiJSON(http.StatusOK, data) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### PureJSON + +Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. +This feature is unavailable in Go 1.6 and lower. + +```go +func main() { + r := gin.Default() + + // Serves unicode entities + r.GET("/json", func(c *gin.Context) { + c.JSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // Serves literal characters + r.GET("/purejson", func(c *gin.Context) { + c.PureJSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Serving static files + +```go +func main() { + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") + + // Listen and serve on 0.0.0.0:8080 + router.Run(":8080") +} +``` + +### Serving data from file + +```go +func main() { + router := gin.Default() + + router.GET("/local/file", func(c *gin.Context) { + c.File("local/file.go") + }) + + var fs http.FileSystem = // ... + router.GET("/fs/file", func(c *gin.Context) { + c.FileFromFS("fs/file.go", fs) + }) +} + +``` + +### Serving data from reader + +```go +func main() { + router := gin.Default() + router.GET("/someDataFromReader", func(c *gin.Context) { + response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") + if err != nil || response.StatusCode != http.StatusOK { + c.Status(http.StatusServiceUnavailable) + return + } + + reader := response.Body + defer reader.Close() + contentLength := response.ContentLength + contentType := response.Header.Get("Content-Type") + + extraHeaders := map[string]string{ + "Content-Disposition": `attachment; filename="gopher.png"`, + } + + c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) + }) + router.Run(":8080") +} +``` + +### HTML rendering + +Using LoadHTMLGlob() or LoadHTMLFiles() + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/*") + //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") + router.GET("/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "index.tmpl", gin.H{ + "title": "Main website", + }) + }) + router.Run(":8080") +} +``` + +templates/index.tmpl + +```html + +

+ {{ .title }} +

+ +``` + +Using templates with same name in different directories + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") +} +``` + +templates/posts/index.tmpl + +```html +{{ define "posts/index.tmpl" }} +

+ {{ .title }} +

+

Using posts/index.tmpl

+ +{{ end }} +``` + +templates/users/index.tmpl + +```html +{{ define "users/index.tmpl" }} +

+ {{ .title }} +

+

Using users/index.tmpl

+ +{{ end }} +``` + +#### Custom Template renderer + +You can also use your own html template render + +```go +import "html/template" + +func main() { + router := gin.Default() + html := template.Must(template.ParseFiles("file1", "file2")) + router.SetHTMLTemplate(html) + router.Run(":8080") +} +``` + +#### Custom Delimiters + +You may use custom delims + +```go + r := gin.Default() + r.Delims("{[{", "}]}") + r.LoadHTMLGlob("/path/to/templates") +``` + +#### Custom Template Funcs + +See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template). + +main.go + +```go +import ( + "fmt" + "html/template" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func formatAsDate(t time.Time) string { + year, month, day := t.Date() + return fmt.Sprintf("%d%02d/%02d", year, month, day) +} + +func main() { + router := gin.Default() + router.Delims("{[{", "}]}") + router.SetFuncMap(template.FuncMap{ + "formatAsDate": formatAsDate, + }) + router.LoadHTMLFiles("./testdata/template/raw.tmpl") + + router.GET("/raw", func(c *gin.Context) { + c.HTML(http.StatusOK, "raw.tmpl", gin.H{ + "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), + }) + }) + + router.Run(":8080") +} + +``` + +raw.tmpl + +```html +Date: {[{.now | formatAsDate}]} +``` + +Result: +``` +Date: 2017/07/01 +``` + +### Multitemplate + +Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`. + +### Redirects + +Issuing a HTTP redirect is easy. Both internal and external locations are supported. + +```go +r.GET("/test", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") +}) +``` + +Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444) +```go +r.POST("/test", func(c *gin.Context) { + c.Redirect(http.StatusFound, "/foo") +}) +``` + +Issuing a Router redirect, use `HandleContext` like below. + +``` go +r.GET("/test", func(c *gin.Context) { + c.Request.URL.Path = "/test2" + r.HandleContext(c) +}) +r.GET("/test2", func(c *gin.Context) { + c.JSON(200, gin.H{"hello": "world"}) +}) +``` + + +### Custom Middleware + +```go +func Logger() gin.HandlerFunc { + return func(c *gin.Context) { + t := time.Now() + + // Set example variable + c.Set("example", "12345") + + // before request + + c.Next() + + // after request + latency := time.Since(t) + log.Print(latency) + + // access the status we are sending + status := c.Writer.Status() + log.Println(status) + } +} + +func main() { + r := gin.New() + r.Use(Logger()) + + r.GET("/test", func(c *gin.Context) { + example := c.MustGet("example").(string) + + // it would print: "12345" + log.Println(example) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Using BasicAuth() middleware + +```go +// simulate some private data +var secrets = gin.H{ + "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, + "austin": gin.H{"email": "austin@example.com", "phone": "666"}, + "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, +} + +func main() { + r := gin.Default() + + // Group using gin.BasicAuth() middleware + // gin.Accounts is a shortcut for map[string]string + authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ + "foo": "bar", + "austin": "1234", + "lena": "hello2", + "manu": "4321", + })) + + // /admin/secrets endpoint + // hit "localhost:8080/admin/secrets + authorized.GET("/secrets", func(c *gin.Context) { + // get user, it was set by the BasicAuth middleware + user := c.MustGet(gin.AuthUserKey).(string) + if secret, ok := secrets[user]; ok { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) + } else { + c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) + } + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Goroutines inside a middleware + +When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy. + +```go +func main() { + r := gin.Default() + + r.GET("/long_async", func(c *gin.Context) { + // create copy to be used inside the goroutine + cCp := c.Copy() + go func() { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // note that you are using the copied context "cCp", IMPORTANT + log.Println("Done! in path " + cCp.Request.URL.Path) + }() + }) + + r.GET("/long_sync", func(c *gin.Context) { + // simulate a long task with time.Sleep(). 5 seconds + time.Sleep(5 * time.Second) + + // since we are NOT using a goroutine, we do not have to copy the context + log.Println("Done! in path " + c.Request.URL.Path) + }) + + // Listen and serve on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +### Custom HTTP configuration + +Use `http.ListenAndServe()` directly, like this: + +```go +func main() { + router := gin.Default() + http.ListenAndServe(":8080", router) +} +``` +or + +```go +func main() { + router := gin.Default() + + s := &http.Server{ + Addr: ":8080", + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + s.ListenAndServe() +} +``` + +### Support Let's Encrypt + +example for 1-line LetsEncrypt HTTPS servers. + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + log.Fatal(autotls.Run(r, "example1.com", "example2.com")) +} +``` + +example for custom autocert manager. + +```go +package main + +import ( + "log" + + "github.com/gin-gonic/autotls" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/acme/autocert" +) + +func main() { + r := gin.Default() + + // Ping handler + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), + Cache: autocert.DirCache("/var/www/.cache"), + } + + log.Fatal(autotls.RunWithManager(r, &m)) +} +``` + +### Run multiple service using Gin + +See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example: + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" +) + +var ( + g errgroup.Group +) + +func router01() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 01", + }, + ) + }) + + return e +} + +func router02() http.Handler { + e := gin.New() + e.Use(gin.Recovery()) + e.GET("/", func(c *gin.Context) { + c.JSON( + http.StatusOK, + gin.H{ + "code": http.StatusOK, + "error": "Welcome server 02", + }, + ) + }) + + return e +} + +func main() { + server01 := &http.Server{ + Addr: ":8080", + Handler: router01(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + server02 := &http.Server{ + Addr: ":8081", + Handler: router02(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + g.Go(func() error { + err := server01.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + g.Go(func() error { + err := server02.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + return err + }) + + if err := g.Wait(); err != nil { + log.Fatal(err) + } +} +``` + +### Graceful shutdown or restart + +There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages. + +#### Third-party packages + +We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. + +```go +router := gin.Default() +router.GET("/", handler) +// [...] +endless.ListenAndServe(":4242", router) +``` + +Alternatives: + +* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. +* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. +* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. + +#### Manually + +In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). + +```go +// +build go1.8 + +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/gin-gonic/gin" +) + +func main() { + router := gin.Default() + router.GET("/", func(c *gin.Context) { + time.Sleep(5 * time.Second) + c.String(http.StatusOK, "Welcome Gin Server") + }) + + srv := &http.Server{ + Addr: ":8080", + Handler: router, + } + + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below + go func() { + if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + log.Printf("listen: %s\n", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server with + // a timeout of 5 seconds. + quit := make(chan os.Signal) + // kill (no param) default send syscall.SIGTERM + // kill -2 is syscall.SIGINT + // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + log.Println("Shutting down server...") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown:", err) + } + + log.Println("Server exiting") +} +``` + +### Build a single binary with templates + +You can build a server into a single binary containing templates by using [go-assets][]. + +[go-assets]: https://github.com/jessevdk/go-assets + +```go +func main() { + r := gin.New() + + t, err := loadTemplate() + if err != nil { + panic(err) + } + r.SetHTMLTemplate(t) + + r.GET("/", func(c *gin.Context) { + c.HTML(http.StatusOK, "/html/index.tmpl",nil) + }) + r.Run(":8080") +} + +// loadTemplate loads templates embedded by go-assets-builder +func loadTemplate() (*template.Template, error) { + t := template.New("") + for name, file := range Assets.Files { + defer file.Close() + if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { + continue + } + h, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + t, err = t.New(name).Parse(string(h)) + if err != nil { + return nil, err + } + } + return t, nil +} +``` + +See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary` directory. + +### Bind form-data request with custom struct + +The follow example using custom struct: + +```go +type StructA struct { + FieldA string `form:"field_a"` +} + +type StructB struct { + NestedStruct StructA + FieldB string `form:"field_b"` +} + +type StructC struct { + NestedStructPointer *StructA + FieldC string `form:"field_c"` +} + +type StructD struct { + NestedAnonyStruct struct { + FieldX string `form:"field_x"` + } + FieldD string `form:"field_d"` +} + +func GetDataB(c *gin.Context) { + var b StructB + c.Bind(&b) + c.JSON(200, gin.H{ + "a": b.NestedStruct, + "b": b.FieldB, + }) +} + +func GetDataC(c *gin.Context) { + var b StructC + c.Bind(&b) + c.JSON(200, gin.H{ + "a": b.NestedStructPointer, + "c": b.FieldC, + }) +} + +func GetDataD(c *gin.Context) { + var b StructD + c.Bind(&b) + c.JSON(200, gin.H{ + "x": b.NestedAnonyStruct, + "d": b.FieldD, + }) +} + +func main() { + r := gin.Default() + r.GET("/getb", GetDataB) + r.GET("/getc", GetDataC) + r.GET("/getd", GetDataD) + + r.Run() +} +``` + +Using the command `curl` command result: + +``` +$ curl "http://localhost:8080/getb?field_a=hello&field_b=world" +{"a":{"FieldA":"hello"},"b":"world"} +$ curl "http://localhost:8080/getc?field_a=hello&field_c=world" +{"a":{"FieldA":"hello"},"c":"world"} +$ curl "http://localhost:8080/getd?field_x=hello&field_d=world" +{"d":"world","x":{"FieldX":"hello"}} +``` + +### Try to bind body into different structs + +The normal methods for binding request body consumes `c.Request.Body` and they +cannot be called multiple times. + +```go +type formA struct { + Foo string `json:"foo" xml:"foo" binding:"required"` +} + +type formB struct { + Bar string `json:"bar" xml:"bar" binding:"required"` +} + +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This c.ShouldBind consumes c.Request.Body and it cannot be reused. + if errA := c.ShouldBind(&objA); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // Always an error is occurred by this because c.Request.Body is EOF now. + } else if errB := c.ShouldBind(&objB); errB == nil { + c.String(http.StatusOK, `the body should be formB`) + } else { + ... + } +} +``` + +For this, you can use `c.ShouldBindBodyWith`. + +```go +func SomeHandler(c *gin.Context) { + objA := formA{} + objB := formB{} + // This reads c.Request.Body and stores the result into the context. + if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil { + c.String(http.StatusOK, `the body should be formA`) + // At this time, it reuses body stored in the context. + } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { + c.String(http.StatusOK, `the body should be formB JSON`) + // And it can accepts other formats + } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { + c.String(http.StatusOK, `the body should be formB XML`) + } else { + ... + } +} +``` + +* `c.ShouldBindBodyWith` stores body into the context before binding. This has +a slight impact to performance, so you should not use this method if you are +enough to call binding at once. +* This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, +`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, +can be called by `c.ShouldBind()` multiple times without any damage to +performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). + +### http2 server push + +http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information. + +```go +package main + +import ( + "html/template" + "log" + + "github.com/gin-gonic/gin" +) + +var html = template.Must(template.New("https").Parse(` + + + Https Test + + + +

Welcome, Ginner!

+ + +`)) + +func main() { + r := gin.Default() + r.Static("/assets", "./assets") + r.SetHTMLTemplate(html) + + r.GET("/", func(c *gin.Context) { + if pusher := c.Writer.Pusher(); pusher != nil { + // use pusher.Push() to do server push + if err := pusher.Push("/assets/app.js", nil); err != nil { + log.Printf("Failed to push: %v", err) + } + } + c.HTML(200, "https", gin.H{ + "status": "success", + }) + }) + + // Listen and Server in https://127.0.0.1:8080 + r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") +} +``` + +### Define format for the log of routes + +The default log of routes is: +``` +[GIN-debug] POST /foo --> main.main.func1 (3 handlers) +[GIN-debug] GET /bar --> main.main.func2 (3 handlers) +[GIN-debug] GET /status --> main.main.func3 (3 handlers) +``` + +If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`. +In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs. +```go +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { + log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) + } + + r.POST("/foo", func(c *gin.Context) { + c.JSON(http.StatusOK, "foo") + }) + + r.GET("/bar", func(c *gin.Context) { + c.JSON(http.StatusOK, "bar") + }) + + r.GET("/status", func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") + }) + + // Listen and Server in http://0.0.0.0:8080 + r.Run() +} +``` + +### Set and get a cookie + +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + + router.GET("/cookie", func(c *gin.Context) { + + cookie, err := c.Cookie("gin_cookie") + + if err != nil { + cookie = "NotSet" + c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) + } + + fmt.Printf("Cookie value: %s \n", cookie) + }) + + router.Run() +} +``` + +## Don't trust all proxies + +Gin lets you specify which headers to hold the real client IP (if any), +as well as specifying which proxies (or direct clients) you trust to +specify one of these headers. + +The `TrustedProxies` slice on your `gin.Engine` specifes network addresses or +network CIDRs from where clients which their request headers related to client +IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or +IPv6 CIDRs. + +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + router.TrustedProxies = []string{"192.168.1.2"} + + router.GET("/", func(c *gin.Context) { + // If the client is 192.168.1.2, use the X-Forwarded-For + // header to deduce the original client IP from the trust- + // worthy parts of that header. + // Otherwise, simply return the direct client IP + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() +} +``` + +## Testing + +The `net/http/httptest` package is preferable way for HTTP testing. + +```go +package main + +func setupRouter() *gin.Engine { + r := gin.Default() + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + return r +} + +func main() { + r := setupRouter() + r.Run(":8080") +} +``` + +Test for code example above: + +```go +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPingRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/ping", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + assert.Equal(t, "pong", w.Body.String()) +} +``` + +## Users + +Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. + +* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. +* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. +* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. +* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares. +* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. +* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. +* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. diff --git a/vendor/github.com/gin-gonic/gin/auth.go b/vendor/github.com/gin-gonic/gin/auth.go new file mode 100644 index 0000000..4d8a6ce --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/auth.go @@ -0,0 +1,91 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "crypto/subtle" + "encoding/base64" + "net/http" + "strconv" + + "github.com/gin-gonic/gin/internal/bytesconv" +) + +// AuthUserKey is the cookie name for user credential in basic auth. +const AuthUserKey = "user" + +// Accounts defines a key/value for user/pass list of authorized logins. +type Accounts map[string]string + +type authPair struct { + value string + user string +} + +type authPairs []authPair + +func (a authPairs) searchCredential(authValue string) (string, bool) { + if authValue == "" { + return "", false + } + for _, pair := range a { + if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 { + return pair.user, true + } + } + return "", false +} + +// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where +// the key is the user name and the value is the password, as well as the name of the Realm. +// If the realm is empty, "Authorization Required" will be used by default. +// (see http://tools.ietf.org/html/rfc2617#section-1.2) +func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { + if realm == "" { + realm = "Authorization Required" + } + realm = "Basic realm=" + strconv.Quote(realm) + pairs := processAccounts(accounts) + return func(c *Context) { + // Search user in the slice of allowed credentials + user, found := pairs.searchCredential(c.requestHeader("Authorization")) + if !found { + // Credentials doesn't match, we return 401 and abort handlers chain. + c.Header("WWW-Authenticate", realm) + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using + // c.MustGet(gin.AuthUserKey). + c.Set(AuthUserKey, user) + } +} + +// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where +// the key is the user name and the value is the password. +func BasicAuth(accounts Accounts) HandlerFunc { + return BasicAuthForRealm(accounts, "") +} + +func processAccounts(accounts Accounts) authPairs { + length := len(accounts) + assert1(length > 0, "Empty list of authorized credentials") + pairs := make(authPairs, 0, length) + for user, password := range accounts { + assert1(user != "", "User can not be empty") + value := authorizationHeader(user, password) + pairs = append(pairs, authPair{ + value: value, + user: user, + }) + } + return pairs +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base)) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/binding.go b/vendor/github.com/gin-gonic/gin/binding/binding.go new file mode 100644 index 0000000..5caeb58 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/binding.go @@ -0,0 +1,118 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !nomsgpack +// +build !nomsgpack + +package binding + +import "net/http" + +// Content-Type MIME of the most common data formats. +const ( + MIMEJSON = "application/json" + MIMEHTML = "text/html" + MIMEXML = "application/xml" + MIMEXML2 = "text/xml" + MIMEPlain = "text/plain" + MIMEPOSTForm = "application/x-www-form-urlencoded" + MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" + MIMEMSGPACK = "application/x-msgpack" + MIMEMSGPACK2 = "application/msgpack" + MIMEYAML = "application/x-yaml" +) + +// Binding describes the interface which needs to be implemented for binding the +// data present in the request such as JSON request body, query parameters or +// the form POST. +type Binding interface { + Name() string + Bind(*http.Request, interface{}) error +} + +// BindingBody adds BindBody method to Binding. BindBody is similar with Bind, +// but it reads the body from supplied bytes instead of req.Body. +type BindingBody interface { + Binding + BindBody([]byte, interface{}) error +} + +// BindingUri adds BindUri method to Binding. BindUri is similar with Bind, +// but it read the Params. +type BindingUri interface { + Name() string + BindUri(map[string][]string, interface{}) error +} + +// StructValidator is the minimal interface which needs to be implemented in +// order for it to be used as the validator engine for ensuring the correctness +// of the request. Gin provides a default implementation for this using +// https://github.com/go-playground/validator/tree/v8.18.2. +type StructValidator interface { + // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. + // If the received type is a slice|array, the validation should be performed travel on every element. + // If the received type is not a struct or slice|array, any validation should be skipped and nil must be returned. + // If the received type is a struct or pointer to a struct, the validation should be performed. + // If the struct is not valid or the validation itself fails, a descriptive error should be returned. + // Otherwise nil must be returned. + ValidateStruct(interface{}) error + + // Engine returns the underlying validator engine which powers the + // StructValidator implementation. + Engine() interface{} +} + +// Validator is the default validator which implements the StructValidator +// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 +// under the hood. +var Validator StructValidator = &defaultValidator{} + +// These implement the Binding interface and can be used to bind the data +// present in the request to struct instances. +var ( + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} + Query = queryBinding{} + FormPost = formPostBinding{} + FormMultipart = formMultipartBinding{} + ProtoBuf = protobufBinding{} + MsgPack = msgpackBinding{} + YAML = yamlBinding{} + Uri = uriBinding{} + Header = headerBinding{} +) + +// Default returns the appropriate Binding instance based on the HTTP method +// and the content type. +func Default(method, contentType string) Binding { + if method == http.MethodGet { + return Form + } + + switch contentType { + case MIMEJSON: + return JSON + case MIMEXML, MIMEXML2: + return XML + case MIMEPROTOBUF: + return ProtoBuf + case MIMEMSGPACK, MIMEMSGPACK2: + return MsgPack + case MIMEYAML: + return YAML + case MIMEMultipartPOSTForm: + return FormMultipart + default: // case MIMEPOSTForm: + return Form + } +} + +func validate(obj interface{}) error { + if Validator == nil { + return nil + } + return Validator.ValidateStruct(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go b/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go new file mode 100644 index 0000000..9afa3dc --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go @@ -0,0 +1,112 @@ +// Copyright 2020 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build nomsgpack +// +build nomsgpack + +package binding + +import "net/http" + +// Content-Type MIME of the most common data formats. +const ( + MIMEJSON = "application/json" + MIMEHTML = "text/html" + MIMEXML = "application/xml" + MIMEXML2 = "text/xml" + MIMEPlain = "text/plain" + MIMEPOSTForm = "application/x-www-form-urlencoded" + MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" + MIMEYAML = "application/x-yaml" +) + +// Binding describes the interface which needs to be implemented for binding the +// data present in the request such as JSON request body, query parameters or +// the form POST. +type Binding interface { + Name() string + Bind(*http.Request, interface{}) error +} + +// BindingBody adds BindBody method to Binding. BindBody is similar with Bind, +// but it reads the body from supplied bytes instead of req.Body. +type BindingBody interface { + Binding + BindBody([]byte, interface{}) error +} + +// BindingUri adds BindUri method to Binding. BindUri is similar with Bind, +// but it read the Params. +type BindingUri interface { + Name() string + BindUri(map[string][]string, interface{}) error +} + +// StructValidator is the minimal interface which needs to be implemented in +// order for it to be used as the validator engine for ensuring the correctness +// of the request. Gin provides a default implementation for this using +// https://github.com/go-playground/validator/tree/v8.18.2. +type StructValidator interface { + // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. + // If the received type is not a struct, any validation should be skipped and nil must be returned. + // If the received type is a struct or pointer to a struct, the validation should be performed. + // If the struct is not valid or the validation itself fails, a descriptive error should be returned. + // Otherwise nil must be returned. + ValidateStruct(interface{}) error + + // Engine returns the underlying validator engine which powers the + // StructValidator implementation. + Engine() interface{} +} + +// Validator is the default validator which implements the StructValidator +// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 +// under the hood. +var Validator StructValidator = &defaultValidator{} + +// These implement the Binding interface and can be used to bind the data +// present in the request to struct instances. +var ( + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} + Query = queryBinding{} + FormPost = formPostBinding{} + FormMultipart = formMultipartBinding{} + ProtoBuf = protobufBinding{} + YAML = yamlBinding{} + Uri = uriBinding{} + Header = headerBinding{} +) + +// Default returns the appropriate Binding instance based on the HTTP method +// and the content type. +func Default(method, contentType string) Binding { + if method == "GET" { + return Form + } + + switch contentType { + case MIMEJSON: + return JSON + case MIMEXML, MIMEXML2: + return XML + case MIMEPROTOBUF: + return ProtoBuf + case MIMEYAML: + return YAML + case MIMEMultipartPOSTForm: + return FormMultipart + default: // case MIMEPOSTForm: + return Form + } +} + +func validate(obj interface{}) error { + if Validator == nil { + return nil + } + return Validator.ValidateStruct(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/default_validator.go b/vendor/github.com/gin-gonic/gin/binding/default_validator.go new file mode 100644 index 0000000..c57a120 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/default_validator.go @@ -0,0 +1,85 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "github.com/go-playground/validator/v10" +) + +type defaultValidator struct { + once sync.Once + validate *validator.Validate +} + +type sliceValidateError []error + +func (err sliceValidateError) Error() string { + var errMsgs []string + for i, e := range err { + if e == nil { + continue + } + errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error())) + } + return strings.Join(errMsgs, "\n") +} + +var _ StructValidator = &defaultValidator{} + +// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type. +func (v *defaultValidator) ValidateStruct(obj interface{}) error { + if obj == nil { + return nil + } + + value := reflect.ValueOf(obj) + switch value.Kind() { + case reflect.Ptr: + return v.ValidateStruct(value.Elem().Interface()) + case reflect.Struct: + return v.validateStruct(obj) + case reflect.Slice, reflect.Array: + count := value.Len() + validateRet := make(sliceValidateError, 0) + for i := 0; i < count; i++ { + if err := v.ValidateStruct(value.Index(i).Interface()); err != nil { + validateRet = append(validateRet, err) + } + } + if len(validateRet) == 0 { + return nil + } + return validateRet + default: + return nil + } +} + +// validateStruct receives struct type +func (v *defaultValidator) validateStruct(obj interface{}) error { + v.lazyinit() + return v.validate.Struct(obj) +} + +// Engine returns the underlying validator engine which powers the default +// Validator instance. This is useful if you want to register custom validations +// or struct level validations. See validator GoDoc for more info - +// https://godoc.org/gopkg.in/go-playground/validator.v8 +func (v *defaultValidator) Engine() interface{} { + v.lazyinit() + return v.validate +} + +func (v *defaultValidator) lazyinit() { + v.once.Do(func() { + v.validate = validator.New() + v.validate.SetTagName("binding") + }) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/form.go b/vendor/github.com/gin-gonic/gin/binding/form.go new file mode 100644 index 0000000..b93c34c --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/form.go @@ -0,0 +1,63 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "net/http" +) + +const defaultMemory = 32 << 20 + +type formBinding struct{} +type formPostBinding struct{} +type formMultipartBinding struct{} + +func (formBinding) Name() string { + return "form" +} + +func (formBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := req.ParseMultipartForm(defaultMemory); err != nil { + if err != http.ErrNotMultipart { + return err + } + } + if err := mapForm(obj, req.Form); err != nil { + return err + } + return validate(obj) +} + +func (formPostBinding) Name() string { + return "form-urlencoded" +} + +func (formPostBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := mapForm(obj, req.PostForm); err != nil { + return err + } + return validate(obj) +} + +func (formMultipartBinding) Name() string { + return "multipart/form-data" +} + +func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseMultipartForm(defaultMemory); err != nil { + return err + } + if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil { + return err + } + + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/form_mapping.go b/vendor/github.com/gin-gonic/gin/binding/form_mapping.go new file mode 100644 index 0000000..2f4e45b --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/form_mapping.go @@ -0,0 +1,392 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin/internal/bytesconv" + "github.com/gin-gonic/gin/internal/json" +) + +var errUnknownType = errors.New("unknown type") + +func mapUri(ptr interface{}, m map[string][]string) error { + return mapFormByTag(ptr, m, "uri") +} + +func mapForm(ptr interface{}, form map[string][]string) error { + return mapFormByTag(ptr, form, "form") +} + +var emptyField = reflect.StructField{} + +func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error { + // Check if ptr is a map + ptrVal := reflect.ValueOf(ptr) + var pointed interface{} + if ptrVal.Kind() == reflect.Ptr { + ptrVal = ptrVal.Elem() + pointed = ptrVal.Interface() + } + if ptrVal.Kind() == reflect.Map && + ptrVal.Type().Key().Kind() == reflect.String { + if pointed != nil { + ptr = pointed + } + return setFormMap(ptr, form) + } + + return mappingByPtr(ptr, formSource(form), tag) +} + +// setter tries to set value on a walking by fields of a struct +type setter interface { + TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) +} + +type formSource map[string][]string + +var _ setter = formSource(nil) + +// TrySet tries to set a value by request's form source (like map[string][]string) +func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) { + return setByForm(value, field, form, tagValue, opt) +} + +func mappingByPtr(ptr interface{}, setter setter, tag string) error { + _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag) + return err +} + +func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { + if field.Tag.Get(tag) == "-" { // just ignoring this field + return false, nil + } + + var vKind = value.Kind() + + if vKind == reflect.Ptr { + var isNew bool + vPtr := value + if value.IsNil() { + isNew = true + vPtr = reflect.New(value.Type().Elem()) + } + isSetted, err := mapping(vPtr.Elem(), field, setter, tag) + if err != nil { + return false, err + } + if isNew && isSetted { + value.Set(vPtr) + } + return isSetted, nil + } + + if vKind != reflect.Struct || !field.Anonymous { + ok, err := tryToSetValue(value, field, setter, tag) + if err != nil { + return false, err + } + if ok { + return true, nil + } + } + + if vKind == reflect.Struct { + tValue := value.Type() + + var isSetted bool + for i := 0; i < value.NumField(); i++ { + sf := tValue.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag) + if err != nil { + return false, err + } + isSetted = isSetted || ok + } + return isSetted, nil + } + return false, nil +} + +type setOptions struct { + isDefaultExists bool + defaultValue string +} + +func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { + var tagValue string + var setOpt setOptions + + tagValue = field.Tag.Get(tag) + tagValue, opts := head(tagValue, ",") + + if tagValue == "" { // default value is FieldName + tagValue = field.Name + } + if tagValue == "" { // when field is "emptyField" variable + return false, nil + } + + var opt string + for len(opts) > 0 { + opt, opts = head(opts, ",") + + if k, v := head(opt, "="); k == "default" { + setOpt.isDefaultExists = true + setOpt.defaultValue = v + } + } + + return setter.TrySet(value, field, tagValue, setOpt) +} + +func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) { + vs, ok := form[tagValue] + if !ok && !opt.isDefaultExists { + return false, nil + } + + switch value.Kind() { + case reflect.Slice: + if !ok { + vs = []string{opt.defaultValue} + } + return true, setSlice(vs, value, field) + case reflect.Array: + if !ok { + vs = []string{opt.defaultValue} + } + if len(vs) != value.Len() { + return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) + } + return true, setArray(vs, value, field) + default: + var val string + if !ok { + val = opt.defaultValue + } + + if len(vs) > 0 { + val = vs[0] + } + return true, setWithProperType(val, value, field) + } +} + +func setWithProperType(val string, value reflect.Value, field reflect.StructField) error { + switch value.Kind() { + case reflect.Int: + return setIntField(val, 0, value) + case reflect.Int8: + return setIntField(val, 8, value) + case reflect.Int16: + return setIntField(val, 16, value) + case reflect.Int32: + return setIntField(val, 32, value) + case reflect.Int64: + switch value.Interface().(type) { + case time.Duration: + return setTimeDuration(val, value, field) + } + return setIntField(val, 64, value) + case reflect.Uint: + return setUintField(val, 0, value) + case reflect.Uint8: + return setUintField(val, 8, value) + case reflect.Uint16: + return setUintField(val, 16, value) + case reflect.Uint32: + return setUintField(val, 32, value) + case reflect.Uint64: + return setUintField(val, 64, value) + case reflect.Bool: + return setBoolField(val, value) + case reflect.Float32: + return setFloatField(val, 32, value) + case reflect.Float64: + return setFloatField(val, 64, value) + case reflect.String: + value.SetString(val) + case reflect.Struct: + switch value.Interface().(type) { + case time.Time: + return setTimeField(val, field, value) + } + return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) + case reflect.Map: + return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) + default: + return errUnknownType + } + return nil +} + +func setIntField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0" + } + intVal, err := strconv.ParseInt(val, 10, bitSize) + if err == nil { + field.SetInt(intVal) + } + return err +} + +func setUintField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0" + } + uintVal, err := strconv.ParseUint(val, 10, bitSize) + if err == nil { + field.SetUint(uintVal) + } + return err +} + +func setBoolField(val string, field reflect.Value) error { + if val == "" { + val = "false" + } + boolVal, err := strconv.ParseBool(val) + if err == nil { + field.SetBool(boolVal) + } + return err +} + +func setFloatField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, bitSize) + if err == nil { + field.SetFloat(floatVal) + } + return err +} + +func setTimeField(val string, structField reflect.StructField, value reflect.Value) error { + timeFormat := structField.Tag.Get("time_format") + if timeFormat == "" { + timeFormat = time.RFC3339 + } + + switch tf := strings.ToLower(timeFormat); tf { + case "unix", "unixnano": + tv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return err + } + + d := time.Duration(1) + if tf == "unixnano" { + d = time.Second + } + + t := time.Unix(tv/int64(d), tv%int64(d)) + value.Set(reflect.ValueOf(t)) + return nil + + } + + if val == "" { + value.Set(reflect.ValueOf(time.Time{})) + return nil + } + + l := time.Local + if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC { + l = time.UTC + } + + if locTag := structField.Tag.Get("time_location"); locTag != "" { + loc, err := time.LoadLocation(locTag) + if err != nil { + return err + } + l = loc + } + + t, err := time.ParseInLocation(timeFormat, val, l) + if err != nil { + return err + } + + value.Set(reflect.ValueOf(t)) + return nil +} + +func setArray(vals []string, value reflect.Value, field reflect.StructField) error { + for i, s := range vals { + err := setWithProperType(s, value.Index(i), field) + if err != nil { + return err + } + } + return nil +} + +func setSlice(vals []string, value reflect.Value, field reflect.StructField) error { + slice := reflect.MakeSlice(value.Type(), len(vals), len(vals)) + err := setArray(vals, slice, field) + if err != nil { + return err + } + value.Set(slice) + return nil +} + +func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error { + d, err := time.ParseDuration(val) + if err != nil { + return err + } + value.Set(reflect.ValueOf(d)) + return nil +} + +func head(str, sep string) (head string, tail string) { + idx := strings.Index(str, sep) + if idx < 0 { + return str, "" + } + return str[:idx], str[idx+len(sep):] +} + +func setFormMap(ptr interface{}, form map[string][]string) error { + el := reflect.TypeOf(ptr).Elem() + + if el.Kind() == reflect.Slice { + ptrMap, ok := ptr.(map[string][]string) + if !ok { + return errors.New("cannot convert to map slices of strings") + } + for k, v := range form { + ptrMap[k] = v + } + + return nil + } + + ptrMap, ok := ptr.(map[string]string) + if !ok { + return errors.New("cannot convert to map of strings") + } + for k, v := range form { + ptrMap[k] = v[len(v)-1] // pick last + } + + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/binding/header.go b/vendor/github.com/gin-gonic/gin/binding/header.go new file mode 100644 index 0000000..179ce4e --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/header.go @@ -0,0 +1,34 @@ +package binding + +import ( + "net/http" + "net/textproto" + "reflect" +) + +type headerBinding struct{} + +func (headerBinding) Name() string { + return "header" +} + +func (headerBinding) Bind(req *http.Request, obj interface{}) error { + + if err := mapHeader(obj, req.Header); err != nil { + return err + } + + return validate(obj) +} + +func mapHeader(ptr interface{}, h map[string][]string) error { + return mappingByPtr(ptr, headerSource(h), "header") +} + +type headerSource map[string][]string + +var _ setter = headerSource(nil) + +func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) { + return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/json.go b/vendor/github.com/gin-gonic/gin/binding/json.go new file mode 100644 index 0000000..d62e070 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/json.go @@ -0,0 +1,56 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "fmt" + "io" + "net/http" + + "github.com/gin-gonic/gin/internal/json" +) + +// EnableDecoderUseNumber is used to call the UseNumber method on the JSON +// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an +// interface{} as a Number instead of as a float64. +var EnableDecoderUseNumber = false + +// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method +// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to +// return an error when the destination is a struct and the input contains object +// keys which do not match any non-ignored, exported fields in the destination. +var EnableDecoderDisallowUnknownFields = false + +type jsonBinding struct{} + +func (jsonBinding) Name() string { + return "json" +} + +func (jsonBinding) Bind(req *http.Request, obj interface{}) error { + if req == nil || req.Body == nil { + return fmt.Errorf("invalid request") + } + return decodeJSON(req.Body, obj) +} + +func (jsonBinding) BindBody(body []byte, obj interface{}) error { + return decodeJSON(bytes.NewReader(body), obj) +} + +func decodeJSON(r io.Reader, obj interface{}) error { + decoder := json.NewDecoder(r) + if EnableDecoderUseNumber { + decoder.UseNumber() + } + if EnableDecoderDisallowUnknownFields { + decoder.DisallowUnknownFields() + } + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/msgpack.go b/vendor/github.com/gin-gonic/gin/binding/msgpack.go new file mode 100644 index 0000000..2a44299 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/msgpack.go @@ -0,0 +1,38 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !nomsgpack +// +build !nomsgpack + +package binding + +import ( + "bytes" + "io" + "net/http" + + "github.com/ugorji/go/codec" +) + +type msgpackBinding struct{} + +func (msgpackBinding) Name() string { + return "msgpack" +} + +func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { + return decodeMsgPack(req.Body, obj) +} + +func (msgpackBinding) BindBody(body []byte, obj interface{}) error { + return decodeMsgPack(bytes.NewReader(body), obj) +} + +func decodeMsgPack(r io.Reader, obj interface{}) error { + cdc := new(codec.MsgpackHandle) + if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go b/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go new file mode 100644 index 0000000..f85a1aa --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go @@ -0,0 +1,66 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "errors" + "mime/multipart" + "net/http" + "reflect" +) + +type multipartRequest http.Request + +var _ setter = (*multipartRequest)(nil) + +// TrySet tries to set a value by the multipart request with the binding a form file +func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) { + if files := r.MultipartForm.File[key]; len(files) != 0 { + return setByMultipartFormFile(value, field, files) + } + + return setByForm(value, field, r.MultipartForm.Value, key, opt) +} + +func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) { + switch value.Kind() { + case reflect.Ptr: + switch value.Interface().(type) { + case *multipart.FileHeader: + value.Set(reflect.ValueOf(files[0])) + return true, nil + } + case reflect.Struct: + switch value.Interface().(type) { + case multipart.FileHeader: + value.Set(reflect.ValueOf(*files[0])) + return true, nil + } + case reflect.Slice: + slice := reflect.MakeSlice(value.Type(), len(files), len(files)) + isSetted, err = setArrayOfMultipartFormFiles(slice, field, files) + if err != nil || !isSetted { + return isSetted, err + } + value.Set(slice) + return true, nil + case reflect.Array: + return setArrayOfMultipartFormFiles(value, field, files) + } + return false, errors.New("unsupported field type for multipart.FileHeader") +} + +func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) { + if value.Len() != len(files) { + return false, errors.New("unsupported len of array for []*multipart.FileHeader") + } + for i := range files { + setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1]) + if err != nil || !setted { + return setted, err + } + } + return true, nil +} diff --git a/vendor/github.com/gin-gonic/gin/binding/protobuf.go b/vendor/github.com/gin-gonic/gin/binding/protobuf.go new file mode 100644 index 0000000..f9ece92 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/protobuf.go @@ -0,0 +1,36 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "io/ioutil" + "net/http" + + "github.com/golang/protobuf/proto" +) + +type protobufBinding struct{} + +func (protobufBinding) Name() string { + return "protobuf" +} + +func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + return b.BindBody(buf, obj) +} + +func (protobufBinding) BindBody(body []byte, obj interface{}) error { + if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { + return err + } + // Here it's same to return validate(obj), but util now we can't add + // `binding:""` to the struct which automatically generate by gen-proto + return nil + // return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/query.go b/vendor/github.com/gin-gonic/gin/binding/query.go new file mode 100644 index 0000000..219743f --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/query.go @@ -0,0 +1,21 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import "net/http" + +type queryBinding struct{} + +func (queryBinding) Name() string { + return "query" +} + +func (queryBinding) Bind(req *http.Request, obj interface{}) error { + values := req.URL.Query() + if err := mapForm(obj, values); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/uri.go b/vendor/github.com/gin-gonic/gin/binding/uri.go new file mode 100644 index 0000000..f91ec38 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/uri.go @@ -0,0 +1,18 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +type uriBinding struct{} + +func (uriBinding) Name() string { + return "uri" +} + +func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { + if err := mapUri(obj, m); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/xml.go b/vendor/github.com/gin-gonic/gin/binding/xml.go new file mode 100644 index 0000000..4e90114 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/xml.go @@ -0,0 +1,33 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "encoding/xml" + "io" + "net/http" +) + +type xmlBinding struct{} + +func (xmlBinding) Name() string { + return "xml" +} + +func (xmlBinding) Bind(req *http.Request, obj interface{}) error { + return decodeXML(req.Body, obj) +} + +func (xmlBinding) BindBody(body []byte, obj interface{}) error { + return decodeXML(bytes.NewReader(body), obj) +} +func decodeXML(r io.Reader, obj interface{}) error { + decoder := xml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/binding/yaml.go b/vendor/github.com/gin-gonic/gin/binding/yaml.go new file mode 100644 index 0000000..a2d36d6 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/yaml.go @@ -0,0 +1,35 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "io" + "net/http" + + "gopkg.in/yaml.v2" +) + +type yamlBinding struct{} + +func (yamlBinding) Name() string { + return "yaml" +} + +func (yamlBinding) Bind(req *http.Request, obj interface{}) error { + return decodeYAML(req.Body, obj) +} + +func (yamlBinding) BindBody(body []byte, obj interface{}) error { + return decodeYAML(bytes.NewReader(body), obj) +} + +func decodeYAML(r io.Reader, obj interface{}) error { + decoder := yaml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/codecov.yml b/vendor/github.com/gin-gonic/gin/codecov.yml new file mode 100644 index 0000000..c9c9a52 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/codecov.yml @@ -0,0 +1,5 @@ +coverage: + notify: + gitter: + default: + url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165 diff --git a/vendor/github.com/gin-gonic/gin/context.go b/vendor/github.com/gin-gonic/gin/context.go new file mode 100644 index 0000000..dc03c35 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/context.go @@ -0,0 +1,1178 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "mime/multipart" + "net" + "net/http" + "net/url" + "os" + "strings" + "sync" + "time" + + "github.com/gin-contrib/sse" + "github.com/gin-gonic/gin/binding" + "github.com/gin-gonic/gin/render" +) + +// Content-Type MIME of the most common data formats. +const ( + MIMEJSON = binding.MIMEJSON + MIMEHTML = binding.MIMEHTML + MIMEXML = binding.MIMEXML + MIMEXML2 = binding.MIMEXML2 + MIMEPlain = binding.MIMEPlain + MIMEPOSTForm = binding.MIMEPOSTForm + MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm + MIMEYAML = binding.MIMEYAML +) + +// BodyBytesKey indicates a default body bytes key. +const BodyBytesKey = "_gin-gonic/gin/bodybyteskey" + +const abortIndex int8 = math.MaxInt8 / 2 + +// Context is the most important part of gin. It allows us to pass variables between middleware, +// manage the flow, validate the JSON of a request and render a JSON response for example. +type Context struct { + writermem responseWriter + Request *http.Request + Writer ResponseWriter + + Params Params + handlers HandlersChain + index int8 + fullPath string + + engine *Engine + params *Params + + // This mutex protect Keys map + mu sync.RWMutex + + // Keys is a key/value pair exclusively for the context of each request. + Keys map[string]interface{} + + // Errors is a list of errors attached to all the handlers/middlewares who used this context. + Errors errorMsgs + + // Accepted defines a list of manually accepted formats for content negotiation. + Accepted []string + + // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() + queryCache url.Values + + // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, + // or PUT body parameters. + formCache url.Values + + // SameSite allows a server to define a cookie attribute making it impossible for + // the browser to send this cookie along with cross-site requests. + sameSite http.SameSite +} + +/************************************/ +/********** CONTEXT CREATION ********/ +/************************************/ + +func (c *Context) reset() { + c.Writer = &c.writermem + c.Params = c.Params[0:0] + c.handlers = nil + c.index = -1 + + c.fullPath = "" + c.Keys = nil + c.Errors = c.Errors[0:0] + c.Accepted = nil + c.queryCache = nil + c.formCache = nil + *c.params = (*c.params)[0:0] +} + +// Copy returns a copy of the current context that can be safely used outside the request's scope. +// This has to be used when the context has to be passed to a goroutine. +func (c *Context) Copy() *Context { + cp := Context{ + writermem: c.writermem, + Request: c.Request, + Params: c.Params, + engine: c.engine, + } + cp.writermem.ResponseWriter = nil + cp.Writer = &cp.writermem + cp.index = abortIndex + cp.handlers = nil + cp.Keys = map[string]interface{}{} + for k, v := range c.Keys { + cp.Keys[k] = v + } + paramCopy := make([]Param, len(cp.Params)) + copy(paramCopy, cp.Params) + cp.Params = paramCopy + return &cp +} + +// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", +// this function will return "main.handleGetUsers". +func (c *Context) HandlerName() string { + return nameOfFunction(c.handlers.Last()) +} + +// HandlerNames returns a list of all registered handlers for this context in descending order, +// following the semantics of HandlerName() +func (c *Context) HandlerNames() []string { + hn := make([]string, 0, len(c.handlers)) + for _, val := range c.handlers { + hn = append(hn, nameOfFunction(val)) + } + return hn +} + +// Handler returns the main handler. +func (c *Context) Handler() HandlerFunc { + return c.handlers.Last() +} + +// FullPath returns a matched route full path. For not found routes +// returns an empty string. +// router.GET("/user/:id", func(c *gin.Context) { +// c.FullPath() == "/user/:id" // true +// }) +func (c *Context) FullPath() string { + return c.fullPath +} + +/************************************/ +/*********** FLOW CONTROL ***********/ +/************************************/ + +// Next should be used only inside middleware. +// It executes the pending handlers in the chain inside the calling handler. +// See example in GitHub. +func (c *Context) Next() { + c.index++ + for c.index < int8(len(c.handlers)) { + c.handlers[c.index](c) + c.index++ + } +} + +// IsAborted returns true if the current context was aborted. +func (c *Context) IsAborted() bool { + return c.index >= abortIndex +} + +// Abort prevents pending handlers from being called. Note that this will not stop the current handler. +// Let's say you have an authorization middleware that validates that the current request is authorized. +// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers +// for this request are not called. +func (c *Context) Abort() { + c.index = abortIndex +} + +// AbortWithStatus calls `Abort()` and writes the headers with the specified status code. +// For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401). +func (c *Context) AbortWithStatus(code int) { + c.Status(code) + c.Writer.WriteHeaderNow() + c.Abort() +} + +// AbortWithStatusJSON calls `Abort()` and then `JSON` internally. +// This method stops the chain, writes the status code and return a JSON body. +// It also sets the Content-Type as "application/json". +func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) { + c.Abort() + c.JSON(code, jsonObj) +} + +// AbortWithError calls `AbortWithStatus()` and `Error()` internally. +// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. +// See Context.Error() for more details. +func (c *Context) AbortWithError(code int, err error) *Error { + c.AbortWithStatus(code) + return c.Error(err) +} + +/************************************/ +/********* ERROR MANAGEMENT *********/ +/************************************/ + +// Error attaches an error to the current context. The error is pushed to a list of errors. +// It's a good idea to call Error for each error that occurred during the resolution of a request. +// A middleware can be used to collect all the errors and push them to a database together, +// print a log, or append it in the HTTP response. +// Error will panic if err is nil. +func (c *Context) Error(err error) *Error { + if err == nil { + panic("err is nil") + } + + parsedError, ok := err.(*Error) + if !ok { + parsedError = &Error{ + Err: err, + Type: ErrorTypePrivate, + } + } + + c.Errors = append(c.Errors, parsedError) + return parsedError +} + +/************************************/ +/******** METADATA MANAGEMENT********/ +/************************************/ + +// Set is used to store a new key/value pair exclusively for this context. +// It also lazy initializes c.Keys if it was not used previously. +func (c *Context) Set(key string, value interface{}) { + c.mu.Lock() + if c.Keys == nil { + c.Keys = make(map[string]interface{}) + } + + c.Keys[key] = value + c.mu.Unlock() +} + +// Get returns the value for the given key, ie: (value, true). +// If the value does not exists it returns (nil, false) +func (c *Context) Get(key string) (value interface{}, exists bool) { + c.mu.RLock() + value, exists = c.Keys[key] + c.mu.RUnlock() + return +} + +// MustGet returns the value for the given key if it exists, otherwise it panics. +func (c *Context) MustGet(key string) interface{} { + if value, exists := c.Get(key); exists { + return value + } + panic("Key \"" + key + "\" does not exist") +} + +// GetString returns the value associated with the key as a string. +func (c *Context) GetString(key string) (s string) { + if val, ok := c.Get(key); ok && val != nil { + s, _ = val.(string) + } + return +} + +// GetBool returns the value associated with the key as a boolean. +func (c *Context) GetBool(key string) (b bool) { + if val, ok := c.Get(key); ok && val != nil { + b, _ = val.(bool) + } + return +} + +// GetInt returns the value associated with the key as an integer. +func (c *Context) GetInt(key string) (i int) { + if val, ok := c.Get(key); ok && val != nil { + i, _ = val.(int) + } + return +} + +// GetInt64 returns the value associated with the key as an integer. +func (c *Context) GetInt64(key string) (i64 int64) { + if val, ok := c.Get(key); ok && val != nil { + i64, _ = val.(int64) + } + return +} + +// GetUint returns the value associated with the key as an unsigned integer. +func (c *Context) GetUint(key string) (ui uint) { + if val, ok := c.Get(key); ok && val != nil { + ui, _ = val.(uint) + } + return +} + +// GetUint64 returns the value associated with the key as an unsigned integer. +func (c *Context) GetUint64(key string) (ui64 uint64) { + if val, ok := c.Get(key); ok && val != nil { + ui64, _ = val.(uint64) + } + return +} + +// GetFloat64 returns the value associated with the key as a float64. +func (c *Context) GetFloat64(key string) (f64 float64) { + if val, ok := c.Get(key); ok && val != nil { + f64, _ = val.(float64) + } + return +} + +// GetTime returns the value associated with the key as time. +func (c *Context) GetTime(key string) (t time.Time) { + if val, ok := c.Get(key); ok && val != nil { + t, _ = val.(time.Time) + } + return +} + +// GetDuration returns the value associated with the key as a duration. +func (c *Context) GetDuration(key string) (d time.Duration) { + if val, ok := c.Get(key); ok && val != nil { + d, _ = val.(time.Duration) + } + return +} + +// GetStringSlice returns the value associated with the key as a slice of strings. +func (c *Context) GetStringSlice(key string) (ss []string) { + if val, ok := c.Get(key); ok && val != nil { + ss, _ = val.([]string) + } + return +} + +// GetStringMap returns the value associated with the key as a map of interfaces. +func (c *Context) GetStringMap(key string) (sm map[string]interface{}) { + if val, ok := c.Get(key); ok && val != nil { + sm, _ = val.(map[string]interface{}) + } + return +} + +// GetStringMapString returns the value associated with the key as a map of strings. +func (c *Context) GetStringMapString(key string) (sms map[string]string) { + if val, ok := c.Get(key); ok && val != nil { + sms, _ = val.(map[string]string) + } + return +} + +// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. +func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { + if val, ok := c.Get(key); ok && val != nil { + smss, _ = val.(map[string][]string) + } + return +} + +/************************************/ +/************ INPUT DATA ************/ +/************************************/ + +// Param returns the value of the URL param. +// It is a shortcut for c.Params.ByName(key) +// router.GET("/user/:id", func(c *gin.Context) { +// // a GET request to /user/john +// id := c.Param("id") // id == "john" +// }) +func (c *Context) Param(key string) string { + return c.Params.ByName(key) +} + +// Query returns the keyed url query value if it exists, +// otherwise it returns an empty string `("")`. +// It is shortcut for `c.Request.URL.Query().Get(key)` +// GET /path?id=1234&name=Manu&value= +// c.Query("id") == "1234" +// c.Query("name") == "Manu" +// c.Query("value") == "" +// c.Query("wtf") == "" +func (c *Context) Query(key string) string { + value, _ := c.GetQuery(key) + return value +} + +// DefaultQuery returns the keyed url query value if it exists, +// otherwise it returns the specified defaultValue string. +// See: Query() and GetQuery() for further information. +// GET /?name=Manu&lastname= +// c.DefaultQuery("name", "unknown") == "Manu" +// c.DefaultQuery("id", "none") == "none" +// c.DefaultQuery("lastname", "none") == "" +func (c *Context) DefaultQuery(key, defaultValue string) string { + if value, ok := c.GetQuery(key); ok { + return value + } + return defaultValue +} + +// GetQuery is like Query(), it returns the keyed url query value +// if it exists `(value, true)` (even when the value is an empty string), +// otherwise it returns `("", false)`. +// It is shortcut for `c.Request.URL.Query().Get(key)` +// GET /?name=Manu&lastname= +// ("Manu", true) == c.GetQuery("name") +// ("", false) == c.GetQuery("id") +// ("", true) == c.GetQuery("lastname") +func (c *Context) GetQuery(key string) (string, bool) { + if values, ok := c.GetQueryArray(key); ok { + return values[0], ok + } + return "", false +} + +// QueryArray returns a slice of strings for a given query key. +// The length of the slice depends on the number of params with the given key. +func (c *Context) QueryArray(key string) []string { + values, _ := c.GetQueryArray(key) + return values +} + +func (c *Context) initQueryCache() { + if c.queryCache == nil { + if c.Request != nil { + c.queryCache = c.Request.URL.Query() + } else { + c.queryCache = url.Values{} + } + } +} + +// GetQueryArray returns a slice of strings for a given query key, plus +// a boolean value whether at least one value exists for the given key. +func (c *Context) GetQueryArray(key string) ([]string, bool) { + c.initQueryCache() + if values, ok := c.queryCache[key]; ok && len(values) > 0 { + return values, true + } + return []string{}, false +} + +// QueryMap returns a map for a given query key. +func (c *Context) QueryMap(key string) map[string]string { + dicts, _ := c.GetQueryMap(key) + return dicts +} + +// GetQueryMap returns a map for a given query key, plus a boolean value +// whether at least one value exists for the given key. +func (c *Context) GetQueryMap(key string) (map[string]string, bool) { + c.initQueryCache() + return c.get(c.queryCache, key) +} + +// PostForm returns the specified key from a POST urlencoded form or multipart form +// when it exists, otherwise it returns an empty string `("")`. +func (c *Context) PostForm(key string) string { + value, _ := c.GetPostForm(key) + return value +} + +// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form +// when it exists, otherwise it returns the specified defaultValue string. +// See: PostForm() and GetPostForm() for further information. +func (c *Context) DefaultPostForm(key, defaultValue string) string { + if value, ok := c.GetPostForm(key); ok { + return value + } + return defaultValue +} + +// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded +// form or multipart form when it exists `(value, true)` (even when the value is an empty string), +// otherwise it returns ("", false). +// For example, during a PATCH request to update the user's email: +// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" +// email= --> ("", true) := GetPostForm("email") // set email to "" +// --> ("", false) := GetPostForm("email") // do nothing with email +func (c *Context) GetPostForm(key string) (string, bool) { + if values, ok := c.GetPostFormArray(key); ok { + return values[0], ok + } + return "", false +} + +// PostFormArray returns a slice of strings for a given form key. +// The length of the slice depends on the number of params with the given key. +func (c *Context) PostFormArray(key string) []string { + values, _ := c.GetPostFormArray(key) + return values +} + +func (c *Context) initFormCache() { + if c.formCache == nil { + c.formCache = make(url.Values) + req := c.Request + if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { + if err != http.ErrNotMultipart { + debugPrint("error on parse multipart form array: %v", err) + } + } + c.formCache = req.PostForm + } +} + +// GetPostFormArray returns a slice of strings for a given form key, plus +// a boolean value whether at least one value exists for the given key. +func (c *Context) GetPostFormArray(key string) ([]string, bool) { + c.initFormCache() + if values := c.formCache[key]; len(values) > 0 { + return values, true + } + return []string{}, false +} + +// PostFormMap returns a map for a given form key. +func (c *Context) PostFormMap(key string) map[string]string { + dicts, _ := c.GetPostFormMap(key) + return dicts +} + +// GetPostFormMap returns a map for a given form key, plus a boolean value +// whether at least one value exists for the given key. +func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { + c.initFormCache() + return c.get(c.formCache, key) +} + +// get is an internal method and returns a map which satisfy conditions. +func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) { + dicts := make(map[string]string) + exist := false + for k, v := range m { + if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key { + if j := strings.IndexByte(k[i+1:], ']'); j >= 1 { + exist = true + dicts[k[i+1:][:j]] = v[0] + } + } + } + return dicts, exist +} + +// FormFile returns the first file for the provided form key. +func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { + if c.Request.MultipartForm == nil { + if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { + return nil, err + } + } + f, fh, err := c.Request.FormFile(name) + if err != nil { + return nil, err + } + f.Close() + return fh, err +} + +// MultipartForm is the parsed multipart form, including file uploads. +func (c *Context) MultipartForm() (*multipart.Form, error) { + err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory) + return c.Request.MultipartForm, err +} + +// SaveUploadedFile uploads the form file to specific dst. +func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, src) + return err +} + +// Bind checks the Content-Type to select a binding engine automatically, +// Depending the "Content-Type" header different bindings are used: +// "application/json" --> JSON binding +// "application/xml" --> XML binding +// otherwise --> returns an error. +// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. +// It decodes the json payload into the struct specified as a pointer. +// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. +func (c *Context) Bind(obj interface{}) error { + b := binding.Default(c.Request.Method, c.ContentType()) + return c.MustBindWith(obj, b) +} + +// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON). +func (c *Context) BindJSON(obj interface{}) error { + return c.MustBindWith(obj, binding.JSON) +} + +// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML). +func (c *Context) BindXML(obj interface{}) error { + return c.MustBindWith(obj, binding.XML) +} + +// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). +func (c *Context) BindQuery(obj interface{}) error { + return c.MustBindWith(obj, binding.Query) +} + +// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML). +func (c *Context) BindYAML(obj interface{}) error { + return c.MustBindWith(obj, binding.YAML) +} + +// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). +func (c *Context) BindHeader(obj interface{}) error { + return c.MustBindWith(obj, binding.Header) +} + +// BindUri binds the passed struct pointer using binding.Uri. +// It will abort the request with HTTP 400 if any error occurs. +func (c *Context) BindUri(obj interface{}) error { + if err := c.ShouldBindUri(obj); err != nil { + c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + return err + } + return nil +} + +// MustBindWith binds the passed struct pointer using the specified binding engine. +// It will abort the request with HTTP 400 if any error occurs. +// See the binding package. +func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { + if err := c.ShouldBindWith(obj, b); err != nil { + c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + return err + } + return nil +} + +// ShouldBind checks the Content-Type to select a binding engine automatically, +// Depending the "Content-Type" header different bindings are used: +// "application/json" --> JSON binding +// "application/xml" --> XML binding +// otherwise --> returns an error +// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. +// It decodes the json payload into the struct specified as a pointer. +// Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid. +func (c *Context) ShouldBind(obj interface{}) error { + b := binding.Default(c.Request.Method, c.ContentType()) + return c.ShouldBindWith(obj, b) +} + +// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON). +func (c *Context) ShouldBindJSON(obj interface{}) error { + return c.ShouldBindWith(obj, binding.JSON) +} + +// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML). +func (c *Context) ShouldBindXML(obj interface{}) error { + return c.ShouldBindWith(obj, binding.XML) +} + +// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). +func (c *Context) ShouldBindQuery(obj interface{}) error { + return c.ShouldBindWith(obj, binding.Query) +} + +// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML). +func (c *Context) ShouldBindYAML(obj interface{}) error { + return c.ShouldBindWith(obj, binding.YAML) +} + +// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). +func (c *Context) ShouldBindHeader(obj interface{}) error { + return c.ShouldBindWith(obj, binding.Header) +} + +// ShouldBindUri binds the passed struct pointer using the specified binding engine. +func (c *Context) ShouldBindUri(obj interface{}) error { + m := make(map[string][]string) + for _, v := range c.Params { + m[v.Key] = []string{v.Value} + } + return binding.Uri.BindUri(m, obj) +} + +// ShouldBindWith binds the passed struct pointer using the specified binding engine. +// See the binding package. +func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { + return b.Bind(c.Request, obj) +} + +// ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request +// body into the context, and reuse when it is called again. +// +// NOTE: This method reads the body before binding. So you should use +// ShouldBindWith for better performance if you need to call only once. +func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) { + var body []byte + if cb, ok := c.Get(BodyBytesKey); ok { + if cbb, ok := cb.([]byte); ok { + body = cbb + } + } + if body == nil { + body, err = ioutil.ReadAll(c.Request.Body) + if err != nil { + return err + } + c.Set(BodyBytesKey, body) + } + return bb.BindBody(body, obj) +} + +// ClientIP implements a best effort algorithm to return the real client IP. +// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. +// If it's it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). +// If the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy, +// the remote IP (coming form Request.RemoteAddr) is returned. +func (c *Context) ClientIP() string { + if c.engine.AppEngine { + if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { + return addr + } + } + + remoteIP, trusted := c.RemoteIP() + if remoteIP == nil { + return "" + } + + if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { + for _, headerName := range c.engine.RemoteIPHeaders { + ip, valid := validateHeader(c.requestHeader(headerName)) + if valid { + return ip + } + } + } + return remoteIP.String() +} + +// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port). +// It also checks if the remoteIP is a trusted proxy or not. +// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks +// defined in Engine.TrustedProxies +func (c *Context) RemoteIP() (net.IP, bool) { + ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) + if err != nil { + return nil, false + } + remoteIP := net.ParseIP(ip) + if remoteIP == nil { + return nil, false + } + + if c.engine.trustedCIDRs != nil { + for _, cidr := range c.engine.trustedCIDRs { + if cidr.Contains(remoteIP) { + return remoteIP, true + } + } + } + + return remoteIP, false +} + +func validateHeader(header string) (clientIP string, valid bool) { + if header == "" { + return "", false + } + items := strings.Split(header, ",") + for i, ipStr := range items { + ipStr = strings.TrimSpace(ipStr) + ip := net.ParseIP(ipStr) + if ip == nil { + return "", false + } + + // We need to return the first IP in the list, but, + // we should not early return since we need to validate that + // the rest of the header is syntactically valid + if i == 0 { + clientIP = ipStr + valid = true + } + } + return +} + +// ContentType returns the Content-Type header of the request. +func (c *Context) ContentType() string { + return filterFlags(c.requestHeader("Content-Type")) +} + +// IsWebsocket returns true if the request headers indicate that a websocket +// handshake is being initiated by the client. +func (c *Context) IsWebsocket() bool { + if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") && + strings.EqualFold(c.requestHeader("Upgrade"), "websocket") { + return true + } + return false +} + +func (c *Context) requestHeader(key string) string { + return c.Request.Header.Get(key) +} + +/************************************/ +/******** RESPONSE RENDERING ********/ +/************************************/ + +// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function. +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == http.StatusNoContent: + return false + case status == http.StatusNotModified: + return false + } + return true +} + +// Status sets the HTTP response code. +func (c *Context) Status(code int) { + c.Writer.WriteHeader(code) +} + +// Header is a intelligent shortcut for c.Writer.Header().Set(key, value). +// It writes a header in the response. +// If value == "", this method removes the header `c.Writer.Header().Del(key)` +func (c *Context) Header(key, value string) { + if value == "" { + c.Writer.Header().Del(key) + return + } + c.Writer.Header().Set(key, value) +} + +// GetHeader returns value from request headers. +func (c *Context) GetHeader(key string) string { + return c.requestHeader(key) +} + +// GetRawData return stream data. +func (c *Context) GetRawData() ([]byte, error) { + return ioutil.ReadAll(c.Request.Body) +} + +// SetSameSite with cookie +func (c *Context) SetSameSite(samesite http.SameSite) { + c.sameSite = samesite +} + +// SetCookie adds a Set-Cookie header to the ResponseWriter's headers. +// The provided cookie must have a valid Name. Invalid cookies may be +// silently dropped. +func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { + if path == "" { + path = "/" + } + http.SetCookie(c.Writer, &http.Cookie{ + Name: name, + Value: url.QueryEscape(value), + MaxAge: maxAge, + Path: path, + Domain: domain, + SameSite: c.sameSite, + Secure: secure, + HttpOnly: httpOnly, + }) +} + +// Cookie returns the named cookie provided in the request or +// ErrNoCookie if not found. And return the named cookie is unescaped. +// If multiple cookies match the given name, only one cookie will +// be returned. +func (c *Context) Cookie(name string) (string, error) { + cookie, err := c.Request.Cookie(name) + if err != nil { + return "", err + } + val, _ := url.QueryUnescape(cookie.Value) + return val, nil +} + +// Render writes the response headers and calls render.Render to render data. +func (c *Context) Render(code int, r render.Render) { + c.Status(code) + + if !bodyAllowedForStatus(code) { + r.WriteContentType(c.Writer) + c.Writer.WriteHeaderNow() + return + } + + if err := r.Render(c.Writer); err != nil { + panic(err) + } +} + +// HTML renders the HTTP template specified by its file name. +// It also updates the HTTP code and sets the Content-Type as "text/html". +// See http://golang.org/doc/articles/wiki/ +func (c *Context) HTML(code int, name string, obj interface{}) { + instance := c.engine.HTMLRender.Instance(name, obj) + c.Render(code, instance) +} + +// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. +// It also sets the Content-Type as "application/json". +// WARNING: we recommend to use this only for development purposes since printing pretty JSON is +// more CPU and bandwidth consuming. Use Context.JSON() instead. +func (c *Context) IndentedJSON(code int, obj interface{}) { + c.Render(code, render.IndentedJSON{Data: obj}) +} + +// SecureJSON serializes the given struct as Secure JSON into the response body. +// Default prepends "while(1)," to response body if the given struct is array values. +// It also sets the Content-Type as "application/json". +func (c *Context) SecureJSON(code int, obj interface{}) { + c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) +} + +// JSONP serializes the given struct as JSON into the response body. +// It adds padding to response body to request data from a server residing in a different domain than the client. +// It also sets the Content-Type as "application/javascript". +func (c *Context) JSONP(code int, obj interface{}) { + callback := c.DefaultQuery("callback", "") + if callback == "" { + c.Render(code, render.JSON{Data: obj}) + return + } + c.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) +} + +// JSON serializes the given struct as JSON into the response body. +// It also sets the Content-Type as "application/json". +func (c *Context) JSON(code int, obj interface{}) { + c.Render(code, render.JSON{Data: obj}) +} + +// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. +// It also sets the Content-Type as "application/json". +func (c *Context) AsciiJSON(code int, obj interface{}) { + c.Render(code, render.AsciiJSON{Data: obj}) +} + +// PureJSON serializes the given struct as JSON into the response body. +// PureJSON, unlike JSON, does not replace special html characters with their unicode entities. +func (c *Context) PureJSON(code int, obj interface{}) { + c.Render(code, render.PureJSON{Data: obj}) +} + +// XML serializes the given struct as XML into the response body. +// It also sets the Content-Type as "application/xml". +func (c *Context) XML(code int, obj interface{}) { + c.Render(code, render.XML{Data: obj}) +} + +// YAML serializes the given struct as YAML into the response body. +func (c *Context) YAML(code int, obj interface{}) { + c.Render(code, render.YAML{Data: obj}) +} + +// ProtoBuf serializes the given struct as ProtoBuf into the response body. +func (c *Context) ProtoBuf(code int, obj interface{}) { + c.Render(code, render.ProtoBuf{Data: obj}) +} + +// String writes the given string into the response body. +func (c *Context) String(code int, format string, values ...interface{}) { + c.Render(code, render.String{Format: format, Data: values}) +} + +// Redirect returns a HTTP redirect to the specific location. +func (c *Context) Redirect(code int, location string) { + c.Render(-1, render.Redirect{ + Code: code, + Location: location, + Request: c.Request, + }) +} + +// Data writes some data into the body stream and updates the HTTP code. +func (c *Context) Data(code int, contentType string, data []byte) { + c.Render(code, render.Data{ + ContentType: contentType, + Data: data, + }) +} + +// DataFromReader writes the specified reader into the body stream and updates the HTTP code. +func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) { + c.Render(code, render.Reader{ + Headers: extraHeaders, + ContentType: contentType, + ContentLength: contentLength, + Reader: reader, + }) +} + +// File writes the specified file into the body stream in an efficient way. +func (c *Context) File(filepath string) { + http.ServeFile(c.Writer, c.Request, filepath) +} + +// FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way. +func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { + defer func(old string) { + c.Request.URL.Path = old + }(c.Request.URL.Path) + + c.Request.URL.Path = filepath + + http.FileServer(fs).ServeHTTP(c.Writer, c.Request) +} + +// FileAttachment writes the specified file into the body stream in an efficient way +// On the client side, the file will typically be downloaded with the given filename +func (c *Context) FileAttachment(filepath, filename string) { + c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + http.ServeFile(c.Writer, c.Request, filepath) +} + +// SSEvent writes a Server-Sent Event into the body stream. +func (c *Context) SSEvent(name string, message interface{}) { + c.Render(-1, sse.Event{ + Event: name, + Data: message, + }) +} + +// Stream sends a streaming response and returns a boolean +// indicates "Is client disconnected in middle of stream" +func (c *Context) Stream(step func(w io.Writer) bool) bool { + w := c.Writer + clientGone := w.CloseNotify() + for { + select { + case <-clientGone: + return true + default: + keepOpen := step(w) + w.Flush() + if !keepOpen { + return false + } + } + } +} + +/************************************/ +/******** CONTENT NEGOTIATION *******/ +/************************************/ + +// Negotiate contains all negotiations data. +type Negotiate struct { + Offered []string + HTMLName string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + YAMLData interface{} + Data interface{} +} + +// Negotiate calls different Render according acceptable Accept format. +func (c *Context) Negotiate(code int, config Negotiate) { + switch c.NegotiateFormat(config.Offered...) { + case binding.MIMEJSON: + data := chooseData(config.JSONData, config.Data) + c.JSON(code, data) + + case binding.MIMEHTML: + data := chooseData(config.HTMLData, config.Data) + c.HTML(code, config.HTMLName, data) + + case binding.MIMEXML: + data := chooseData(config.XMLData, config.Data) + c.XML(code, data) + + case binding.MIMEYAML: + data := chooseData(config.YAMLData, config.Data) + c.YAML(code, data) + + default: + c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck + } +} + +// NegotiateFormat returns an acceptable Accept format. +func (c *Context) NegotiateFormat(offered ...string) string { + assert1(len(offered) > 0, "you must provide at least one offer") + + if c.Accepted == nil { + c.Accepted = parseAccept(c.requestHeader("Accept")) + } + if len(c.Accepted) == 0 { + return offered[0] + } + for _, accepted := range c.Accepted { + for _, offer := range offered { + // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, + // therefore we can just iterate over the string without casting it into []rune + i := 0 + for ; i < len(accepted); i++ { + if accepted[i] == '*' || offer[i] == '*' { + return offer + } + if accepted[i] != offer[i] { + break + } + } + if i == len(accepted) { + return offer + } + } + } + return "" +} + +// SetAccepted sets Accept header data. +func (c *Context) SetAccepted(formats ...string) { + c.Accepted = formats +} + +/************************************/ +/***** GOLANG.ORG/X/NET/CONTEXT *****/ +/************************************/ + +// Deadline always returns that there is no deadline (ok==false), +// maybe you want to use Request.Context().Deadline() instead. +func (c *Context) Deadline() (deadline time.Time, ok bool) { + return +} + +// Done always returns nil (chan which will wait forever), +// if you want to abort your work when the connection was closed +// you should use Request.Context().Done() instead. +func (c *Context) Done() <-chan struct{} { + return nil +} + +// Err always returns nil, maybe you want to use Request.Context().Err() instead. +func (c *Context) Err() error { + return nil +} + +// Value returns the value associated with this context for key, or nil +// if no value is associated with key. Successive calls to Value with +// the same key returns the same result. +func (c *Context) Value(key interface{}) interface{} { + if key == 0 { + return c.Request + } + if keyAsString, ok := key.(string); ok { + val, _ := c.Get(keyAsString) + return val + } + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/context_appengine.go b/vendor/github.com/gin-gonic/gin/context_appengine.go new file mode 100644 index 0000000..d565843 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/context_appengine.go @@ -0,0 +1,12 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build appengine +// +build appengine + +package gin + +func init() { + defaultAppEngine = true +} diff --git a/vendor/github.com/gin-gonic/gin/debug.go b/vendor/github.com/gin-gonic/gin/debug.go new file mode 100644 index 0000000..4c7cd0c --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/debug.go @@ -0,0 +1,103 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "fmt" + "html/template" + "runtime" + "strconv" + "strings" +) + +const ginSupportMinGoVer = 12 + +// IsDebugging returns true if the framework is running in debug mode. +// Use SetMode(gin.ReleaseMode) to disable debug mode. +func IsDebugging() bool { + return ginMode == debugCode +} + +// DebugPrintRouteFunc indicates debug log output format. +var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int) + +func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) { + if IsDebugging() { + nuHandlers := len(handlers) + handlerName := nameOfFunction(handlers.Last()) + if DebugPrintRouteFunc == nil { + debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) + } else { + DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers) + } + } +} + +func debugPrintLoadTemplate(tmpl *template.Template) { + if IsDebugging() { + var buf strings.Builder + for _, tmpl := range tmpl.Templates() { + buf.WriteString("\t- ") + buf.WriteString(tmpl.Name()) + buf.WriteString("\n") + } + debugPrint("Loaded HTML Templates (%d): \n%s\n", len(tmpl.Templates()), buf.String()) + } +} + +func debugPrint(format string, values ...interface{}) { + if IsDebugging() { + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...) + } +} + +func getMinVer(v string) (uint64, error) { + first := strings.IndexByte(v, '.') + last := strings.LastIndexByte(v, '.') + if first == last { + return strconv.ParseUint(v[first+1:], 10, 64) + } + return strconv.ParseUint(v[first+1:last], 10, 64) +} + +func debugPrintWARNINGDefault() { + if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer { + debugPrint(`[WARNING] Now Gin requires Go 1.12+. + +`) + } + debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. + +`) +} + +func debugPrintWARNINGNew() { + debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production. + - using env: export GIN_MODE=release + - using code: gin.SetMode(gin.ReleaseMode) + +`) +} + +func debugPrintWARNINGSetHTMLTemplate() { + debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called +at initialization. ie. before any route is registered or the router is listening in a socket: + + router := gin.Default() + router.SetHTMLTemplate(template) // << good place + +`) +} + +func debugPrintError(err error) { + if err != nil { + if IsDebugging() { + fmt.Fprintf(DefaultErrorWriter, "[GIN-debug] [ERROR] %v\n", err) + } + } +} diff --git a/vendor/github.com/gin-gonic/gin/deprecated.go b/vendor/github.com/gin-gonic/gin/deprecated.go new file mode 100644 index 0000000..ab44742 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/deprecated.go @@ -0,0 +1,21 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "log" + + "github.com/gin-gonic/gin/binding" +) + +// BindWith binds the passed struct pointer using the specified binding engine. +// See the binding package. +func (c *Context) BindWith(obj interface{}, b binding.Binding) error { + log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to + be deprecated, please check issue #662 and either use MustBindWith() if you + want HTTP 400 to be automatically returned if any error occur, or use + ShouldBindWith() if you need to manage the error.`) + return c.MustBindWith(obj, b) +} diff --git a/vendor/github.com/gin-gonic/gin/doc.go b/vendor/github.com/gin-gonic/gin/doc.go new file mode 100644 index 0000000..1bd0386 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/doc.go @@ -0,0 +1,6 @@ +/* +Package gin implements a HTTP web framework called gin. + +See https://gin-gonic.com/ for more information about gin. +*/ +package gin // import "github.com/gin-gonic/gin" diff --git a/vendor/github.com/gin-gonic/gin/errors.go b/vendor/github.com/gin-gonic/gin/errors.go new file mode 100644 index 0000000..0f276c1 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/errors.go @@ -0,0 +1,174 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "fmt" + "reflect" + "strings" + + "github.com/gin-gonic/gin/internal/json" +) + +// ErrorType is an unsigned 64-bit error code as defined in the gin spec. +type ErrorType uint64 + +const ( + // ErrorTypeBind is used when Context.Bind() fails. + ErrorTypeBind ErrorType = 1 << 63 + // ErrorTypeRender is used when Context.Render() fails. + ErrorTypeRender ErrorType = 1 << 62 + // ErrorTypePrivate indicates a private error. + ErrorTypePrivate ErrorType = 1 << 0 + // ErrorTypePublic indicates a public error. + ErrorTypePublic ErrorType = 1 << 1 + // ErrorTypeAny indicates any other error. + ErrorTypeAny ErrorType = 1<<64 - 1 + // ErrorTypeNu indicates any other error. + ErrorTypeNu = 2 +) + +// Error represents a error's specification. +type Error struct { + Err error + Type ErrorType + Meta interface{} +} + +type errorMsgs []*Error + +var _ error = &Error{} + +// SetType sets the error's type. +func (msg *Error) SetType(flags ErrorType) *Error { + msg.Type = flags + return msg +} + +// SetMeta sets the error's meta data. +func (msg *Error) SetMeta(data interface{}) *Error { + msg.Meta = data + return msg +} + +// JSON creates a properly formatted JSON +func (msg *Error) JSON() interface{} { + jsonData := H{} + if msg.Meta != nil { + value := reflect.ValueOf(msg.Meta) + switch value.Kind() { + case reflect.Struct: + return msg.Meta + case reflect.Map: + for _, key := range value.MapKeys() { + jsonData[key.String()] = value.MapIndex(key).Interface() + } + default: + jsonData["meta"] = msg.Meta + } + } + if _, ok := jsonData["error"]; !ok { + jsonData["error"] = msg.Error() + } + return jsonData +} + +// MarshalJSON implements the json.Marshaller interface. +func (msg *Error) MarshalJSON() ([]byte, error) { + return json.Marshal(msg.JSON()) +} + +// Error implements the error interface. +func (msg Error) Error() string { + return msg.Err.Error() +} + +// IsType judges one error. +func (msg *Error) IsType(flags ErrorType) bool { + return (msg.Type & flags) > 0 +} + +// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap() +func (msg *Error) Unwrap() error { + return msg.Err +} + +// ByType returns a readonly copy filtered the byte. +// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic. +func (a errorMsgs) ByType(typ ErrorType) errorMsgs { + if len(a) == 0 { + return nil + } + if typ == ErrorTypeAny { + return a + } + var result errorMsgs + for _, msg := range a { + if msg.IsType(typ) { + result = append(result, msg) + } + } + return result +} + +// Last returns the last error in the slice. It returns nil if the array is empty. +// Shortcut for errors[len(errors)-1]. +func (a errorMsgs) Last() *Error { + if length := len(a); length > 0 { + return a[length-1] + } + return nil +} + +// Errors returns an array will all the error messages. +// Example: +// c.Error(errors.New("first")) +// c.Error(errors.New("second")) +// c.Error(errors.New("third")) +// c.Errors.Errors() // == []string{"first", "second", "third"} +func (a errorMsgs) Errors() []string { + if len(a) == 0 { + return nil + } + errorStrings := make([]string, len(a)) + for i, err := range a { + errorStrings[i] = err.Error() + } + return errorStrings +} + +func (a errorMsgs) JSON() interface{} { + switch length := len(a); length { + case 0: + return nil + case 1: + return a.Last().JSON() + default: + jsonData := make([]interface{}, length) + for i, err := range a { + jsonData[i] = err.JSON() + } + return jsonData + } +} + +// MarshalJSON implements the json.Marshaller interface. +func (a errorMsgs) MarshalJSON() ([]byte, error) { + return json.Marshal(a.JSON()) +} + +func (a errorMsgs) String() string { + if len(a) == 0 { + return "" + } + var buffer strings.Builder + for i, msg := range a { + fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err) + if msg.Meta != nil { + fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) + } + } + return buffer.String() +} diff --git a/vendor/github.com/gin-gonic/gin/fs.go b/vendor/github.com/gin-gonic/gin/fs.go new file mode 100644 index 0000000..007d9b7 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/fs.go @@ -0,0 +1,45 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "net/http" + "os" +) + +type onlyFilesFS struct { + fs http.FileSystem +} + +type neuteredReaddirFile struct { + http.File +} + +// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally +// in router.Static(). +// if listDirectory == true, then it works the same as http.Dir() otherwise it returns +// a filesystem that prevents http.FileServer() to list the directory files. +func Dir(root string, listDirectory bool) http.FileSystem { + fs := http.Dir(root) + if listDirectory { + return fs + } + return &onlyFilesFS{fs} +} + +// Open conforms to http.Filesystem. +func (fs onlyFilesFS) Open(name string) (http.File, error) { + f, err := fs.fs.Open(name) + if err != nil { + return nil, err + } + return neuteredReaddirFile{f}, nil +} + +// Readdir overrides the http.File default implementation. +func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { + // this disables directory listing + return nil, nil +} diff --git a/vendor/github.com/gin-gonic/gin/gin.go b/vendor/github.com/gin-gonic/gin/gin.go new file mode 100644 index 0000000..03a0e12 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/gin.go @@ -0,0 +1,577 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "fmt" + "html/template" + "net" + "net/http" + "os" + "path" + "strings" + "sync" + + "github.com/gin-gonic/gin/internal/bytesconv" + "github.com/gin-gonic/gin/render" +) + +const defaultMultipartMemory = 32 << 20 // 32 MB + +var ( + default404Body = []byte("404 page not found") + default405Body = []byte("405 method not allowed") +) + +var defaultAppEngine bool + +// HandlerFunc defines the handler used by gin middleware as return value. +type HandlerFunc func(*Context) + +// HandlersChain defines a HandlerFunc array. +type HandlersChain []HandlerFunc + +// Last returns the last handler in the chain. ie. the last handler is the main one. +func (c HandlersChain) Last() HandlerFunc { + if length := len(c); length > 0 { + return c[length-1] + } + return nil +} + +// RouteInfo represents a request route's specification which contains method and path and its handler. +type RouteInfo struct { + Method string + Path string + Handler string + HandlerFunc HandlerFunc +} + +// RoutesInfo defines a RouteInfo array. +type RoutesInfo []RouteInfo + +// Engine is the framework's instance, it contains the muxer, middleware and configuration settings. +// Create an instance of Engine, by using New() or Default() +type Engine struct { + RouterGroup + + // Enables automatic redirection if the current route can't be matched but a + // handler for the path with (without) the trailing slash exists. + // For example if /foo/ is requested but a route only exists for /foo, the + // client is redirected to /foo with http status code 301 for GET requests + // and 307 for all other request methods. + RedirectTrailingSlash bool + + // If enabled, the router tries to fix the current request path, if no + // handle is registered for it. + // First superfluous path elements like ../ or // are removed. + // Afterwards the router does a case-insensitive lookup of the cleaned path. + // If a handle can be found for this route, the router makes a redirection + // to the corrected path with status code 301 for GET requests and 307 for + // all other request methods. + // For example /FOO and /..//Foo could be redirected to /foo. + // RedirectTrailingSlash is independent of this option. + RedirectFixedPath bool + + // If enabled, the router checks if another method is allowed for the + // current route, if the current request can not be routed. + // If this is the case, the request is answered with 'Method Not Allowed' + // and HTTP status code 405. + // If no other Method is allowed, the request is delegated to the NotFound + // handler. + HandleMethodNotAllowed bool + + // If enabled, client IP will be parsed from the request's headers that + // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was + // fetched, it falls back to the IP obtained from + // `(*gin.Context).Request.RemoteAddr`. + ForwardedByClientIP bool + + // List of headers used to obtain the client IP when + // `(*gin.Engine).ForwardedByClientIP` is `true` and + // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the + // network origins of `(*gin.Engine).TrustedProxies`. + RemoteIPHeaders []string + + // List of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or + // IPv6 CIDRs) from which to trust request's headers that contain + // alternative client IP when `(*gin.Engine).ForwardedByClientIP` is + // `true`. + TrustedProxies []string + + // #726 #755 If enabled, it will trust some headers starting with + // 'X-AppEngine...' for better integration with that PaaS. + AppEngine bool + + // If enabled, the url.RawPath will be used to find parameters. + UseRawPath bool + + // If true, the path value will be unescaped. + // If UseRawPath is false (by default), the UnescapePathValues effectively is true, + // as url.Path gonna be used, which is already unescaped. + UnescapePathValues bool + + // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm + // method call. + MaxMultipartMemory int64 + + // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. + // See the PR #1817 and issue #1644 + RemoveExtraSlash bool + + delims render.Delims + secureJSONPrefix string + HTMLRender render.HTMLRender + FuncMap template.FuncMap + allNoRoute HandlersChain + allNoMethod HandlersChain + noRoute HandlersChain + noMethod HandlersChain + pool sync.Pool + trees methodTrees + maxParams uint16 + trustedCIDRs []*net.IPNet +} + +var _ IRouter = &Engine{} + +// New returns a new blank Engine instance without any middleware attached. +// By default the configuration is: +// - RedirectTrailingSlash: true +// - RedirectFixedPath: false +// - HandleMethodNotAllowed: false +// - ForwardedByClientIP: true +// - UseRawPath: false +// - UnescapePathValues: true +func New() *Engine { + debugPrintWARNINGNew() + engine := &Engine{ + RouterGroup: RouterGroup{ + Handlers: nil, + basePath: "/", + root: true, + }, + FuncMap: template.FuncMap{}, + RedirectTrailingSlash: true, + RedirectFixedPath: false, + HandleMethodNotAllowed: false, + ForwardedByClientIP: true, + RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"}, + TrustedProxies: []string{"0.0.0.0/0"}, + AppEngine: defaultAppEngine, + UseRawPath: false, + RemoveExtraSlash: false, + UnescapePathValues: true, + MaxMultipartMemory: defaultMultipartMemory, + trees: make(methodTrees, 0, 9), + delims: render.Delims{Left: "{{", Right: "}}"}, + secureJSONPrefix: "while(1);", + } + engine.RouterGroup.engine = engine + engine.pool.New = func() interface{} { + return engine.allocateContext() + } + return engine +} + +// Default returns an Engine instance with the Logger and Recovery middleware already attached. +func Default() *Engine { + debugPrintWARNINGDefault() + engine := New() + engine.Use(Logger(), Recovery()) + return engine +} + +func (engine *Engine) allocateContext() *Context { + v := make(Params, 0, engine.maxParams) + return &Context{engine: engine, params: &v} +} + +// Delims sets template left and right delims and returns a Engine instance. +func (engine *Engine) Delims(left, right string) *Engine { + engine.delims = render.Delims{Left: left, Right: right} + return engine +} + +// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON. +func (engine *Engine) SecureJsonPrefix(prefix string) *Engine { + engine.secureJSONPrefix = prefix + return engine +} + +// LoadHTMLGlob loads HTML files identified by glob pattern +// and associates the result with HTML renderer. +func (engine *Engine) LoadHTMLGlob(pattern string) { + left := engine.delims.Left + right := engine.delims.Right + templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern)) + + if IsDebugging() { + debugPrintLoadTemplate(templ) + engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims} + return + } + + engine.SetHTMLTemplate(templ) +} + +// LoadHTMLFiles loads a slice of HTML files +// and associates the result with HTML renderer. +func (engine *Engine) LoadHTMLFiles(files ...string) { + if IsDebugging() { + engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims} + return + } + + templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...)) + engine.SetHTMLTemplate(templ) +} + +// SetHTMLTemplate associate a template with HTML renderer. +func (engine *Engine) SetHTMLTemplate(templ *template.Template) { + if len(engine.trees) > 0 { + debugPrintWARNINGSetHTMLTemplate() + } + + engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)} +} + +// SetFuncMap sets the FuncMap used for template.FuncMap. +func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { + engine.FuncMap = funcMap +} + +// NoRoute adds handlers for NoRoute. It return a 404 code by default. +func (engine *Engine) NoRoute(handlers ...HandlerFunc) { + engine.noRoute = handlers + engine.rebuild404Handlers() +} + +// NoMethod sets the handlers called when... TODO. +func (engine *Engine) NoMethod(handlers ...HandlerFunc) { + engine.noMethod = handlers + engine.rebuild405Handlers() +} + +// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be +// included in the handlers chain for every single request. Even 404, 405, static files... +// For example, this is the right place for a logger or error management middleware. +func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { + engine.RouterGroup.Use(middleware...) + engine.rebuild404Handlers() + engine.rebuild405Handlers() + return engine +} + +func (engine *Engine) rebuild404Handlers() { + engine.allNoRoute = engine.combineHandlers(engine.noRoute) +} + +func (engine *Engine) rebuild405Handlers() { + engine.allNoMethod = engine.combineHandlers(engine.noMethod) +} + +func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { + assert1(path[0] == '/', "path must begin with '/'") + assert1(method != "", "HTTP method can not be empty") + assert1(len(handlers) > 0, "there must be at least one handler") + + debugPrintRoute(method, path, handlers) + + root := engine.trees.get(method) + if root == nil { + root = new(node) + root.fullPath = "/" + engine.trees = append(engine.trees, methodTree{method: method, root: root}) + } + root.addRoute(path, handlers) + + // Update maxParams + if paramsCount := countParams(path); paramsCount > engine.maxParams { + engine.maxParams = paramsCount + } +} + +// Routes returns a slice of registered routes, including some useful information, such as: +// the http method, path and the handler name. +func (engine *Engine) Routes() (routes RoutesInfo) { + for _, tree := range engine.trees { + routes = iterate("", tree.method, routes, tree.root) + } + return routes +} + +func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { + path += root.path + if len(root.handlers) > 0 { + handlerFunc := root.handlers.Last() + routes = append(routes, RouteInfo{ + Method: method, + Path: path, + Handler: nameOfFunction(handlerFunc), + HandlerFunc: handlerFunc, + }) + } + for _, child := range root.children { + routes = iterate(path, method, routes, child) + } + return routes +} + +// Run attaches the router to a http.Server and starts listening and serving HTTP requests. +// It is a shortcut for http.ListenAndServe(addr, router) +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) Run(addr ...string) (err error) { + defer func() { debugPrintError(err) }() + + trustedCIDRs, err := engine.prepareTrustedCIDRs() + if err != nil { + return err + } + engine.trustedCIDRs = trustedCIDRs + address := resolveAddress(addr) + debugPrint("Listening and serving HTTP on %s\n", address) + err = http.ListenAndServe(address, engine) + return +} + +func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) { + if engine.TrustedProxies == nil { + return nil, nil + } + + cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies)) + for _, trustedProxy := range engine.TrustedProxies { + if !strings.Contains(trustedProxy, "/") { + ip := parseIP(trustedProxy) + if ip == nil { + return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy} + } + + switch len(ip) { + case net.IPv4len: + trustedProxy += "/32" + case net.IPv6len: + trustedProxy += "/128" + } + } + _, cidrNet, err := net.ParseCIDR(trustedProxy) + if err != nil { + return cidr, err + } + cidr = append(cidr, cidrNet) + } + return cidr, nil +} + +// parseIP parse a string representation of an IP and returns a net.IP with the +// minimum byte representation or nil if input is invalid. +func parseIP(ip string) net.IP { + parsedIP := net.ParseIP(ip) + + if ipv4 := parsedIP.To4(); ipv4 != nil { + // return ip in a 4-byte representation + return ipv4 + } + + // return ip in a 16-byte representation or nil + return parsedIP +} + +// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests. +// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) { + debugPrint("Listening and serving HTTPS on %s\n", addr) + defer func() { debugPrintError(err) }() + + err = http.ListenAndServeTLS(addr, certFile, keyFile, engine) + return +} + +// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests +// through the specified unix socket (ie. a file). +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) RunUnix(file string) (err error) { + debugPrint("Listening and serving HTTP on unix:/%s", file) + defer func() { debugPrintError(err) }() + + listener, err := net.Listen("unix", file) + if err != nil { + return + } + defer listener.Close() + defer os.Remove(file) + + err = http.Serve(listener, engine) + return +} + +// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests +// through the specified file descriptor. +// Note: this method will block the calling goroutine indefinitely unless an error happens. +func (engine *Engine) RunFd(fd int) (err error) { + debugPrint("Listening and serving HTTP on fd@%d", fd) + defer func() { debugPrintError(err) }() + + f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd)) + listener, err := net.FileListener(f) + if err != nil { + return + } + defer listener.Close() + err = engine.RunListener(listener) + return +} + +// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests +// through the specified net.Listener +func (engine *Engine) RunListener(listener net.Listener) (err error) { + debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr()) + defer func() { debugPrintError(err) }() + err = http.Serve(listener, engine) + return +} + +// ServeHTTP conforms to the http.Handler interface. +func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { + c := engine.pool.Get().(*Context) + c.writermem.reset(w) + c.Request = req + c.reset() + + engine.handleHTTPRequest(c) + + engine.pool.Put(c) +} + +// HandleContext re-enter a context that has been rewritten. +// This can be done by setting c.Request.URL.Path to your new target. +// Disclaimer: You can loop yourself to death with this, use wisely. +func (engine *Engine) HandleContext(c *Context) { + oldIndexValue := c.index + c.reset() + engine.handleHTTPRequest(c) + + c.index = oldIndexValue +} + +func (engine *Engine) handleHTTPRequest(c *Context) { + httpMethod := c.Request.Method + rPath := c.Request.URL.Path + unescape := false + if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 { + rPath = c.Request.URL.RawPath + unescape = engine.UnescapePathValues + } + + if engine.RemoveExtraSlash { + rPath = cleanPath(rPath) + } + + // Find root of the tree for the given HTTP method + t := engine.trees + for i, tl := 0, len(t); i < tl; i++ { + if t[i].method != httpMethod { + continue + } + root := t[i].root + // Find route in tree + value := root.getValue(rPath, c.params, unescape) + if value.params != nil { + c.Params = *value.params + } + if value.handlers != nil { + c.handlers = value.handlers + c.fullPath = value.fullPath + c.Next() + c.writermem.WriteHeaderNow() + return + } + if httpMethod != "CONNECT" && rPath != "/" { + if value.tsr && engine.RedirectTrailingSlash { + redirectTrailingSlash(c) + return + } + if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { + return + } + } + break + } + + if engine.HandleMethodNotAllowed { + for _, tree := range engine.trees { + if tree.method == httpMethod { + continue + } + if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil { + c.handlers = engine.allNoMethod + serveError(c, http.StatusMethodNotAllowed, default405Body) + return + } + } + } + c.handlers = engine.allNoRoute + serveError(c, http.StatusNotFound, default404Body) +} + +var mimePlain = []string{MIMEPlain} + +func serveError(c *Context, code int, defaultMessage []byte) { + c.writermem.status = code + c.Next() + if c.writermem.Written() { + return + } + if c.writermem.Status() == code { + c.writermem.Header()["Content-Type"] = mimePlain + _, err := c.Writer.Write(defaultMessage) + if err != nil { + debugPrint("cannot write message to writer during serve error: %v", err) + } + return + } + c.writermem.WriteHeaderNow() +} + +func redirectTrailingSlash(c *Context) { + req := c.Request + p := req.URL.Path + if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." { + p = prefix + "/" + req.URL.Path + } + req.URL.Path = p + "/" + if length := len(p); length > 1 && p[length-1] == '/' { + req.URL.Path = p[:length-1] + } + redirectRequest(c) +} + +func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { + req := c.Request + rPath := req.URL.Path + + if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok { + req.URL.Path = bytesconv.BytesToString(fixedPath) + redirectRequest(c) + return true + } + return false +} + +func redirectRequest(c *Context) { + req := c.Request + rPath := req.URL.Path + rURL := req.URL.String() + + code := http.StatusMovedPermanently // Permanent redirect, request with GET method + if req.Method != http.MethodGet { + code = http.StatusTemporaryRedirect + } + debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) + http.Redirect(c.Writer, req, rURL, code) + c.writermem.WriteHeaderNow() +} diff --git a/vendor/github.com/gin-gonic/gin/go.mod b/vendor/github.com/gin-gonic/gin/go.mod new file mode 100644 index 0000000..884ff85 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/go.mod @@ -0,0 +1,14 @@ +module github.com/gin-gonic/gin + +go 1.13 + +require ( + github.com/gin-contrib/sse v0.1.0 + github.com/go-playground/validator/v10 v10.4.1 + github.com/golang/protobuf v1.3.3 + github.com/json-iterator/go v1.1.9 + github.com/mattn/go-isatty v0.0.12 + github.com/stretchr/testify v1.4.0 + github.com/ugorji/go/codec v1.1.7 + gopkg.in/yaml.v2 v2.2.8 +) diff --git a/vendor/github.com/gin-gonic/gin/go.sum b/vendor/github.com/gin-gonic/gin/go.sum new file mode 100644 index 0000000..a64b331 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/go.sum @@ -0,0 +1,52 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go b/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go new file mode 100644 index 0000000..86e4c4d --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go @@ -0,0 +1,24 @@ +// Copyright 2020 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package bytesconv + +import ( + "unsafe" +) + +// StringToBytes converts string to byte slice without a memory allocation. +func StringToBytes(s string) []byte { + return *(*[]byte)(unsafe.Pointer( + &struct { + string + Cap int + }{s, len(s)}, + )) +} + +// BytesToString converts byte slice to string without a memory allocation. +func BytesToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/vendor/github.com/gin-gonic/gin/internal/json/json.go b/vendor/github.com/gin-gonic/gin/internal/json/json.go new file mode 100644 index 0000000..172aeb2 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/internal/json/json.go @@ -0,0 +1,23 @@ +// Copyright 2017 Bo-Yi Wu. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !jsoniter +// +build !jsoniter + +package json + +import "encoding/json" + +var ( + // Marshal is exported by gin/json package. + Marshal = json.Marshal + // Unmarshal is exported by gin/json package. + Unmarshal = json.Unmarshal + // MarshalIndent is exported by gin/json package. + MarshalIndent = json.MarshalIndent + // NewDecoder is exported by gin/json package. + NewDecoder = json.NewDecoder + // NewEncoder is exported by gin/json package. + NewEncoder = json.NewEncoder +) diff --git a/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go b/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go new file mode 100644 index 0000000..232f8dc --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go @@ -0,0 +1,24 @@ +// Copyright 2017 Bo-Yi Wu. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build jsoniter +// +build jsoniter + +package json + +import jsoniter "github.com/json-iterator/go" + +var ( + json = jsoniter.ConfigCompatibleWithStandardLibrary + // Marshal is exported by gin/json package. + Marshal = json.Marshal + // Unmarshal is exported by gin/json package. + Unmarshal = json.Unmarshal + // MarshalIndent is exported by gin/json package. + MarshalIndent = json.MarshalIndent + // NewDecoder is exported by gin/json package. + NewDecoder = json.NewDecoder + // NewEncoder is exported by gin/json package. + NewEncoder = json.NewEncoder +) diff --git a/vendor/github.com/gin-gonic/gin/logger.go b/vendor/github.com/gin-gonic/gin/logger.go new file mode 100644 index 0000000..d361b74 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/logger.go @@ -0,0 +1,271 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "fmt" + "io" + "net/http" + "os" + "time" + + "github.com/mattn/go-isatty" +) + +type consoleColorModeValue int + +const ( + autoColor consoleColorModeValue = iota + disableColor + forceColor +) + +const ( + green = "\033[97;42m" + white = "\033[90;47m" + yellow = "\033[90;43m" + red = "\033[97;41m" + blue = "\033[97;44m" + magenta = "\033[97;45m" + cyan = "\033[97;46m" + reset = "\033[0m" +) + +var consoleColorMode = autoColor + +// LoggerConfig defines the config for Logger middleware. +type LoggerConfig struct { + // Optional. Default value is gin.defaultLogFormatter + Formatter LogFormatter + + // Output is a writer where logs are written. + // Optional. Default value is gin.DefaultWriter. + Output io.Writer + + // SkipPaths is a url path array which logs are not written. + // Optional. + SkipPaths []string +} + +// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter +type LogFormatter func(params LogFormatterParams) string + +// LogFormatterParams is the structure any formatter will be handed when time to log comes +type LogFormatterParams struct { + Request *http.Request + + // TimeStamp shows the time after the server returns a response. + TimeStamp time.Time + // StatusCode is HTTP response code. + StatusCode int + // Latency is how much time the server cost to process a certain request. + Latency time.Duration + // ClientIP equals Context's ClientIP method. + ClientIP string + // Method is the HTTP method given to the request. + Method string + // Path is a path the client requests. + Path string + // ErrorMessage is set if error has occurred in processing the request. + ErrorMessage string + // isTerm shows whether does gin's output descriptor refers to a terminal. + isTerm bool + // BodySize is the size of the Response Body + BodySize int + // Keys are the keys set on the request's context. + Keys map[string]interface{} +} + +// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal. +func (p *LogFormatterParams) StatusCodeColor() string { + code := p.StatusCode + + switch { + case code >= http.StatusOK && code < http.StatusMultipleChoices: + return green + case code >= http.StatusMultipleChoices && code < http.StatusBadRequest: + return white + case code >= http.StatusBadRequest && code < http.StatusInternalServerError: + return yellow + default: + return red + } +} + +// MethodColor is the ANSI color for appropriately logging http method to a terminal. +func (p *LogFormatterParams) MethodColor() string { + method := p.Method + + switch method { + case http.MethodGet: + return blue + case http.MethodPost: + return cyan + case http.MethodPut: + return yellow + case http.MethodDelete: + return red + case http.MethodPatch: + return green + case http.MethodHead: + return magenta + case http.MethodOptions: + return white + default: + return reset + } +} + +// ResetColor resets all escape attributes. +func (p *LogFormatterParams) ResetColor() string { + return reset +} + +// IsOutputColor indicates whether can colors be outputted to the log. +func (p *LogFormatterParams) IsOutputColor() bool { + return consoleColorMode == forceColor || (consoleColorMode == autoColor && p.isTerm) +} + +// defaultLogFormatter is the default log format function Logger middleware uses. +var defaultLogFormatter = func(param LogFormatterParams) string { + var statusColor, methodColor, resetColor string + if param.IsOutputColor() { + statusColor = param.StatusCodeColor() + methodColor = param.MethodColor() + resetColor = param.ResetColor() + } + + if param.Latency > time.Minute { + // Truncate in a golang < 1.8 safe way + param.Latency = param.Latency - param.Latency%time.Second + } + return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s", + param.TimeStamp.Format("2006/01/02 - 15:04:05"), + statusColor, param.StatusCode, resetColor, + param.Latency, + param.ClientIP, + methodColor, param.Method, resetColor, + param.Path, + param.ErrorMessage, + ) +} + +// DisableConsoleColor disables color output in the console. +func DisableConsoleColor() { + consoleColorMode = disableColor +} + +// ForceConsoleColor force color output in the console. +func ForceConsoleColor() { + consoleColorMode = forceColor +} + +// ErrorLogger returns a handlerfunc for any error type. +func ErrorLogger() HandlerFunc { + return ErrorLoggerT(ErrorTypeAny) +} + +// ErrorLoggerT returns a handlerfunc for a given error type. +func ErrorLoggerT(typ ErrorType) HandlerFunc { + return func(c *Context) { + c.Next() + errors := c.Errors.ByType(typ) + if len(errors) > 0 { + c.JSON(-1, errors) + } + } +} + +// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter. +// By default gin.DefaultWriter = os.Stdout. +func Logger() HandlerFunc { + return LoggerWithConfig(LoggerConfig{}) +} + +// LoggerWithFormatter instance a Logger middleware with the specified log format function. +func LoggerWithFormatter(f LogFormatter) HandlerFunc { + return LoggerWithConfig(LoggerConfig{ + Formatter: f, + }) +} + +// LoggerWithWriter instance a Logger middleware with the specified writer buffer. +// Example: os.Stdout, a file opened in write mode, a socket... +func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { + return LoggerWithConfig(LoggerConfig{ + Output: out, + SkipPaths: notlogged, + }) +} + +// LoggerWithConfig instance a Logger middleware with config. +func LoggerWithConfig(conf LoggerConfig) HandlerFunc { + formatter := conf.Formatter + if formatter == nil { + formatter = defaultLogFormatter + } + + out := conf.Output + if out == nil { + out = DefaultWriter + } + + notlogged := conf.SkipPaths + + isTerm := true + + if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) { + isTerm = false + } + + var skip map[string]struct{} + + if length := len(notlogged); length > 0 { + skip = make(map[string]struct{}, length) + + for _, path := range notlogged { + skip[path] = struct{}{} + } + } + + return func(c *Context) { + // Start timer + start := time.Now() + path := c.Request.URL.Path + raw := c.Request.URL.RawQuery + + // Process request + c.Next() + + // Log only when path is not being skipped + if _, ok := skip[path]; !ok { + param := LogFormatterParams{ + Request: c.Request, + isTerm: isTerm, + Keys: c.Keys, + } + + // Stop timer + param.TimeStamp = time.Now() + param.Latency = param.TimeStamp.Sub(start) + + param.ClientIP = c.ClientIP() + param.Method = c.Request.Method + param.StatusCode = c.Writer.Status() + param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String() + + param.BodySize = c.Writer.Size() + + if raw != "" { + path = path + "?" + raw + } + + param.Path = path + + fmt.Fprint(out, formatter(param)) + } + } +} diff --git a/vendor/github.com/gin-gonic/gin/mode.go b/vendor/github.com/gin-gonic/gin/mode.go new file mode 100644 index 0000000..c8813af --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/mode.go @@ -0,0 +1,92 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "io" + "os" + + "github.com/gin-gonic/gin/binding" +) + +// EnvGinMode indicates environment name for gin mode. +const EnvGinMode = "GIN_MODE" + +const ( + // DebugMode indicates gin mode is debug. + DebugMode = "debug" + // ReleaseMode indicates gin mode is release. + ReleaseMode = "release" + // TestMode indicates gin mode is test. + TestMode = "test" +) + +const ( + debugCode = iota + releaseCode + testCode +) + +// DefaultWriter is the default io.Writer used by Gin for debug output and +// middleware output like Logger() or Recovery(). +// Note that both Logger and Recovery provides custom ways to configure their +// output io.Writer. +// To support coloring in Windows use: +// import "github.com/mattn/go-colorable" +// gin.DefaultWriter = colorable.NewColorableStdout() +var DefaultWriter io.Writer = os.Stdout + +// DefaultErrorWriter is the default io.Writer used by Gin to debug errors +var DefaultErrorWriter io.Writer = os.Stderr + +var ginMode = debugCode +var modeName = DebugMode + +func init() { + mode := os.Getenv(EnvGinMode) + SetMode(mode) +} + +// SetMode sets gin mode according to input string. +func SetMode(value string) { + if value == "" { + value = DebugMode + } + + switch value { + case DebugMode: + ginMode = debugCode + case ReleaseMode: + ginMode = releaseCode + case TestMode: + ginMode = testCode + default: + panic("gin mode unknown: " + value + " (available mode: debug release test)") + } + + modeName = value +} + +// DisableBindValidation closes the default validator. +func DisableBindValidation() { + binding.Validator = nil +} + +// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumber to +// call the UseNumber method on the JSON Decoder instance. +func EnableJsonDecoderUseNumber() { + binding.EnableDecoderUseNumber = true +} + +// EnableJsonDecoderDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to +// call the DisallowUnknownFields method on the JSON Decoder instance. +func EnableJsonDecoderDisallowUnknownFields() { + binding.EnableDecoderDisallowUnknownFields = true +} + +// Mode returns currently gin mode. +func Mode() string { + return modeName +} diff --git a/vendor/github.com/gin-gonic/gin/path.go b/vendor/github.com/gin-gonic/gin/path.go new file mode 100644 index 0000000..d42d6b9 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/path.go @@ -0,0 +1,150 @@ +// Copyright 2013 Julien Schmidt. All rights reserved. +// Based on the path package, Copyright 2009 The Go Authors. +// Use of this source code is governed by a BSD-style license that can be found +// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE. + +package gin + +// cleanPath is the URL version of path.Clean, it returns a canonical URL path +// for p, eliminating . and .. elements. +// +// The following rules are applied iteratively until no further processing can +// be done: +// 1. Replace multiple slashes with a single slash. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path. +// +// If the result of this process is an empty string, "/" is returned. +func cleanPath(p string) string { + const stackBufSize = 128 + // Turn empty string into "/" + if p == "" { + return "/" + } + + // Reasonably sized buffer on stack to avoid allocations in the common case. + // If a larger buffer is required, it gets allocated dynamically. + buf := make([]byte, 0, stackBufSize) + + n := len(p) + + // Invariants: + // reading from path; r is index of next byte to process. + // writing to buf; w is index of next byte to write. + + // path must start with '/' + r := 1 + w := 1 + + if p[0] != '/' { + r = 0 + + if n+1 > stackBufSize { + buf = make([]byte, n+1) + } else { + buf = buf[:n+1] + } + buf[0] = '/' + } + + trailing := n > 1 && p[n-1] == '/' + + // A bit more clunky without a 'lazybuf' like the path package, but the loop + // gets completely inlined (bufApp calls). + // loop has no expensive function calls (except 1x make) // So in contrast to the path package this loop has no expensive function + // calls (except make, if needed). + + for r < n { + switch { + case p[r] == '/': + // empty path element, trailing slash is added after the end + r++ + + case p[r] == '.' && r+1 == n: + trailing = true + r++ + + case p[r] == '.' && p[r+1] == '/': + // . element + r += 2 + + case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): + // .. element: remove to last / + r += 3 + + if w > 1 { + // can backtrack + w-- + + if len(buf) == 0 { + for w > 1 && p[w] != '/' { + w-- + } + } else { + for w > 1 && buf[w] != '/' { + w-- + } + } + } + + default: + // Real path element. + // Add slash if needed + if w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + + // Copy element + for r < n && p[r] != '/' { + bufApp(&buf, p, w, p[r]) + w++ + r++ + } + } + } + + // Re-append trailing slash + if trailing && w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + + // If the original string was not modified (or only shortened at the end), + // return the respective substring of the original string. + // Otherwise return a new string from the buffer. + if len(buf) == 0 { + return p[:w] + } + return string(buf[:w]) +} + +// Internal helper to lazily create a buffer if necessary. +// Calls to this function get inlined. +func bufApp(buf *[]byte, s string, w int, c byte) { + b := *buf + if len(b) == 0 { + // No modification of the original string so far. + // If the next character is the same as in the original string, we do + // not yet have to allocate a buffer. + if s[w] == c { + return + } + + // Otherwise use either the stack buffer, if it is large enough, or + // allocate a new buffer on the heap, and copy all previous characters. + length := len(s) + if length > cap(b) { + *buf = make([]byte, length) + } else { + *buf = (*buf)[:length] + } + b = *buf + + copy(b, s[:w]) + } + b[w] = c +} diff --git a/vendor/github.com/gin-gonic/gin/recovery.go b/vendor/github.com/gin-gonic/gin/recovery.go new file mode 100644 index 0000000..563f5aa --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/recovery.go @@ -0,0 +1,171 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "net/http/httputil" + "os" + "runtime" + "strings" + "time" +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// RecoveryFunc defines the function passable to CustomRecovery. +type RecoveryFunc func(c *Context, err interface{}) + +// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. +func Recovery() HandlerFunc { + return RecoveryWithWriter(DefaultErrorWriter) +} + +//CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it. +func CustomRecovery(handle RecoveryFunc) HandlerFunc { + return RecoveryWithWriter(DefaultErrorWriter, handle) +} + +// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one. +func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc { + if len(recovery) > 0 { + return CustomRecoveryWithWriter(out, recovery[0]) + } + return CustomRecoveryWithWriter(out, defaultHandleRecovery) +} + +// CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it. +func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc { + var logger *log.Logger + if out != nil { + logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) + } + return func(c *Context) { + defer func() { + if err := recover(); err != nil { + // Check for a broken connection, as it is not really a + // condition that warrants a panic stack trace. + var brokenPipe bool + if ne, ok := err.(*net.OpError); ok { + if se, ok := ne.Err.(*os.SyscallError); ok { + if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { + brokenPipe = true + } + } + } + if logger != nil { + stack := stack(3) + httpRequest, _ := httputil.DumpRequest(c.Request, false) + headers := strings.Split(string(httpRequest), "\r\n") + for idx, header := range headers { + current := strings.Split(header, ":") + if current[0] == "Authorization" { + headers[idx] = current[0] + ": *" + } + } + headersToStr := strings.Join(headers, "\r\n") + if brokenPipe { + logger.Printf("%s\n%s%s", err, headersToStr, reset) + } else if IsDebugging() { + logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", + timeFormat(time.Now()), headersToStr, err, stack, reset) + } else { + logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s", + timeFormat(time.Now()), err, stack, reset) + } + } + if brokenPipe { + // If the connection is dead, we can't write a status to it. + c.Error(err.(error)) // nolint: errcheck + c.Abort() + } else { + handle(c, err) + } + } + }() + c.Next() + } +} + +func defaultHandleRecovery(c *Context, err interface{}) { + c.AbortWithStatus(http.StatusInternalServerError) +} + +// stack returns a nicely formatted stack frame, skipping skip frames. +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { + name = name[lastSlash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +func timeFormat(t time.Time) string { + timeString := t.Format("2006/01/02 - 15:04:05") + return timeString +} diff --git a/vendor/github.com/gin-gonic/gin/render/data.go b/vendor/github.com/gin-gonic/gin/render/data.go new file mode 100644 index 0000000..6ba657b --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/data.go @@ -0,0 +1,25 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import "net/http" + +// Data contains ContentType and bytes data. +type Data struct { + ContentType string + Data []byte +} + +// Render (Data) writes data with custom ContentType. +func (r Data) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + _, err = w.Write(r.Data) + return +} + +// WriteContentType (Data) writes custom ContentType. +func (r Data) WriteContentType(w http.ResponseWriter) { + writeContentType(w, []string{r.ContentType}) +} diff --git a/vendor/github.com/gin-gonic/gin/render/html.go b/vendor/github.com/gin-gonic/gin/render/html.go new file mode 100644 index 0000000..6696ece --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/html.go @@ -0,0 +1,92 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "html/template" + "net/http" +) + +// Delims represents a set of Left and Right delimiters for HTML template rendering. +type Delims struct { + // Left delimiter, defaults to {{. + Left string + // Right delimiter, defaults to }}. + Right string +} + +// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug. +type HTMLRender interface { + // Instance returns an HTML instance. + Instance(string, interface{}) Render +} + +// HTMLProduction contains template reference and its delims. +type HTMLProduction struct { + Template *template.Template + Delims Delims +} + +// HTMLDebug contains template delims and pattern and function with file list. +type HTMLDebug struct { + Files []string + Glob string + Delims Delims + FuncMap template.FuncMap +} + +// HTML contains template reference and its name with given interface object. +type HTML struct { + Template *template.Template + Name string + Data interface{} +} + +var htmlContentType = []string{"text/html; charset=utf-8"} + +// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface. +func (r HTMLProduction) Instance(name string, data interface{}) Render { + return HTML{ + Template: r.Template, + Name: name, + Data: data, + } +} + +// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface. +func (r HTMLDebug) Instance(name string, data interface{}) Render { + return HTML{ + Template: r.loadTemplate(), + Name: name, + Data: data, + } +} +func (r HTMLDebug) loadTemplate() *template.Template { + if r.FuncMap == nil { + r.FuncMap = template.FuncMap{} + } + if len(r.Files) > 0 { + return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...)) + } + if r.Glob != "" { + return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob)) + } + panic("the HTML debug render was created without files or glob pattern") +} + +// Render (HTML) executes template and writes its result with custom ContentType for response. +func (r HTML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + if r.Name == "" { + return r.Template.Execute(w, r.Data) + } + return r.Template.ExecuteTemplate(w, r.Name, r.Data) +} + +// WriteContentType (HTML) writes HTML ContentType. +func (r HTML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, htmlContentType) +} diff --git a/vendor/github.com/gin-gonic/gin/render/json.go b/vendor/github.com/gin-gonic/gin/render/json.go new file mode 100644 index 0000000..4186309 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/json.go @@ -0,0 +1,193 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "bytes" + "fmt" + "html/template" + "net/http" + + "github.com/gin-gonic/gin/internal/bytesconv" + "github.com/gin-gonic/gin/internal/json" +) + +// JSON contains the given interface object. +type JSON struct { + Data interface{} +} + +// IndentedJSON contains the given interface object. +type IndentedJSON struct { + Data interface{} +} + +// SecureJSON contains the given interface object and its prefix. +type SecureJSON struct { + Prefix string + Data interface{} +} + +// JsonpJSON contains the given interface object its callback. +type JsonpJSON struct { + Callback string + Data interface{} +} + +// AsciiJSON contains the given interface object. +type AsciiJSON struct { + Data interface{} +} + +// PureJSON contains the given interface object. +type PureJSON struct { + Data interface{} +} + +var jsonContentType = []string{"application/json; charset=utf-8"} +var jsonpContentType = []string{"application/javascript; charset=utf-8"} +var jsonAsciiContentType = []string{"application/json"} + +// Render (JSON) writes data with custom ContentType. +func (r JSON) Render(w http.ResponseWriter) (err error) { + if err = WriteJSON(w, r.Data); err != nil { + panic(err) + } + return +} + +// WriteContentType (JSON) writes JSON ContentType. +func (r JSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// WriteJSON marshals the given interface object and writes it with custom ContentType. +func WriteJSON(w http.ResponseWriter, obj interface{}) error { + writeContentType(w, jsonContentType) + jsonBytes, err := json.Marshal(obj) + if err != nil { + return err + } + _, err = w.Write(jsonBytes) + return err +} + +// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType. +func (r IndentedJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + jsonBytes, err := json.MarshalIndent(r.Data, "", " ") + if err != nil { + return err + } + _, err = w.Write(jsonBytes) + return err +} + +// WriteContentType (IndentedJSON) writes JSON ContentType. +func (r IndentedJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType. +func (r SecureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + jsonBytes, err := json.Marshal(r.Data) + if err != nil { + return err + } + // if the jsonBytes is array values + if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes, + bytesconv.StringToBytes("]")) { + _, err = w.Write(bytesconv.StringToBytes(r.Prefix)) + if err != nil { + return err + } + } + _, err = w.Write(jsonBytes) + return err +} + +// WriteContentType (SecureJSON) writes JSON ContentType. +func (r SecureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType. +func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + ret, err := json.Marshal(r.Data) + if err != nil { + return err + } + + if r.Callback == "" { + _, err = w.Write(ret) + return err + } + + callback := template.JSEscapeString(r.Callback) + _, err = w.Write(bytesconv.StringToBytes(callback)) + if err != nil { + return err + } + _, err = w.Write(bytesconv.StringToBytes("(")) + if err != nil { + return err + } + _, err = w.Write(ret) + if err != nil { + return err + } + _, err = w.Write(bytesconv.StringToBytes(");")) + if err != nil { + return err + } + + return nil +} + +// WriteContentType (JsonpJSON) writes Javascript ContentType. +func (r JsonpJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonpContentType) +} + +// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType. +func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + ret, err := json.Marshal(r.Data) + if err != nil { + return err + } + + var buffer bytes.Buffer + for _, r := range bytesconv.BytesToString(ret) { + cvt := string(r) + if r >= 128 { + cvt = fmt.Sprintf("\\u%04x", int64(r)) + } + buffer.WriteString(cvt) + } + + _, err = w.Write(buffer.Bytes()) + return err +} + +// WriteContentType (AsciiJSON) writes JSON ContentType. +func (r AsciiJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonAsciiContentType) +} + +// Render (PureJSON) writes custom ContentType and encodes the given interface object. +func (r PureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + encoder := json.NewEncoder(w) + encoder.SetEscapeHTML(false) + return encoder.Encode(r.Data) +} + +// WriteContentType (PureJSON) writes custom ContentType. +func (r PureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} diff --git a/vendor/github.com/gin-gonic/gin/render/msgpack.go b/vendor/github.com/gin-gonic/gin/render/msgpack.go new file mode 100644 index 0000000..6ef5b6e --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/msgpack.go @@ -0,0 +1,42 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +//go:build !nomsgpack +// +build !nomsgpack + +package render + +import ( + "net/http" + + "github.com/ugorji/go/codec" +) + +var ( + _ Render = MsgPack{} +) + +// MsgPack contains the given interface object. +type MsgPack struct { + Data interface{} +} + +var msgpackContentType = []string{"application/msgpack; charset=utf-8"} + +// WriteContentType (MsgPack) writes MsgPack ContentType. +func (r MsgPack) WriteContentType(w http.ResponseWriter) { + writeContentType(w, msgpackContentType) +} + +// Render (MsgPack) encodes the given interface object and writes data with custom ContentType. +func (r MsgPack) Render(w http.ResponseWriter) error { + return WriteMsgPack(w, r.Data) +} + +// WriteMsgPack writes MsgPack ContentType and encodes the given interface object. +func WriteMsgPack(w http.ResponseWriter, obj interface{}) error { + writeContentType(w, msgpackContentType) + var mh codec.MsgpackHandle + return codec.NewEncoder(w, &mh).Encode(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/render/protobuf.go b/vendor/github.com/gin-gonic/gin/render/protobuf.go new file mode 100644 index 0000000..15aca99 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/protobuf.go @@ -0,0 +1,36 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "net/http" + + "github.com/golang/protobuf/proto" +) + +// ProtoBuf contains the given interface object. +type ProtoBuf struct { + Data interface{} +} + +var protobufContentType = []string{"application/x-protobuf"} + +// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType. +func (r ProtoBuf) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + bytes, err := proto.Marshal(r.Data.(proto.Message)) + if err != nil { + return err + } + + _, err = w.Write(bytes) + return err +} + +// WriteContentType (ProtoBuf) writes ProtoBuf ContentType. +func (r ProtoBuf) WriteContentType(w http.ResponseWriter) { + writeContentType(w, protobufContentType) +} diff --git a/vendor/github.com/gin-gonic/gin/render/reader.go b/vendor/github.com/gin-gonic/gin/render/reader.go new file mode 100644 index 0000000..d5282e4 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/reader.go @@ -0,0 +1,48 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "io" + "net/http" + "strconv" +) + +// Reader contains the IO reader and its length, and custom ContentType and other headers. +type Reader struct { + ContentType string + ContentLength int64 + Reader io.Reader + Headers map[string]string +} + +// Render (Reader) writes data with custom ContentType and headers. +func (r Reader) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + if r.ContentLength >= 0 { + if r.Headers == nil { + r.Headers = map[string]string{} + } + r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) + } + r.writeHeaders(w, r.Headers) + _, err = io.Copy(w, r.Reader) + return +} + +// WriteContentType (Reader) writes custom ContentType. +func (r Reader) WriteContentType(w http.ResponseWriter) { + writeContentType(w, []string{r.ContentType}) +} + +// writeHeaders writes custom Header. +func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { + header := w.Header() + for k, v := range headers { + if header.Get(k) == "" { + header.Set(k, v) + } + } +} diff --git a/vendor/github.com/gin-gonic/gin/render/redirect.go b/vendor/github.com/gin-gonic/gin/render/redirect.go new file mode 100644 index 0000000..c006691 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/redirect.go @@ -0,0 +1,29 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "fmt" + "net/http" +) + +// Redirect contains the http request reference and redirects status code and location. +type Redirect struct { + Code int + Request *http.Request + Location string +} + +// Render (Redirect) redirects the http request to new location and writes redirect response. +func (r Redirect) Render(w http.ResponseWriter) error { + if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated { + panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) + } + http.Redirect(w, r.Request, r.Location, r.Code) + return nil +} + +// WriteContentType (Redirect) don't write any ContentType. +func (r Redirect) WriteContentType(http.ResponseWriter) {} diff --git a/vendor/github.com/gin-gonic/gin/render/render.go b/vendor/github.com/gin-gonic/gin/render/render.go new file mode 100644 index 0000000..bcd568b --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/render.go @@ -0,0 +1,40 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import "net/http" + +// Render interface is to be implemented by JSON, XML, HTML, YAML and so on. +type Render interface { + // Render writes data with custom ContentType. + Render(http.ResponseWriter) error + // WriteContentType writes custom ContentType. + WriteContentType(w http.ResponseWriter) +} + +var ( + _ Render = JSON{} + _ Render = IndentedJSON{} + _ Render = SecureJSON{} + _ Render = JsonpJSON{} + _ Render = XML{} + _ Render = String{} + _ Render = Redirect{} + _ Render = Data{} + _ Render = HTML{} + _ HTMLRender = HTMLDebug{} + _ HTMLRender = HTMLProduction{} + _ Render = YAML{} + _ Render = Reader{} + _ Render = AsciiJSON{} + _ Render = ProtoBuf{} +) + +func writeContentType(w http.ResponseWriter, value []string) { + header := w.Header() + if val := header["Content-Type"]; len(val) == 0 { + header["Content-Type"] = value + } +} diff --git a/vendor/github.com/gin-gonic/gin/render/text.go b/vendor/github.com/gin-gonic/gin/render/text.go new file mode 100644 index 0000000..461b720 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/text.go @@ -0,0 +1,41 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin/internal/bytesconv" +) + +// String contains the given interface object slice and its format. +type String struct { + Format string + Data []interface{} +} + +var plainContentType = []string{"text/plain; charset=utf-8"} + +// Render (String) writes data with custom ContentType. +func (r String) Render(w http.ResponseWriter) error { + return WriteString(w, r.Format, r.Data) +} + +// WriteContentType (String) writes Plain ContentType. +func (r String) WriteContentType(w http.ResponseWriter) { + writeContentType(w, plainContentType) +} + +// WriteString writes data according to its format and write custom ContentType. +func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) { + writeContentType(w, plainContentType) + if len(data) > 0 { + _, err = fmt.Fprintf(w, format, data...) + return + } + _, err = w.Write(bytesconv.StringToBytes(format)) + return +} diff --git a/vendor/github.com/gin-gonic/gin/render/xml.go b/vendor/github.com/gin-gonic/gin/render/xml.go new file mode 100644 index 0000000..cc5390a --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/xml.go @@ -0,0 +1,28 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "encoding/xml" + "net/http" +) + +// XML contains the given interface object. +type XML struct { + Data interface{} +} + +var xmlContentType = []string{"application/xml; charset=utf-8"} + +// Render (XML) encodes the given interface object and writes data with custom ContentType. +func (r XML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + return xml.NewEncoder(w).Encode(r.Data) +} + +// WriteContentType (XML) writes XML ContentType for response. +func (r XML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, xmlContentType) +} diff --git a/vendor/github.com/gin-gonic/gin/render/yaml.go b/vendor/github.com/gin-gonic/gin/render/yaml.go new file mode 100644 index 0000000..0df7836 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/yaml.go @@ -0,0 +1,36 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "net/http" + + "gopkg.in/yaml.v2" +) + +// YAML contains the given interface object. +type YAML struct { + Data interface{} +} + +var yamlContentType = []string{"application/x-yaml; charset=utf-8"} + +// Render (YAML) marshals the given interface object and writes data with custom ContentType. +func (r YAML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + bytes, err := yaml.Marshal(r.Data) + if err != nil { + return err + } + + _, err = w.Write(bytes) + return err +} + +// WriteContentType (YAML) writes YAML ContentType for response. +func (r YAML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, yamlContentType) +} diff --git a/vendor/github.com/gin-gonic/gin/response_writer.go b/vendor/github.com/gin-gonic/gin/response_writer.go new file mode 100644 index 0000000..2682668 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/response_writer.go @@ -0,0 +1,126 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "bufio" + "io" + "net" + "net/http" +) + +const ( + noWritten = -1 + defaultStatus = http.StatusOK +) + +// ResponseWriter ... +type ResponseWriter interface { + http.ResponseWriter + http.Hijacker + http.Flusher + http.CloseNotifier + + // Returns the HTTP response status code of the current request. + Status() int + + // Returns the number of bytes already written into the response http body. + // See Written() + Size() int + + // Writes the string into the response body. + WriteString(string) (int, error) + + // Returns true if the response body was already written. + Written() bool + + // Forces to write the http header (status code + headers). + WriteHeaderNow() + + // get the http.Pusher for server push + Pusher() http.Pusher +} + +type responseWriter struct { + http.ResponseWriter + size int + status int +} + +var _ ResponseWriter = &responseWriter{} + +func (w *responseWriter) reset(writer http.ResponseWriter) { + w.ResponseWriter = writer + w.size = noWritten + w.status = defaultStatus +} + +func (w *responseWriter) WriteHeader(code int) { + if code > 0 && w.status != code { + if w.Written() { + debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code) + } + w.status = code + } +} + +func (w *responseWriter) WriteHeaderNow() { + if !w.Written() { + w.size = 0 + w.ResponseWriter.WriteHeader(w.status) + } +} + +func (w *responseWriter) Write(data []byte) (n int, err error) { + w.WriteHeaderNow() + n, err = w.ResponseWriter.Write(data) + w.size += n + return +} + +func (w *responseWriter) WriteString(s string) (n int, err error) { + w.WriteHeaderNow() + n, err = io.WriteString(w.ResponseWriter, s) + w.size += n + return +} + +func (w *responseWriter) Status() int { + return w.status +} + +func (w *responseWriter) Size() int { + return w.size +} + +func (w *responseWriter) Written() bool { + return w.size != noWritten +} + +// Hijack implements the http.Hijacker interface. +func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if w.size < 0 { + w.size = 0 + } + return w.ResponseWriter.(http.Hijacker).Hijack() +} + +// CloseNotify implements the http.CloseNotify interface. +func (w *responseWriter) CloseNotify() <-chan bool { + return w.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +// Flush implements the http.Flush interface. +func (w *responseWriter) Flush() { + w.WriteHeaderNow() + w.ResponseWriter.(http.Flusher).Flush() +} + +func (w *responseWriter) Pusher() (pusher http.Pusher) { + if pusher, ok := w.ResponseWriter.(http.Pusher); ok { + return pusher + } + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/routergroup.go b/vendor/github.com/gin-gonic/gin/routergroup.go new file mode 100644 index 0000000..15d9930 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/routergroup.go @@ -0,0 +1,230 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "net/http" + "path" + "regexp" + "strings" +) + +// IRouter defines all router handle interface includes single and group router. +type IRouter interface { + IRoutes + Group(string, ...HandlerFunc) *RouterGroup +} + +// IRoutes defines all router handle interface. +type IRoutes interface { + Use(...HandlerFunc) IRoutes + + Handle(string, string, ...HandlerFunc) IRoutes + Any(string, ...HandlerFunc) IRoutes + GET(string, ...HandlerFunc) IRoutes + POST(string, ...HandlerFunc) IRoutes + DELETE(string, ...HandlerFunc) IRoutes + PATCH(string, ...HandlerFunc) IRoutes + PUT(string, ...HandlerFunc) IRoutes + OPTIONS(string, ...HandlerFunc) IRoutes + HEAD(string, ...HandlerFunc) IRoutes + + StaticFile(string, string) IRoutes + Static(string, string) IRoutes + StaticFS(string, http.FileSystem) IRoutes +} + +// RouterGroup is used internally to configure router, a RouterGroup is associated with +// a prefix and an array of handlers (middleware). +type RouterGroup struct { + Handlers HandlersChain + basePath string + engine *Engine + root bool +} + +var _ IRouter = &RouterGroup{} + +// Use adds middleware to the group, see example code in GitHub. +func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { + group.Handlers = append(group.Handlers, middleware...) + return group.returnObj() +} + +// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix. +// For example, all the routes that use a common middleware for authorization could be grouped. +func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup { + return &RouterGroup{ + Handlers: group.combineHandlers(handlers), + basePath: group.calculateAbsolutePath(relativePath), + engine: group.engine, + } +} + +// BasePath returns the base path of router group. +// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api". +func (group *RouterGroup) BasePath() string { + return group.basePath +} + +func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { + absolutePath := group.calculateAbsolutePath(relativePath) + handlers = group.combineHandlers(handlers) + group.engine.addRoute(httpMethod, absolutePath, handlers) + return group.returnObj() +} + +// Handle registers a new request handle and middleware with the given path and method. +// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes. +// See the example code in GitHub. +// +// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut +// functions can be used. +// +// This function is intended for bulk loading and to allow the usage of less +// frequently used, non-standardized or custom methods (e.g. for internal +// communication with a proxy). +func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes { + if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil { + panic("http method " + httpMethod + " is not valid") + } + return group.handle(httpMethod, relativePath, handlers) +} + +// POST is a shortcut for router.Handle("POST", path, handle). +func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodPost, relativePath, handlers) +} + +// GET is a shortcut for router.Handle("GET", path, handle). +func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodGet, relativePath, handlers) +} + +// DELETE is a shortcut for router.Handle("DELETE", path, handle). +func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodDelete, relativePath, handlers) +} + +// PATCH is a shortcut for router.Handle("PATCH", path, handle). +func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodPatch, relativePath, handlers) +} + +// PUT is a shortcut for router.Handle("PUT", path, handle). +func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodPut, relativePath, handlers) +} + +// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle). +func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodOptions, relativePath, handlers) +} + +// HEAD is a shortcut for router.Handle("HEAD", path, handle). +func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { + return group.handle(http.MethodHead, relativePath, handlers) +} + +// Any registers a route that matches all the HTTP methods. +// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. +func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { + group.handle(http.MethodGet, relativePath, handlers) + group.handle(http.MethodPost, relativePath, handlers) + group.handle(http.MethodPut, relativePath, handlers) + group.handle(http.MethodPatch, relativePath, handlers) + group.handle(http.MethodHead, relativePath, handlers) + group.handle(http.MethodOptions, relativePath, handlers) + group.handle(http.MethodDelete, relativePath, handlers) + group.handle(http.MethodConnect, relativePath, handlers) + group.handle(http.MethodTrace, relativePath, handlers) + return group.returnObj() +} + +// StaticFile registers a single route in order to serve a single file of the local filesystem. +// router.StaticFile("favicon.ico", "./resources/favicon.ico") +func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { + if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { + panic("URL parameters can not be used when serving a static file") + } + handler := func(c *Context) { + c.File(filepath) + } + group.GET(relativePath, handler) + group.HEAD(relativePath, handler) + return group.returnObj() +} + +// Static serves files from the given file system root. +// Internally a http.FileServer is used, therefore http.NotFound is used instead +// of the Router's NotFound handler. +// To use the operating system's file system implementation, +// use : +// router.Static("/static", "/var/www") +func (group *RouterGroup) Static(relativePath, root string) IRoutes { + return group.StaticFS(relativePath, Dir(root, false)) +} + +// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead. +// Gin by default user: gin.Dir() +func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes { + if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { + panic("URL parameters can not be used when serving a static folder") + } + handler := group.createStaticHandler(relativePath, fs) + urlPattern := path.Join(relativePath, "/*filepath") + + // Register GET and HEAD handlers + group.GET(urlPattern, handler) + group.HEAD(urlPattern, handler) + return group.returnObj() +} + +func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc { + absolutePath := group.calculateAbsolutePath(relativePath) + fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) + + return func(c *Context) { + if _, noListing := fs.(*onlyFilesFS); noListing { + c.Writer.WriteHeader(http.StatusNotFound) + } + + file := c.Param("filepath") + // Check if file exists and/or if we have permission to access it + f, err := fs.Open(file) + if err != nil { + c.Writer.WriteHeader(http.StatusNotFound) + c.handlers = group.engine.noRoute + // Reset index + c.index = -1 + return + } + f.Close() + + fileServer.ServeHTTP(c.Writer, c.Request) + } +} + +func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { + finalSize := len(group.Handlers) + len(handlers) + if finalSize >= int(abortIndex) { + panic("too many handlers") + } + mergedHandlers := make(HandlersChain, finalSize) + copy(mergedHandlers, group.Handlers) + copy(mergedHandlers[len(group.Handlers):], handlers) + return mergedHandlers +} + +func (group *RouterGroup) calculateAbsolutePath(relativePath string) string { + return joinPaths(group.basePath, relativePath) +} + +func (group *RouterGroup) returnObj() IRoutes { + if group.root { + return group.engine + } + return group +} diff --git a/vendor/github.com/gin-gonic/gin/test_helpers.go b/vendor/github.com/gin-gonic/gin/test_helpers.go new file mode 100644 index 0000000..3a7a5dd --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/test_helpers.go @@ -0,0 +1,16 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import "net/http" + +// CreateTestContext returns a fresh engine and context for testing purposes +func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) { + r = New() + c = r.allocateContext() + c.reset() + c.writermem.reset(w) + return +} diff --git a/vendor/github.com/gin-gonic/gin/tree.go b/vendor/github.com/gin-gonic/gin/tree.go new file mode 100644 index 0000000..ca753e6 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/tree.go @@ -0,0 +1,777 @@ +// Copyright 2013 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE + +package gin + +import ( + "bytes" + "net/url" + "strings" + "unicode" + "unicode/utf8" + + "github.com/gin-gonic/gin/internal/bytesconv" +) + +var ( + strColon = []byte(":") + strStar = []byte("*") +) + +// Param is a single URL parameter, consisting of a key and a value. +type Param struct { + Key string + Value string +} + +// Params is a Param-slice, as returned by the router. +// The slice is ordered, the first URL parameter is also the first slice value. +// It is therefore safe to read values by the index. +type Params []Param + +// Get returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. +func (ps Params) Get(name string) (string, bool) { + for _, entry := range ps { + if entry.Key == name { + return entry.Value, true + } + } + return "", false +} + +// ByName returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. +func (ps Params) ByName(name string) (va string) { + va, _ = ps.Get(name) + return +} + +type methodTree struct { + method string + root *node +} + +type methodTrees []methodTree + +func (trees methodTrees) get(method string) *node { + for _, tree := range trees { + if tree.method == method { + return tree.root + } + } + return nil +} + +func min(a, b int) int { + if a <= b { + return a + } + return b +} + +func longestCommonPrefix(a, b string) int { + i := 0 + max := min(len(a), len(b)) + for i < max && a[i] == b[i] { + i++ + } + return i +} + +// addChild will add a child node, keeping wildcards at the end +func (n *node) addChild(child *node) { + if n.wildChild && len(n.children) > 0 { + wildcardChild := n.children[len(n.children)-1] + n.children = append(n.children[:len(n.children)-1], child, wildcardChild) + } else { + n.children = append(n.children, child) + } +} + +func countParams(path string) uint16 { + var n uint16 + s := bytesconv.StringToBytes(path) + n += uint16(bytes.Count(s, strColon)) + n += uint16(bytes.Count(s, strStar)) + return n +} + +type nodeType uint8 + +const ( + static nodeType = iota // default + root + param + catchAll +) + +type node struct { + path string + indices string + wildChild bool + nType nodeType + priority uint32 + children []*node // child nodes, at most 1 :param style node at the end of the array + handlers HandlersChain + fullPath string +} + +// Increments priority of the given child and reorders if necessary +func (n *node) incrementChildPrio(pos int) int { + cs := n.children + cs[pos].priority++ + prio := cs[pos].priority + + // Adjust position (move to front) + newPos := pos + for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { + // Swap node positions + cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] + } + + // Build new index char string + if newPos != pos { + n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty + n.indices[pos:pos+1] + // The index char we move + n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos' + } + + return newPos +} + +// addRoute adds a node with the given handle to the path. +// Not concurrency-safe! +func (n *node) addRoute(path string, handlers HandlersChain) { + fullPath := path + n.priority++ + + // Empty tree + if len(n.path) == 0 && len(n.children) == 0 { + n.insertChild(path, fullPath, handlers) + n.nType = root + return + } + + parentFullPathIndex := 0 + +walk: + for { + // Find the longest common prefix. + // This also implies that the common prefix contains no ':' or '*' + // since the existing key can't contain those chars. + i := longestCommonPrefix(path, n.path) + + // Split edge + if i < len(n.path) { + child := node{ + path: n.path[i:], + wildChild: n.wildChild, + indices: n.indices, + children: n.children, + handlers: n.handlers, + priority: n.priority - 1, + fullPath: n.fullPath, + } + + n.children = []*node{&child} + // []byte for proper unicode char conversion, see #65 + n.indices = bytesconv.BytesToString([]byte{n.path[i]}) + n.path = path[:i] + n.handlers = nil + n.wildChild = false + n.fullPath = fullPath[:parentFullPathIndex+i] + } + + // Make new node a child of this node + if i < len(path) { + path = path[i:] + c := path[0] + + // '/' after param + if n.nType == param && c == '/' && len(n.children) == 1 { + parentFullPathIndex += len(n.path) + n = n.children[0] + n.priority++ + continue walk + } + + // Check if a child with the next path byte exists + for i, max := 0, len(n.indices); i < max; i++ { + if c == n.indices[i] { + parentFullPathIndex += len(n.path) + i = n.incrementChildPrio(i) + n = n.children[i] + continue walk + } + } + + // Otherwise insert it + if c != ':' && c != '*' && n.nType != catchAll { + // []byte for proper unicode char conversion, see #65 + n.indices += bytesconv.BytesToString([]byte{c}) + child := &node{ + fullPath: fullPath, + } + n.addChild(child) + n.incrementChildPrio(len(n.indices) - 1) + n = child + } else if n.wildChild { + // inserting a wildcard node, need to check if it conflicts with the existing wildcard + n = n.children[len(n.children)-1] + n.priority++ + + // Check if the wildcard matches + if len(path) >= len(n.path) && n.path == path[:len(n.path)] && + // Adding a child to a catchAll is not possible + n.nType != catchAll && + // Check for longer wildcard, e.g. :name and :names + (len(n.path) >= len(path) || path[len(n.path)] == '/') { + continue walk + } + + // Wildcard conflict + pathSeg := path + if n.nType != catchAll { + pathSeg = strings.SplitN(pathSeg, "/", 2)[0] + } + prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path + panic("'" + pathSeg + + "' in new path '" + fullPath + + "' conflicts with existing wildcard '" + n.path + + "' in existing prefix '" + prefix + + "'") + } + + n.insertChild(path, fullPath, handlers) + return + } + + // Otherwise add handle to current node + if n.handlers != nil { + panic("handlers are already registered for path '" + fullPath + "'") + } + n.handlers = handlers + n.fullPath = fullPath + return + } +} + +// Search for a wildcard segment and check the name for invalid characters. +// Returns -1 as index, if no wildcard was found. +func findWildcard(path string) (wildcard string, i int, valid bool) { + // Find start + for start, c := range []byte(path) { + // A wildcard starts with ':' (param) or '*' (catch-all) + if c != ':' && c != '*' { + continue + } + + // Find end and check for invalid characters + valid = true + for end, c := range []byte(path[start+1:]) { + switch c { + case '/': + return path[start : start+1+end], start, valid + case ':', '*': + valid = false + } + } + return path[start:], start, valid + } + return "", -1, false +} + +func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { + for { + // Find prefix until first wildcard + wildcard, i, valid := findWildcard(path) + if i < 0 { // No wildcard found + break + } + + // The wildcard name must not contain ':' and '*' + if !valid { + panic("only one wildcard per path segment is allowed, has: '" + + wildcard + "' in path '" + fullPath + "'") + } + + // check if the wildcard has a name + if len(wildcard) < 2 { + panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") + } + + if wildcard[0] == ':' { // param + if i > 0 { + // Insert prefix before the current wildcard + n.path = path[:i] + path = path[i:] + } + + child := &node{ + nType: param, + path: wildcard, + fullPath: fullPath, + } + n.addChild(child) + n.wildChild = true + n = child + n.priority++ + + // if the path doesn't end with the wildcard, then there + // will be another non-wildcard subpath starting with '/' + if len(wildcard) < len(path) { + path = path[len(wildcard):] + + child := &node{ + priority: 1, + fullPath: fullPath, + } + n.addChild(child) + n = child + continue + } + + // Otherwise we're done. Insert the handle in the new leaf + n.handlers = handlers + return + } + + // catchAll + if i+len(wildcard) != len(path) { + panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") + } + + if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { + panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") + } + + // currently fixed width 1 for '/' + i-- + if path[i] != '/' { + panic("no / before catch-all in path '" + fullPath + "'") + } + + n.path = path[:i] + + // First node: catchAll node with empty path + child := &node{ + wildChild: true, + nType: catchAll, + fullPath: fullPath, + } + + n.addChild(child) + n.indices = string('/') + n = child + n.priority++ + + // second node: node holding the variable + child = &node{ + path: path[i:], + nType: catchAll, + handlers: handlers, + priority: 1, + fullPath: fullPath, + } + n.children = []*node{child} + + return + } + + // If no wildcard was found, simply insert the path and handle + n.path = path + n.handlers = handlers + n.fullPath = fullPath +} + +// nodeValue holds return values of (*Node).getValue method +type nodeValue struct { + handlers HandlersChain + params *Params + tsr bool + fullPath string +} + +// Returns the handle registered with the given path (key). The values of +// wildcards are saved to a map. +// If no handle can be found, a TSR (trailing slash redirect) recommendation is +// made if a handle exists with an extra (without the) trailing slash for the +// given path. +func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { +walk: // Outer loop for walking the tree + for { + prefix := n.path + if len(path) > len(prefix) { + if path[:len(prefix)] == prefix { + path = path[len(prefix):] + + // Try all the non-wildcard children first by matching the indices + idxc := path[0] + for i, c := range []byte(n.indices) { + if c == idxc { + n = n.children[i] + continue walk + } + } + + // If there is no wildcard pattern, recommend a redirection + if !n.wildChild { + // Nothing found. + // We can recommend to redirect to the same URL without a + // trailing slash if a leaf exists for that path. + value.tsr = (path == "/" && n.handlers != nil) + return + } + + // Handle wildcard child, which is always at the end of the array + n = n.children[len(n.children)-1] + + switch n.nType { + case param: + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path[:end] + if unescape { + if v, err := url.QueryUnescape(val); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[1:], + Value: val, + } + } + + // we need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + path = path[end:] + n = n.children[0] + continue walk + } + + // ... but we can't + value.tsr = (len(path) == end+1) + return + } + + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + if len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists for TSR recommendation + n = n.children[0] + value.tsr = (n.path == "/" && n.handlers != nil) + } + return + + case catchAll: + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path + if unescape { + if v, err := url.QueryUnescape(path); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[2:], + Value: val, + } + } + + value.handlers = n.handlers + value.fullPath = n.fullPath + return + + default: + panic("invalid node type") + } + } + } + + if path == prefix { + // We should have reached the node containing the handle. + // Check if this node has a handle registered. + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + + // If there is no handle for this route, but this route has a + // wildcard child, there must be a handle for this path with an + // additional trailing slash + if path == "/" && n.wildChild && n.nType != root { + value.tsr = true + return + } + + // No handle found. Check if a handle for this path + a + // trailing slash exists for trailing slash recommendation + for i, c := range []byte(n.indices) { + if c == '/' { + n = n.children[i] + value.tsr = (len(n.path) == 1 && n.handlers != nil) || + (n.nType == catchAll && n.children[0].handlers != nil) + return + } + } + + return + } + + // Nothing found. We can recommend to redirect to the same URL with an + // extra trailing slash if a leaf exists for that path + value.tsr = (path == "/") || + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) + return + } +} + +// Makes a case-insensitive lookup of the given path and tries to find a handler. +// It can optionally also fix trailing slashes. +// It returns the case-corrected path and a bool indicating whether the lookup +// was successful. +func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) { + const stackBufSize = 128 + + // Use a static sized buffer on the stack in the common case. + // If the path is too long, allocate a buffer on the heap instead. + buf := make([]byte, 0, stackBufSize) + if length := len(path) + 1; length > stackBufSize { + buf = make([]byte, 0, length) + } + + ciPath := n.findCaseInsensitivePathRec( + path, + buf, // Preallocate enough memory for new path + [4]byte{}, // Empty rune buffer + fixTrailingSlash, + ) + + return ciPath, ciPath != nil +} + +// Shift bytes in array by n bytes left +func shiftNRuneBytes(rb [4]byte, n int) [4]byte { + switch n { + case 0: + return rb + case 1: + return [4]byte{rb[1], rb[2], rb[3], 0} + case 2: + return [4]byte{rb[2], rb[3]} + case 3: + return [4]byte{rb[3]} + default: + return [4]byte{} + } +} + +// Recursive case-insensitive lookup function used by n.findCaseInsensitivePath +func (n *node) findCaseInsensitivePathRec(path string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) []byte { + npLen := len(n.path) + +walk: // Outer loop for walking the tree + for len(path) >= npLen && (npLen == 0 || strings.EqualFold(path[1:npLen], n.path[1:])) { + // Add common prefix to result + oldPath := path + path = path[npLen:] + ciPath = append(ciPath, n.path...) + + if len(path) == 0 { + // We should have reached the node containing the handle. + // Check if this node has a handle registered. + if n.handlers != nil { + return ciPath + } + + // No handle found. + // Try to fix the path by adding a trailing slash + if fixTrailingSlash { + for i, c := range []byte(n.indices) { + if c == '/' { + n = n.children[i] + if (len(n.path) == 1 && n.handlers != nil) || + (n.nType == catchAll && n.children[0].handlers != nil) { + return append(ciPath, '/') + } + return nil + } + } + } + return nil + } + + // If this node does not have a wildcard (param or catchAll) child, + // we can just look up the next child node and continue to walk down + // the tree + if !n.wildChild { + // Skip rune bytes already processed + rb = shiftNRuneBytes(rb, npLen) + + if rb[0] != 0 { + // Old rune not finished + idxc := rb[0] + for i, c := range []byte(n.indices) { + if c == idxc { + // continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } else { + // Process a new rune + var rv rune + + // Find rune start. + // Runes are up to 4 byte long, + // -4 would definitely be another rune. + var off int + for max := min(npLen, 3); off < max; off++ { + if i := npLen - off; utf8.RuneStart(oldPath[i]) { + // read rune from cached path + rv, _ = utf8.DecodeRuneInString(oldPath[i:]) + break + } + } + + // Calculate lowercase bytes of current rune + lo := unicode.ToLower(rv) + utf8.EncodeRune(rb[:], lo) + + // Skip already processed bytes + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Lowercase matches + if c == idxc { + // must use a recursive approach since both the + // uppercase byte and the lowercase byte might exist + // as an index + if out := n.children[i].findCaseInsensitivePathRec( + path, ciPath, rb, fixTrailingSlash, + ); out != nil { + return out + } + break + } + } + + // If we found no match, the same for the uppercase rune, + // if it differs + if up := unicode.ToUpper(rv); up != lo { + utf8.EncodeRune(rb[:], up) + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Uppercase matches + if c == idxc { + // Continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } + } + + // Nothing found. We can recommend to redirect to the same URL + // without a trailing slash if a leaf exists for that path + if fixTrailingSlash && path == "/" && n.handlers != nil { + return ciPath + } + return nil + } + + n = n.children[0] + switch n.nType { + case param: + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Add param value to case insensitive path + ciPath = append(ciPath, path[:end]...) + + // We need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + // Continue with child node + n = n.children[0] + npLen = len(n.path) + path = path[end:] + continue + } + + // ... but we can't + if fixTrailingSlash && len(path) == end+1 { + return ciPath + } + return nil + } + + if n.handlers != nil { + return ciPath + } + + if fixTrailingSlash && len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists + n = n.children[0] + if n.path == "/" && n.handlers != nil { + return append(ciPath, '/') + } + } + + return nil + + case catchAll: + return append(ciPath, path...) + + default: + panic("invalid node type") + } + } + + // Nothing found. + // Try to fix the path by adding / removing a trailing slash + if fixTrailingSlash { + if path == "/" { + return ciPath + } + if len(path)+1 == npLen && n.path[len(path)] == '/' && + strings.EqualFold(path[1:], n.path[1:len(path)]) && n.handlers != nil { + return append(ciPath, n.path...) + } + } + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/utils.go b/vendor/github.com/gin-gonic/gin/utils.go new file mode 100644 index 0000000..c32f0ee --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/utils.go @@ -0,0 +1,153 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "encoding/xml" + "net/http" + "os" + "path" + "reflect" + "runtime" + "strings" +) + +// BindKey indicates a default bind key. +const BindKey = "_gin-gonic/gin/bindkey" + +// Bind is a helper function for given interface object and returns a Gin middleware. +func Bind(val interface{}) HandlerFunc { + value := reflect.ValueOf(val) + if value.Kind() == reflect.Ptr { + panic(`Bind struct can not be a pointer. Example: + Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{}) +`) + } + typ := value.Type() + + return func(c *Context) { + obj := reflect.New(typ).Interface() + if c.Bind(obj) == nil { + c.Set(BindKey, obj) + } + } +} + +// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware. +func WrapF(f http.HandlerFunc) HandlerFunc { + return func(c *Context) { + f(c.Writer, c.Request) + } +} + +// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware. +func WrapH(h http.Handler) HandlerFunc { + return func(c *Context) { + h.ServeHTTP(c.Writer, c.Request) + } +} + +// H is a shortcut for map[string]interface{} +type H map[string]interface{} + +// MarshalXML allows type H to be used with xml.Marshal. +func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + start.Name = xml.Name{ + Space: "", + Local: "map", + } + if err := e.EncodeToken(start); err != nil { + return err + } + for key, value := range h { + elem := xml.StartElement{ + Name: xml.Name{Space: "", Local: key}, + Attr: []xml.Attr{}, + } + if err := e.EncodeElement(value, elem); err != nil { + return err + } + } + + return e.EncodeToken(xml.EndElement{Name: start.Name}) +} + +func assert1(guard bool, text string) { + if !guard { + panic(text) + } +} + +func filterFlags(content string) string { + for i, char := range content { + if char == ' ' || char == ';' { + return content[:i] + } + } + return content +} + +func chooseData(custom, wildcard interface{}) interface{} { + if custom != nil { + return custom + } + if wildcard != nil { + return wildcard + } + panic("negotiation config is invalid") +} + +func parseAccept(acceptHeader string) []string { + parts := strings.Split(acceptHeader, ",") + out := make([]string, 0, len(parts)) + for _, part := range parts { + if i := strings.IndexByte(part, ';'); i > 0 { + part = part[:i] + } + if part = strings.TrimSpace(part); part != "" { + out = append(out, part) + } + } + return out +} + +func lastChar(str string) uint8 { + if str == "" { + panic("The length of the string can't be 0") + } + return str[len(str)-1] +} + +func nameOfFunction(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} + +func joinPaths(absolutePath, relativePath string) string { + if relativePath == "" { + return absolutePath + } + + finalPath := path.Join(absolutePath, relativePath) + if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' { + return finalPath + "/" + } + return finalPath +} + +func resolveAddress(addr []string) string { + switch len(addr) { + case 0: + if port := os.Getenv("PORT"); port != "" { + debugPrint("Environment variable PORT=\"%s\"", port) + return ":" + port + } + debugPrint("Environment variable PORT is undefined. Using port :8080 by default") + return ":8080" + case 1: + return addr[0] + default: + panic("too many parameters") + } +} diff --git a/vendor/github.com/gin-gonic/gin/version.go b/vendor/github.com/gin-gonic/gin/version.go new file mode 100644 index 0000000..3647461 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/version.go @@ -0,0 +1,8 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +// Version is the current gin framework's version. +const Version = "v1.7.1" diff --git a/vendor/github.com/go-playground/form/.gitignore b/vendor/github.com/go-playground/form/.gitignore new file mode 100644 index 0000000..4b0b7d2 --- /dev/null +++ b/vendor/github.com/go-playground/form/.gitignore @@ -0,0 +1,26 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +old.txt +new.txt \ No newline at end of file diff --git a/vendor/github.com/go-playground/form/LICENSE b/vendor/github.com/go-playground/form/LICENSE new file mode 100644 index 0000000..8d8aba1 --- /dev/null +++ b/vendor/github.com/go-playground/form/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Go Playground + +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. diff --git a/vendor/github.com/go-playground/form/README.md b/vendor/github.com/go-playground/form/README.md new file mode 100644 index 0000000..a1cd515 --- /dev/null +++ b/vendor/github.com/go-playground/form/README.md @@ -0,0 +1,333 @@ +Package form +============ +![Project status](https://img.shields.io/badge/version-3.1.4-green.svg) +[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/form/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/form) +[![Coverage Status](https://coveralls.io/repos/github/go-playground/form/badge.svg?branch=master)](https://coveralls.io/github/go-playground/form?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/form)](https://goreportcard.com/report/github.com/go-playground/form) +[![GoDoc](https://godoc.org/github.com/go-playground/form?status.svg)](https://godoc.org/github.com/go-playground/form) +![License](https://img.shields.io/dub/l/vibe-d.svg) +[![Gitter](https://badges.gitter.im/go-playground/form.svg)](https://gitter.im/go-playground/form?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. + +It has the following features: + +- Supports map of almost all types. +- Supports both Numbered and Normal arrays eg. `"Array[0]"` and just `"Array"` with multiple values passed. +- Slice honours the specified index. eg. if "Slice[2]" is the only Slice value passed down, it will be put at index 2; if slice isn't big enough it will be expanded. +- Array honours the specified index. eg. if "Array[2]" is the only Array value passed down, it will be put at index 2; if array isn't big enough a warning will be printed and value ignored. +- Only creates objects as necessary eg. if no `array` or `map` values are passed down, the `array` and `map` are left as their default values in the struct. +- Allows for Custom Type registration. +- Handles time.Time using RFC3339 time format by default, but can easily be changed by registering a Custom Type, see below. +- Handles Encoding & Decoding of almost all Go types eg. can Decode into struct, array, map, int... and Encode a struct, array, map, int... + +Common Questions + +- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable. +- Mixing `array/slice` with `array[idx]/slice[idx]`, in which order are they parsed? `array/slice` then `array[idx]/slice[idx]` + +Supported Types ( out of the box ) +---------- + +* `string` +* `bool` +* `int`, `int8`, `int16`, `int32`, `int64` +* `uint`, `uint8`, `uint16`, `uint32`, `uint64` +* `float32`, `float64` +* `struct` and `anonymous struct` +* `interface{}` +* `time.Time` - by default using RFC3339 +* a `pointer` to one of the above types +* `slice`, `array` +* `map` +* `custom types` can override any of the above types +* many other types may be supported inherently + +**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum. + +Installation +------------ + +Use go get. + + go get -u github.com/go-playground/form + +Then import the form package into your own code. + + import "github.com/go-playground/form" + +Usage +----- + +- Use symbol `.` for separating fields/structs. (eg. `structfield.field`) +- Use `[index or key]` for access to index of a slice/array or key for map. (eg. `arrayfield[0]`, `mapfield[keyvalue]`) + +```html +
+ + + + + + + + + + + + +
+``` + +Examples +------- + +Decoding +```go +package main + +import ( + "fmt" + "log" + "net/url" + + "github.com/go-playground/form" +) + +// Address contains address information +type Address struct { + Name string + Phone string +} + +// User contains user information +type User struct { + Name string + Age uint8 + Gender string + Address []Address + Active bool `form:"active"` + MapExample map[string]string + NestedMap map[string]map[string]string + NestedArray [][]string +} + +// use a single instance of Decoder, it caches struct info +var decoder *form.Decoder + +func main() { + decoder = form.NewDecoder() + + // this simulates the results of http.Request's ParseForm() function + values := parseForm() + + var user User + + // must pass a pointer + err := decoder.Decode(&user, values) + if err != nil { + log.Panic(err) + } + + fmt.Printf("%#v\n", user) +} + +// this simulates the results of http.Request's ParseForm() function +func parseForm() url.Values { + return url.Values{ + "Name": []string{"joeybloggs"}, + "Age": []string{"3"}, + "Gender": []string{"Male"}, + "Address[0].Name": []string{"26 Here Blvd."}, + "Address[0].Phone": []string{"9(999)999-9999"}, + "Address[1].Name": []string{"26 There Blvd."}, + "Address[1].Phone": []string{"1(111)111-1111"}, + "active": []string{"true"}, + "MapExample[key]": []string{"value"}, + "NestedMap[key][key]": []string{"value"}, + "NestedArray[0][0]": []string{"value"}, + } +} +``` + +Encoding +```go +package main + +import ( + "fmt" + "log" + + "github.com/go-playground/form" +) + +// Address contains address information +type Address struct { + Name string + Phone string +} + +// User contains user information +type User struct { + Name string + Age uint8 + Gender string + Address []Address + Active bool `form:"active"` + MapExample map[string]string + NestedMap map[string]map[string]string + NestedArray [][]string +} + +// use a single instance of Encoder, it caches struct info +var encoder *form.Encoder + +func main() { + encoder = form.NewEncoder() + + user := User{ + Name: "joeybloggs", + Age: 3, + Gender: "Male", + Address: []Address{ + {Name: "26 Here Blvd.", Phone: "9(999)999-9999"}, + {Name: "26 There Blvd.", Phone: "1(111)111-1111"}, + }, + Active: true, + MapExample: map[string]string{"key": "value"}, + NestedMap: map[string]map[string]string{"key": {"key": "value"}}, + NestedArray: [][]string{{"value"}}, + } + + // must pass a pointer + values, err := encoder.Encode(&user) + if err != nil { + log.Panic(err) + } + + fmt.Printf("%#v\n", values) +} +``` + +Registering Custom Types +-------------- + +Decoder +```go +decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) { + return time.Parse("2006-01-02", vals[0]) + }, time.Time{}) +``` +ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for +the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the +custom type function with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not. + + +Encoder +```go +encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) { + return []string{x.(time.Time).Format("2006-01-02")}, nil + }, time.Time{}) +``` + +Ignoring Fields +-------------- +you can tell form to ignore fields using `-` in the tag +```go +type MyStruct struct { + Field string `form:"-"` +} +``` + +Omitempty +-------------- +you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag +```go +type MyStruct struct { + Field string `form:",omitempty"` + Field2 string `form:"CustomFieldName,omitempty"` +} +``` + +Notes +------ +To maximize compatibility with other systems the Encoder attempts +to avoid using array indexes in url.Values if at all possible. + +eg. +```go +// A struct field of +Field []string{"1", "2", "3"} + +// will be output a url.Value as +"Field": []string{"1", "2", "3"} + +and not +"Field[0]": []string{"1"} +"Field[1]": []string{"2"} +"Field[2]": []string{"3"} + +// however there are times where it is unavoidable, like with pointers +i := int(1) +Field []*string{nil, nil, &i} + +// to avoid index 1 and 2 must use index +"Field[2]": []string{"1"} +``` + +Benchmarks +------ +###### Run on MacBook Pro (15-inch, 2017) using go version go1.10.1 darwin/amd64 + +NOTE: the 1 allocation and B/op in the first 4 decodes is actually the struct allocating when passing it in, so primitives are actually zero allocation. + +```go +go test -run=NONE -bench=. -benchmem=true +goos: darwin +goarch: amd64 +pkg: github.com/go-playground/form/benchmarks + +BenchmarkSimpleUserDecodeStruct-8 5000000 236 ns/op 64 B/op 1 allocs/op +BenchmarkSimpleUserDecodeStructParallel-8 20000000 82.1 ns/op 64 B/op 1 allocs/op +BenchmarkSimpleUserEncodeStruct-8 2000000 627 ns/op 485 B/op 10 allocs/op +BenchmarkSimpleUserEncodeStructParallel-8 10000000 223 ns/op 485 B/op 10 allocs/op +BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 2000000 724 ns/op 96 B/op 1 allocs/op +BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 10000000 246 ns/op 96 B/op 1 allocs/op +BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 500000 3187 ns/op 2977 B/op 36 allocs/op +BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1000000 1106 ns/op 2977 B/op 36 allocs/op +BenchmarkComplexArrayDecodeStructAllTypes-8 100000 13748 ns/op 2248 B/op 121 allocs/op +BenchmarkComplexArrayDecodeStructAllTypesParallel-8 500000 4313 ns/op 2249 B/op 121 allocs/op +BenchmarkComplexArrayEncodeStructAllTypes-8 200000 10758 ns/op 7113 B/op 104 allocs/op +BenchmarkComplexArrayEncodeStructAllTypesParallel-8 500000 3532 ns/op 7113 B/op 104 allocs/op +BenchmarkComplexMapDecodeStructAllTypes-8 100000 17644 ns/op 5305 B/op 130 allocs/op +BenchmarkComplexMapDecodeStructAllTypesParallel-8 300000 5470 ns/op 5308 B/op 130 allocs/op +BenchmarkComplexMapEncodeStructAllTypes-8 200000 11155 ns/op 6971 B/op 129 allocs/op +BenchmarkComplexMapEncodeStructAllTypesParallel-8 500000 3768 ns/op 6971 B/op 129 allocs/op +BenchmarkDecodeNestedStruct-8 500000 2462 ns/op 384 B/op 14 allocs/op +BenchmarkDecodeNestedStructParallel-8 2000000 814 ns/op 384 B/op 14 allocs/op +BenchmarkEncodeNestedStruct-8 1000000 1483 ns/op 693 B/op 16 allocs/op +BenchmarkEncodeNestedStructParallel-8 3000000 525 ns/op 693 B/op 16 allocs/op +``` + +Competitor benchmarks can be found [here](https://github.com/go-playground/form/blob/master/benchmarks/benchmarks.md) + +Complimentary Software +---------------------- + +Here is a list of software that compliments using this library post decoding. + +* [Validator](https://github.com/go-playground/validator) - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving. +* [mold](https://github.com/go-playground/mold) - Is a general library to help modify or set data within data structures and other objects. + +Package Versioning +---------- +I'm jumping on the vendoring bandwagon, you should vendor this package as I will not +be creating different version with gopkg.in like allot of my other libraries. + +Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE, +it is so freeing not to worry about it and will help me keep pouring out bigger and better +things for you the community. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/form/cache.go b/vendor/github.com/go-playground/form/cache.go new file mode 100644 index 0000000..47e5fce --- /dev/null +++ b/vendor/github.com/go-playground/form/cache.go @@ -0,0 +1,133 @@ +package form + +import ( + "reflect" + "sort" + "strings" + "sync" + "sync/atomic" +) + +type cacheFields []cachedField + +func (s cacheFields) Len() int { + return len(s) +} + +func (s cacheFields) Less(i, j int) bool { + return !s[i].isAnonymous +} + +func (s cacheFields) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +type cachedField struct { + idx int + name string + isAnonymous bool + isOmitEmpty bool +} + +type cachedStruct struct { + fields cacheFields +} + +type structCacheMap struct { + m atomic.Value // map[reflect.Type]*cachedStruct + lock sync.Mutex + tagFn TagNameFunc +} + +// TagNameFunc allows for adding of a custom tag name parser +type TagNameFunc func(field reflect.StructField) string + +func newStructCacheMap() *structCacheMap { + + sc := new(structCacheMap) + sc.m.Store(make(map[reflect.Type]*cachedStruct)) + + return sc +} + +func (s *structCacheMap) Get(key reflect.Type) (value *cachedStruct, ok bool) { + value, ok = s.m.Load().(map[reflect.Type]*cachedStruct)[key] + return +} + +func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) { + + m := s.m.Load().(map[reflect.Type]*cachedStruct) + + nm := make(map[reflect.Type]*cachedStruct, len(m)+1) + for k, v := range m { + nm[k] = v + } + nm[key] = value + s.m.Store(nm) +} + +func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key reflect.Type, tagName string) *cachedStruct { + + s.lock.Lock() + + // could have been multiple trying to access, but once first is done this ensures struct + // isn't parsed again. + cs, ok := s.Get(key) + if ok { + s.lock.Unlock() + return cs + } + + typ := current.Type() + cs = &cachedStruct{fields: make([]cachedField, 0, 4)} // init 4, betting most structs decoding into have at aleast 4 fields. + + numFields := current.NumField() + + var fld reflect.StructField + var name string + var idx int + var isOmitEmpty bool + + for i := 0; i < numFields; i++ { + isOmitEmpty = false + fld = typ.Field(i) + + if fld.PkgPath != blank && !fld.Anonymous { + continue + } + + if s.tagFn != nil { + name = s.tagFn(fld) + } else { + name = fld.Tag.Get(tagName) + } + + if name == ignore { + continue + } + + if mode == ModeExplicit && len(name) == 0 { + continue + } + + // check for omitempty + if idx = strings.LastIndexByte(name, ','); idx != -1 { + isOmitEmpty = name[idx+1:] == "omitempty" + name = name[:idx] + } + + if len(name) == 0 { + name = fld.Name + } + + cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty}) + } + + sort.Sort(cs.fields) + s.Set(typ, cs) + + s.lock.Unlock() + + return cs +} diff --git a/vendor/github.com/go-playground/form/decoder.go b/vendor/github.com/go-playground/form/decoder.go new file mode 100644 index 0000000..cf858f0 --- /dev/null +++ b/vendor/github.com/go-playground/form/decoder.go @@ -0,0 +1,754 @@ +package form + +import ( + "fmt" + "log" + "net/url" + "reflect" + "strconv" + "time" +) + +const ( + errArraySize = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)" + errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket" + errMissingEndBracket = "Invalid formatting for key '%s' missing ']' bracket" +) + +type decoder struct { + d *Decoder + errs DecodeErrors + dm dataMap + values url.Values + maxKeyLen int + namespace []byte +} + +func (d *decoder) setError(namespace []byte, err error) { + if d.errs == nil { + d.errs = make(DecodeErrors) + } + + d.errs[string(namespace)] = err +} + +func (d *decoder) findAlias(ns string) *recursiveData { + + for i := 0; i < len(d.dm); i++ { + + if d.dm[i].alias == ns { + return d.dm[i] + } + } + + return nil +} + +func (d *decoder) parseMapData() { + + // already parsed + if len(d.dm) > 0 { + return + } + + d.maxKeyLen = 0 + d.dm = d.dm[0:0] + + var i int + var idx int + var l int + var insideBracket bool + var rd *recursiveData + var isNum bool + + for k := range d.values { + + if len(k) > d.maxKeyLen { + d.maxKeyLen = len(k) + } + + for i = 0; i < len(k); i++ { + + switch k[i] { + case '[': + idx = i + insideBracket = true + isNum = true + case ']': + + if !insideBracket { + log.Panicf(errMissingStartBracket, k) + } + + if rd = d.findAlias(k[:idx]); rd == nil { + + l = len(d.dm) + 1 + + if l > cap(d.dm) { + dm := make(dataMap, l) + copy(dm, d.dm) + rd = new(recursiveData) + dm[len(d.dm)] = rd + d.dm = dm + } else { + l = len(d.dm) + d.dm = d.dm[:l+1] + rd = d.dm[l] + rd.sliceLen = 0 + rd.keys = rd.keys[0:0] + } + + rd.alias = k[:idx] + } + + // is map + key + ke := key{ + ivalue: -1, + value: k[idx+1 : i], + searchValue: k[idx : i+1], + } + + // is key is number, most likely array key, keep track of just in case an array/slice. + if isNum { + + // no need to check for error, it will always pass + // as we have done the checking to ensure + // the value is a number ahead of time. + ke.ivalue, _ = strconv.Atoi(ke.value) + + if ke.ivalue > rd.sliceLen { + rd.sliceLen = ke.ivalue + + } + } + + rd.keys = append(rd.keys, ke) + + insideBracket = false + default: + // checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') + if insideBracket && (k[i] > 57 || k[i] < 48) { + isNum = false + } + } + } + + // if still inside bracket, that means no ending bracket was ever specified + if insideBracket { + log.Panicf(errMissingEndBracket, k) + } + } +} + +func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) { + + l := len(namespace) + first := l == 0 + + // anonymous structs will still work for caching as the whole definition is stored + // including tags + s, ok := d.d.structCache.Get(typ) + if !ok { + s = d.d.structCache.parseStruct(d.d.mode, v, typ, d.d.tagName) + } + + for _, f := range s.fields { + + namespace = namespace[:l] + + if f.isAnonymous { + if d.setFieldByType(v.Field(f.idx), namespace, 0) { + set = true + } + } + + if first { + namespace = append(namespace, f.name...) + } else { + namespace = append(namespace, namespaceSeparator) + namespace = append(namespace, f.name...) + } + + if d.setFieldByType(v.Field(f.idx), namespace, 0) { + set = true + } + } + + return +} + +func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) { + + var err error + v, kind := ExtractType(current) + + arr, ok := d.values[string(namespace)] + + if d.d.customTypeFuncs != nil { + + if ok { + if cf, ok := d.d.customTypeFuncs[v.Type()]; ok { + val, err := cf(arr[idx:]) + if err != nil { + d.setError(namespace, err) + return + } + + v.Set(reflect.ValueOf(val)) + set = true + return + } + } + } + switch kind { + case reflect.Interface: + if !ok { + return + } + v.Set(reflect.ValueOf(arr[idx])) + set = true + + case reflect.Ptr: + + newVal := reflect.New(v.Type().Elem()) + if set = d.setFieldByType(newVal.Elem(), namespace, idx); set { + v.Set(newVal) + } + + case reflect.String: + if !ok { + return + } + v.SetString(arr[idx]) + set = true + + case reflect.Uint, reflect.Uint64: + if !ok || len(arr[idx]) == 0 { + return + } + var u64 uint64 + if u64, err = strconv.ParseUint(arr[idx], 10, 64); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetUint(u64) + set = true + + case reflect.Uint8: + if !ok || len(arr[idx]) == 0 { + return + } + var u64 uint64 + if u64, err = strconv.ParseUint(arr[idx], 10, 8); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetUint(u64) + set = true + + case reflect.Uint16: + if !ok || len(arr[idx]) == 0 { + return + } + var u64 uint64 + if u64, err = strconv.ParseUint(arr[idx], 10, 16); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetUint(u64) + set = true + + case reflect.Uint32: + if !ok || len(arr[idx]) == 0 { + return + } + var u64 uint64 + if u64, err = strconv.ParseUint(arr[idx], 10, 32); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetUint(u64) + set = true + + case reflect.Int, reflect.Int64: + if !ok || len(arr[idx]) == 0 { + return + } + var i64 int64 + if i64, err = strconv.ParseInt(arr[idx], 10, 64); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetInt(i64) + set = true + + case reflect.Int8: + if !ok || len(arr[idx]) == 0 { + return + } + var i64 int64 + if i64, err = strconv.ParseInt(arr[idx], 10, 8); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetInt(i64) + set = true + + case reflect.Int16: + if !ok || len(arr[idx]) == 0 { + return + } + var i64 int64 + if i64, err = strconv.ParseInt(arr[idx], 10, 16); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetInt(i64) + set = true + + case reflect.Int32: + if !ok || len(arr[idx]) == 0 { + return + } + var i64 int64 + if i64, err = strconv.ParseInt(arr[idx], 10, 32); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetInt(i64) + set = true + + case reflect.Float32: + if !ok || len(arr[idx]) == 0 { + return + } + var f float64 + if f, err = strconv.ParseFloat(arr[idx], 32); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[0], v.Type(), string(namespace))) + return + } + v.SetFloat(f) + set = true + + case reflect.Float64: + if !ok || len(arr[idx]) == 0 { + return + } + var f float64 + if f, err = strconv.ParseFloat(arr[idx], 64); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[0], v.Type(), string(namespace))) + return + } + v.SetFloat(f) + set = true + + case reflect.Bool: + if !ok { + return + } + var b bool + if b, err = parseBool(arr[idx]); err != nil { + d.setError(namespace, fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace))) + return + } + v.SetBool(b) + set = true + + case reflect.Slice: + d.parseMapData() + // slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"} + + if ok && len(arr) > 0 { + var varr reflect.Value + + var ol int + l := len(arr) + + if v.IsNil() { + varr = reflect.MakeSlice(v.Type(), len(arr), len(arr)) + } else { + + ol = v.Len() + l += ol + + if v.Cap() <= l { + varr = reflect.MakeSlice(v.Type(), l, l) + } else { + // preserve predefined capacity, possibly for reuse after decoding + varr = reflect.MakeSlice(v.Type(), l, v.Cap()) + } + reflect.Copy(varr, v) + } + + for i := ol; i < l; i++ { + newVal := reflect.New(v.Type().Elem()).Elem() + + if d.setFieldByType(newVal, namespace, i-ol) { + set = true + varr.Index(i).Set(newVal) + } + } + + v.Set(varr) + } + + // maybe it's an numbered array i.e. Phone[0].Number + if rd := d.findAlias(string(namespace)); rd != nil { + + var varr reflect.Value + var kv key + + sl := rd.sliceLen + 1 + + // checking below for maxArraySize, but if array exists and already + // has sufficient capacity allocated then we do not check as the code + // obviously allows a capacity greater than the maxArraySize. + + if v.IsNil() { + + if sl > d.d.maxArraySize { + d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize)) + return + } + + varr = reflect.MakeSlice(v.Type(), sl, sl) + + } else if v.Len() < sl { + + if v.Cap() <= sl { + + if sl > d.d.maxArraySize { + d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize)) + return + } + + varr = reflect.MakeSlice(v.Type(), sl, sl) + } else { + varr = reflect.MakeSlice(v.Type(), sl, v.Cap()) + } + + reflect.Copy(varr, v) + + } else { + varr = v + } + + for i := 0; i < len(rd.keys); i++ { + + kv = rd.keys[i] + newVal := reflect.New(varr.Type().Elem()).Elem() + + if kv.ivalue == -1 { + d.setError(namespace, fmt.Errorf("invalid slice index '%s'", kv.value)) + continue + } + + if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) { + set = true + varr.Index(kv.ivalue).Set(newVal) + } + } + + if !set { + return + } + + v.Set(varr) + } + + case reflect.Array: + d.parseMapData() + + // array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"} + + if ok && len(arr) > 0 { + var varr reflect.Value + l := len(arr) + overCapacity := v.Len() < l + if overCapacity { + // more values than array capacity, ignore values over capacity as it's possible some would just want + // to grab the first x number of elements; in the future strict mode logic should return an error + fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values") + } + varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem()))) + reflect.Copy(varr, v) + + if v.Len() < len(arr) { + l = v.Len() + } + for i := 0; i < l; i++ { + newVal := reflect.New(v.Type().Elem()).Elem() + + if d.setFieldByType(newVal, namespace, i) { + set = true + varr.Index(i).Set(newVal) + } + } + v.Set(varr) + } + + // maybe it's an numbered array i.e. Phone[0].Number + if rd := d.findAlias(string(namespace)); rd != nil { + var varr reflect.Value + var kv key + + overCapacity := rd.sliceLen >= v.Len() + if overCapacity { + // more values than array capacity, ignore values over capacity as it's possible some would just want + // to grab the first x number of elements; in the future strict mode logic should return an error + fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values") + } + varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem()))) + reflect.Copy(varr, v) + + for i := 0; i < len(rd.keys); i++ { + kv = rd.keys[i] + if kv.ivalue >= v.Len() { + continue + } + newVal := reflect.New(varr.Type().Elem()).Elem() + + if kv.ivalue == -1 { + d.setError(namespace, fmt.Errorf("invalid array index '%s'", kv.value)) + continue + } + + if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) { + set = true + varr.Index(kv.ivalue).Set(newVal) + } + } + + if !set { + return + } + v.Set(varr) + } + + case reflect.Map: + var rd *recursiveData + + d.parseMapData() + + // no natural map support so skip directly to dm lookup + if rd = d.findAlias(string(namespace)); rd == nil { + return + } + + var existing bool + var kv key + var mp reflect.Value + var mk reflect.Value + + typ := v.Type() + + if v.IsNil() { + mp = reflect.MakeMap(typ) + } else { + existing = true + mp = v + } + + for i := 0; i < len(rd.keys); i++ { + newVal := reflect.New(typ.Elem()).Elem() + mk = reflect.New(typ.Key()).Elem() + kv = rd.keys[i] + + if err := d.getMapKey(kv.value, mk, namespace); err != nil { + d.setError(namespace, err) + continue + } + + if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) { + set = true + mp.SetMapIndex(mk, newVal) + } + } + + if !set || existing { + return + } + + v.Set(mp) + + case reflect.Struct: + typ := v.Type() + + // if we get here then no custom time function declared so use RFC3339 by default + if typ == timeType { + + if !ok || len(arr[idx]) == 0 { + return + } + + t, err := time.Parse(time.RFC3339, arr[idx]) + if err != nil { + d.setError(namespace, err) + } + + v.Set(reflect.ValueOf(t)) + set = true + return + } + + d.parseMapData() + + // we must be recursing infinitly...but that's ok we caught it on the very first overun. + if len(namespace) > d.maxKeyLen { + return + } + + set = d.traverseStruct(v, typ, namespace) + } + return +} + +func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) { + + v, kind := ExtractType(current) + + if d.d.customTypeFuncs != nil { + if cf, ok := d.d.customTypeFuncs[v.Type()]; ok { + + val, er := cf([]string{key}) + if er != nil { + err = er + return + } + + v.Set(reflect.ValueOf(val)) + return + } + } + + switch kind { + case reflect.Interface: + // If interface would have been set on the struct before decoding, + // say to a struct value we would not get here but kind would be struct. + v.Set(reflect.ValueOf(key)) + return + case reflect.Ptr: + newVal := reflect.New(v.Type().Elem()) + if err = d.getMapKey(key, newVal.Elem(), namespace); err == nil { + v.Set(newVal) + } + + case reflect.String: + v.SetString(key) + + case reflect.Uint, reflect.Uint64: + + u64, e := strconv.ParseUint(key, 10, 64) + if e != nil { + err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetUint(u64) + + case reflect.Uint8: + + u64, e := strconv.ParseUint(key, 10, 8) + if e != nil { + err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetUint(u64) + + case reflect.Uint16: + + u64, e := strconv.ParseUint(key, 10, 16) + if e != nil { + err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetUint(u64) + + case reflect.Uint32: + + u64, e := strconv.ParseUint(key, 10, 32) + if e != nil { + err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetUint(u64) + + case reflect.Int, reflect.Int64: + + i64, e := strconv.ParseInt(key, 10, 64) + if e != nil { + err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetInt(i64) + + case reflect.Int8: + + i64, e := strconv.ParseInt(key, 10, 8) + if e != nil { + err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetInt(i64) + + case reflect.Int16: + + i64, e := strconv.ParseInt(key, 10, 16) + if e != nil { + err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetInt(i64) + + case reflect.Int32: + + i64, e := strconv.ParseInt(key, 10, 32) + if e != nil { + err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetInt(i64) + + case reflect.Float32: + + f, e := strconv.ParseFloat(key, 32) + if e != nil { + err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetFloat(f) + + case reflect.Float64: + + f, e := strconv.ParseFloat(key, 64) + if e != nil { + err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetFloat(f) + + case reflect.Bool: + + b, e := parseBool(key) + if e != nil { + err = fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + return + } + + v.SetBool(b) + + default: + err = fmt.Errorf("Unsupported Map Key '%s', Type '%v' Namespace '%s'", key, v.Type(), string(namespace)) + } + + return +} diff --git a/vendor/github.com/go-playground/form/doc.go b/vendor/github.com/go-playground/form/doc.go new file mode 100644 index 0000000..b2201eb --- /dev/null +++ b/vendor/github.com/go-playground/form/doc.go @@ -0,0 +1,275 @@ +/* +Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. + + +It has the following features: + + - Primitives types cause zero allocations. + - Supports map of almost all types. + - Supports both Numbered and Normal arrays eg. "Array[0]" and just "Array" + with multiple values passed. + - Slice honours the specified index. eg. if "Slice[2]" is the only Slice + value passed down, it will be put at index 2; if slice isn't big enough + it will be expanded. + - Array honours the specified index. eg. if "Array[2]" is the only Array + value passed down, it will be put at index 2; if array isn't big enough + a warning will be printed and value ignored. + - Only creates objects as necessary eg. if no `array` or `map` values are + passed down, the `array` and `map` are left as their default values in + the struct. + - Allows for Custom Type registration. + - Handles time.Time using RFC3339 time format by default, + but can easily be changed by registering a Custom Type, see below. + - Handles Encoding & Decoding of almost all Go types eg. can Decode into + struct, array, map, int... and Encode a struct, array, map, int... + +Common Questions + +Questions + + Does it support encoding.TextUnmarshaler? + No because TextUnmarshaler only accepts []byte but posted values can have + multiple values, so is not suitable. + + Mixing array/slice with array[idx]/slice[idx], in which order are they parsed? + array/slice then array[idx]/slice[idx] + +Supported Types + +out of the box supported types + + - string + - bool + - int, int8, int16, int32, int64 + - uint, uint8, uint16, uint32, uint64 + - float32, float64 + - struct and anonymous struct + - interface{} + - time.Time` - by default using RFC3339 + - a `pointer` to one of the above types + - slice, array + - map + - `custom types` can override any of the above types + - many other types may be supported inherently (eg. bson.ObjectId is + type ObjectId string, which will get populated by the string type + + **NOTE**: map, struct and slice nesting are ad infinitum. + +Usage + +symbols + + - Use symbol `.` for separating fields/structs. (eg. `structfield.field`) + - Use `[index or key]` for access to index of a slice/array or key for map. + (eg. `arrayfield[0]`, `mapfield[keyvalue]`) + +html + +
+ + + + + + + + + + + + +
+ +Example + +example decoding the above HTML + + package main + + import ( + "fmt" + "log" + "net/url" + + "github.com/go-playground/form" + ) + + // Address contains address information + type Address struct { + Name string + Phone string + } + + // User contains user information + type User struct { + Name string + Age uint8 + Gender string + Address []Address + Active bool `form:"active"` + MapExample map[string]string + NestedMap map[string]map[string]string + NestedArray [][]string + } + + // use a single instance of Decoder, it caches struct info + var decoder *form.Decoder + + func main() { + decoder = form.NewDecoder() + + // this simulates the results of http.Request's ParseForm() function + values := parseForm() + + var user User + + // must pass a pointer + err := decoder.Decode(&user, values) + if err != nil { + log.Panic(err) + } + + fmt.Printf("%#v\n", user) + } + + // this simulates the results of http.Request's ParseForm() function + func parseForm() url.Values { + return url.Values{ + "Name": []string{"joeybloggs"}, + "Age": []string{"3"}, + "Gender": []string{"Male"}, + "Address[0].Name": []string{"26 Here Blvd."}, + "Address[0].Phone": []string{"9(999)999-9999"}, + "Address[1].Name": []string{"26 There Blvd."}, + "Address[1].Phone": []string{"1(111)111-1111"}, + "active": []string{"true"}, + "MapExample[key]": []string{"value"}, + "NestedMap[key][key]": []string{"value"}, + "NestedArray[0][0]": []string{"value"}, + } + } + +example encoding + + package main + + import ( + "fmt" + "log" + + "github.com/go-playground/form" + ) + + // Address contains address information + type Address struct { + Name string + Phone string + } + + // User contains user information + type User struct { + Name string + Age uint8 + Gender string + Address []Address + Active bool `form:"active"` + MapExample map[string]string + NestedMap map[string]map[string]string + NestedArray [][]string + } + + // use a single instance of Encoder, it caches struct info + var encoder *form.Encoder + + func main() { + encoder = form.NewEncoder() + + user := User{ + Name: "joeybloggs", + Age: 3, + Gender: "Male", + Address: []Address{ + {Name: "26 Here Blvd.", Phone: "9(999)999-9999"}, + {Name: "26 There Blvd.", Phone: "1(111)111-1111"}, + }, + Active: true, + MapExample: map[string]string{"key": "value"}, + NestedMap: map[string]map[string]string{"key": {"key": "value"}}, + NestedArray: [][]string{{"value"}}, + } + + // must pass a pointer + values, err := encoder.Encode(&user) + if err != nil { + log.Panic(err) + } + + fmt.Printf("%#v\n", values) + } + + +Registering Custom Types + +Decoder + + decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) { + return time.Parse("2006-01-02", vals[0]) + }, time.Time{}) + + ADDITIONAL: if a struct type is registered, the function will only be called + if a url.Value exists for the struct and not just the struct fields + eg. url.Values{"User":"Name%3Djoeybloggs"} will call the custom type function + with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not. + +Encoder + + encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) { + return []string{x.(time.Time).Format("2006-01-02")}, nil + }, time.Time{}) + + +Ignoring Fields + +you can tell form to ignore fields using `-` in the tag + + type MyStruct struct { + Field string `form:"-"` + } + +Omitempty + +you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag + + type MyStruct struct { + Field string `form:",omitempty"` + Field2 string `form:"CustomFieldName,omitempty"` + } + + +Notes + +To maximize compatibility with other systems the Encoder attempts +to avoid using array indexes in url.Values if at all possible. + + eg. + + // A struct field of + Field []string{"1", "2", "3"} + + // will be output a url.Value as + "Field": []string{"1", "2", "3"} + + and not + "Field[0]": []string{"1"} + "Field[1]": []string{"2"} + "Field[2]": []string{"3"} + + // however there are times where it is unavoidable, like with pointers + i := int(1) + Field []*string{nil, nil, &i} + + // to avoid index 1 and 2 must use index + "Field[2]": []string{"1"} + +*/ +package form diff --git a/vendor/github.com/go-playground/form/encoder.go b/vendor/github.com/go-playground/form/encoder.go new file mode 100644 index 0000000..9f26866 --- /dev/null +++ b/vendor/github.com/go-playground/form/encoder.go @@ -0,0 +1,260 @@ +package form + +import ( + "fmt" + "net/url" + "reflect" + "strconv" + "time" +) + +type encoder struct { + e *Encoder + errs EncodeErrors + values url.Values + namespace []byte +} + +func (e *encoder) setError(namespace []byte, err error) { + if e.errs == nil { + e.errs = make(EncodeErrors) + } + + e.errs[string(namespace)] = err +} + +func (e *encoder) setVal(namespace []byte, idx int, vals ...string) { + + arr, ok := e.values[string(namespace)] + if ok { + arr = append(arr, vals...) + } else { + arr = vals + } + + e.values[string(namespace)] = arr +} + +func (e *encoder) traverseStruct(v reflect.Value, namespace []byte, idx int) { + + typ := v.Type() + l := len(namespace) + first := l == 0 + + // anonymous structs will still work for caching as the whole definition is stored + // including tags + s, ok := e.e.structCache.Get(typ) + if !ok { + s = e.e.structCache.parseStruct(e.e.mode, v, typ, e.e.tagName) + } + + for _, f := range s.fields { + namespace = namespace[:l] + + if f.isAnonymous && e.e.embedAnonymous { + e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty) + continue + } + + if first { + namespace = append(namespace, f.name...) + } else { + namespace = append(namespace, namespaceSeparator) + namespace = append(namespace, f.name...) + } + + e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty) + } +} + +func (e *encoder) setFieldByType(current reflect.Value, namespace []byte, idx int, isOmitEmpty bool) { + + if idx > -1 && current.Kind() == reflect.Ptr { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + idx = -2 + } + + if isOmitEmpty && !hasValue(current) { + return + } + v, kind := ExtractType(current) + + if e.e.customTypeFuncs != nil { + + if cf, ok := e.e.customTypeFuncs[v.Type()]; ok { + + arr, err := cf(v.Interface()) + if err != nil { + e.setError(namespace, err) + return + } + + if idx > -1 { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + } + + e.setVal(namespace, idx, arr...) + return + } + } + + switch kind { + case reflect.Ptr, reflect.Interface, reflect.Invalid: + return + + case reflect.String: + + e.setVal(namespace, idx, v.String()) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + + e.setVal(namespace, idx, strconv.FormatUint(v.Uint(), 10)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + e.setVal(namespace, idx, strconv.FormatInt(v.Int(), 10)) + + case reflect.Float32: + + e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 32)) + + case reflect.Float64: + + e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 64)) + + case reflect.Bool: + + e.setVal(namespace, idx, strconv.FormatBool(v.Bool())) + + case reflect.Slice, reflect.Array: + + if idx == -1 { + + for i := 0; i < v.Len(); i++ { + e.setFieldByType(v.Index(i), namespace, i, false) + } + + return + } + + if idx > -1 { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + } + + namespace = append(namespace, '[') + l := len(namespace) + + for i := 0; i < v.Len(); i++ { + namespace = namespace[:l] + namespace = strconv.AppendInt(namespace, int64(i), 10) + namespace = append(namespace, ']') + e.setFieldByType(v.Index(i), namespace, -2, false) + } + + case reflect.Map: + + if idx > -1 { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + } + + var valid bool + var s string + l := len(namespace) + + for _, key := range v.MapKeys() { + + namespace = namespace[:l] + + if s, valid = e.getMapKey(key, namespace); !valid { + continue + } + + namespace = append(namespace, '[') + namespace = append(namespace, s...) + namespace = append(namespace, ']') + + e.setFieldByType(current.MapIndex(key), namespace, -2, false) + } + + case reflect.Struct: + + // if we get here then no custom time function declared so use RFC3339 by default + if v.Type() == timeType { + + if idx > -1 { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + } + + e.setVal(namespace, idx, v.Interface().(time.Time).Format(time.RFC3339)) + return + } + + if idx == -1 { + e.traverseStruct(v, namespace, idx) + return + } + + if idx > -1 { + namespace = append(namespace, '[') + namespace = strconv.AppendInt(namespace, int64(idx), 10) + namespace = append(namespace, ']') + } + + e.traverseStruct(v, namespace, -2) + } +} + +func (e *encoder) getMapKey(key reflect.Value, namespace []byte) (string, bool) { + + v, kind := ExtractType(key) + + if e.e.customTypeFuncs != nil { + + if cf, ok := e.e.customTypeFuncs[v.Type()]; ok { + arr, err := cf(v.Interface()) + if err != nil { + e.setError(namespace, err) + return "", false + } + + return arr[0], true + } + } + + switch kind { + case reflect.Interface, reflect.Ptr: + return "", false + + case reflect.String: + return v.String(), true + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(v.Uint(), 10), true + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(v.Int(), 10), true + + case reflect.Float32: + return strconv.FormatFloat(v.Float(), 'f', -1, 32), true + + case reflect.Float64: + return strconv.FormatFloat(v.Float(), 'f', -1, 64), true + + case reflect.Bool: + return strconv.FormatBool(v.Bool()), true + + default: + e.setError(namespace, fmt.Errorf("Unsupported Map Key '%v' Namespace '%s'", v.String(), namespace)) + return "", false + } +} diff --git a/vendor/github.com/go-playground/form/form.go b/vendor/github.com/go-playground/form/form.go new file mode 100644 index 0000000..0ce5917 --- /dev/null +++ b/vendor/github.com/go-playground/form/form.go @@ -0,0 +1,50 @@ +package form + +import ( + "reflect" + "time" +) + +const ( + blank = "" + namespaceSeparator = '.' + ignore = "-" + fieldNS = "Field Namespace:" + errorText = " ERROR:" +) + +var ( + timeType = reflect.TypeOf(time.Time{}) +) + +// Mode specifies which mode the form decoder is to run +type Mode uint8 + +const ( + + // ModeImplicit tries to parse values for all + // fields that do not have an ignore '-' tag + ModeImplicit Mode = iota + + // ModeExplicit only parses values for field with a field tag + // and that tag is not the ignore '-' tag + ModeExplicit +) + +// AnonymousMode specifies how data should be rolled up +// or separated from anonymous structs +type AnonymousMode uint8 + +const ( + // AnonymousEmbed embeds anonymous data when encoding + // eg. type A struct { Field string } + // type B struct { A, Field string } + // encode results: url.Values{"Field":[]string{"B FieldVal", "A FieldVal"}} + AnonymousEmbed AnonymousMode = iota + + // AnonymousSeparate does not embed anonymous data when encoding + // eg. type A struct { Field string } + // type B struct { A, Field string } + // encode results: url.Values{"Field":[]string{"B FieldVal"}, "A.Field":[]string{"A FieldVal"}} + AnonymousSeparate +) diff --git a/vendor/github.com/go-playground/form/form_decoder.go b/vendor/github.com/go-playground/form/form_decoder.go new file mode 100644 index 0000000..fe8b36f --- /dev/null +++ b/vendor/github.com/go-playground/form/form_decoder.go @@ -0,0 +1,174 @@ +package form + +import ( + "bytes" + "net/url" + "reflect" + "strings" + "sync" +) + +// DecodeCustomTypeFunc allows for registering/overriding types to be parsed. +type DecodeCustomTypeFunc func([]string) (interface{}, error) + +// DecodeErrors is a map of errors encountered during form decoding +type DecodeErrors map[string]error + +func (d DecodeErrors) Error() string { + buff := bytes.NewBufferString(blank) + + for k, err := range d { + buff.WriteString(fieldNS) + buff.WriteString(k) + buff.WriteString(errorText) + buff.WriteString(err.Error()) + buff.WriteString("\n") + } + + return strings.TrimSpace(buff.String()) +} + +// An InvalidDecoderError describes an invalid argument passed to Decode. +// (The argument passed to Decode must be a non-nil pointer.) +type InvalidDecoderError struct { + Type reflect.Type +} + +func (e *InvalidDecoderError) Error() string { + + if e.Type == nil { + return "form: Decode(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "form: Decode(non-pointer " + e.Type.String() + ")" + } + + return "form: Decode(nil " + e.Type.String() + ")" +} + +type key struct { + ivalue int + value string + searchValue string +} + +type recursiveData struct { + alias string + sliceLen int + keys []key +} + +type dataMap []*recursiveData + +// Decoder is the main decode instance +type Decoder struct { + tagName string + mode Mode + structCache *structCacheMap + customTypeFuncs map[reflect.Type]DecodeCustomTypeFunc + maxArraySize int + dataPool *sync.Pool +} + +// NewDecoder creates a new decoder instance with sane defaults +func NewDecoder() *Decoder { + + d := &Decoder{ + tagName: "form", + mode: ModeImplicit, + structCache: newStructCacheMap(), + maxArraySize: 10000, + } + + d.dataPool = &sync.Pool{New: func() interface{} { + return &decoder{ + d: d, + namespace: make([]byte, 0, 64), + } + }} + + return d +} + +// SetTagName sets the given tag name to be used by the decoder. +// Default is "form" +func (d *Decoder) SetTagName(tagName string) { + d.tagName = tagName +} + +// SetMode sets the mode the decoder should run +// Default is ModeImplicit +func (d *Decoder) SetMode(mode Mode) { + d.mode = mode +} + +// SetMaxArraySize sets maximum array size that can be created. +// This limit is for the array indexing this library supports to +// avoid potential DOS or man-in-the-middle attacks using an unusually +// high number. +// DEFAULT: 10000 +func (d *Decoder) SetMaxArraySize(size uint) { + d.maxArraySize = int(size) +} + +// RegisterTagNameFunc registers a custom tag name parser function +// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing +// +// ADDITIONAL: once a custom function has been registered the default, or custom set, tag name is ignored +// and relies 100% on the function for the name data. The return value WILL BE CACHED and so return value +// must be consistent. +func (d *Decoder) RegisterTagNameFunc(fn TagNameFunc) { + d.structCache.tagFn = fn +} + +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types. +// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing +// +// ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for +// the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the +// custom type function with `User` as the type, however url.Values{"User.Name":"joeybloggs"} will not. +func (d *Decoder) RegisterCustomTypeFunc(fn DecodeCustomTypeFunc, types ...interface{}) { + + if d.customTypeFuncs == nil { + d.customTypeFuncs = map[reflect.Type]DecodeCustomTypeFunc{} + } + + for _, t := range types { + d.customTypeFuncs[reflect.TypeOf(t)] = fn + } +} + +// Decode parses the given values and sets the corresponding struct and/or type values +// +// Decode returns an InvalidDecoderError if interface passed is invalid. +func (d *Decoder) Decode(v interface{}, values url.Values) (err error) { + + val := reflect.ValueOf(v) + + if val.Kind() != reflect.Ptr || val.IsNil() { + return &InvalidDecoderError{reflect.TypeOf(v)} + } + + dec := d.dataPool.Get().(*decoder) + dec.values = values + dec.dm = dec.dm[0:0] + + val = val.Elem() + typ := val.Type() + + if val.Kind() == reflect.Struct && typ != timeType { + dec.traverseStruct(val, typ, dec.namespace[0:0]) + } else { + dec.setFieldByType(val, dec.namespace[0:0], 0) + } + + if len(dec.errs) > 0 { + err = dec.errs + dec.errs = nil + } + + d.dataPool.Put(dec) + + return +} diff --git a/vendor/github.com/go-playground/form/form_encoder.go b/vendor/github.com/go-playground/form/form_encoder.go new file mode 100644 index 0000000..ad3cc10 --- /dev/null +++ b/vendor/github.com/go-playground/form/form_encoder.go @@ -0,0 +1,144 @@ +package form + +import ( + "bytes" + "net/url" + "reflect" + "strings" + "sync" +) + +// EncodeCustomTypeFunc allows for registering/overriding types to be parsed. +type EncodeCustomTypeFunc func(x interface{}) ([]string, error) + +// EncodeErrors is a map of errors encountered during form encoding +type EncodeErrors map[string]error + +func (e EncodeErrors) Error() string { + buff := bytes.NewBufferString(blank) + + for k, err := range e { + buff.WriteString(fieldNS) + buff.WriteString(k) + buff.WriteString(errorText) + buff.WriteString(err.Error()) + buff.WriteString("\n") + } + + return strings.TrimSpace(buff.String()) +} + +// An InvalidEncodeError describes an invalid argument passed to Encode. +type InvalidEncodeError struct { + Type reflect.Type +} + +func (e *InvalidEncodeError) Error() string { + + if e.Type == nil { + return "form: Encode(nil)" + } + + return "form: Encode(nil " + e.Type.String() + ")" +} + +// Encoder is the main encode instance +type Encoder struct { + tagName string + structCache *structCacheMap + customTypeFuncs map[reflect.Type]EncodeCustomTypeFunc + dataPool *sync.Pool + mode Mode + embedAnonymous bool +} + +// NewEncoder creates a new encoder instance with sane defaults +func NewEncoder() *Encoder { + + e := &Encoder{ + tagName: "form", + mode: ModeImplicit, + structCache: newStructCacheMap(), + embedAnonymous: true, + } + + e.dataPool = &sync.Pool{New: func() interface{} { + return &encoder{ + e: e, + namespace: make([]byte, 0, 64), + } + }} + + return e +} + +// SetTagName sets the given tag name to be used by the encoder. +// Default is "form" +func (e *Encoder) SetTagName(tagName string) { + e.tagName = tagName +} + +// SetMode sets the mode the encoder should run +// Default is ModeImplicit +func (e *Encoder) SetMode(mode Mode) { + e.mode = mode +} + +// SetAnonymousMode sets the mode the encoder should run +// Default is AnonymousEmbed +func (e *Encoder) SetAnonymousMode(mode AnonymousMode) { + e.embedAnonymous = mode == AnonymousEmbed +} + +// RegisterTagNameFunc registers a custom tag name parser function +// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing +// +// ADDITIONAL: once a custom function has been registered the default, or custom set, tag name is ignored +// and relies 100% on the function for the name data. The return value WILL BE CACHED and so return value +// must be consistent. +func (e *Encoder) RegisterTagNameFunc(fn TagNameFunc) { + e.structCache.tagFn = fn +} + +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types +// NOTE: this method is not thread-safe it is intended that these all be registered prior to any parsing +func (e *Encoder) RegisterCustomTypeFunc(fn EncodeCustomTypeFunc, types ...interface{}) { + + if e.customTypeFuncs == nil { + e.customTypeFuncs = map[reflect.Type]EncodeCustomTypeFunc{} + } + + for _, t := range types { + e.customTypeFuncs[reflect.TypeOf(t)] = fn + } +} + +// Encode encodes the given values and sets the corresponding struct values +func (e *Encoder) Encode(v interface{}) (values url.Values, err error) { + + val, kind := ExtractType(reflect.ValueOf(v)) + + if kind == reflect.Ptr || kind == reflect.Interface || kind == reflect.Invalid { + return nil, &InvalidEncodeError{reflect.TypeOf(v)} + } + + enc := e.dataPool.Get().(*encoder) + enc.values = make(url.Values) + + if kind == reflect.Struct && val.Type() != timeType { + enc.traverseStruct(val, enc.namespace[0:0], -1) + } else { + enc.setFieldByType(val, enc.namespace[0:0], -1, false) + } + + if len(enc.errs) > 0 { + err = enc.errs + enc.errs = nil + } + + values = enc.values + + e.dataPool.Put(enc) + + return +} diff --git a/vendor/github.com/go-playground/form/logo.jpg b/vendor/github.com/go-playground/form/logo.jpg new file mode 100644 index 0000000..2ef34f8 Binary files /dev/null and b/vendor/github.com/go-playground/form/logo.jpg differ diff --git a/vendor/github.com/go-playground/form/util.go b/vendor/github.com/go-playground/form/util.go new file mode 100644 index 0000000..13db877 --- /dev/null +++ b/vendor/github.com/go-playground/form/util.go @@ -0,0 +1,56 @@ +package form + +import ( + "reflect" + "strconv" +) + +// ExtractType gets the actual underlying type of field value. +// it is exposed for use within you Custom Functions +func ExtractType(current reflect.Value) (reflect.Value, reflect.Kind) { + + switch current.Kind() { + case reflect.Ptr: + + if current.IsNil() { + return current, reflect.Ptr + } + + return ExtractType(current.Elem()) + + case reflect.Interface: + + if current.IsNil() { + return current, reflect.Interface + } + + return ExtractType(current.Elem()) + + default: + return current, current.Kind() + } +} + +func parseBool(str string) (bool, error) { + + switch str { + case "1", "t", "T", "true", "TRUE", "True", "on", "yes", "ok": + return true, nil + case "", "0", "f", "F", "false", "FALSE", "False", "off", "no": + return false, nil + } + + // strconv.NumError mimicing exactly the strconv.ParseBool(..) error and type + // to ensure compatibility with std library and beyond. + return false, &strconv.NumError{Func: "ParseBool", Num: str, Err: strconv.ErrSyntax} +} + +// hasValue determines if a reflect.Value is it's default value +func hasValue(field reflect.Value) bool { + switch field.Kind() { + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() + default: + return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() + } +} diff --git a/vendor/github.com/go-playground/locales/.gitignore b/vendor/github.com/go-playground/locales/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/go-playground/locales/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/go-playground/locales/.travis.yml b/vendor/github.com/go-playground/locales/.travis.yml new file mode 100644 index 0000000..d50237a --- /dev/null +++ b/vendor/github.com/go-playground/locales/.travis.yml @@ -0,0 +1,26 @@ +language: go +go: + - 1.13.1 + - tip +matrix: + allow_failures: + - go: tip + +notifications: + email: + recipients: dean.karn@gmail.com + on_success: change + on_failure: always + +before_install: + - go install github.com/mattn/goveralls + +# Only clone the most recent commit. +git: + depth: 1 + +script: + - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./... + +after_success: | + goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN \ No newline at end of file diff --git a/vendor/github.com/go-playground/locales/LICENSE b/vendor/github.com/go-playground/locales/LICENSE new file mode 100644 index 0000000..75854ac --- /dev/null +++ b/vendor/github.com/go-playground/locales/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Go Playground + +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. \ No newline at end of file diff --git a/vendor/github.com/go-playground/locales/README.md b/vendor/github.com/go-playground/locales/README.md new file mode 100644 index 0000000..ba1b068 --- /dev/null +++ b/vendor/github.com/go-playground/locales/README.md @@ -0,0 +1,172 @@ +## locales +![Project status](https://img.shields.io/badge/version-0.13.0-green.svg) +[![Build Status](https://travis-ci.org/go-playground/locales.svg?branch=master)](https://travis-ci.org/go-playground/locales) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/locales)](https://goreportcard.com/report/github.com/go-playground/locales) +[![GoDoc](https://godoc.org/github.com/go-playground/locales?status.svg)](https://godoc.org/github.com/go-playground/locales) +![License](https://img.shields.io/dub/l/vibe-d.svg) +[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within +an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator). + +Features +-------- +- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1 +- [x] Contains Cardinal, Ordinal and Range Plural Rules +- [x] Contains Month, Weekday and Timezone translations built in +- [x] Contains Date & Time formatting functions +- [x] Contains Number, Currency, Accounting and Percent formatting functions +- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) + +Full Tests +-------------------- +I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment; +any help would be **greatly appreciated!!!!** please see [issue](https://github.com/go-playground/locales/issues/1) for details. + +Installation +----------- + +Use go get + +```shell +go get github.com/go-playground/locales +``` + +NOTES +-------- +You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body +of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string. + +Usage +------- +```go +package main + +import ( + "fmt" + "time" + + "github.com/go-playground/locales/currency" + "github.com/go-playground/locales/en_CA" +) + +func main() { + + loc, _ := time.LoadLocation("America/Toronto") + datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc) + + l := en_CA.New() + + // Dates + fmt.Println(l.FmtDateFull(datetime)) + fmt.Println(l.FmtDateLong(datetime)) + fmt.Println(l.FmtDateMedium(datetime)) + fmt.Println(l.FmtDateShort(datetime)) + + // Times + fmt.Println(l.FmtTimeFull(datetime)) + fmt.Println(l.FmtTimeLong(datetime)) + fmt.Println(l.FmtTimeMedium(datetime)) + fmt.Println(l.FmtTimeShort(datetime)) + + // Months Wide + fmt.Println(l.MonthWide(time.January)) + fmt.Println(l.MonthWide(time.February)) + fmt.Println(l.MonthWide(time.March)) + // ... + + // Months Abbreviated + fmt.Println(l.MonthAbbreviated(time.January)) + fmt.Println(l.MonthAbbreviated(time.February)) + fmt.Println(l.MonthAbbreviated(time.March)) + // ... + + // Months Narrow + fmt.Println(l.MonthNarrow(time.January)) + fmt.Println(l.MonthNarrow(time.February)) + fmt.Println(l.MonthNarrow(time.March)) + // ... + + // Weekdays Wide + fmt.Println(l.WeekdayWide(time.Sunday)) + fmt.Println(l.WeekdayWide(time.Monday)) + fmt.Println(l.WeekdayWide(time.Tuesday)) + // ... + + // Weekdays Abbreviated + fmt.Println(l.WeekdayAbbreviated(time.Sunday)) + fmt.Println(l.WeekdayAbbreviated(time.Monday)) + fmt.Println(l.WeekdayAbbreviated(time.Tuesday)) + // ... + + // Weekdays Short + fmt.Println(l.WeekdayShort(time.Sunday)) + fmt.Println(l.WeekdayShort(time.Monday)) + fmt.Println(l.WeekdayShort(time.Tuesday)) + // ... + + // Weekdays Narrow + fmt.Println(l.WeekdayNarrow(time.Sunday)) + fmt.Println(l.WeekdayNarrow(time.Monday)) + fmt.Println(l.WeekdayNarrow(time.Tuesday)) + // ... + + var f64 float64 + + f64 = -10356.4523 + + // Number + fmt.Println(l.FmtNumber(f64, 2)) + + // Currency + fmt.Println(l.FmtCurrency(f64, 2, currency.CAD)) + fmt.Println(l.FmtCurrency(f64, 2, currency.USD)) + + // Accounting + fmt.Println(l.FmtAccounting(f64, 2, currency.CAD)) + fmt.Println(l.FmtAccounting(f64, 2, currency.USD)) + + f64 = 78.12 + + // Percent + fmt.Println(l.FmtPercent(f64, 0)) + + // Plural Rules for locale, so you know what rules you must cover + fmt.Println(l.PluralsCardinal()) + fmt.Println(l.PluralsOrdinal()) + + // Cardinal Plural Rules + fmt.Println(l.CardinalPluralRule(1, 0)) + fmt.Println(l.CardinalPluralRule(1.0, 0)) + fmt.Println(l.CardinalPluralRule(1.0, 1)) + fmt.Println(l.CardinalPluralRule(3, 0)) + + // Ordinal Plural Rules + fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st + fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd + fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd + fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th + + // Range Plural Rules + fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1 + fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2 + fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8 +} +``` + +NOTES: +------- +These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues +I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time +these locales are regenerated the fix will come with. + +I do however realize that time constraints are often important and so there are two options: + +1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface. +2. Add an exception in the locale generation code directly and once regenerated, fix will be in place. + +Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/locales/currency/currency.go b/vendor/github.com/go-playground/locales/currency/currency.go new file mode 100644 index 0000000..cdaba59 --- /dev/null +++ b/vendor/github.com/go-playground/locales/currency/currency.go @@ -0,0 +1,308 @@ +package currency + +// Type is the currency type associated with the locales currency enum +type Type int + +// locale currencies +const ( + ADP Type = iota + AED + AFA + AFN + ALK + ALL + AMD + ANG + AOA + AOK + AON + AOR + ARA + ARL + ARM + ARP + ARS + ATS + AUD + AWG + AZM + AZN + BAD + BAM + BAN + BBD + BDT + BEC + BEF + BEL + BGL + BGM + BGN + BGO + BHD + BIF + BMD + BND + BOB + BOL + BOP + BOV + BRB + BRC + BRE + BRL + BRN + BRR + BRZ + BSD + BTN + BUK + BWP + BYB + BYN + BYR + BZD + CAD + CDF + CHE + CHF + CHW + CLE + CLF + CLP + CNH + CNX + CNY + COP + COU + CRC + CSD + CSK + CUC + CUP + CVE + CYP + CZK + DDM + DEM + DJF + DKK + DOP + DZD + ECS + ECV + EEK + EGP + ERN + ESA + ESB + ESP + ETB + EUR + FIM + FJD + FKP + FRF + GBP + GEK + GEL + GHC + GHS + GIP + GMD + GNF + GNS + GQE + GRD + GTQ + GWE + GWP + GYD + HKD + HNL + HRD + HRK + HTG + HUF + IDR + IEP + ILP + ILR + ILS + INR + IQD + IRR + ISJ + ISK + ITL + JMD + JOD + JPY + KES + KGS + KHR + KMF + KPW + KRH + KRO + KRW + KWD + KYD + KZT + LAK + LBP + LKR + LRD + LSL + LTL + LTT + LUC + LUF + LUL + LVL + LVR + LYD + MAD + MAF + MCF + MDC + MDL + MGA + MGF + MKD + MKN + MLF + MMK + MNT + MOP + MRO + MTL + MTP + MUR + MVP + MVR + MWK + MXN + MXP + MXV + MYR + MZE + MZM + MZN + NAD + NGN + NIC + NIO + NLG + NOK + NPR + NZD + OMR + PAB + PEI + PEN + PES + PGK + PHP + PKR + PLN + PLZ + PTE + PYG + QAR + RHD + ROL + RON + RSD + RUB + RUR + RWF + SAR + SBD + SCR + SDD + SDG + SDP + SEK + SGD + SHP + SIT + SKK + SLL + SOS + SRD + SRG + SSP + STD + STN + SUR + SVC + SYP + SZL + THB + TJR + TJS + TMM + TMT + TND + TOP + TPE + TRL + TRY + TTD + TWD + TZS + UAH + UAK + UGS + UGX + USD + USN + USS + UYI + UYP + UYU + UZS + VEB + VEF + VND + VNN + VUV + WST + XAF + XAG + XAU + XBA + XBB + XBC + XBD + XCD + XDR + XEU + XFO + XFU + XOF + XPD + XPF + XPT + XRE + XSU + XTS + XUA + XXX + YDD + YER + YUD + YUM + YUN + YUR + ZAL + ZAR + ZMK + ZMW + ZRN + ZRZ + ZWD + ZWL + ZWR +) diff --git a/vendor/github.com/go-playground/locales/go.mod b/vendor/github.com/go-playground/locales/go.mod new file mode 100644 index 0000000..34ab6f2 --- /dev/null +++ b/vendor/github.com/go-playground/locales/go.mod @@ -0,0 +1,5 @@ +module github.com/go-playground/locales + +go 1.13 + +require golang.org/x/text v0.3.2 diff --git a/vendor/github.com/go-playground/locales/go.sum b/vendor/github.com/go-playground/locales/go.sum new file mode 100644 index 0000000..63c9200 --- /dev/null +++ b/vendor/github.com/go-playground/locales/go.sum @@ -0,0 +1,3 @@ +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/go-playground/locales/logo.png b/vendor/github.com/go-playground/locales/logo.png new file mode 100644 index 0000000..3038276 Binary files /dev/null and b/vendor/github.com/go-playground/locales/logo.png differ diff --git a/vendor/github.com/go-playground/locales/rules.go b/vendor/github.com/go-playground/locales/rules.go new file mode 100644 index 0000000..9202900 --- /dev/null +++ b/vendor/github.com/go-playground/locales/rules.go @@ -0,0 +1,293 @@ +package locales + +import ( + "strconv" + "time" + + "github.com/go-playground/locales/currency" +) + +// // ErrBadNumberValue is returned when the number passed for +// // plural rule determination cannot be parsed +// type ErrBadNumberValue struct { +// NumberValue string +// InnerError error +// } + +// // Error returns ErrBadNumberValue error string +// func (e *ErrBadNumberValue) Error() string { +// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError) +// } + +// var _ error = new(ErrBadNumberValue) + +// PluralRule denotes the type of plural rules +type PluralRule int + +// PluralRule's +const ( + PluralRuleUnknown PluralRule = iota + PluralRuleZero // zero + PluralRuleOne // one - singular + PluralRuleTwo // two - dual + PluralRuleFew // few - paucal + PluralRuleMany // many - also used for fractions if they have a separate class + PluralRuleOther // other - required—general plural form—also used if the language only has a single form +) + +const ( + pluralsString = "UnknownZeroOneTwoFewManyOther" +) + +// Translator encapsulates an instance of a locale +// NOTE: some values are returned as a []byte just in case the caller +// wishes to add more and can help avoid allocations; otherwise just cast as string +type Translator interface { + + // The following Functions are for overriding, debugging or developing + // with a Translator Locale + + // Locale returns the string value of the translator + Locale() string + + // returns an array of cardinal plural rules associated + // with this translator + PluralsCardinal() []PluralRule + + // returns an array of ordinal plural rules associated + // with this translator + PluralsOrdinal() []PluralRule + + // returns an array of range plural rules associated + // with this translator + PluralsRange() []PluralRule + + // returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale + CardinalPluralRule(num float64, v uint64) PluralRule + + // returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale + OrdinalPluralRule(num float64, v uint64) PluralRule + + // returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale + RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule + + // returns the locales abbreviated month given the 'month' provided + MonthAbbreviated(month time.Month) string + + // returns the locales abbreviated months + MonthsAbbreviated() []string + + // returns the locales narrow month given the 'month' provided + MonthNarrow(month time.Month) string + + // returns the locales narrow months + MonthsNarrow() []string + + // returns the locales wide month given the 'month' provided + MonthWide(month time.Month) string + + // returns the locales wide months + MonthsWide() []string + + // returns the locales abbreviated weekday given the 'weekday' provided + WeekdayAbbreviated(weekday time.Weekday) string + + // returns the locales abbreviated weekdays + WeekdaysAbbreviated() []string + + // returns the locales narrow weekday given the 'weekday' provided + WeekdayNarrow(weekday time.Weekday) string + + // WeekdaysNarrowreturns the locales narrow weekdays + WeekdaysNarrow() []string + + // returns the locales short weekday given the 'weekday' provided + WeekdayShort(weekday time.Weekday) string + + // returns the locales short weekdays + WeekdaysShort() []string + + // returns the locales wide weekday given the 'weekday' provided + WeekdayWide(weekday time.Weekday) string + + // returns the locales wide weekdays + WeekdaysWide() []string + + // The following Functions are common Formatting functionsfor the Translator's Locale + + // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' + FmtNumber(num float64, v uint64) string + + // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' + // NOTE: 'num' passed into FmtPercent is assumed to be in percent already + FmtPercent(num float64, v uint64) string + + // returns the currency representation of 'num' with digits/precision of 'v' for locale + FmtCurrency(num float64, v uint64, currency currency.Type) string + + // returns the currency representation of 'num' with digits/precision of 'v' for locale + // in accounting notation. + FmtAccounting(num float64, v uint64, currency currency.Type) string + + // returns the short date representation of 't' for locale + FmtDateShort(t time.Time) string + + // returns the medium date representation of 't' for locale + FmtDateMedium(t time.Time) string + + // returns the long date representation of 't' for locale + FmtDateLong(t time.Time) string + + // returns the full date representation of 't' for locale + FmtDateFull(t time.Time) string + + // returns the short time representation of 't' for locale + FmtTimeShort(t time.Time) string + + // returns the medium time representation of 't' for locale + FmtTimeMedium(t time.Time) string + + // returns the long time representation of 't' for locale + FmtTimeLong(t time.Time) string + + // returns the full time representation of 't' for locale + FmtTimeFull(t time.Time) string +} + +// String returns the string value of PluralRule +func (p PluralRule) String() string { + + switch p { + case PluralRuleZero: + return pluralsString[7:11] + case PluralRuleOne: + return pluralsString[11:14] + case PluralRuleTwo: + return pluralsString[14:17] + case PluralRuleFew: + return pluralsString[17:20] + case PluralRuleMany: + return pluralsString[20:24] + case PluralRuleOther: + return pluralsString[24:] + default: + return pluralsString[:7] + } +} + +// +// Precision Notes: +// +// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh +// +// v := float64(3.141) +// i := float64(int64(v)) +// +// fmt.Println(v - i) +// +// or +// +// s := strconv.FormatFloat(v-i, 'f', -1, 64) +// fmt.Println(s) +// +// these will not print what you'd expect: 0.14100000000000001 +// and so this library requires a precision to be specified, or +// inaccurate plural rules could be applied. +// +// +// +// n - absolute value of the source number (integer and decimals). +// i - integer digits of n. +// v - number of visible fraction digits in n, with trailing zeros. +// w - number of visible fraction digits in n, without trailing zeros. +// f - visible fractional digits in n, with trailing zeros. +// t - visible fractional digits in n, without trailing zeros. +// +// +// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above. +// +// n := math.Abs(num) +// i := int64(n) +// v := v +// +// +// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's.... +// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64 +// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's.... +// +// +// +// General Inclusion Rules +// - v will always be available inherently +// - all require n +// - w requires i +// + +// W returns the number of visible fraction digits in N, without trailing zeros. +func W(n float64, v uint64) (w int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then w will be zero + // otherwise need to parse + if len(s) != 1 { + + s = s[2:] + end := len(s) + 1 + + for i := end; i >= 0; i-- { + if s[i] != '0' { + end = i + 1 + break + } + } + + w = int64(len(s[:end])) + } + + return +} + +// F returns the visible fractional digits in N, with trailing zeros. +func F(n float64, v uint64) (f int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then f will be zero + // otherwise need to parse + if len(s) != 1 { + + // ignoring error, because it can't fail as we generated + // the string internally from a real number + f, _ = strconv.ParseInt(s[2:], 10, 64) + } + + return +} + +// T returns the visible fractional digits in N, without trailing zeros. +func T(n float64, v uint64) (t int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then t will be zero + // otherwise need to parse + if len(s) != 1 { + + s = s[2:] + end := len(s) + 1 + + for i := end; i >= 0; i-- { + if s[i] != '0' { + end = i + 1 + break + } + } + + // ignoring error, because it can't fail as we generated + // the string internally from a real number + t, _ = strconv.ParseInt(s[:end], 10, 64) + } + + return +} diff --git a/vendor/github.com/go-playground/universal-translator/.gitignore b/vendor/github.com/go-playground/universal-translator/.gitignore new file mode 100644 index 0000000..bc4e07f --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.coverprofile \ No newline at end of file diff --git a/vendor/github.com/go-playground/universal-translator/.travis.yml b/vendor/github.com/go-playground/universal-translator/.travis.yml new file mode 100644 index 0000000..39b8b92 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/.travis.yml @@ -0,0 +1,27 @@ +language: go +go: + - 1.13.4 + - tip +matrix: + allow_failures: + - go: tip + +notifications: + email: + recipients: dean.karn@gmail.com + on_success: change + on_failure: always + +before_install: + - go install github.com/mattn/goveralls + +# Only clone the most recent commit. +git: + depth: 1 + +script: + - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./... + +after_success: | + [ $TRAVIS_GO_VERSION = 1.13.4 ] && + goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN \ No newline at end of file diff --git a/vendor/github.com/go-playground/universal-translator/LICENSE b/vendor/github.com/go-playground/universal-translator/LICENSE new file mode 100644 index 0000000..8d8aba1 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Go Playground + +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. diff --git a/vendor/github.com/go-playground/universal-translator/README.md b/vendor/github.com/go-playground/universal-translator/README.md new file mode 100644 index 0000000..071f33a --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/README.md @@ -0,0 +1,89 @@ +## universal-translator +![Project status](https://img.shields.io/badge/version-0.17.0-green.svg) +[![Build Status](https://travis-ci.org/go-playground/universal-translator.svg?branch=master)](https://travis-ci.org/go-playground/universal-translator) +[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator) +[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator) +![License](https://img.shields.io/dub/l/vibe-d.svg) +[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules + +Why another i18n library? +-------------------------- +Because none of the plural rules seem to be correct out there, including the previous implementation of this package, +so I took it upon myself to create [locales](https://github.com/go-playground/locales) for everyone to use; this package +is a thin wrapper around [locales](https://github.com/go-playground/locales) in order to store and translate text for +use in your applications. + +Features +-------- +- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3 +- [x] Contains Cardinal, Ordinal and Range Plural Rules +- [x] Contains Month, Weekday and Timezone translations built in +- [x] Contains Date & Time formatting functions +- [x] Contains Number, Currency, Accounting and Percent formatting functions +- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) +- [x] Support loading translations from files +- [x] Exporting translations to file(s), mainly for getting them professionally translated +- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated +- [ ] Tests for all languages, I need help with this, please see [here](https://github.com/go-playground/locales/issues/1) + +Installation +----------- + +Use go get + +```shell +go get github.com/go-playground/universal-translator +``` + +Usage & Documentation +------- + +Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs + +##### Examples: + +- [Basic](https://github.com/go-playground/universal-translator/tree/master/_examples/basic) +- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-no-files) +- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-with-files) + +File formatting +-------------- +All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s); +they are only separated for easy viewing. + +##### Examples: + +- [Formats](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats) + +##### Basic Makeup +NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats) +```json +{ + "locale": "en", + "key": "days-left", + "trans": "You have {0} day left.", + "type": "Cardinal", + "rule": "One", + "override": false +} +``` +|Field|Description| +|---|---| +|locale|The locale for which the translation is for.| +|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.| +|trans|The actual translation text.| +|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)| +|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)| +|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.| + +Help With Tests +--------------- +To anyone interesting in helping or contributing, I sure could use some help creating tests for each language. +Please see issue [here](https://github.com/go-playground/locales/issues/1) for details. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/universal-translator/errors.go b/vendor/github.com/go-playground/universal-translator/errors.go new file mode 100644 index 0000000..38b163b --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/errors.go @@ -0,0 +1,148 @@ +package ut + +import ( + "errors" + "fmt" + + "github.com/go-playground/locales" +) + +var ( + // ErrUnknowTranslation indicates the translation could not be found + ErrUnknowTranslation = errors.New("Unknown Translation") +) + +var _ error = new(ErrConflictingTranslation) +var _ error = new(ErrRangeTranslation) +var _ error = new(ErrOrdinalTranslation) +var _ error = new(ErrCardinalTranslation) +var _ error = new(ErrMissingPluralTranslation) +var _ error = new(ErrExistingTranslator) + +// ErrExistingTranslator is the error representing a conflicting translator +type ErrExistingTranslator struct { + locale string +} + +// Error returns ErrExistingTranslator's internal error text +func (e *ErrExistingTranslator) Error() string { + return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale) +} + +// ErrConflictingTranslation is the error representing a conflicting translation +type ErrConflictingTranslation struct { + locale string + key interface{} + rule locales.PluralRule + text string +} + +// Error returns ErrConflictingTranslation's internal error text +func (e *ErrConflictingTranslation) Error() string { + + if _, ok := e.key.(string); !ok { + return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) + } + + return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) +} + +// ErrRangeTranslation is the error representing a range translation error +type ErrRangeTranslation struct { + text string +} + +// Error returns ErrRangeTranslation's internal error text +func (e *ErrRangeTranslation) Error() string { + return e.text +} + +// ErrOrdinalTranslation is the error representing an ordinal translation error +type ErrOrdinalTranslation struct { + text string +} + +// Error returns ErrOrdinalTranslation's internal error text +func (e *ErrOrdinalTranslation) Error() string { + return e.text +} + +// ErrCardinalTranslation is the error representing a cardinal translation error +type ErrCardinalTranslation struct { + text string +} + +// Error returns ErrCardinalTranslation's internal error text +func (e *ErrCardinalTranslation) Error() string { + return e.text +} + +// ErrMissingPluralTranslation is the error signifying a missing translation given +// the locales plural rules. +type ErrMissingPluralTranslation struct { + locale string + key interface{} + rule locales.PluralRule + translationType string +} + +// Error returns ErrMissingPluralTranslation's internal error text +func (e *ErrMissingPluralTranslation) Error() string { + + if _, ok := e.key.(string); !ok { + return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale) + } + + return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale) +} + +// ErrMissingBracket is the error representing a missing bracket in a translation +// eg. This is a {0 <-- missing ending '}' +type ErrMissingBracket struct { + locale string + key interface{} + text string +} + +// Error returns ErrMissingBracket error message +func (e *ErrMissingBracket) Error() string { + return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text) +} + +// ErrBadParamSyntax is the error representing a bad parameter definition in a translation +// eg. This is a {must-be-int} +type ErrBadParamSyntax struct { + locale string + param string + key interface{} + text string +} + +// Error returns ErrBadParamSyntax error message +func (e *ErrBadParamSyntax) Error() string { + return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text) +} + +// import/export errors + +// ErrMissingLocale is the error representing an expected locale that could +// not be found aka locale not registered with the UniversalTranslator Instance +type ErrMissingLocale struct { + locale string +} + +// Error returns ErrMissingLocale's internal error text +func (e *ErrMissingLocale) Error() string { + return fmt.Sprintf("error: locale '%s' not registered.", e.locale) +} + +// ErrBadPluralDefinition is the error representing an incorrect plural definition +// usually found within translations defined within files during the import process. +type ErrBadPluralDefinition struct { + tl translation +} + +// Error returns ErrBadPluralDefinition's internal error text +func (e *ErrBadPluralDefinition) Error() string { + return fmt.Sprintf("error: bad plural definition '%#v'", e.tl) +} diff --git a/vendor/github.com/go-playground/universal-translator/go.mod b/vendor/github.com/go-playground/universal-translator/go.mod new file mode 100644 index 0000000..8079590 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/go.mod @@ -0,0 +1,5 @@ +module github.com/go-playground/universal-translator + +go 1.13 + +require github.com/go-playground/locales v0.13.0 diff --git a/vendor/github.com/go-playground/universal-translator/go.sum b/vendor/github.com/go-playground/universal-translator/go.sum new file mode 100644 index 0000000..cbbf324 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/go.sum @@ -0,0 +1,4 @@ +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/go-playground/universal-translator/import_export.go b/vendor/github.com/go-playground/universal-translator/import_export.go new file mode 100644 index 0000000..7bd76f2 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/import_export.go @@ -0,0 +1,274 @@ +package ut + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "io" + + "github.com/go-playground/locales" +) + +type translation struct { + Locale string `json:"locale"` + Key interface{} `json:"key"` // either string or integer + Translation string `json:"trans"` + PluralType string `json:"type,omitempty"` + PluralRule string `json:"rule,omitempty"` + OverrideExisting bool `json:"override,omitempty"` +} + +const ( + cardinalType = "Cardinal" + ordinalType = "Ordinal" + rangeType = "Range" +) + +// ImportExportFormat is the format of the file import or export +type ImportExportFormat uint8 + +// supported Export Formats +const ( + FormatJSON ImportExportFormat = iota +) + +// Export writes the translations out to a file on disk. +// +// NOTE: this currently only works with string or int translations keys. +func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error { + + _, err := os.Stat(dirname) + fmt.Println(dirname, err, os.IsNotExist(err)) + if err != nil { + + if !os.IsNotExist(err) { + return err + } + + if err = os.MkdirAll(dirname, 0744); err != nil { + return err + } + } + + // build up translations + var trans []translation + var b []byte + var ext string + + for _, locale := range t.translators { + + for k, v := range locale.(*translator).translations { + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k, + Translation: v.text, + }) + } + + for k, pluralTrans := range locale.(*translator).cardinalTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: cardinalType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + for k, pluralTrans := range locale.(*translator).ordinalTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: ordinalType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + for k, pluralTrans := range locale.(*translator).rangeTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: rangeType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + switch format { + case FormatJSON: + b, err = json.MarshalIndent(trans, "", " ") + ext = ".json" + } + + if err != nil { + return err + } + + err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644) + if err != nil { + return err + } + + trans = trans[0:0] + } + + return nil +} + +// Import reads the translations out of a file or directory on disk. +// +// NOTE: this currently only works with string or int translations keys. +func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error { + + fi, err := os.Stat(dirnameOrFilename) + if err != nil { + return err + } + + processFn := func(filename string) error { + + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + + return t.ImportByReader(format, f) + } + + if !fi.IsDir() { + return processFn(dirnameOrFilename) + } + + // recursively go through directory + walker := func(path string, info os.FileInfo, err error) error { + + if info.IsDir() { + return nil + } + + switch format { + case FormatJSON: + // skip non JSON files + if filepath.Ext(info.Name()) != ".json" { + return nil + } + } + + return processFn(path) + } + + return filepath.Walk(dirnameOrFilename, walker) +} + +// ImportByReader imports the the translations found within the contents read from the supplied reader. +// +// NOTE: generally used when assets have been embedded into the binary and are already in memory. +func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error { + + b, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + + var trans []translation + + switch format { + case FormatJSON: + err = json.Unmarshal(b, &trans) + } + + if err != nil { + return err + } + + for _, tl := range trans { + + locale, found := t.FindTranslator(tl.Locale) + if !found { + return &ErrMissingLocale{locale: tl.Locale} + } + + pr := stringToPR(tl.PluralRule) + + if pr == locales.PluralRuleUnknown { + + err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting) + if err != nil { + return err + } + + continue + } + + switch tl.PluralType { + case cardinalType: + err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) + case ordinalType: + err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) + case rangeType: + err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting) + default: + return &ErrBadPluralDefinition{tl: tl} + } + + if err != nil { + return err + } + } + + return nil +} + +func stringToPR(s string) locales.PluralRule { + + switch s { + case "One": + return locales.PluralRuleOne + case "Two": + return locales.PluralRuleTwo + case "Few": + return locales.PluralRuleFew + case "Many": + return locales.PluralRuleMany + case "Other": + return locales.PluralRuleOther + default: + return locales.PluralRuleUnknown + } + +} diff --git a/vendor/github.com/go-playground/universal-translator/logo.png b/vendor/github.com/go-playground/universal-translator/logo.png new file mode 100644 index 0000000..a37aa8c Binary files /dev/null and b/vendor/github.com/go-playground/universal-translator/logo.png differ diff --git a/vendor/github.com/go-playground/universal-translator/translator.go b/vendor/github.com/go-playground/universal-translator/translator.go new file mode 100644 index 0000000..cfafce8 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/translator.go @@ -0,0 +1,420 @@ +package ut + +import ( + "fmt" + "strconv" + "strings" + + "github.com/go-playground/locales" +) + +const ( + paramZero = "{0}" + paramOne = "{1}" + unknownTranslation = "" +) + +// Translator is universal translators +// translator instance which is a thin wrapper +// around locales.Translator instance providing +// some extra functionality +type Translator interface { + locales.Translator + + // adds a normal translation for a particular language/locale + // {#} is the only replacement type accepted and are ad infinitum + // eg. one: '{0} day left' other: '{0} days left' + Add(key interface{}, text string, override bool) error + + // adds a cardinal plural translation for a particular language/locale + // {0} is the only replacement type accepted and only one variable is accepted as + // multiple cannot be used for a plural rule determination, unless it is a range; + // see AddRange below. + // eg. in locale 'en' one: '{0} day left' other: '{0} days left' + AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error + + // adds an ordinal plural translation for a particular language/locale + // {0} is the only replacement type accepted and only one variable is accepted as + // multiple cannot be used for a plural rule determination, unless it is a range; + // see AddRange below. + // eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' + // - 1st, 2nd, 3rd... + AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error + + // adds a range plural translation for a particular language/locale + // {0} and {1} are the only replacement types accepted and only these are accepted. + // eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left' + AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error + + // creates the translation for the locale given the 'key' and params passed in + T(key interface{}, params ...string) (string, error) + + // creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments + // and param passed in + C(key interface{}, num float64, digits uint64, param string) (string, error) + + // creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments + // and param passed in + O(key interface{}, num float64, digits uint64, param string) (string, error) + + // creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and + // 'digit2' arguments and 'param1' and 'param2' passed in + R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) + + // VerifyTranslations checks to ensures that no plural rules have been + // missed within the translations. + VerifyTranslations() error +} + +var _ Translator = new(translator) +var _ locales.Translator = new(translator) + +type translator struct { + locales.Translator + translations map[interface{}]*transText + cardinalTanslations map[interface{}][]*transText // array index is mapped to locales.PluralRule index + the locales.PluralRuleUnknown + ordinalTanslations map[interface{}][]*transText + rangeTanslations map[interface{}][]*transText +} + +type transText struct { + text string + indexes []int +} + +func newTranslator(trans locales.Translator) Translator { + return &translator{ + Translator: trans, + translations: make(map[interface{}]*transText), // translation text broken up by byte index + cardinalTanslations: make(map[interface{}][]*transText), + ordinalTanslations: make(map[interface{}][]*transText), + rangeTanslations: make(map[interface{}][]*transText), + } +} + +// Add adds a normal translation for a particular language/locale +// {#} is the only replacement type accepted and are ad infinitum +// eg. one: '{0} day left' other: '{0} days left' +func (t *translator) Add(key interface{}, text string, override bool) error { + + if _, ok := t.translations[key]; ok && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text} + } + + lb := strings.Count(text, "{") + rb := strings.Count(text, "}") + + if lb != rb { + return &ErrMissingBracket{locale: t.Locale(), key: key, text: text} + } + + trans := &transText{ + text: text, + } + + var idx int + + for i := 0; i < lb; i++ { + s := "{" + strconv.Itoa(i) + "}" + idx = strings.Index(text, s) + if idx == -1 { + return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text} + } + + trans.indexes = append(trans.indexes, idx) + trans.indexes = append(trans.indexes, idx+len(s)) + } + + t.translations[key] = trans + + return nil +} + +// AddCardinal adds a cardinal plural translation for a particular language/locale +// {0} is the only replacement type accepted and only one variable is accepted as +// multiple cannot be used for a plural rule determination, unless it is a range; +// see AddRange below. +// eg. in locale 'en' one: '{0} day left' other: '{0} days left' +func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error { + + var verified bool + + // verify plural rule exists for locale + for _, pr := range t.PluralsCardinal() { + if pr == rule { + verified = true + break + } + } + + if !verified { + return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} + } + + tarr, ok := t.cardinalTanslations[key] + if ok { + // verify not adding a conflicting record + if len(tarr) > 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.cardinalTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 2, 2), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + return nil +} + +// AddOrdinal adds an ordinal plural translation for a particular language/locale +// {0} is the only replacement type accepted and only one variable is accepted as +// multiple cannot be used for a plural rule determination, unless it is a range; +// see AddRange below. +// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd... +func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error { + + var verified bool + + // verify plural rule exists for locale + for _, pr := range t.PluralsOrdinal() { + if pr == rule { + verified = true + break + } + } + + if !verified { + return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} + } + + tarr, ok := t.ordinalTanslations[key] + if ok { + // verify not adding a conflicting record + if len(tarr) > 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.ordinalTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 2, 2), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + return nil +} + +// AddRange adds a range plural translation for a particular language/locale +// {0} and {1} are the only replacement types accepted and only these are accepted. +// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left' +func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error { + + var verified bool + + // verify plural rule exists for locale + for _, pr := range t.PluralsRange() { + if pr == rule { + verified = true + break + } + } + + if !verified { + return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} + } + + tarr, ok := t.rangeTanslations[key] + if ok { + // verify not adding a conflicting record + if len(tarr) > 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.rangeTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 4, 4), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + idx = strings.Index(text, paramOne) + if idx == -1 { + tarr[rule] = nil + return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)} + } + + trans.indexes[2] = idx + trans.indexes[3] = idx + len(paramOne) + + return nil +} + +// T creates the translation for the locale given the 'key' and params passed in +func (t *translator) T(key interface{}, params ...string) (string, error) { + + trans, ok := t.translations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + b := make([]byte, 0, 64) + + var start, end, count int + + for i := 0; i < len(trans.indexes); i++ { + end = trans.indexes[i] + b = append(b, trans.text[start:end]...) + b = append(b, params[count]...) + i++ + start = trans.indexes[i] + count++ + } + + b = append(b, trans.text[start:]...) + + return string(b), nil +} + +// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in +func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) { + + tarr, ok := t.cardinalTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.CardinalPluralRule(num, digits) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param...) + b = append(b, trans.text[trans.indexes[1]:]...) + + return string(b), nil +} + +// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in +func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) { + + tarr, ok := t.ordinalTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.OrdinalPluralRule(num, digits) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param...) + b = append(b, trans.text[trans.indexes[1]:]...) + + return string(b), nil +} + +// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments +// and 'param1' and 'param2' passed in +func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) { + + tarr, ok := t.rangeTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.RangePluralRule(num1, digits1, num2, digits2) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param1...) + b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...) + b = append(b, param2...) + b = append(b, trans.text[trans.indexes[3]:]...) + + return string(b), nil +} + +// VerifyTranslations checks to ensures that no plural rules have been +// missed within the translations. +func (t *translator) VerifyTranslations() error { + + for k, v := range t.cardinalTanslations { + + for _, rule := range t.PluralsCardinal() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k} + } + } + } + + for k, v := range t.ordinalTanslations { + + for _, rule := range t.PluralsOrdinal() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k} + } + } + } + + for k, v := range t.rangeTanslations { + + for _, rule := range t.PluralsRange() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k} + } + } + } + + return nil +} diff --git a/vendor/github.com/go-playground/universal-translator/universal_translator.go b/vendor/github.com/go-playground/universal-translator/universal_translator.go new file mode 100644 index 0000000..dbf707f --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/universal_translator.go @@ -0,0 +1,113 @@ +package ut + +import ( + "strings" + + "github.com/go-playground/locales" +) + +// UniversalTranslator holds all locale & translation data +type UniversalTranslator struct { + translators map[string]Translator + fallback Translator +} + +// New returns a new UniversalTranslator instance set with +// the fallback locale and locales it should support +func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator { + + t := &UniversalTranslator{ + translators: make(map[string]Translator), + } + + for _, v := range supportedLocales { + + trans := newTranslator(v) + t.translators[strings.ToLower(trans.Locale())] = trans + + if fallback.Locale() == v.Locale() { + t.fallback = trans + } + } + + if t.fallback == nil && fallback != nil { + t.fallback = newTranslator(fallback) + } + + return t +} + +// FindTranslator trys to find a Translator based on an array of locales +// and returns the first one it can find, otherwise returns the +// fallback translator. +func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) { + + for _, locale := range locales { + + if trans, found = t.translators[strings.ToLower(locale)]; found { + return + } + } + + return t.fallback, false +} + +// GetTranslator returns the specified translator for the given locale, +// or fallback if not found +func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) { + + if trans, found = t.translators[strings.ToLower(locale)]; found { + return + } + + return t.fallback, false +} + +// GetFallback returns the fallback locale +func (t *UniversalTranslator) GetFallback() Translator { + return t.fallback +} + +// AddTranslator adds the supplied translator, if it already exists the override param +// will be checked and if false an error will be returned, otherwise the translator will be +// overridden; if the fallback matches the supplied translator it will be overridden as well +// NOTE: this is normally only used when translator is embedded within a library +func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error { + + lc := strings.ToLower(translator.Locale()) + _, ok := t.translators[lc] + if ok && !override { + return &ErrExistingTranslator{locale: translator.Locale()} + } + + trans := newTranslator(translator) + + if t.fallback.Locale() == translator.Locale() { + + // because it's optional to have a fallback, I don't impose that limitation + // don't know why you wouldn't but... + if !override { + return &ErrExistingTranslator{locale: translator.Locale()} + } + + t.fallback = trans + } + + t.translators[lc] = trans + + return nil +} + +// VerifyTranslations runs through all locales and identifies any issues +// eg. missing plural rules for a locale +func (t *UniversalTranslator) VerifyTranslations() (err error) { + + for _, trans := range t.translators { + err = trans.VerifyTranslations() + if err != nil { + return + } + } + + return +} diff --git a/vendor/github.com/go-playground/validator/v10/.gitignore b/vendor/github.com/go-playground/validator/v10/.gitignore new file mode 100644 index 0000000..6e43fac --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/.gitignore @@ -0,0 +1,30 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +bin + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.test +*.out +*.txt +cover.html +README.html diff --git a/vendor/github.com/go-playground/validator/v10/.travis.yml b/vendor/github.com/go-playground/validator/v10/.travis.yml new file mode 100644 index 0000000..85a7be3 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/.travis.yml @@ -0,0 +1,29 @@ +language: go +go: + - 1.15.2 + - tip +matrix: + allow_failures: + - go: tip + +notifications: + email: + recipients: dean.karn@gmail.com + on_success: change + on_failure: always + +before_install: + - go install github.com/mattn/goveralls + - mkdir -p $GOPATH/src/gopkg.in + - ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/validator.v9 + +# Only clone the most recent commit. +git: + depth: 1 + +script: + - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./... + +after_success: | + [ $TRAVIS_GO_VERSION = 1.15.2 ] && + goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN diff --git a/vendor/github.com/go-playground/validator/v10/LICENSE b/vendor/github.com/go-playground/validator/v10/LICENSE new file mode 100644 index 0000000..6a2ae9a --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dean Karn + +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. + diff --git a/vendor/github.com/go-playground/validator/v10/Makefile b/vendor/github.com/go-playground/validator/v10/Makefile new file mode 100644 index 0000000..19c91ed --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/Makefile @@ -0,0 +1,18 @@ +GOCMD=GO111MODULE=on go + +linters-install: + @golangci-lint --version >/dev/null 2>&1 || { \ + echo "installing linting tools..."; \ + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0; \ + } + +lint: linters-install + $(PWD)/bin/golangci-lint run + +test: + $(GOCMD) test -cover -race ./... + +bench: + $(GOCMD) test -bench=. -benchmem ./... + +.PHONY: test lint linters-install \ No newline at end of file diff --git a/vendor/github.com/go-playground/validator/v10/README.md b/vendor/github.com/go-playground/validator/v10/README.md new file mode 100644 index 0000000..04fbb3c --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/README.md @@ -0,0 +1,299 @@ +Package validator +================ +[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +![Project status](https://img.shields.io/badge/version-10.4.1-green.svg) +[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) +[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) +[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/github.com/go-playground/validator/v10) +![License](https://img.shields.io/dub/l/vibe-d.svg) + +Package validator implements value validations for structs and individual fields based on tags. + +It has the following **unique** features: + +- Cross Field and Cross Struct validations by using validation tags or custom validators. +- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated. +- Ability to dive into both map keys and values for validation +- Handles type interface by determining it's underlying type prior to validation. +- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29) +- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs +- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError +- Customizable i18n aware error messages. +- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding) + +Installation +------------ + +Use go get. + + go get github.com/go-playground/validator/v10 + +Then import the validator package into your own code. + + import "github.com/go-playground/validator/v10" + +Error Return Value +------- + +Validation functions return type error + +They return type error to avoid the issue discussed in the following, where err is always != nil: + +* http://stackoverflow.com/a/29138676/3158232 +* https://github.com/go-playground/validator/issues/134 + +Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so: + +```go +err := validate.Struct(mystruct) +validationErrors := err.(validator.ValidationErrors) + ``` + +Usage and documentation +------ + +Please see https://godoc.org/github.com/go-playground/validator for detailed usage docs. + +##### Examples: + +- [Simple](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go) +- [Custom Field Types](https://github.com/go-playground/validator/blob/master/_examples/custom/main.go) +- [Struct Level](https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go) +- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/master/_examples/translations/main.go) +- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding) +- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash) + +Baked-in Validations +------ + +### Fields: + +| Tag | Description | +| - | - | +| eqcsfield | Field Equals Another Field (relative)| +| eqfield | Field Equals Another Field | +| fieldcontains | NOT DOCUMENTED IN doc.go | +| fieldexcludes | NOT DOCUMENTED IN doc.go | +| gtcsfield | Field Greater Than Another Relative Field | +| gtecsfield | Field Greater Than or Equal To Another Relative Field | +| gtefield | Field Greater Than or Equal To Another Field | +| gtfield | Field Greater Than Another Field | +| ltcsfield | Less Than Another Relative Field | +| ltecsfield | Less Than or Equal To Another Relative Field | +| ltefield | Less Than or Equal To Another Field | +| ltfield | Less Than Another Field | +| necsfield | Field Does Not Equal Another Field (relative) | +| nefield | Field Does Not Equal Another Field | + +### Network: + +| Tag | Description | +| - | - | +| cidr | Classless Inter-Domain Routing CIDR | +| cidrv4 | Classless Inter-Domain Routing CIDRv4 | +| cidrv6 | Classless Inter-Domain Routing CIDRv6 | +| datauri | Data URL | +| fqdn | Full Qualified Domain Name (FQDN) | +| hostname | Hostname RFC 952 | +| hostname_port | HostPort | +| hostname_rfc1123 | Hostname RFC 1123 | +| ip | Internet Protocol Address IP | +| ip4_addr | Internet Protocol Address IPv4 | +| ip6_addr |Internet Protocol Address IPv6 | +| ip_addr | Internet Protocol Address IP | +| ipv4 | Internet Protocol Address IPv4 | +| ipv6 | Internet Protocol Address IPv6 | +| mac | Media Access Control Address MAC | +| tcp4_addr | Transmission Control Protocol Address TCPv4 | +| tcp6_addr | Transmission Control Protocol Address TCPv6 | +| tcp_addr | Transmission Control Protocol Address TCP | +| udp4_addr | User Datagram Protocol Address UDPv4 | +| udp6_addr | User Datagram Protocol Address UDPv6 | +| udp_addr | User Datagram Protocol Address UDP | +| unix_addr | Unix domain socket end point Address | +| uri | URI String | +| url | URL String | +| url_encoded | URL Encoded | +| urn_rfc2141 | Urn RFC 2141 String | + +### Strings: + +| Tag | Description | +| - | - | +| alpha | Alpha Only | +| alphanum | Alphanumeric | +| alphanumunicode | Alphanumeric Unicode | +| alphaunicode | Alpha Unicode | +| ascii | ASCII | +| contains | Contains | +| containsany | Contains Any | +| containsrune | Contains Rune | +| endswith | Ends With | +| lowercase | Lowercase | +| multibyte | Multi-Byte Characters | +| number | NOT DOCUMENTED IN doc.go | +| numeric | Numeric | +| printascii | Printable ASCII | +| startswith | Starts With | +| uppercase | Uppercase | + +### Format: +| Tag | Description | +| - | - | +| base64 | Base64 String | +| base64url | Base64URL String | +| btc_addr | Bitcoin Address | +| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) | +| datetime | Datetime | +| e164 | e164 formatted phone number | +| email | E-mail String +| eth_addr | Ethereum Address | +| hexadecimal | Hexadecimal String | +| hexcolor | Hexcolor String | +| hsl | HSL String | +| hsla | HSLA String | +| html | HTML Tags | +| html_encoded | HTML Encoded | +| isbn | International Standard Book Number | +| isbn10 | International Standard Book Number 10 | +| isbn13 | International Standard Book Number 13 | +| json | JSON | +| latitude | Latitude | +| longitude | Longitude | +| rgb | RGB String | +| rgba | RGBA String | +| ssn | Social Security Number SSN | +| uuid | Universally Unique Identifier UUID | +| uuid3 | Universally Unique Identifier UUID v3 | +| uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 | +| uuid4 | Universally Unique Identifier UUID v4 | +| uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 | +| uuid5 | Universally Unique Identifier UUID v5 | +| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 | +| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 | + +### Comparisons: +| Tag | Description | +| - | - | +| eq | Equals | +| gt | Greater than| +| gte |Greater than or equal | +| lt | Less Than | +| lte | Less Than or Equal | +| ne | Not Equal | + +### Other: +| Tag | Description | +| - | - | +| dir | Directory | +| endswith | Ends With | +| excludes | Excludes | +| excludesall | Excludes All | +| excludesrune | Excludes Rune | +| file | File path | +| isdefault | Is Default | +| len | Length | +| max | Maximum | +| min | Minimum | +| oneof | One Of | +| required | Required | +| required_if | Required If | +| required_unless | Required Unless | +| required_with | Required With | +| required_with_all | Required With All | +| required_without | Required Without | +| required_without_all | Required Without All | +| excluded_with | Excluded With | +| excluded_with_all | Excluded With All | +| excluded_without | Excluded Without | +| excluded_without_all | Excluded Without All | +| unique | Unique | + +Benchmarks +------ +###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64 +```go +goos: darwin +goarch: amd64 +pkg: github.com/go-playground/validator +BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op +BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op +BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op +BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op +BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op +BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op +BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op +BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op +BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op +BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op +BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op +BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op +BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op +BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op +BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op +BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op +BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op +BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op +BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op +BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op +BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op +BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op +``` + +Complementary Software +---------------------- + +Here is a list of software that complements using this library either pre or post validation. + +* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support. +* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects + +How to Contribute +------ + +Make a pull request... + +License +------ +Distributed under MIT License, please see license file within the code for more details. diff --git a/vendor/github.com/go-playground/validator/v10/baked_in.go b/vendor/github.com/go-playground/validator/v10/baked_in.go new file mode 100644 index 0000000..6ce762d --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/baked_in.go @@ -0,0 +1,2285 @@ +package validator + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "net" + "net/url" + "os" + "reflect" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" + + "golang.org/x/crypto/sha3" + + urn "github.com/leodido/go-urn" +) + +// Func accepts a FieldLevel interface for all validation needs. The return +// value should be true when validation succeeds. +type Func func(fl FieldLevel) bool + +// FuncCtx accepts a context.Context and FieldLevel interface for all +// validation needs. The return value should be true when validation succeeds. +type FuncCtx func(ctx context.Context, fl FieldLevel) bool + +// wrapFunc wraps noramal Func makes it compatible with FuncCtx +func wrapFunc(fn Func) FuncCtx { + if fn == nil { + return nil // be sure not to wrap a bad function. + } + return func(ctx context.Context, fl FieldLevel) bool { + return fn(fl) + } +} + +var ( + restrictedTags = map[string]struct{}{ + diveTag: {}, + keysTag: {}, + endKeysTag: {}, + structOnlyTag: {}, + omitempty: {}, + skipValidationTag: {}, + utf8HexComma: {}, + utf8Pipe: {}, + noStructLevelTag: {}, + requiredTag: {}, + isdefault: {}, + } + + // BakedInAliasValidators is a default mapping of a single validation tag that + // defines a common or complex set of validation(s) to simplify + // adding validation to structs. + bakedInAliases = map[string]string{ + "iscolor": "hexcolor|rgb|rgba|hsl|hsla", + "country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric", + } + + // BakedInValidators is the default map of ValidationFunc + // you can add, remove or even replace items to suite your needs, + // or even disregard and use your own map if so desired. + bakedInValidators = map[string]Func{ + "required": hasValue, + "required_if": requiredIf, + "required_unless": requiredUnless, + "required_with": requiredWith, + "required_with_all": requiredWithAll, + "required_without": requiredWithout, + "required_without_all": requiredWithoutAll, + "excluded_with": excludedWith, + "excluded_with_all": excludedWithAll, + "excluded_without": excludedWithout, + "excluded_without_all": excludedWithoutAll, + "isdefault": isDefault, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, + "gtcsfield": isGtCrossStructField, + "gtecsfield": isGteCrossStructField, + "ltcsfield": isLtCrossStructField, + "ltecsfield": isLteCrossStructField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "fieldcontains": fieldContains, + "fieldexcludes": fieldExcludes, + "alpha": isAlpha, + "alphanum": isAlphanum, + "alphaunicode": isAlphaUnicode, + "alphanumunicode": isAlphanumUnicode, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHEXColor, + "rgb": isRGB, + "rgba": isRGBA, + "hsl": isHSL, + "hsla": isHSLA, + "e164": isE164, + "email": isEmail, + "url": isURL, + "uri": isURI, + "urn_rfc2141": isUrnRFC2141, // RFC 2141 + "file": isFile, + "base64": isBase64, + "base64url": isBase64URL, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "startswith": startsWith, + "endswith": endsWith, + "startsnotwith": startsNotWith, + "endsnotwith": endsNotWith, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "eth_addr": isEthereumAddress, + "btc_addr": isBitcoinAddress, + "btc_addr_bech32": isBitcoinBech32Address, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "uuid_rfc4122": isUUIDRFC4122, + "uuid3_rfc4122": isUUID3RFC4122, + "uuid4_rfc4122": isUUID4RFC4122, + "uuid5_rfc4122": isUUID5RFC4122, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, + "ipv4": isIPv4, + "ipv6": isIPv6, + "ip": isIP, + "cidrv4": isCIDRv4, + "cidrv6": isCIDRv6, + "cidr": isCIDR, + "tcp4_addr": isTCP4AddrResolvable, + "tcp6_addr": isTCP6AddrResolvable, + "tcp_addr": isTCPAddrResolvable, + "udp4_addr": isUDP4AddrResolvable, + "udp6_addr": isUDP6AddrResolvable, + "udp_addr": isUDPAddrResolvable, + "ip4_addr": isIP4AddrResolvable, + "ip6_addr": isIP6AddrResolvable, + "ip_addr": isIPAddrResolvable, + "unix_addr": isUnixAddrResolvable, + "mac": isMAC, + "hostname": isHostnameRFC952, // RFC 952 + "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 + "fqdn": isFQDN, + "unique": isUnique, + "oneof": isOneOf, + "html": isHTML, + "html_encoded": isHTMLEncoded, + "url_encoded": isURLEncoded, + "dir": isDir, + "json": isJSON, + "hostname_port": isHostnamePort, + "lowercase": isLowercase, + "uppercase": isUppercase, + "datetime": isDatetime, + "timezone": isTimeZone, + "iso3166_1_alpha2": isIso3166Alpha2, + "iso3166_1_alpha3": isIso3166Alpha3, + "iso3166_1_alpha_numeric": isIso3166AlphaNumeric, + } +) + +var oneofValsCache = map[string][]string{} +var oneofValsCacheRWLock = sync.RWMutex{} + +func parseOneOfParam2(s string) []string { + oneofValsCacheRWLock.RLock() + vals, ok := oneofValsCache[s] + oneofValsCacheRWLock.RUnlock() + if !ok { + oneofValsCacheRWLock.Lock() + vals = splitParamsRegex.FindAllString(s, -1) + for i := 0; i < len(vals); i++ { + vals[i] = strings.Replace(vals[i], "'", "", -1) + } + oneofValsCache[s] = vals + oneofValsCacheRWLock.Unlock() + } + return vals +} + +func isURLEncoded(fl FieldLevel) bool { + return uRLEncodedRegex.MatchString(fl.Field().String()) +} + +func isHTMLEncoded(fl FieldLevel) bool { + return hTMLEncodedRegex.MatchString(fl.Field().String()) +} + +func isHTML(fl FieldLevel) bool { + return hTMLRegex.MatchString(fl.Field().String()) +} + +func isOneOf(fl FieldLevel) bool { + vals := parseOneOfParam2(fl.Param()) + + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + for i := 0; i < len(vals); i++ { + if vals[i] == v { + return true + } + } + return false +} + +// isUnique is the validation function for validating if each array|slice|map value is unique +func isUnique(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + v := reflect.ValueOf(struct{}{}) + + switch field.Kind() { + case reflect.Slice, reflect.Array: + elem := field.Type().Elem() + if elem.Kind() == reflect.Ptr { + elem = elem.Elem() + } + + if param == "" { + m := reflect.MakeMap(reflect.MapOf(elem, v.Type())) + + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(reflect.Indirect(field.Index(i)), v) + } + return field.Len() == m.Len() + } + + sf, ok := elem.FieldByName(param) + if !ok { + panic(fmt.Sprintf("Bad field name %s", param)) + } + + sfTyp := sf.Type + if sfTyp.Kind() == reflect.Ptr { + sfTyp = sfTyp.Elem() + } + + m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type())) + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v) + } + return field.Len() == m.Len() + case reflect.Map: + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for _, k := range field.MapKeys() { + m.SetMapIndex(field.MapIndex(k), v) + } + return field.Len() == m.Len() + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } +} + +// IsMAC is the validation function for validating if the field's value is a valid MAC address. +func isMAC(fl FieldLevel) bool { + + _, err := net.ParseMAC(fl.Field().String()) + + return err == nil +} + +// IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. +func isCIDRv4(fl FieldLevel) bool { + + ip, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil && ip.To4() != nil +} + +// IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. +func isCIDRv6(fl FieldLevel) bool { + + ip, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil && ip.To4() == nil +} + +// IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. +func isCIDR(fl FieldLevel) bool { + + _, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil +} + +// IsIPv4 is the validation function for validating if a value is a valid v4 IP address. +func isIPv4(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil && ip.To4() != nil +} + +// IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address. +func isIPv6(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil && ip.To4() == nil +} + +// IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. +func isIP(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil +} + +// IsSSN is the validation function for validating if the field's value is a valid SSN. +func isSSN(fl FieldLevel) bool { + + field := fl.Field() + + if field.Len() != 11 { + return false + } + + return sSNRegex.MatchString(field.String()) +} + +// IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate. +func isLongitude(fl FieldLevel) bool { + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + case reflect.Float32: + v = strconv.FormatFloat(field.Float(), 'f', -1, 32) + case reflect.Float64: + v = strconv.FormatFloat(field.Float(), 'f', -1, 64) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + + return longitudeRegex.MatchString(v) +} + +// IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate. +func isLatitude(fl FieldLevel) bool { + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + case reflect.Float32: + v = strconv.FormatFloat(field.Float(), 'f', -1, 32) + case reflect.Float64: + v = strconv.FormatFloat(field.Float(), 'f', -1, 64) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + + return latitudeRegex.MatchString(v) +} + +// IsDataURI is the validation function for validating if the field's value is a valid data URI. +func isDataURI(fl FieldLevel) bool { + + uri := strings.SplitN(fl.Field().String(), ",", 2) + + if len(uri) != 2 { + return false + } + + if !dataURIRegex.MatchString(uri[0]) { + return false + } + + return base64Regex.MatchString(uri[1]) +} + +// HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. +func hasMultiByteCharacter(fl FieldLevel) bool { + + field := fl.Field() + + if field.Len() == 0 { + return true + } + + return multibyteRegex.MatchString(field.String()) +} + +// IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character. +func isPrintableASCII(fl FieldLevel) bool { + return printableASCIIRegex.MatchString(fl.Field().String()) +} + +// IsASCII is the validation function for validating if the field's value is a valid ASCII character. +func isASCII(fl FieldLevel) bool { + return aSCIIRegex.MatchString(fl.Field().String()) +} + +// IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID. +func isUUID5(fl FieldLevel) bool { + return uUID5Regex.MatchString(fl.Field().String()) +} + +// IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID. +func isUUID4(fl FieldLevel) bool { + return uUID4Regex.MatchString(fl.Field().String()) +} + +// IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID. +func isUUID3(fl FieldLevel) bool { + return uUID3Regex.MatchString(fl.Field().String()) +} + +// IsUUID is the validation function for validating if the field's value is a valid UUID of any version. +func isUUID(fl FieldLevel) bool { + return uUIDRegex.MatchString(fl.Field().String()) +} + +// IsUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID. +func isUUID5RFC4122(fl FieldLevel) bool { + return uUID5RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID. +func isUUID4RFC4122(fl FieldLevel) bool { + return uUID4RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID. +func isUUID3RFC4122(fl FieldLevel) bool { + return uUID3RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version. +func isUUIDRFC4122(fl FieldLevel) bool { + return uUIDRFC4122Regex.MatchString(fl.Field().String()) +} + +// IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. +func isISBN(fl FieldLevel) bool { + return isISBN10(fl) || isISBN13(fl) +} + +// IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. +func isISBN13(fl FieldLevel) bool { + + s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4) + + if !iSBN13Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + factor := []int32{1, 3} + + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(s[i]-'0') + } + + return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 +} + +// IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. +func isISBN10(fl FieldLevel) bool { + + s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3) + + if !iSBN10Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(s[i]-'0') + } + + if s[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(s[9]-'0') + } + + return checksum%11 == 0 +} + +// IsEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address. +func isEthereumAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !ethAddressRegex.MatchString(address) { + return false + } + + if ethaddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { + return true + } + + // Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md + address = address[2:] // Skip "0x" prefix. + h := sha3.NewLegacyKeccak256() + // hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash + _, _ = h.Write([]byte(strings.ToLower(address))) + hash := hex.EncodeToString(h.Sum(nil)) + + for i := 0; i < len(address); i++ { + if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case. + continue + } + if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' { + return false + } + } + + return true +} + +// IsBitcoinAddress is the validation function for validating if the field's value is a valid btc address +func isBitcoinAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcAddressRegex.MatchString(address) { + return false + } + + alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + decode := [25]byte{} + + for _, n := range []byte(address) { + d := bytes.IndexByte(alphabet, n) + + for i := 24; i >= 0; i-- { + d += 58 * int(decode[i]) + decode[i] = byte(d % 256) + d /= 256 + } + } + + h := sha256.New() + _, _ = h.Write(decode[:21]) + d := h.Sum([]byte{}) + h = sha256.New() + _, _ = h.Write(d) + + validchecksum := [4]byte{} + computedchecksum := [4]byte{} + + copy(computedchecksum[:], h.Sum(d[:0])) + copy(validchecksum[:], decode[21:]) + + return validchecksum == computedchecksum +} + +// IsBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address +func isBitcoinBech32Address(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { + return false + } + + am := len(address) % 8 + + if am == 0 || am == 3 || am == 5 { + return false + } + + address = strings.ToLower(address) + + alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc + addr := address[3:] + dp := make([]int, 0, len(addr)) + + for _, c := range addr { + dp = append(dp, strings.IndexRune(alphabet, c)) + } + + ver := dp[0] + + if ver < 0 || ver > 16 { + return false + } + + if ver == 0 { + if len(address) != 42 && len(address) != 62 { + return false + } + } + + values := append(hr, dp...) + + GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + + p := 1 + + for _, v := range values { + b := p >> 25 + p = (p&0x1ffffff)<<5 ^ v + + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + p ^= GEN[i] + } + } + } + + if p != 1 { + return false + } + + b := uint(0) + acc := 0 + mv := (1 << 5) - 1 + var sw []int + + for _, v := range dp[1 : len(dp)-6] { + acc = (acc << 5) | v + b += 5 + for b >= 8 { + b -= 8 + sw = append(sw, (acc>>b)&mv) + } + } + + if len(sw) < 2 || len(sw) > 40 { + return false + } + + return true +} + +// ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. +func excludesRune(fl FieldLevel) bool { + return !containsRune(fl) +} + +// ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param. +func excludesAll(fl FieldLevel) bool { + return !containsAny(fl) +} + +// Excludes is the validation function for validating that the field's value does not contain the text specified within the param. +func excludes(fl FieldLevel) bool { + return !contains(fl) +} + +// ContainsRune is the validation function for validating that the field's value contains the rune specified within the param. +func containsRune(fl FieldLevel) bool { + + r, _ := utf8.DecodeRuneInString(fl.Param()) + + return strings.ContainsRune(fl.Field().String(), r) +} + +// ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param. +func containsAny(fl FieldLevel) bool { + return strings.ContainsAny(fl.Field().String(), fl.Param()) +} + +// Contains is the validation function for validating that the field's value contains the text specified within the param. +func contains(fl FieldLevel) bool { + return strings.Contains(fl.Field().String(), fl.Param()) +} + +// StartsWith is the validation function for validating that the field's value starts with the text specified within the param. +func startsWith(fl FieldLevel) bool { + return strings.HasPrefix(fl.Field().String(), fl.Param()) +} + +// EndsWith is the validation function for validating that the field's value ends with the text specified within the param. +func endsWith(fl FieldLevel) bool { + return strings.HasSuffix(fl.Field().String(), fl.Param()) +} + +// StartsNotWith is the validation function for validating that the field's value does not start with the text specified within the param. +func startsNotWith(fl FieldLevel) bool { + return !startsWith(fl) +} + +// EndsNotWith is the validation function for validating that the field's value does not end with the text specified within the param. +func endsNotWith(fl FieldLevel) bool { + return !endsWith(fl) +} + +// FieldContains is the validation function for validating if the current field's value contains the field specified by the param's value. +func fieldContains(fl FieldLevel) bool { + field := fl.Field() + + currentField, _, ok := fl.GetStructFieldOK() + + if !ok { + return false + } + + return strings.Contains(field.String(), currentField.String()) +} + +// FieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value. +func fieldExcludes(fl FieldLevel) bool { + field := fl.Field() + + currentField, _, ok := fl.GetStructFieldOK() + if !ok { + return true + } + + return !strings.Contains(field.String(), currentField.String()) +} + +// IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value. +func isNeField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + + if !ok || currentKind != kind { + return true + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() != currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() != currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() != currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) != int64(currentField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return true + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() != currentField.String() +} + +// IsNe is the validation function for validating that the field's value does not equal the provided param value. +func isNe(fl FieldLevel) bool { + return !isEq(fl) +} + +// IsLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value. +func isLteCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() <= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() <= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() <= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) <= int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() <= topField.String() +} + +// IsLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func isLtCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() < topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() < topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() < topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) < int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) + } + } + + // default reflect.String: + return field.String() < topField.String() +} + +// IsGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value. +func isGteCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() >= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() >= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() >= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) >= int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() >= topField.String() +} + +// IsGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value. +func isGtCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() > topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() > topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() > topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) > int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) + } + } + + // default reflect.String: + return field.String() > topField.String() +} + +// IsNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value. +func isNeCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return true + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() != field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() != field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() != field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) != int64(field.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return true + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() != field.String() +} + +// IsEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value. +func isEqCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() == field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() == field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() == field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) == int64(field.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() == field.String() +} + +// IsEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value. +func isEqField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() == currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() == currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() == currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) == int64(currentField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() == currentField.String() +} + +// IsEq is the validation function for validating if the current field's value is equal to the param's value. +func isEq(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + return field.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + + case reflect.Bool: + p := asBool(param) + + return field.Bool() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsBase64 is the validation function for validating if the current field's value is a valid base 64. +func isBase64(fl FieldLevel) bool { + return base64Regex.MatchString(fl.Field().String()) +} + +// IsBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string. +func isBase64URL(fl FieldLevel) bool { + return base64URLRegex.MatchString(fl.Field().String()) +} + +// IsURI is the validation function for validating if the current field's value is a valid URI. +func isURI(fl FieldLevel) bool { + + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i := strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if len(s) == 0 { + return false + } + + _, err := url.ParseRequestURI(s) + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsURL is the validation function for validating if the current field's value is a valid URL. +func isURL(fl FieldLevel) bool { + + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + var i int + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i = strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if len(s) == 0 { + return false + } + + url, err := url.ParseRequestURI(s) + + if err != nil || url.Scheme == "" { + return false + } + + return true + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. +func isUrnRFC2141(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + str := field.String() + + _, match := urn.Parse([]byte(str)) + + return match + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsFile is the validation function for validating if the current field's value is a valid file path. +func isFile(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + case reflect.String: + fileInfo, err := os.Stat(field.String()) + if err != nil { + return false + } + + return !fileInfo.IsDir() + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. +func isE164(fl FieldLevel) bool { + return e164Regex.MatchString(fl.Field().String()) +} + +// IsEmail is the validation function for validating if the current field's value is a valid email address. +func isEmail(fl FieldLevel) bool { + return emailRegex.MatchString(fl.Field().String()) +} + +// IsHSLA is the validation function for validating if the current field's value is a valid HSLA color. +func isHSLA(fl FieldLevel) bool { + return hslaRegex.MatchString(fl.Field().String()) +} + +// IsHSL is the validation function for validating if the current field's value is a valid HSL color. +func isHSL(fl FieldLevel) bool { + return hslRegex.MatchString(fl.Field().String()) +} + +// IsRGBA is the validation function for validating if the current field's value is a valid RGBA color. +func isRGBA(fl FieldLevel) bool { + return rgbaRegex.MatchString(fl.Field().String()) +} + +// IsRGB is the validation function for validating if the current field's value is a valid RGB color. +func isRGB(fl FieldLevel) bool { + return rgbRegex.MatchString(fl.Field().String()) +} + +// IsHEXColor is the validation function for validating if the current field's value is a valid HEX color. +func isHEXColor(fl FieldLevel) bool { + return hexcolorRegex.MatchString(fl.Field().String()) +} + +// IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. +func isHexadecimal(fl FieldLevel) bool { + return hexadecimalRegex.MatchString(fl.Field().String()) +} + +// IsNumber is the validation function for validating if the current field's value is a valid number. +func isNumber(fl FieldLevel) bool { + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numberRegex.MatchString(fl.Field().String()) + } +} + +// IsNumeric is the validation function for validating if the current field's value is a valid numeric value. +func isNumeric(fl FieldLevel) bool { + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numericRegex.MatchString(fl.Field().String()) + } +} + +// IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. +func isAlphanum(fl FieldLevel) bool { + return alphaNumericRegex.MatchString(fl.Field().String()) +} + +// IsAlpha is the validation function for validating if the current field's value is a valid alpha value. +func isAlpha(fl FieldLevel) bool { + return alphaRegex.MatchString(fl.Field().String()) +} + +// IsAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value. +func isAlphanumUnicode(fl FieldLevel) bool { + return alphaUnicodeNumericRegex.MatchString(fl.Field().String()) +} + +// IsAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value. +func isAlphaUnicode(fl FieldLevel) bool { + return alphaUnicodeRegex.MatchString(fl.Field().String()) +} + +// isDefault is the opposite of required aka hasValue +func isDefault(fl FieldLevel) bool { + return !hasValue(fl) +} + +// HasValue is the validation function for validating if the current field's value is not the default static value. +func hasValue(fl FieldLevel) bool { + field := fl.Field() + switch field.Kind() { + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() + default: + if fl.(*validate).fldIsPointer && field.Interface() != nil { + return true + } + return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() + } +} + +// requireCheckField is a func for check field kind +func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { + field := fl.Field() + kind := field.Kind() + var nullable, found bool + if len(param) > 0 { + field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param) + if !found { + return defaultNotFoundValue + } + } + switch kind { + case reflect.Invalid: + return defaultNotFoundValue + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return field.IsNil() + default: + if nullable && field.Interface() != nil { + return false + } + return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface() + } +} + +// requireCheckFieldValue is a func for check field value +func requireCheckFieldValue(fl FieldLevel, param string, value string, defaultNotFoundValue bool) bool { + field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param) + if !found { + return defaultNotFoundValue + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() == asInt(value) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() == asUint(value) + + case reflect.Float32, reflect.Float64: + return field.Float() == asFloat(value) + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) == asInt(value) + } + + // default reflect.String: + return field.String() == value +} + +// requiredIf is the validation function +// The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field. +func requiredIf(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + if len(params)%2 != 0 { + panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName())) + } + for i := 0; i < len(params); i += 2 { + if !requireCheckFieldValue(fl, params[i], params[i+1], false) { + return true + } + } + return hasValue(fl) +} + +// requiredUnless is the validation function +// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field. +func requiredUnless(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + if len(params)%2 != 0 { + panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName())) + } + + for i := 0; i < len(params); i += 2 { + if requireCheckFieldValue(fl, params[i], params[i+1], false) { + return true + } + } + return hasValue(fl) +} + +// ExcludedWith is the validation function +// The field under validation must not be present or is empty if any of the other specified fields are present. +func excludedWith(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if !requireCheckFieldKind(fl, param, true) { + return !hasValue(fl) + } + } + return true +} + +// RequiredWith is the validation function +// The field under validation must be present and not empty only if any of the other specified fields are present. +func requiredWith(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if !requireCheckFieldKind(fl, param, true) { + return hasValue(fl) + } + } + return true +} + +// ExcludedWithAll is the validation function +// The field under validation must not be present or is empty if all of the other specified fields are present. +func excludedWithAll(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if requireCheckFieldKind(fl, param, true) { + return true + } + } + return !hasValue(fl) +} + +// RequiredWithAll is the validation function +// The field under validation must be present and not empty only if all of the other specified fields are present. +func requiredWithAll(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if requireCheckFieldKind(fl, param, true) { + return true + } + } + return hasValue(fl) +} + +// ExcludedWithout is the validation function +// The field under validation must not be present or is empty when any of the other specified fields are not present. +func excludedWithout(fl FieldLevel) bool { + if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) { + return !hasValue(fl) + } + return true +} + +// RequiredWithout is the validation function +// The field under validation must be present and not empty only when any of the other specified fields are not present. +func requiredWithout(fl FieldLevel) bool { + if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) { + return hasValue(fl) + } + return true +} + +// RequiredWithoutAll is the validation function +// The field under validation must not be present or is empty when all of the other specified fields are not present. +func excludedWithoutAll(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if !requireCheckFieldKind(fl, param, true) { + return true + } + } + return !hasValue(fl) +} + +// RequiredWithoutAll is the validation function +// The field under validation must be present and not empty only when all of the other specified fields are not present. +func requiredWithoutAll(fl FieldLevel) bool { + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + if !requireCheckFieldKind(fl, param, true) { + return true + } + } + return hasValue(fl) +} + +// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. +func isGteField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() >= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() >= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() >= currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) >= len(currentField.String()) +} + +// IsGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value. +func isGtField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() > currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() > currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() > currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) + } + } + + // default reflect.String + return len(field.String()) > len(currentField.String()) +} + +// IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value. +func isGte(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) >= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) >= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() >= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() >= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() >= p + + case reflect.Struct: + + if field.Type() == timeType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.After(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsGt is the validation function for validating if the current field's value is greater than the param's value. +func isGt(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) > p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) > p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() > p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() > p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() > p + case reflect.Struct: + + if field.Type() == timeType { + + return field.Interface().(time.Time).After(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasLengthOf is the validation function for validating if the current field's value is equal to the param's value. +func hasLengthOf(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) == p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value. +func hasMinOf(fl FieldLevel) bool { + return isGte(fl) +} + +// IsLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value. +func isLteField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() <= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() <= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() <= currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) <= len(currentField.String()) +} + +// IsLtField is the validation function for validating if the current field's value is less than the field specified by the param's value. +func isLtField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() < currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() < currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() < currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) + } + } + + // default reflect.String + return len(field.String()) < len(currentField.String()) +} + +// IsLte is the validation function for validating if the current field's value is less than or equal to the param's value. +func isLte(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) <= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) <= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() <= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() <= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() <= p + + case reflect.Struct: + + if field.Type() == timeType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.Before(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsLt is the validation function for validating if the current field's value is less than the param's value. +func isLt(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) < p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) < p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asIntFromType(field.Type(), param) + + return field.Int() < p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() < p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() < p + + case reflect.Struct: + + if field.Type() == timeType { + + return field.Interface().(time.Time).Before(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value. +func hasMaxOf(fl FieldLevel) bool { + return isLte(fl) +} + +// IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. +func isTCP4AddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp4", fl.Field().String()) + return err == nil +} + +// IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. +func isTCP6AddrResolvable(fl FieldLevel) bool { + + if !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp6", fl.Field().String()) + + return err == nil +} + +// IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. +func isTCPAddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) && !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp", fl.Field().String()) + + return err == nil +} + +// IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. +func isUDP4AddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp4", fl.Field().String()) + + return err == nil +} + +// IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. +func isUDP6AddrResolvable(fl FieldLevel) bool { + + if !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp6", fl.Field().String()) + + return err == nil +} + +// IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. +func isUDPAddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) && !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp", fl.Field().String()) + + return err == nil +} + +// IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. +func isIP4AddrResolvable(fl FieldLevel) bool { + + if !isIPv4(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip4", fl.Field().String()) + + return err == nil +} + +// IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. +func isIP6AddrResolvable(fl FieldLevel) bool { + + if !isIPv6(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip6", fl.Field().String()) + + return err == nil +} + +// IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. +func isIPAddrResolvable(fl FieldLevel) bool { + + if !isIP(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip", fl.Field().String()) + + return err == nil +} + +// IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. +func isUnixAddrResolvable(fl FieldLevel) bool { + + _, err := net.ResolveUnixAddr("unix", fl.Field().String()) + + return err == nil +} + +func isIP4Addr(fl FieldLevel) bool { + + val := fl.Field().String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + val = val[0:idx] + } + + ip := net.ParseIP(val) + + return ip != nil && ip.To4() != nil +} + +func isIP6Addr(fl FieldLevel) bool { + + val := fl.Field().String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + if idx != 0 && val[idx-1:idx] == "]" { + val = val[1 : idx-1] + } + } + + ip := net.ParseIP(val) + + return ip != nil && ip.To4() == nil +} + +func isHostnameRFC952(fl FieldLevel) bool { + return hostnameRegexRFC952.MatchString(fl.Field().String()) +} + +func isHostnameRFC1123(fl FieldLevel) bool { + return hostnameRegexRFC1123.MatchString(fl.Field().String()) +} + +func isFQDN(fl FieldLevel) bool { + val := fl.Field().String() + + if val == "" { + return false + } + + return fqdnRegexRFC1123.MatchString(val) +} + +// IsDir is the validation function for validating if the current field's value is a valid directory. +func isDir(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + fileInfo, err := os.Stat(field.String()) + if err != nil { + return false + } + + return fileInfo.IsDir() + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isJSON is the validation function for validating if the current field's value is a valid json string. +func isJSON(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + val := field.String() + return json.Valid([]byte(val)) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isHostnamePort validates a : combination for fields typically used for socket address. +func isHostnamePort(fl FieldLevel) bool { + val := fl.Field().String() + host, port, err := net.SplitHostPort(val) + if err != nil { + return false + } + // Port must be a iny <= 65535. + if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 1 { + return false + } + + // If host is specified, it should match a DNS name + if host != "" { + return hostnameRegexRFC1123.MatchString(host) + } + return true +} + +// isLowercase is the validation function for validating if the current field's value is a lowercase string. +func isLowercase(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + if field.String() == "" { + return false + } + return field.String() == strings.ToLower(field.String()) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isUppercase is the validation function for validating if the current field's value is an uppercase string. +func isUppercase(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + if field.String() == "" { + return false + } + return field.String() == strings.ToUpper(field.String()) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isDatetime is the validation function for validating if the current field's value is a valid datetime string. +func isDatetime(fl FieldLevel) bool { + field := fl.Field() + param := fl.Param() + + if field.Kind() == reflect.String { + _, err := time.Parse(param, field.String()) + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isTimeZone is the validation function for validating if the current field's value is a valid time zone string. +func isTimeZone(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name + if field.String() == "" { + return false + } + + // Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name + if strings.ToLower(field.String()) == "local" { + return false + } + + _, err := time.LoadLocation(field.String()) + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code. +func isIso3166Alpha2(fl FieldLevel) bool { + val := fl.Field().String() + return iso3166_1_alpha2[val] +} + +// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code. +func isIso3166Alpha3(fl FieldLevel) bool { + val := fl.Field().String() + return iso3166_1_alpha3[val] +} + +// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code. +func isIso3166AlphaNumeric(fl FieldLevel) bool { + field := fl.Field() + + var code int + switch field.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + code = int(field.Int() % 1000) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + code = int(field.Uint() % 1000) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + return iso3166_1_alpha_numeric[code] +} diff --git a/vendor/github.com/go-playground/validator/v10/cache.go b/vendor/github.com/go-playground/validator/v10/cache.go new file mode 100644 index 0000000..0d18d6e --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/cache.go @@ -0,0 +1,322 @@ +package validator + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +type tagType uint8 + +const ( + typeDefault tagType = iota + typeOmitEmpty + typeIsDefault + typeNoStructLevel + typeStructOnly + typeDive + typeOr + typeKeys + typeEndKeys +) + +const ( + invalidValidation = "Invalid validation tag on field '%s'" + undefinedValidation = "Undefined validation function '%s' on field '%s'" + keysTagNotDefined = "'" + endKeysTag + "' tag encountered without a corresponding '" + keysTag + "' tag" +) + +type structCache struct { + lock sync.Mutex + m atomic.Value // map[reflect.Type]*cStruct +} + +func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) { + c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] + return +} + +func (sc *structCache) Set(key reflect.Type, value *cStruct) { + m := sc.m.Load().(map[reflect.Type]*cStruct) + nm := make(map[reflect.Type]*cStruct, len(m)+1) + for k, v := range m { + nm[k] = v + } + nm[key] = value + sc.m.Store(nm) +} + +type tagCache struct { + lock sync.Mutex + m atomic.Value // map[string]*cTag +} + +func (tc *tagCache) Get(key string) (c *cTag, found bool) { + c, found = tc.m.Load().(map[string]*cTag)[key] + return +} + +func (tc *tagCache) Set(key string, value *cTag) { + m := tc.m.Load().(map[string]*cTag) + nm := make(map[string]*cTag, len(m)+1) + for k, v := range m { + nm[k] = v + } + nm[key] = value + tc.m.Store(nm) +} + +type cStruct struct { + name string + fields []*cField + fn StructLevelFuncCtx +} + +type cField struct { + idx int + name string + altName string + namesEqual bool + cTags *cTag +} + +type cTag struct { + tag string + aliasTag string + actualAliasTag string + param string + keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation + next *cTag + fn FuncCtx + typeof tagType + hasTag bool + hasAlias bool + hasParam bool // true if parameter used eg. eq= where the equal sign has been set + isBlockEnd bool // indicates the current tag represents the last validation in the block + runValidationWhenNil bool +} + +func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { + v.structCache.lock.Lock() + defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! + + typ := current.Type() + + // could have been multiple trying to access, but once first is done this ensures struct + // isn't parsed again. + cs, ok := v.structCache.Get(typ) + if ok { + return cs + } + + cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]} + + numFields := current.NumField() + + var ctag *cTag + var fld reflect.StructField + var tag string + var customName string + + for i := 0; i < numFields; i++ { + + fld = typ.Field(i) + + if !fld.Anonymous && len(fld.PkgPath) > 0 { + continue + } + + tag = fld.Tag.Get(v.tagName) + + if tag == skipValidationTag { + continue + } + + customName = fld.Name + + if v.hasTagNameFunc { + name := v.tagNameFunc(fld) + if len(name) > 0 { + customName = name + } + } + + // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different + // and so only struct level caching can be used instead of combined with Field tag caching + + if len(tag) > 0 { + ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, "", false) + } else { + // even if field doesn't have validations need cTag for traversing to potential inner/nested + // elements of the field. + ctag = new(cTag) + } + + cs.fields = append(cs.fields, &cField{ + idx: i, + name: fld.Name, + altName: customName, + cTags: ctag, + namesEqual: fld.Name == customName, + }) + } + v.structCache.Set(typ, cs) + return cs +} + +func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { + var t string + noAlias := len(alias) == 0 + tags := strings.Split(tag, tagSeparator) + + for i := 0; i < len(tags); i++ { + t = tags[i] + if noAlias { + alias = t + } + + // check map for alias and process new tags, otherwise process as usual + if tagsVal, found := v.aliases[t]; found { + if i == 0 { + firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + } else { + next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + current.next, current = next, curr + + } + continue + } + + var prevTag tagType + + if i == 0 { + current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault} + firstCtag = current + } else { + prevTag = current.typeof + current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + current = current.next + } + + switch t { + case diveTag: + current.typeof = typeDive + continue + + case keysTag: + current.typeof = typeKeys + + if i == 0 || prevTag != typeDive { + panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag)) + } + + current.typeof = typeKeys + + // need to pass along only keys tag + // need to increment i to skip over the keys tags + b := make([]byte, 0, 64) + + i++ + + for ; i < len(tags); i++ { + + b = append(b, tags[i]...) + b = append(b, ',') + + if tags[i] == endKeysTag { + break + } + } + + current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false) + continue + + case endKeysTag: + current.typeof = typeEndKeys + + // if there are more in tags then there was no keysTag defined + // and an error should be thrown + if i != len(tags)-1 { + panic(keysTagNotDefined) + } + return + + case omitempty: + current.typeof = typeOmitEmpty + continue + + case structOnlyTag: + current.typeof = typeStructOnly + continue + + case noStructLevelTag: + current.typeof = typeNoStructLevel + continue + + default: + if t == isdefault { + current.typeof = typeIsDefault + } + // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" + orVals := strings.Split(t, orSeparator) + + for j := 0; j < len(orVals); j++ { + vals := strings.SplitN(orVals[j], tagKeySeparator, 2) + if noAlias { + alias = vals[0] + current.aliasTag = alias + } else { + current.actualAliasTag = t + } + + if j > 0 { + current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true} + current = current.next + } + current.hasParam = len(vals) > 1 + + current.tag = vals[0] + if len(current.tag) == 0 { + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) + } + + if wrapper, ok := v.validations[current.tag]; ok { + current.fn = wrapper.fn + current.runValidationWhenNil = wrapper.runValidatinOnNil + } else { + panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName))) + } + + if len(orVals) > 1 { + current.typeof = typeOr + } + + if len(vals) > 1 { + current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } + } + current.isBlockEnd = true + } + } + return +} + +func (v *Validate) fetchCacheTag(tag string) *cTag { + // find cached tag + ctag, found := v.tagCache.Get(tag) + if !found { + v.tagCache.lock.Lock() + defer v.tagCache.lock.Unlock() + + // could have been multiple trying to access, but once first is done this ensures tag + // isn't parsed again. + ctag, found = v.tagCache.Get(tag) + if !found { + ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false) + v.tagCache.Set(tag, ctag) + } + } + return ctag +} diff --git a/vendor/github.com/go-playground/validator/v10/country_codes.go b/vendor/github.com/go-playground/validator/v10/country_codes.go new file mode 100644 index 0000000..ef81ead --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/country_codes.go @@ -0,0 +1,162 @@ +package validator + +var iso3166_1_alpha2 = map[string]bool{ + // see: https://www.iso.org/iso-3166-country-codes.html + "AF": true, "AX": true, "AL": true, "DZ": true, "AS": true, + "AD": true, "AO": true, "AI": true, "AQ": true, "AG": true, + "AR": true, "AM": true, "AW": true, "AU": true, "AT": true, + "AZ": true, "BS": true, "BH": true, "BD": true, "BB": true, + "BY": true, "BE": true, "BZ": true, "BJ": true, "BM": true, + "BT": true, "BO": true, "BQ": true, "BA": true, "BW": true, + "BV": true, "BR": true, "IO": true, "BN": true, "BG": true, + "BF": true, "BI": true, "KH": true, "CM": true, "CA": true, + "CV": true, "KY": true, "CF": true, "TD": true, "CL": true, + "CN": true, "CX": true, "CC": true, "CO": true, "KM": true, + "CG": true, "CD": true, "CK": true, "CR": true, "CI": true, + "HR": true, "CU": true, "CW": true, "CY": true, "CZ": true, + "DK": true, "DJ": true, "DM": true, "DO": true, "EC": true, + "EG": true, "SV": true, "GQ": true, "ER": true, "EE": true, + "ET": true, "FK": true, "FO": true, "FJ": true, "FI": true, + "FR": true, "GF": true, "PF": true, "TF": true, "GA": true, + "GM": true, "GE": true, "DE": true, "GH": true, "GI": true, + "GR": true, "GL": true, "GD": true, "GP": true, "GU": true, + "GT": true, "GG": true, "GN": true, "GW": true, "GY": true, + "HT": true, "HM": true, "VA": true, "HN": true, "HK": true, + "HU": true, "IS": true, "IN": true, "ID": true, "IR": true, + "IQ": true, "IE": true, "IM": true, "IL": true, "IT": true, + "JM": true, "JP": true, "JE": true, "JO": true, "KZ": true, + "KE": true, "KI": true, "KP": true, "KR": true, "KW": true, + "KG": true, "LA": true, "LV": true, "LB": true, "LS": true, + "LR": true, "LY": true, "LI": true, "LT": true, "LU": true, + "MO": true, "MK": true, "MG": true, "MW": true, "MY": true, + "MV": true, "ML": true, "MT": true, "MH": true, "MQ": true, + "MR": true, "MU": true, "YT": true, "MX": true, "FM": true, + "MD": true, "MC": true, "MN": true, "ME": true, "MS": true, + "MA": true, "MZ": true, "MM": true, "NA": true, "NR": true, + "NP": true, "NL": true, "NC": true, "NZ": true, "NI": true, + "NE": true, "NG": true, "NU": true, "NF": true, "MP": true, + "NO": true, "OM": true, "PK": true, "PW": true, "PS": true, + "PA": true, "PG": true, "PY": true, "PE": true, "PH": true, + "PN": true, "PL": true, "PT": true, "PR": true, "QA": true, + "RE": true, "RO": true, "RU": true, "RW": true, "BL": true, + "SH": true, "KN": true, "LC": true, "MF": true, "PM": true, + "VC": true, "WS": true, "SM": true, "ST": true, "SA": true, + "SN": true, "RS": true, "SC": true, "SL": true, "SG": true, + "SX": true, "SK": true, "SI": true, "SB": true, "SO": true, + "ZA": true, "GS": true, "SS": true, "ES": true, "LK": true, + "SD": true, "SR": true, "SJ": true, "SZ": true, "SE": true, + "CH": true, "SY": true, "TW": true, "TJ": true, "TZ": true, + "TH": true, "TL": true, "TG": true, "TK": true, "TO": true, + "TT": true, "TN": true, "TR": true, "TM": true, "TC": true, + "TV": true, "UG": true, "UA": true, "AE": true, "GB": true, + "US": true, "UM": true, "UY": true, "UZ": true, "VU": true, + "VE": true, "VN": true, "VG": true, "VI": true, "WF": true, + "EH": true, "YE": true, "ZM": true, "ZW": true, +} + +var iso3166_1_alpha3 = map[string]bool{ + // see: https://www.iso.org/iso-3166-country-codes.html + "AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true, + "AGO": true, "AIA": true, "ATA": true, "ATG": true, "ARG": true, + "ARM": true, "ABW": true, "AUS": true, "AUT": true, "AZE": true, + "BHS": true, "BHR": true, "BGD": true, "BRB": true, "BLR": true, + "BEL": true, "BLZ": true, "BEN": true, "BMU": true, "BTN": true, + "BOL": true, "BES": true, "BIH": true, "BWA": true, "BVT": true, + "BRA": true, "IOT": true, "BRN": true, "BGR": true, "BFA": true, + "BDI": true, "CPV": true, "KHM": true, "CMR": true, "CAN": true, + "CYM": true, "CAF": true, "TCD": true, "CHL": true, "CHN": true, + "CXR": true, "CCK": true, "COL": true, "COM": true, "COD": true, + "COG": true, "COK": true, "CRI": true, "HRV": true, "CUB": true, + "CUW": true, "CYP": true, "CZE": true, "CIV": true, "DNK": true, + "DJI": true, "DMA": true, "DOM": true, "ECU": true, "EGY": true, + "SLV": true, "GNQ": true, "ERI": true, "EST": true, "SWZ": true, + "ETH": true, "FLK": true, "FRO": true, "FJI": true, "FIN": true, + "FRA": true, "GUF": true, "PYF": true, "ATF": true, "GAB": true, + "GMB": true, "GEO": true, "DEU": true, "GHA": true, "GIB": true, + "GRC": true, "GRL": true, "GRD": true, "GLP": true, "GUM": true, + "GTM": true, "GGY": true, "GIN": true, "GNB": true, "GUY": true, + "HTI": true, "HMD": true, "VAT": true, "HND": true, "HKG": true, + "HUN": true, "ISL": true, "IND": true, "IDN": true, "IRN": true, + "IRQ": true, "IRL": true, "IMN": true, "ISR": true, "ITA": true, + "JAM": true, "JPN": true, "JEY": true, "JOR": true, "KAZ": true, + "KEN": true, "KIR": true, "PRK": true, "KOR": true, "KWT": true, + "KGZ": true, "LAO": true, "LVA": true, "LBN": true, "LSO": true, + "LBR": true, "LBY": true, "LIE": true, "LTU": true, "LUX": true, + "MAC": true, "MDG": true, "MWI": true, "MYS": true, "MDV": true, + "MLI": true, "MLT": true, "MHL": true, "MTQ": true, "MRT": true, + "MUS": true, "MYT": true, "MEX": true, "FSM": true, "MDA": true, + "MCO": true, "MNG": true, "MNE": true, "MSR": true, "MAR": true, + "MOZ": true, "MMR": true, "NAM": true, "NRU": true, "NPL": true, + "NLD": true, "NCL": true, "NZL": true, "NIC": true, "NER": true, + "NGA": true, "NIU": true, "NFK": true, "MKD": true, "MNP": true, + "NOR": true, "OMN": true, "PAK": true, "PLW": true, "PSE": true, + "PAN": true, "PNG": true, "PRY": true, "PER": true, "PHL": true, + "PCN": true, "POL": true, "PRT": true, "PRI": true, "QAT": true, + "ROU": true, "RUS": true, "RWA": true, "REU": true, "BLM": true, + "SHN": true, "KNA": true, "LCA": true, "MAF": true, "SPM": true, + "VCT": true, "WSM": true, "SMR": true, "STP": true, "SAU": true, + "SEN": true, "SRB": true, "SYC": true, "SLE": true, "SGP": true, + "SXM": true, "SVK": true, "SVN": true, "SLB": true, "SOM": true, + "ZAF": true, "SGS": true, "SSD": true, "ESP": true, "LKA": true, + "SDN": true, "SUR": true, "SJM": true, "SWE": true, "CHE": true, + "SYR": true, "TWN": true, "TJK": true, "TZA": true, "THA": true, + "TLS": true, "TGO": true, "TKL": true, "TON": true, "TTO": true, + "TUN": true, "TUR": true, "TKM": true, "TCA": true, "TUV": true, + "UGA": true, "UKR": true, "ARE": true, "GBR": true, "UMI": true, + "USA": true, "URY": true, "UZB": true, "VUT": true, "VEN": true, + "VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true, + "YEM": true, "ZMB": true, "ZWE": true, "ALA": true, +} +var iso3166_1_alpha_numeric = map[int]bool{ + // see: https://www.iso.org/iso-3166-country-codes.html + 4: true, 8: true, 12: true, 16: true, 20: true, + 24: true, 660: true, 10: true, 28: true, 32: true, + 51: true, 533: true, 36: true, 40: true, 31: true, + 44: true, 48: true, 50: true, 52: true, 112: true, + 56: true, 84: true, 204: true, 60: true, 64: true, + 68: true, 535: true, 70: true, 72: true, 74: true, + 76: true, 86: true, 96: true, 100: true, 854: true, + 108: true, 132: true, 116: true, 120: true, 124: true, + 136: true, 140: true, 148: true, 152: true, 156: true, + 162: true, 166: true, 170: true, 174: true, 180: true, + 178: true, 184: true, 188: true, 191: true, 192: true, + 531: true, 196: true, 203: true, 384: true, 208: true, + 262: true, 212: true, 214: true, 218: true, 818: true, + 222: true, 226: true, 232: true, 233: true, 748: true, + 231: true, 238: true, 234: true, 242: true, 246: true, + 250: true, 254: true, 258: true, 260: true, 266: true, + 270: true, 268: true, 276: true, 288: true, 292: true, + 300: true, 304: true, 308: true, 312: true, 316: true, + 320: true, 831: true, 324: true, 624: true, 328: true, + 332: true, 334: true, 336: true, 340: true, 344: true, + 348: true, 352: true, 356: true, 360: true, 364: true, + 368: true, 372: true, 833: true, 376: true, 380: true, + 388: true, 392: true, 832: true, 400: true, 398: true, + 404: true, 296: true, 408: true, 410: true, 414: true, + 417: true, 418: true, 428: true, 422: true, 426: true, + 430: true, 434: true, 438: true, 440: true, 442: true, + 446: true, 450: true, 454: true, 458: true, 462: true, + 466: true, 470: true, 584: true, 474: true, 478: true, + 480: true, 175: true, 484: true, 583: true, 498: true, + 492: true, 496: true, 499: true, 500: true, 504: true, + 508: true, 104: true, 516: true, 520: true, 524: true, + 528: true, 540: true, 554: true, 558: true, 562: true, + 566: true, 570: true, 574: true, 807: true, 580: true, + 578: true, 512: true, 586: true, 585: true, 275: true, + 591: true, 598: true, 600: true, 604: true, 608: true, + 612: true, 616: true, 620: true, 630: true, 634: true, + 642: true, 643: true, 646: true, 638: true, 652: true, + 654: true, 659: true, 662: true, 663: true, 666: true, + 670: true, 882: true, 674: true, 678: true, 682: true, + 686: true, 688: true, 690: true, 694: true, 702: true, + 534: true, 703: true, 705: true, 90: true, 706: true, + 710: true, 239: true, 728: true, 724: true, 144: true, + 729: true, 740: true, 744: true, 752: true, 756: true, + 760: true, 158: true, 762: true, 834: true, 764: true, + 626: true, 768: true, 772: true, 776: true, 780: true, + 788: true, 792: true, 795: true, 796: true, 798: true, + 800: true, 804: true, 784: true, 826: true, 581: true, + 840: true, 858: true, 860: true, 548: true, 862: true, + 704: true, 92: true, 850: true, 876: true, 732: true, + 887: true, 894: true, 716: true, 248: true, +} diff --git a/vendor/github.com/go-playground/validator/v10/doc.go b/vendor/github.com/go-playground/validator/v10/doc.go new file mode 100644 index 0000000..a816c20 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/doc.go @@ -0,0 +1,1308 @@ +/* +Package validator implements value validations for structs and individual fields +based on tags. + +It can also handle Cross-Field and Cross-Struct validation for nested structs +and has the ability to dive into arrays and maps of any type. + +see more examples https://github.com/go-playground/validator/tree/master/_examples + +Validation Functions Return Type error + +Doing things this way is actually the way the standard library does, see the +file.Open method here: + + https://golang.org/pkg/os/#Open. + +The authors return type "error" to avoid the issue discussed in the following, +where err is always != nil: + + http://stackoverflow.com/a/29138676/3158232 + https://github.com/go-playground/validator/issues/134 + +Validator only InvalidValidationError for bad validation input, nil or +ValidationErrors as type error; so, in your code all you need to do is check +if the error returned is not nil, and if it's not check if error is +InvalidValidationError ( if necessary, most of the time it isn't ) type cast +it to type ValidationErrors like so err.(validator.ValidationErrors). + +Custom Validation Functions + +Custom Validation functions can be added. Example: + + // Structure + func customFunc(fl validator.FieldLevel) bool { + + if fl.Field().String() == "invalid" { + return false + } + + return true + } + + validate.RegisterValidation("custom tag name", customFunc) + // NOTES: using the same tag name as an existing function + // will overwrite the existing one + +Cross-Field Validation + +Cross-Field Validation can be done via the following tags: + - eqfield + - nefield + - gtfield + - gtefield + - ltfield + - ltefield + - eqcsfield + - necsfield + - gtcsfield + - gtecsfield + - ltcsfield + - ltecsfield + +If, however, some custom cross-field validation is required, it can be done +using a custom validation. + +Why not just have cross-fields validation tags (i.e. only eqcsfield and not +eqfield)? + +The reason is efficiency. If you want to check a field within the same struct +"eqfield" only has to find the field on the same struct (1 level). But, if we +used "eqcsfield" it could be multiple levels down. Example: + + type Inner struct { + StartDate time.Time + } + + type Outer struct { + InnerStructField *Inner + CreatedAt time.Time `validate:"ltecsfield=InnerStructField.StartDate"` + } + + now := time.Now() + + inner := &Inner{ + StartDate: now, + } + + outer := &Outer{ + InnerStructField: inner, + CreatedAt: now, + } + + errs := validate.Struct(outer) + + // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed + // into the function + // when calling validate.VarWithValue(val, field, tag) val will be + // whatever you pass, struct, field... + // when calling validate.Field(field, tag) val will be nil + +Multiple Validators + +Multiple validators on a field will process in the order defined. Example: + + type Test struct { + Field `validate:"max=10,min=1"` + } + + // max will be checked then min + +Bad Validator definitions are not handled by the library. Example: + + type Test struct { + Field `validate:"min=10,max=0"` + } + + // this definition of min max will never succeed + +Using Validator Tags + +Baked In Cross-Field validation only compares fields on the same struct. +If Cross-Field + Cross-Struct validation is needed you should implement your +own custom validator. + +Comma (",") is the default separator of validation tags. If you wish to +have a comma included within the parameter (i.e. excludesall=,) you will need to +use the UTF-8 hex representation 0x2C, which is replaced in the code as a comma, +so the above will become excludesall=0x2C. + + type Test struct { + Field `validate:"excludesall=,"` // BAD! Do not include a comma. + Field `validate:"excludesall=0x2C"` // GOOD! Use the UTF-8 hex representation. + } + +Pipe ("|") is the 'or' validation tags deparator. If you wish to +have a pipe included within the parameter i.e. excludesall=| you will need to +use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe, +so the above will become excludesall=0x7C + + type Test struct { + Field `validate:"excludesall=|"` // BAD! Do not include a a pipe! + Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation. + } + + +Baked In Validators and Tags + +Here is a list of the current built in validators: + + +Skip Field + +Tells the validation to skip this struct field; this is particularly +handy in ignoring embedded structs from being validated. (Usage: -) + Usage: - + + +Or Operator + +This is the 'or' operator allowing multiple validators to be used and +accepted. (Usage: rgb|rgba) <-- this would allow either rgb or rgba +colors to be accepted. This can also be combined with 'and' for example +( Usage: omitempty,rgb|rgba) + + Usage: | + +StructOnly + +When a field that is a nested struct is encountered, and contains this flag +any validation on the nested struct will be run, but none of the nested +struct fields will be validated. This is useful if inside of your program +you know the struct will be valid, but need to verify it has been assigned. +NOTE: only "required" and "omitempty" can be used on a struct itself. + + Usage: structonly + +NoStructLevel + +Same as structonly tag except that any struct level validations will not run. + + Usage: nostructlevel + +Omit Empty + +Allows conditional validation, for example if a field is not set with +a value (Determined by the "required" validator) then other validation +such as min or max won't run, but if a value is set validation will run. + + Usage: omitempty + +Dive + +This tells the validator to dive into a slice, array or map and validate that +level of the slice, array or map with the validation tags that follow. +Multidimensional nesting is also supported, each level you wish to dive will +require another dive tag. dive has some sub-tags, 'keys' & 'endkeys', please see +the Keys & EndKeys section just below. + + Usage: dive + +Example #1 + + [][]string with validation tag "gt=0,dive,len=1,dive,required" + // gt=0 will be applied to [] + // len=1 will be applied to []string + // required will be applied to string + +Example #2 + + [][]string with validation tag "gt=0,dive,dive,required" + // gt=0 will be applied to [] + // []string will be spared validation + // required will be applied to string + +Keys & EndKeys + +These are to be used together directly after the dive tag and tells the validator +that anything between 'keys' and 'endkeys' applies to the keys of a map and not the +values; think of it like the 'dive' tag, but for map keys instead of values. +Multidimensional nesting is also supported, each level you wish to validate will +require another 'keys' and 'endkeys' tag. These tags are only valid for maps. + + Usage: dive,keys,othertagvalidation(s),endkeys,valuevalidationtags + +Example #1 + + map[string]string with validation tag "gt=0,dive,keys,eg=1|eq=2,endkeys,required" + // gt=0 will be applied to the map itself + // eg=1|eq=2 will be applied to the map keys + // required will be applied to map values + +Example #2 + + map[[2]string]string with validation tag "gt=0,dive,keys,dive,eq=1|eq=2,endkeys,required" + // gt=0 will be applied to the map itself + // eg=1|eq=2 will be applied to each array element in the the map keys + // required will be applied to map values + +Required + +This validates that the value is not the data types default zero value. +For numbers ensures value is not zero. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required + +Required If + +The field under validation must be present and not empty only if all +the other specified fields are equal to the value following the specified +field. For strings ensures value is not "". For slices, maps, pointers, +interfaces, channels and functions ensures the value is not nil. + + Usage: required_if + +Examples: + + // require the field if the Field1 is equal to the parameter given: + Usage: required_if=Field1 foobar + + // require the field if the Field1 and Field2 is equal to the value respectively: + Usage: required_if=Field1 foo Field2 bar + +Required Unless + +The field under validation must be present and not empty unless all +the other specified fields are equal to the value following the specified +field. For strings ensures value is not "". For slices, maps, pointers, +interfaces, channels and functions ensures the value is not nil. + + Usage: required_unless + +Examples: + + // require the field unless the Field1 is equal to the parameter given: + Usage: required_unless=Field1 foobar + + // require the field unless the Field1 and Field2 is equal to the value respectively: + Usage: required_unless=Field1 foo Field2 bar + +Required With + +The field under validation must be present and not empty only if any +of the other specified fields are present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_with + +Examples: + + // require the field if the Field1 is present: + Usage: required_with=Field1 + + // require the field if the Field1 or Field2 is present: + Usage: required_with=Field1 Field2 + +Required With All + +The field under validation must be present and not empty only if all +of the other specified fields are present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_with_all + +Example: + + // require the field if the Field1 and Field2 is present: + Usage: required_with_all=Field1 Field2 + +Required Without + +The field under validation must be present and not empty only when any +of the other specified fields are not present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_without + +Examples: + + // require the field if the Field1 is not present: + Usage: required_without=Field1 + + // require the field if the Field1 or Field2 is not present: + Usage: required_without=Field1 Field2 + +Required Without All + +The field under validation must be present and not empty only when all +of the other specified fields are not present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_without_all + +Example: + + // require the field if the Field1 and Field2 is not present: + Usage: required_without_all=Field1 Field2 + +Is Default + +This validates that the value is the default value and is almost the +opposite of required. + + Usage: isdefault + +Length + +For numbers, length will ensure that the value is +equal to the parameter given. For strings, it checks that +the string length is exactly that number of characters. For slices, +arrays, and maps, validates the number of items. + +Example #1 + + Usage: len=10 + +Example #2 (time.Duration) + +For time.Duration, len will ensure that the value is equal to the duration given +in the parameter. + + Usage: len=1h30m + +Maximum + +For numbers, max will ensure that the value is +less than or equal to the parameter given. For strings, it checks +that the string length is at most that number of characters. For +slices, arrays, and maps, validates the number of items. + +Example #1 + + Usage: max=10 + +Example #2 (time.Duration) + +For time.Duration, max will ensure that the value is less than or equal to the +duration given in the parameter. + + Usage: max=1h30m + +Minimum + +For numbers, min will ensure that the value is +greater or equal to the parameter given. For strings, it checks that +the string length is at least that number of characters. For slices, +arrays, and maps, validates the number of items. + +Example #1 + + Usage: min=10 + +Example #2 (time.Duration) + +For time.Duration, min will ensure that the value is greater than or equal to +the duration given in the parameter. + + Usage: min=1h30m + +Equals + +For strings & numbers, eq will ensure that the value is +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + +Example #1 + + Usage: eq=10 + +Example #2 (time.Duration) + +For time.Duration, eq will ensure that the value is equal to the duration given +in the parameter. + + Usage: eq=1h30m + +Not Equal + +For strings & numbers, ne will ensure that the value is not +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + +Example #1 + + Usage: ne=10 + +Example #2 (time.Duration) + +For time.Duration, ne will ensure that the value is not equal to the duration +given in the parameter. + + Usage: ne=1h30m + +One Of + +For strings, ints, and uints, oneof will ensure that the value +is one of the values in the parameter. The parameter should be +a list of values separated by whitespace. Values may be +strings or numbers. To match strings with spaces in them, include +the target string between single quotes. + + Usage: oneof=red green + oneof='red green' 'blue yellow' + oneof=5 7 9 + +Greater Than + +For numbers, this will ensure that the value is greater than the +parameter given. For strings, it checks that the string length +is greater than that number of characters. For slices, arrays +and maps it validates the number of items. + +Example #1 + + Usage: gt=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than time.Now.UTC(). + + Usage: gt + +Example #3 (time.Duration) + +For time.Duration, gt will ensure that the value is greater than the duration +given in the parameter. + + Usage: gt=1h30m + +Greater Than or Equal + +Same as 'min' above. Kept both to make terminology with 'len' easier. + +Example #1 + + Usage: gte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than or equal to time.Now.UTC(). + + Usage: gte + +Example #3 (time.Duration) + +For time.Duration, gte will ensure that the value is greater than or equal to +the duration given in the parameter. + + Usage: gte=1h30m + +Less Than + +For numbers, this will ensure that the value is less than the parameter given. +For strings, it checks that the string length is less than that number of +characters. For slices, arrays, and maps it validates the number of items. + +Example #1 + + Usage: lt=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is less than time.Now.UTC(). + + Usage: lt + +Example #3 (time.Duration) + +For time.Duration, lt will ensure that the value is less than the duration given +in the parameter. + + Usage: lt=1h30m + +Less Than or Equal + +Same as 'max' above. Kept both to make terminology with 'len' easier. + +Example #1 + + Usage: lte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is less than or equal to time.Now.UTC(). + + Usage: lte + +Example #3 (time.Duration) + +For time.Duration, lte will ensure that the value is less than or equal to the +duration given in the parameter. + + Usage: lte=1h30m + +Field Equals Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Example #1: + + // Validation on Password field using: + Usage: eqfield=ConfirmPassword + +Example #2: + + // Validating by field: + validate.VarWithValue(password, confirmpassword, "eqfield") + +Field Equals Another Field (relative) + +This does the same as eqfield except that it validates the field provided relative +to the top level struct. + + Usage: eqcsfield=InnerStructField.Field) + +Field Does Not Equal Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Examples: + + // Confirm two colors are not the same: + // + // Validation on Color field: + Usage: nefield=Color2 + + // Validating by field: + validate.VarWithValue(color1, color2, "nefield") + +Field Does Not Equal Another Field (relative) + +This does the same as nefield except that it validates the field provided +relative to the top level struct. + + Usage: necsfield=InnerStructField.Field + +Field Greater Than Another Field + +Only valid for Numbers, time.Duration and time.Time types, this will validate +the field value against another fields value either within a struct or passed in +field. usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtfield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "gtfield") + +Field Greater Than Another Relative Field + +This does the same as gtfield except that it validates the field provided +relative to the top level struct. + + Usage: gtcsfield=InnerStructField.Field + +Field Greater Than or Equal To Another Field + +Only valid for Numbers, time.Duration and time.Time types, this will validate +the field value against another fields value either within a struct or passed in +field. usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtefield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "gtefield") + +Field Greater Than or Equal To Another Relative Field + +This does the same as gtefield except that it validates the field provided relative +to the top level struct. + + Usage: gtecsfield=InnerStructField.Field + +Less Than Another Field + +Only valid for Numbers, time.Duration and time.Time types, this will validate +the field value against another fields value either within a struct or passed in +field. usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltfield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "ltfield") + +Less Than Another Relative Field + +This does the same as ltfield except that it validates the field provided relative +to the top level struct. + + Usage: ltcsfield=InnerStructField.Field + +Less Than or Equal To Another Field + +Only valid for Numbers, time.Duration and time.Time types, this will validate +the field value against another fields value either within a struct or passed in +field. usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltefield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "ltefield") + +Less Than or Equal To Another Relative Field + +This does the same as ltefield except that it validates the field provided relative +to the top level struct. + + Usage: ltecsfield=InnerStructField.Field + +Field Contains Another Field + +This does the same as contains except for struct fields. It should only be used +with string types. See the behavior of reflect.Value.String() for behavior on +other types. + + Usage: containsfield=InnerStructField.Field + +Field Excludes Another Field + +This does the same as excludes except for struct fields. It should only be used +with string types. See the behavior of reflect.Value.String() for behavior on +other types. + + Usage: excludesfield=InnerStructField.Field + +Unique + +For arrays & slices, unique will ensure that there are no duplicates. +For maps, unique will ensure that there are no duplicate values. +For slices of struct, unique will ensure that there are no duplicate values +in a field of the struct specified via a parameter. + + // For arrays, slices, and maps: + Usage: unique + + // For slices of struct: + Usage: unique=field + +Alpha Only + +This validates that a string value contains ASCII alpha characters only + + Usage: alpha + +Alphanumeric + +This validates that a string value contains ASCII alphanumeric characters only + + Usage: alphanum + +Alpha Unicode + +This validates that a string value contains unicode alpha characters only + + Usage: alphaunicode + +Alphanumeric Unicode + +This validates that a string value contains unicode alphanumeric characters only + + Usage: alphanumunicode + +Number + +This validates that a string value contains number values only. +For integers or float it returns true. + + Usage: number + +Numeric + +This validates that a string value contains a basic numeric value. +basic excludes exponents etc... +for integers or float it returns true. + + Usage: numeric + +Hexadecimal String + +This validates that a string value contains a valid hexadecimal. + + Usage: hexadecimal + +Hexcolor String + +This validates that a string value contains a valid hex color including +hashtag (#) + + Usage: hexcolor + +Lowercase String + +This validates that a string value contains only lowercase characters. An empty string is not a valid lowercase string. + + Usage: lowercase + +Uppercase String + +This validates that a string value contains only uppercase characters. An empty string is not a valid uppercase string. + + Usage: uppercase + +RGB String + +This validates that a string value contains a valid rgb color + + Usage: rgb + +RGBA String + +This validates that a string value contains a valid rgba color + + Usage: rgba + +HSL String + +This validates that a string value contains a valid hsl color + + Usage: hsl + +HSLA String + +This validates that a string value contains a valid hsla color + + Usage: hsla + +E.164 Phone Number String + +This validates that a string value contains a valid E.164 Phone number +https://en.wikipedia.org/wiki/E.164 (ex. +1123456789) + + Usage: e164 + +E-mail String + +This validates that a string value contains a valid email +This may not conform to all possibilities of any rfc standard, but neither +does any email provider accept all possibilities. + + Usage: email + +JSON String + +This validates that a string value is valid JSON + + Usage: json + +File path + +This validates that a string value contains a valid file path and that +the file exists on the machine. +This is done using os.Stat, which is a platform independent function. + + Usage: file + +URL String + +This validates that a string value contains a valid url +This will accept any url the golang request uri accepts but must contain +a schema for example http:// or rtmp:// + + Usage: url + +URI String + +This validates that a string value contains a valid uri +This will accept any uri the golang request uri accepts + + Usage: uri + +Urn RFC 2141 String + +This validataes that a string value contains a valid URN +according to the RFC 2141 spec. + + Usage: urn_rfc2141 + +Base64 String + +This validates that a string value contains a valid base64 value. +Although an empty string is valid base64 this will report an empty string +as an error, if you wish to accept an empty string as valid you can use +this with the omitempty tag. + + Usage: base64 + +Base64URL String + +This validates that a string value contains a valid base64 URL safe value +according the the RFC4648 spec. +Although an empty string is a valid base64 URL safe value, this will report +an empty string as an error, if you wish to accept an empty string as valid +you can use this with the omitempty tag. + + Usage: base64url + +Bitcoin Address + +This validates that a string value contains a valid bitcoin address. +The format of the string is checked to ensure it matches one of the three formats +P2PKH, P2SH and performs checksum validation. + + Usage: btc_addr + +Bitcoin Bech32 Address (segwit) + +This validates that a string value contains a valid bitcoin Bech32 address as defined +by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) +Special thanks to Pieter Wuille for providng reference implementations. + + Usage: btc_addr_bech32 + +Ethereum Address + +This validates that a string value contains a valid ethereum address. +The format of the string is checked to ensure it matches the standard Ethereum address format. + + Usage: eth_addr + +Contains + +This validates that a string value contains the substring value. + + Usage: contains=@ + +Contains Any + +This validates that a string value contains any Unicode code points +in the substring value. + + Usage: containsany=!@#? + +Contains Rune + +This validates that a string value contains the supplied rune value. + + Usage: containsrune=@ + +Excludes + +This validates that a string value does not contain the substring value. + + Usage: excludes=@ + +Excludes All + +This validates that a string value does not contain any Unicode code +points in the substring value. + + Usage: excludesall=!@#? + +Excludes Rune + +This validates that a string value does not contain the supplied rune value. + + Usage: excludesrune=@ + +Starts With + +This validates that a string value starts with the supplied string value + + Usage: startswith=hello + +Ends With + +This validates that a string value ends with the supplied string value + + Usage: endswith=goodbye + +Does Not Start With + +This validates that a string value does not start with the supplied string value + + Usage: startsnotwith=hello + +Does Not End With + +This validates that a string value does not end with the supplied string value + + Usage: endsnotwith=goodbye + +International Standard Book Number + +This validates that a string value contains a valid isbn10 or isbn13 value. + + Usage: isbn + +International Standard Book Number 10 + +This validates that a string value contains a valid isbn10 value. + + Usage: isbn10 + +International Standard Book Number 13 + +This validates that a string value contains a valid isbn13 value. + + Usage: isbn13 + +Universally Unique Identifier UUID + +This validates that a string value contains a valid UUID. Uppercase UUID values will not pass - use `uuid_rfc4122` instead. + + Usage: uuid + +Universally Unique Identifier UUID v3 + +This validates that a string value contains a valid version 3 UUID. Uppercase UUID values will not pass - use `uuid3_rfc4122` instead. + + Usage: uuid3 + +Universally Unique Identifier UUID v4 + +This validates that a string value contains a valid version 4 UUID. Uppercase UUID values will not pass - use `uuid4_rfc4122` instead. + + Usage: uuid4 + +Universally Unique Identifier UUID v5 + +This validates that a string value contains a valid version 5 UUID. Uppercase UUID values will not pass - use `uuid5_rfc4122` instead. + + Usage: uuid5 + +ASCII + +This validates that a string value contains only ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: ascii + +Printable ASCII + +This validates that a string value contains only printable ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: printascii + +Multi-Byte Characters + +This validates that a string value contains one or more multibyte characters. +NOTE: if the string is blank, this validates as true. + + Usage: multibyte + +Data URL + +This validates that a string value contains a valid DataURI. +NOTE: this will also validate that the data portion is valid base64 + + Usage: datauri + +Latitude + +This validates that a string value contains a valid latitude. + + Usage: latitude + +Longitude + +This validates that a string value contains a valid longitude. + + Usage: longitude + +Social Security Number SSN + +This validates that a string value contains a valid U.S. Social Security Number. + + Usage: ssn + +Internet Protocol Address IP + +This validates that a string value contains a valid IP Address. + + Usage: ip + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid v4 IP Address. + + Usage: ipv4 + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid v6 IP Address. + + Usage: ipv6 + +Classless Inter-Domain Routing CIDR + +This validates that a string value contains a valid CIDR Address. + + Usage: cidr + +Classless Inter-Domain Routing CIDRv4 + +This validates that a string value contains a valid v4 CIDR Address. + + Usage: cidrv4 + +Classless Inter-Domain Routing CIDRv6 + +This validates that a string value contains a valid v6 CIDR Address. + + Usage: cidrv6 + +Transmission Control Protocol Address TCP + +This validates that a string value contains a valid resolvable TCP Address. + + Usage: tcp_addr + +Transmission Control Protocol Address TCPv4 + +This validates that a string value contains a valid resolvable v4 TCP Address. + + Usage: tcp4_addr + +Transmission Control Protocol Address TCPv6 + +This validates that a string value contains a valid resolvable v6 TCP Address. + + Usage: tcp6_addr + +User Datagram Protocol Address UDP + +This validates that a string value contains a valid resolvable UDP Address. + + Usage: udp_addr + +User Datagram Protocol Address UDPv4 + +This validates that a string value contains a valid resolvable v4 UDP Address. + + Usage: udp4_addr + +User Datagram Protocol Address UDPv6 + +This validates that a string value contains a valid resolvable v6 UDP Address. + + Usage: udp6_addr + +Internet Protocol Address IP + +This validates that a string value contains a valid resolvable IP Address. + + Usage: ip_addr + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid resolvable v4 IP Address. + + Usage: ip4_addr + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid resolvable v6 IP Address. + + Usage: ip6_addr + +Unix domain socket end point Address + +This validates that a string value contains a valid Unix Address. + + Usage: unix_addr + +Media Access Control Address MAC + +This validates that a string value contains a valid MAC Address. + + Usage: mac + +Note: See Go's ParseMAC for accepted formats and types: + + http://golang.org/src/net/mac.go?s=866:918#L29 + +Hostname RFC 952 + +This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 + + Usage: hostname + +Hostname RFC 1123 + +This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 + + Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias. + +Full Qualified Domain Name (FQDN) + +This validates that a string value contains a valid FQDN. + + Usage: fqdn + +HTML Tags + +This validates that a string value appears to be an HTML element tag +including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element + + Usage: html + +HTML Encoded + +This validates that a string value is a proper character reference in decimal +or hexadecimal format + + Usage: html_encoded + +URL Encoded + +This validates that a string value is percent-encoded (URL encoded) according +to https://tools.ietf.org/html/rfc3986#section-2.1 + + Usage: url_encoded + +Directory + +This validates that a string value contains a valid directory and that +it exists on the machine. +This is done using os.Stat, which is a platform independent function. + + Usage: dir + +HostPort + +This validates that a string value contains a valid DNS hostname and port that +can be used to valiate fields typically passed to sockets and connections. + + Usage: hostname_port + +Datetime + +This validates that a string value is a valid datetime based on the supplied datetime format. +Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/ + + Usage: datetime=2006-01-02 + +Iso3166-1 alpha-2 + +This validates that a string value is a valid country code based on iso3166-1 alpha-2 standard. +see: https://www.iso.org/iso-3166-country-codes.html + + Usage: iso3166_1_alpha2 + +Iso3166-1 alpha-3 + +This validates that a string value is a valid country code based on iso3166-1 alpha-3 standard. +see: https://www.iso.org/iso-3166-country-codes.html + + Usage: iso3166_1_alpha3 + +Iso3166-1 alpha-numeric + +This validates that a string value is a valid country code based on iso3166-1 alpha-numeric standard. +see: https://www.iso.org/iso-3166-country-codes.html + + Usage: iso3166_1_alpha3 + +TimeZone + +This validates that a string value is a valid time zone based on the time zone database present on the system. +Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator. +More information on https://golang.org/pkg/time/#LoadLocation + + Usage: timezone + + +Alias Validators and Tags + +NOTE: When returning an error, the tag returned in "FieldError" will be +the alias tag unless the dive tag is part of the alias. Everything after the +dive tag is not reported as the alias tag. Also, the "ActualTag" in the before +case will be the actual tag within the alias that failed. + +Here is a list of the current built in alias tags: + + "iscolor" + alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor) + "country_code" + alias is "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric" (Usage: country_code) + +Validator notes: + + regex + a regex validator won't be added because commas and = signs can be part + of a regex which conflict with the validation definitions. Although + workarounds can be made, they take away from using pure regex's. + Furthermore it's quick and dirty but the regex's become harder to + maintain and are not reusable, so it's as much a programming philosophy + as anything. + + In place of this new validator functions should be created; a regex can + be used within the validator function and even be precompiled for better + efficiency within regexes.go. + + And the best reason, you can submit a pull request and we can keep on + adding to the validation library of this package! + +Non standard validators + +A collection of validation rules that are frequently needed but are more +complex than the ones found in the baked in validators. +A non standard validator must be registered manually like you would +with your own custom validation functions. + +Example of registration and use: + + type Test struct { + TestField string `validate:"yourtag"` + } + + t := &Test{ + TestField: "Test" + } + + validate := validator.New() + validate.RegisterValidation("yourtag", validators.NotBlank) + +Here is a list of the current non standard validators: + + NotBlank + This validates that the value is not blank or with length zero. + For strings ensures they do not contain only spaces. For channels, maps, slices and arrays + ensures they don't have zero length. For others, a non empty value is required. + + Usage: notblank + +Panics + +This package panics when bad input is provided, this is by design, bad code like +that should not make it to production. + + type Test struct { + TestField string `validate:"nonexistantfunction=1"` + } + + t := &Test{ + TestField: "Test" + } + + validate.Struct(t) // this will panic +*/ +package validator diff --git a/vendor/github.com/go-playground/validator/v10/errors.go b/vendor/github.com/go-playground/validator/v10/errors.go new file mode 100644 index 0000000..63293cf --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/errors.go @@ -0,0 +1,275 @@ +package validator + +import ( + "bytes" + "fmt" + "reflect" + "strings" + + ut "github.com/go-playground/universal-translator" +) + +const ( + fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" +) + +// ValidationErrorsTranslations is the translation return type +type ValidationErrorsTranslations map[string]string + +// InvalidValidationError describes an invalid argument passed to +// `Struct`, `StructExcept`, StructPartial` or `Field` +type InvalidValidationError struct { + Type reflect.Type +} + +// Error returns InvalidValidationError message +func (e *InvalidValidationError) Error() string { + + if e.Type == nil { + return "validator: (nil)" + } + + return "validator: (nil " + e.Type.String() + ")" +} + +// ValidationErrors is an array of FieldError's +// for use in custom error messages post validation. +type ValidationErrors []FieldError + +// Error is intended for use in development + debugging and not intended to be a production error message. +// It allows ValidationErrors to subscribe to the Error interface. +// All information to create an error message specific to your application is contained within +// the FieldError found within the ValidationErrors array +func (ve ValidationErrors) Error() string { + + buff := bytes.NewBufferString("") + + var fe *fieldError + + for i := 0; i < len(ve); i++ { + + fe = ve[i].(*fieldError) + buff.WriteString(fe.Error()) + buff.WriteString("\n") + } + + return strings.TrimSpace(buff.String()) +} + +// Translate translates all of the ValidationErrors +func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations { + + trans := make(ValidationErrorsTranslations) + + var fe *fieldError + + for i := 0; i < len(ve); i++ { + fe = ve[i].(*fieldError) + + // // in case an Anonymous struct was used, ensure that the key + // // would be 'Username' instead of ".Username" + // if len(fe.ns) > 0 && fe.ns[:1] == "." { + // trans[fe.ns[1:]] = fe.Translate(ut) + // continue + // } + + trans[fe.ns] = fe.Translate(ut) + } + + return trans +} + +// FieldError contains all functions to get error details +type FieldError interface { + + // returns the validation tag that failed. if the + // validation was an alias, this will return the + // alias name and not the underlying tag that failed. + // + // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" + // will return "iscolor" + Tag() string + + // returns the validation tag that failed, even if an + // alias the actual tag within the alias will be returned. + // If an 'or' validation fails the entire or will be returned. + // + // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" + // will return "hexcolor|rgb|rgba|hsl|hsla" + ActualTag() string + + // returns the namespace for the field error, with the tag + // name taking precedence over the field's actual name. + // + // eg. JSON name "User.fname" + // + // See StructNamespace() for a version that returns actual names. + // + // NOTE: this field can be blank when validating a single primitive field + // using validate.Field(...) as there is no way to extract it's name + Namespace() string + + // returns the namespace for the field error, with the field's + // actual name. + // + // eq. "User.FirstName" see Namespace for comparison + // + // NOTE: this field can be blank when validating a single primitive field + // using validate.Field(...) as there is no way to extract its name + StructNamespace() string + + // returns the fields name with the tag name taking precedence over the + // field's actual name. + // + // eq. JSON name "fname" + // see StructField for comparison + Field() string + + // returns the field's actual name from the struct, when able to determine. + // + // eq. "FirstName" + // see Field for comparison + StructField() string + + // returns the actual field's value in case needed for creating the error + // message + Value() interface{} + + // returns the param value, in string form for comparison; this will also + // help with generating an error message + Param() string + + // Kind returns the Field's reflect Kind + // + // eg. time.Time's kind is a struct + Kind() reflect.Kind + + // Type returns the Field's reflect Type + // + // // eg. time.Time's type is time.Time + Type() reflect.Type + + // returns the FieldError's translated error + // from the provided 'ut.Translator' and registered 'TranslationFunc' + // + // NOTE: if no registered translator can be found it returns the same as + // calling fe.Error() + Translate(ut ut.Translator) string + + // Error returns the FieldError's message + Error() string +} + +// compile time interface checks +var _ FieldError = new(fieldError) +var _ error = new(fieldError) + +// fieldError contains a single field's validation error along +// with other properties that may be needed for error message creation +// it complies with the FieldError interface +type fieldError struct { + v *Validate + tag string + actualTag string + ns string + structNs string + fieldLen uint8 + structfieldLen uint8 + value interface{} + param string + kind reflect.Kind + typ reflect.Type +} + +// Tag returns the validation tag that failed. +func (fe *fieldError) Tag() string { + return fe.tag +} + +// ActualTag returns the validation tag that failed, even if an +// alias the actual tag within the alias will be returned. +func (fe *fieldError) ActualTag() string { + return fe.actualTag +} + +// Namespace returns the namespace for the field error, with the tag +// name taking precedence over the field's actual name. +func (fe *fieldError) Namespace() string { + return fe.ns +} + +// StructNamespace returns the namespace for the field error, with the field's +// actual name. +func (fe *fieldError) StructNamespace() string { + return fe.structNs +} + +// Field returns the field's name with the tag name taking precedence over the +// field's actual name. +func (fe *fieldError) Field() string { + + return fe.ns[len(fe.ns)-int(fe.fieldLen):] + // // return fe.field + // fld := fe.ns[len(fe.ns)-int(fe.fieldLen):] + + // log.Println("FLD:", fld) + + // if len(fld) > 0 && fld[:1] == "." { + // return fld[1:] + // } + + // return fld +} + +// returns the field's actual name from the struct, when able to determine. +func (fe *fieldError) StructField() string { + // return fe.structField + return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):] +} + +// Value returns the actual field's value in case needed for creating the error +// message +func (fe *fieldError) Value() interface{} { + return fe.value +} + +// Param returns the param value, in string form for comparison; this will +// also help with generating an error message +func (fe *fieldError) Param() string { + return fe.param +} + +// Kind returns the Field's reflect Kind +func (fe *fieldError) Kind() reflect.Kind { + return fe.kind +} + +// Type returns the Field's reflect Type +func (fe *fieldError) Type() reflect.Type { + return fe.typ +} + +// Error returns the fieldError's error message +func (fe *fieldError) Error() string { + return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag) +} + +// Translate returns the FieldError's translated error +// from the provided 'ut.Translator' and registered 'TranslationFunc' +// +// NOTE: if no registered translation can be found, it returns the original +// untranslated error message. +func (fe *fieldError) Translate(ut ut.Translator) string { + + m, ok := fe.v.transTagFunc[ut] + if !ok { + return fe.Error() + } + + fn, ok := m[fe.tag] + if !ok { + return fe.Error() + } + + return fn(ut, fe) +} diff --git a/vendor/github.com/go-playground/validator/v10/field_level.go b/vendor/github.com/go-playground/validator/v10/field_level.go new file mode 100644 index 0000000..f0e2a9a --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/field_level.go @@ -0,0 +1,119 @@ +package validator + +import "reflect" + +// FieldLevel contains all the information and helper functions +// to validate a field +type FieldLevel interface { + // returns the top level struct, if any + Top() reflect.Value + + // returns the current fields parent struct, if any or + // the comparison value if called 'VarWithValue' + Parent() reflect.Value + + // returns current field for validation + Field() reflect.Value + + // returns the field's name with the tag + // name taking precedence over the fields actual name. + FieldName() string + + // returns the struct field's name + StructFieldName() string + + // returns param for validation against current field + Param() string + + // GetTag returns the current validations tag name + GetTag() string + + // ExtractType gets the actual underlying type of field value. + // It will dive into pointers, customTypes and return you the + // underlying value and it's kind. + ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) + + // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // in the param and returns the field, field kind and whether is was successful in retrieving + // the field at all. + // + // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field + // could not be retrieved because it didn't exist. + // + // Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable. + GetStructFieldOK() (reflect.Value, reflect.Kind, bool) + + // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for + // the field and namespace allowing more extensibility for validators. + // + // Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable. + GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) + + // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving + // the field at all. + // + // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field + // could not be retrieved because it didn't exist. + GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) + + // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for + // the field and namespace allowing more extensibility for validators. + GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) +} + +var _ FieldLevel = new(validate) + +// Field returns current field for validation +func (v *validate) Field() reflect.Value { + return v.flField +} + +// FieldName returns the field's name with the tag +// name taking precedence over the fields actual name. +func (v *validate) FieldName() string { + return v.cf.altName +} + +// GetTag returns the current validations tag name +func (v *validate) GetTag() string { + return v.ct.tag +} + +// StructFieldName returns the struct field's name +func (v *validate) StructFieldName() string { + return v.cf.name +} + +// Param returns param for validation against current field +func (v *validate) Param() string { + return v.ct.param +} + +// GetStructFieldOK returns Param returns param for validation against current field +// +// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable. +func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { + current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param) + return current, kind, found +} + +// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for +// the field and namespace allowing more extensibility for validators. +// +// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable. +func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { + current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace) + return current, kind, found +} + +// GetStructFieldOK returns Param returns param for validation against current field +func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) { + return v.getStructFieldOKInternal(v.slflParent, v.ct.param) +} + +// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for +// the field and namespace allowing more extensibility for validators. +func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) { + return v.getStructFieldOKInternal(val, namespace) +} diff --git a/vendor/github.com/go-playground/validator/v10/go.mod b/vendor/github.com/go-playground/validator/v10/go.mod new file mode 100644 index 0000000..d457100 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/go.mod @@ -0,0 +1,11 @@ +module github.com/go-playground/validator/v10 + +go 1.13 + +require ( + github.com/go-playground/assert/v2 v2.0.1 + github.com/go-playground/locales v0.13.0 + github.com/go-playground/universal-translator v0.17.0 + github.com/leodido/go-urn v1.2.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 +) diff --git a/vendor/github.com/go-playground/validator/v10/go.sum b/vendor/github.com/go-playground/validator/v10/go.sum new file mode 100644 index 0000000..0152642 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/go.sum @@ -0,0 +1,28 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-playground/validator/v10/logo.png b/vendor/github.com/go-playground/validator/v10/logo.png new file mode 100644 index 0000000..355000f Binary files /dev/null and b/vendor/github.com/go-playground/validator/v10/logo.png differ diff --git a/vendor/github.com/go-playground/validator/v10/regexes.go b/vendor/github.com/go-playground/validator/v10/regexes.go new file mode 100644 index 0000000..b741f4e --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/regexes.go @@ -0,0 +1,101 @@ +package validator + +import "regexp" + +const ( + alphaRegexString = "^[a-zA-Z]+$" + alphaNumericRegexString = "^[a-zA-Z0-9]+$" + alphaUnicodeRegexString = "^[\\p{L}]+$" + alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$" + numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" + numberRegexString = "^[0-9]+$" + hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$" + hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" + rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" + hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" + hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" + emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" + base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" + base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" + iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" + iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" + uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" + uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + aSCIIRegexString = "^[\x00-\x7F]*$" + printableASCIIRegexString = "^[\x20-\x7E]*$" + multibyteRegexString = "[^\x00-\x7F]" + dataURIRegexString = `^data:((?:\w+\/(?:([^;]|;[^;]).)+)?)` + latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$` + hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952 + hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 + fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.') + btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address + btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + ethAddressRegexString = `^0x[0-9a-fA-F]{40}$` + ethAddressUpperRegexString = `^0x[0-9A-F]{40}$` + ethAddressLowerRegexString = `^0x[0-9a-f]{40}$` + uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})` + hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?` + hTMLRegexString = `<[/]?([a-zA-Z]+).*?>` + splitParamsRegexString = `'[^']*'|\S+` +) + +var ( + alphaRegex = regexp.MustCompile(alphaRegexString) + alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) + alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString) + alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString) + numericRegex = regexp.MustCompile(numericRegexString) + numberRegex = regexp.MustCompile(numberRegexString) + hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) + hexcolorRegex = regexp.MustCompile(hexcolorRegexString) + rgbRegex = regexp.MustCompile(rgbRegexString) + rgbaRegex = regexp.MustCompile(rgbaRegexString) + hslRegex = regexp.MustCompile(hslRegexString) + hslaRegex = regexp.MustCompile(hslaRegexString) + e164Regex = regexp.MustCompile(e164RegexString) + emailRegex = regexp.MustCompile(emailRegexString) + base64Regex = regexp.MustCompile(base64RegexString) + base64URLRegex = regexp.MustCompile(base64URLRegexString) + iSBN10Regex = regexp.MustCompile(iSBN10RegexString) + iSBN13Regex = regexp.MustCompile(iSBN13RegexString) + uUID3Regex = regexp.MustCompile(uUID3RegexString) + uUID4Regex = regexp.MustCompile(uUID4RegexString) + uUID5Regex = regexp.MustCompile(uUID5RegexString) + uUIDRegex = regexp.MustCompile(uUIDRegexString) + uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString) + uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString) + uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString) + uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString) + aSCIIRegex = regexp.MustCompile(aSCIIRegexString) + printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) + multibyteRegex = regexp.MustCompile(multibyteRegexString) + dataURIRegex = regexp.MustCompile(dataURIRegexString) + latitudeRegex = regexp.MustCompile(latitudeRegexString) + longitudeRegex = regexp.MustCompile(longitudeRegexString) + sSNRegex = regexp.MustCompile(sSNRegexString) + hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952) + hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123) + fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123) + btcAddressRegex = regexp.MustCompile(btcAddressRegexString) + btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32) + btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32) + ethAddressRegex = regexp.MustCompile(ethAddressRegexString) + ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString) + ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString) + uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) + hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) + hTMLRegex = regexp.MustCompile(hTMLRegexString) + splitParamsRegex = regexp.MustCompile(splitParamsRegexString) +) diff --git a/vendor/github.com/go-playground/validator/v10/struct_level.go b/vendor/github.com/go-playground/validator/v10/struct_level.go new file mode 100644 index 0000000..57691ee --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/struct_level.go @@ -0,0 +1,175 @@ +package validator + +import ( + "context" + "reflect" +) + +// StructLevelFunc accepts all values needed for struct level validation +type StructLevelFunc func(sl StructLevel) + +// StructLevelFuncCtx accepts all values needed for struct level validation +// but also allows passing of contextual validation information via context.Context. +type StructLevelFuncCtx func(ctx context.Context, sl StructLevel) + +// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx +func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx { + return func(ctx context.Context, sl StructLevel) { + fn(sl) + } +} + +// StructLevel contains all the information and helper functions +// to validate a struct +type StructLevel interface { + + // returns the main validation object, in case one wants to call validations internally. + // this is so you don't have to use anonymous functions to get access to the validate + // instance. + Validator() *Validate + + // returns the top level struct, if any + Top() reflect.Value + + // returns the current fields parent struct, if any + Parent() reflect.Value + + // returns the current struct. + Current() reflect.Value + + // ExtractType gets the actual underlying type of field value. + // It will dive into pointers, customTypes and return you the + // underlying value and its kind. + ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) + + // reports an error just by passing the field and tag information + // + // NOTES: + // + // fieldName and altName get appended to the existing namespace that + // validator is on. e.g. pass 'FirstName' or 'Names[0]' depending + // on the nesting + // + // tag can be an existing validation tag or just something you make up + // and process on the flip side it's up to you. + ReportError(field interface{}, fieldName, structFieldName string, tag, param string) + + // reports an error just by passing ValidationErrors + // + // NOTES: + // + // relativeNamespace and relativeActualNamespace get appended to the + // existing namespace that validator is on. + // e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending + // on the nesting. most of the time they will be blank, unless you validate + // at a level lower the the current field depth + ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors) +} + +var _ StructLevel = new(validate) + +// Top returns the top level struct +// +// NOTE: this can be the same as the current struct being validated +// if not is a nested struct. +// +// this is only called when within Struct and Field Level validation and +// should not be relied upon for an acurate value otherwise. +func (v *validate) Top() reflect.Value { + return v.top +} + +// Parent returns the current structs parent +// +// NOTE: this can be the same as the current struct being validated +// if not is a nested struct. +// +// this is only called when within Struct and Field Level validation and +// should not be relied upon for an acurate value otherwise. +func (v *validate) Parent() reflect.Value { + return v.slflParent +} + +// Current returns the current struct. +func (v *validate) Current() reflect.Value { + return v.slCurrent +} + +// Validator returns the main validation object, in case one want to call validations internally. +func (v *validate) Validator() *Validate { + return v.v +} + +// ExtractType gets the actual underlying type of field value. +func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) { + return v.extractTypeInternal(field, false) +} + +// ReportError reports an error just by passing the field and tag information +func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) { + + fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false) + + if len(structFieldName) == 0 { + structFieldName = fieldName + } + + v.str1 = string(append(v.ns, fieldName...)) + + if v.v.hasTagNameFunc || fieldName != structFieldName { + v.str2 = string(append(v.actualNs, structFieldName...)) + } else { + v.str2 = v.str1 + } + + if kind == reflect.Invalid { + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: tag, + actualTag: tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(fieldName)), + structfieldLen: uint8(len(structFieldName)), + param: param, + kind: kind, + }, + ) + return + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: tag, + actualTag: tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(fieldName)), + structfieldLen: uint8(len(structFieldName)), + value: fv.Interface(), + param: param, + kind: kind, + typ: fv.Type(), + }, + ) +} + +// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation. +// +// NOTE: this function prepends the current namespace to the relative ones. +func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) { + + var err *fieldError + + for i := 0; i < len(errs); i++ { + + err = errs[i].(*fieldError) + err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...)) + err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...)) + + v.errs = append(v.errs, err) + } +} diff --git a/vendor/github.com/go-playground/validator/v10/translations.go b/vendor/github.com/go-playground/validator/v10/translations.go new file mode 100644 index 0000000..4d9d75c --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/translations.go @@ -0,0 +1,11 @@ +package validator + +import ut "github.com/go-playground/universal-translator" + +// TranslationFunc is the function type used to register or override +// custom translations +type TranslationFunc func(ut ut.Translator, fe FieldError) string + +// RegisterTranslationsFunc allows for registering of translations +// for a 'ut.Translator' for use within the 'TranslationFunc' +type RegisterTranslationsFunc func(ut ut.Translator) error diff --git a/vendor/github.com/go-playground/validator/v10/util.go b/vendor/github.com/go-playground/validator/v10/util.go new file mode 100644 index 0000000..56420f4 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/util.go @@ -0,0 +1,288 @@ +package validator + +import ( + "reflect" + "strconv" + "strings" + "time" +) + +// extractTypeInternal gets the actual underlying type of field value. +// It will dive into pointers, customTypes and return you the +// underlying value and it's kind. +func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { + +BEGIN: + switch current.Kind() { + case reflect.Ptr: + + nullable = true + + if current.IsNil() { + return current, reflect.Ptr, nullable + } + + current = current.Elem() + goto BEGIN + + case reflect.Interface: + + nullable = true + + if current.IsNil() { + return current, reflect.Interface, nullable + } + + current = current.Elem() + goto BEGIN + + case reflect.Invalid: + return current, reflect.Invalid, nullable + + default: + + if v.v.hasCustomFuncs { + + if fn, ok := v.v.customFuncs[current.Type()]; ok { + current = reflect.ValueOf(fn(current)) + goto BEGIN + } + } + + return current, current.Kind(), nullable + } +} + +// getStructFieldOKInternal traverses a struct to retrieve a specific field denoted by the provided namespace and +// returns the field, field kind and whether is was successful in retrieving the field at all. +// +// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field +// could not be retrieved because it didn't exist. +func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) { + +BEGIN: + current, kind, nullable = v.ExtractType(val) + if kind == reflect.Invalid { + return + } + + if namespace == "" { + found = true + return + } + + switch kind { + + case reflect.Ptr, reflect.Interface: + return + + case reflect.Struct: + + typ := current.Type() + fld := namespace + var ns string + + if typ != timeType { + + idx := strings.Index(namespace, namespaceSeparator) + + if idx != -1 { + fld = namespace[:idx] + ns = namespace[idx+1:] + } else { + ns = "" + } + + bracketIdx := strings.Index(fld, leftBracket) + if bracketIdx != -1 { + fld = fld[:bracketIdx] + + ns = namespace[bracketIdx:] + } + + val = current.FieldByName(fld) + namespace = ns + goto BEGIN + } + + case reflect.Array, reflect.Slice: + idx := strings.Index(namespace, leftBracket) + idx2 := strings.Index(namespace, rightBracket) + + arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) + + if arrIdx >= current.Len() { + return + } + + startIdx := idx2 + 1 + + if startIdx < len(namespace) { + if namespace[startIdx:startIdx+1] == namespaceSeparator { + startIdx++ + } + } + + val = current.Index(arrIdx) + namespace = namespace[startIdx:] + goto BEGIN + + case reflect.Map: + idx := strings.Index(namespace, leftBracket) + 1 + idx2 := strings.Index(namespace, rightBracket) + + endIdx := idx2 + + if endIdx+1 < len(namespace) { + if namespace[endIdx+1:endIdx+2] == namespaceSeparator { + endIdx++ + } + } + + key := namespace[idx:idx2] + + switch current.Type().Key().Kind() { + case reflect.Int: + i, _ := strconv.Atoi(key) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Int8: + i, _ := strconv.ParseInt(key, 10, 8) + val = current.MapIndex(reflect.ValueOf(int8(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int16: + i, _ := strconv.ParseInt(key, 10, 16) + val = current.MapIndex(reflect.ValueOf(int16(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int32: + i, _ := strconv.ParseInt(key, 10, 32) + val = current.MapIndex(reflect.ValueOf(int32(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int64: + i, _ := strconv.ParseInt(key, 10, 64) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Uint: + i, _ := strconv.ParseUint(key, 10, 0) + val = current.MapIndex(reflect.ValueOf(uint(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint8: + i, _ := strconv.ParseUint(key, 10, 8) + val = current.MapIndex(reflect.ValueOf(uint8(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint16: + i, _ := strconv.ParseUint(key, 10, 16) + val = current.MapIndex(reflect.ValueOf(uint16(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint32: + i, _ := strconv.ParseUint(key, 10, 32) + val = current.MapIndex(reflect.ValueOf(uint32(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint64: + i, _ := strconv.ParseUint(key, 10, 64) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Float32: + f, _ := strconv.ParseFloat(key, 32) + val = current.MapIndex(reflect.ValueOf(float32(f))) + namespace = namespace[endIdx+1:] + + case reflect.Float64: + f, _ := strconv.ParseFloat(key, 64) + val = current.MapIndex(reflect.ValueOf(f)) + namespace = namespace[endIdx+1:] + + case reflect.Bool: + b, _ := strconv.ParseBool(key) + val = current.MapIndex(reflect.ValueOf(b)) + namespace = namespace[endIdx+1:] + + // reflect.Type = string + default: + val = current.MapIndex(reflect.ValueOf(key)) + namespace = namespace[endIdx+1:] + } + + goto BEGIN + } + + // if got here there was more namespace, cannot go any deeper + panic("Invalid field namespace") +} + +// asInt returns the parameter as a int64 +// or panics if it can't convert +func asInt(param string) int64 { + i, err := strconv.ParseInt(param, 0, 64) + panicIf(err) + + return i +} + +// asIntFromTimeDuration parses param as time.Duration and returns it as int64 +// or panics on error. +func asIntFromTimeDuration(param string) int64 { + d, err := time.ParseDuration(param) + if err != nil { + // attempt parsing as an an integer assuming nanosecond precision + return asInt(param) + } + return int64(d) +} + +// asIntFromType calls the proper function to parse param as int64, +// given a field's Type t. +func asIntFromType(t reflect.Type, param string) int64 { + switch t { + case timeDurationType: + return asIntFromTimeDuration(param) + default: + return asInt(param) + } +} + +// asUint returns the parameter as a uint64 +// or panics if it can't convert +func asUint(param string) uint64 { + + i, err := strconv.ParseUint(param, 0, 64) + panicIf(err) + + return i +} + +// asFloat returns the parameter as a float64 +// or panics if it can't convert +func asFloat(param string) float64 { + + i, err := strconv.ParseFloat(param, 64) + panicIf(err) + + return i +} + +// asBool returns the parameter as a bool +// or panics if it can't convert +func asBool(param string) bool { + + i, err := strconv.ParseBool(param) + panicIf(err) + + return i +} + +func panicIf(err error) { + if err != nil { + panic(err.Error()) + } +} diff --git a/vendor/github.com/go-playground/validator/v10/validator.go b/vendor/github.com/go-playground/validator/v10/validator.go new file mode 100644 index 0000000..f097f39 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/validator.go @@ -0,0 +1,477 @@ +package validator + +import ( + "context" + "fmt" + "reflect" + "strconv" +) + +// per validate construct +type validate struct { + v *Validate + top reflect.Value + ns []byte + actualNs []byte + errs ValidationErrors + includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise + ffn FilterFunc + slflParent reflect.Value // StructLevel & FieldLevel + slCurrent reflect.Value // StructLevel & FieldLevel + flField reflect.Value // StructLevel & FieldLevel + cf *cField // StructLevel & FieldLevel + ct *cTag // StructLevel & FieldLevel + misc []byte // misc reusable + str1 string // misc reusable + str2 string // misc reusable + fldIsPointer bool // StructLevel & FieldLevel + isPartial bool + hasExcludes bool +} + +// parent and current will be the same the first run of validateStruct +func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) { + + cs, ok := v.v.structCache.Get(typ) + if !ok { + cs = v.v.extractStructCache(current, typ.Name()) + } + + if len(ns) == 0 && len(cs.name) != 0 { + + ns = append(ns, cs.name...) + ns = append(ns, '.') + + structNs = append(structNs, cs.name...) + structNs = append(structNs, '.') + } + + // ct is nil on top level struct, and structs as fields that have no tag info + // so if nil or if not nil and the structonly tag isn't present + if ct == nil || ct.typeof != typeStructOnly { + + var f *cField + + for i := 0; i < len(cs.fields); i++ { + + f = cs.fields[i] + + if v.isPartial { + + if v.ffn != nil { + // used with StructFiltered + if v.ffn(append(structNs, f.name...)) { + continue + } + + } else { + // used with StructPartial & StructExcept + _, ok = v.includeExclude[string(append(structNs, f.name...))] + + if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) { + continue + } + } + } + + v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags) + } + } + + // check if any struct level validations, after all field validations already checked. + // first iteration will have no info about nostructlevel tag, and is checked prior to + // calling the next iteration of validateStruct called from traverseField. + if cs.fn != nil { + + v.slflParent = parent + v.slCurrent = current + v.ns = ns + v.actualNs = structNs + + cs.fn(ctx, v) + } +} + +// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options +func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { + var typ reflect.Type + var kind reflect.Kind + + current, kind, v.fldIsPointer = v.extractTypeInternal(current, false) + + switch kind { + case reflect.Ptr, reflect.Interface, reflect.Invalid: + + if ct == nil { + return + } + + if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault { + return + } + + if ct.hasTag { + if kind == reflect.Invalid { + v.str1 = string(append(ns, cf.altName...)) + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + param: ct.param, + kind: kind, + }, + ) + return + } + + v.str1 = string(append(ns, cf.altName...)) + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + if !ct.runValidationWhenNil { + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: current.Type(), + }, + ) + return + } + } + + case reflect.Struct: + + typ = current.Type() + + if typ != timeType { + + if ct != nil { + + if ct.typeof == typeStructOnly { + goto CONTINUE + } else if ct.typeof == typeIsDefault { + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !ct.fn(ctx, v) { + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + return + } + } + + ct = ct.next + } + + if ct != nil && ct.typeof == typeNoStructLevel { + return + } + + CONTINUE: + // if len == 0 then validating using 'Var' or 'VarWithValue' + // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm... + // VarWithField - this allows for validating against each field within the struct against a specific value + // pretty handy in certain situations + if len(cf.name) > 0 { + ns = append(append(ns, cf.altName...), '.') + structNs = append(append(structNs, cf.name...), '.') + } + + v.validateStruct(ctx, current, current, typ, ns, structNs, ct) + return + } + } + + if !ct.hasTag { + return + } + + typ = current.Type() + +OUTER: + for { + if ct == nil { + return + } + + switch ct.typeof { + + case typeOmitEmpty: + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !hasValue(v) { + return + } + + ct = ct.next + continue + + case typeEndKeys: + return + + case typeDive: + + ct = ct.next + + // traverse slice or map here + // or panic ;) + switch kind { + case reflect.Slice, reflect.Array: + + var i64 int64 + reusableCF := &cField{} + + for i := 0; i < current.Len(); i++ { + + i64 = int64(i) + + v.misc = append(v.misc[0:0], cf.name...) + v.misc = append(v.misc, '[') + v.misc = strconv.AppendInt(v.misc, i64, 10) + v.misc = append(v.misc, ']') + + reusableCF.name = string(v.misc) + + if cf.namesEqual { + reusableCF.altName = reusableCF.name + } else { + + v.misc = append(v.misc[0:0], cf.altName...) + v.misc = append(v.misc, '[') + v.misc = strconv.AppendInt(v.misc, i64, 10) + v.misc = append(v.misc, ']') + + reusableCF.altName = string(v.misc) + } + v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct) + } + + case reflect.Map: + + var pv string + reusableCF := &cField{} + + for _, key := range current.MapKeys() { + + pv = fmt.Sprintf("%v", key.Interface()) + + v.misc = append(v.misc[0:0], cf.name...) + v.misc = append(v.misc, '[') + v.misc = append(v.misc, pv...) + v.misc = append(v.misc, ']') + + reusableCF.name = string(v.misc) + + if cf.namesEqual { + reusableCF.altName = reusableCF.name + } else { + v.misc = append(v.misc[0:0], cf.altName...) + v.misc = append(v.misc, '[') + v.misc = append(v.misc, pv...) + v.misc = append(v.misc, ']') + + reusableCF.altName = string(v.misc) + } + + if ct != nil && ct.typeof == typeKeys && ct.keys != nil { + v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys) + // can be nil when just keys being validated + if ct.next != nil { + v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next) + } + } else { + v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct) + } + } + + default: + // throw error, if not a slice or map then should not have gotten here + // bad dive tag + panic("dive error! can't dive on a non slice or map") + } + + return + + case typeOr: + + v.misc = v.misc[0:0] + + for { + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if ct.fn(ctx, v) { + + // drain rest of the 'or' values, then continue or leave + for { + + ct = ct.next + + if ct == nil { + return + } + + if ct.typeof != typeOr { + continue OUTER + } + } + } + + v.misc = append(v.misc, '|') + v.misc = append(v.misc, ct.tag...) + + if ct.hasParam { + v.misc = append(v.misc, '=') + v.misc = append(v.misc, ct.param...) + } + + if ct.isBlockEnd || ct.next == nil { + // if we get here, no valid 'or' value and no more tags + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + if ct.hasAlias { + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.actualAliasTag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + + } else { + + tVal := string(v.misc)[1:] + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: tVal, + actualTag: tVal, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + } + + return + } + + ct = ct.next + } + + default: + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !ct.fn(ctx, v) { + + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + + return + } + ct = ct.next + } + } + +} diff --git a/vendor/github.com/go-playground/validator/v10/validator_instance.go b/vendor/github.com/go-playground/validator/v10/validator_instance.go new file mode 100644 index 0000000..fe6a487 --- /dev/null +++ b/vendor/github.com/go-playground/validator/v10/validator_instance.go @@ -0,0 +1,619 @@ +package validator + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + "sync" + "time" + + ut "github.com/go-playground/universal-translator" +) + +const ( + defaultTagName = "validate" + utf8HexComma = "0x2C" + utf8Pipe = "0x7C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + noStructLevelTag = "nostructlevel" + omitempty = "omitempty" + isdefault = "isdefault" + requiredWithoutAllTag = "required_without_all" + requiredWithoutTag = "required_without" + requiredWithTag = "required_with" + requiredWithAllTag = "required_with_all" + requiredIfTag = "required_if" + requiredUnlessTag = "required_unless" + skipValidationTag = "-" + diveTag = "dive" + keysTag = "keys" + endKeysTag = "endkeys" + requiredTag = "required" + namespaceSeparator = "." + leftBracket = "[" + rightBracket = "]" + restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" + restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" +) + +var ( + timeDurationType = reflect.TypeOf(time.Duration(0)) + timeType = reflect.TypeOf(time.Time{}) + + defaultCField = &cField{namesEqual: true} +) + +// FilterFunc is the type used to filter fields using +// StructFiltered(...) function. +// returning true results in the field being filtered/skiped from +// validation +type FilterFunc func(ns []byte) bool + +// CustomTypeFunc allows for overriding or adding custom field type handler functions +// field = field value of the type to return a value to be validated +// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29 +type CustomTypeFunc func(field reflect.Value) interface{} + +// TagNameFunc allows for adding of a custom tag name parser +type TagNameFunc func(field reflect.StructField) string + +type internalValidationFuncWrapper struct { + fn FuncCtx + runValidatinOnNil bool +} + +// Validate contains the validator settings and cache +type Validate struct { + tagName string + pool *sync.Pool + hasCustomFuncs bool + hasTagNameFunc bool + tagNameFunc TagNameFunc + structLevelFuncs map[reflect.Type]StructLevelFuncCtx + customFuncs map[reflect.Type]CustomTypeFunc + aliases map[string]string + validations map[string]internalValidationFuncWrapper + transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc + tagCache *tagCache + structCache *structCache +} + +// New returns a new instance of 'validate' with sane defaults. +func New() *Validate { + + tc := new(tagCache) + tc.m.Store(make(map[string]*cTag)) + + sc := new(structCache) + sc.m.Store(make(map[reflect.Type]*cStruct)) + + v := &Validate{ + tagName: defaultTagName, + aliases: make(map[string]string, len(bakedInAliases)), + validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), + tagCache: tc, + structCache: sc, + } + + // must copy alias validators for separate validations to be used in each validator instance + for k, val := range bakedInAliases { + v.RegisterAlias(k, val) + } + + // must copy validators for separate validations to be used in each instance + for k, val := range bakedInValidators { + + switch k { + // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour + case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag: + _ = v.registerValidation(k, wrapFunc(val), true, true) + default: + // no need to error check here, baked in will always be valid + _ = v.registerValidation(k, wrapFunc(val), true, false) + } + } + + v.pool = &sync.Pool{ + New: func() interface{} { + return &validate{ + v: v, + ns: make([]byte, 0, 64), + actualNs: make([]byte, 0, 64), + misc: make([]byte, 32), + } + }, + } + + return v +} + +// SetTagName allows for changing of the default tag name of 'validate' +func (v *Validate) SetTagName(name string) { + v.tagName = name +} + +// RegisterTagNameFunc registers a function to get alternate names for StructFields. +// +// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: +// +// validate.RegisterTagNameFunc(func(fld reflect.StructField) string { +// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] +// if name == "-" { +// return "" +// } +// return name +// }) +func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { + v.tagNameFunc = fn + v.hasTagNameFunc = true +} + +// RegisterValidation adds a validation with the given tag +// +// NOTES: +// - if the key already exists, the previous validation function will be replaced. +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error { + return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...) +} + +// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation +// allowing context.Context validation support. +func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error { + var nilCheckable bool + if len(callValidationEvenIfNull) > 0 { + nilCheckable = callValidationEvenIfNull[0] + } + return v.registerValidation(tag, fn, false, nilCheckable) +} + +func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error { + if len(tag) == 0 { + return errors.New("Function Key cannot be empty") + } + + if fn == nil { + return errors.New("Function cannot be empty") + } + + _, ok := restrictedTags[tag] + if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { + panic(fmt.Sprintf(restrictedTagErr, tag)) + } + v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable} + return nil +} + +// RegisterAlias registers a mapping of a single validation tag that +// defines a common or complex set of validation(s) to simplify adding validation +// to structs. +// +// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterAlias(alias, tags string) { + + _, ok := restrictedTags[alias] + + if ok || strings.ContainsAny(alias, restrictedTagChars) { + panic(fmt.Sprintf(restrictedAliasErr, alias)) + } + + v.aliases[alias] = tags +} + +// RegisterStructValidation registers a StructLevelFunc against a number of types. +// +// NOTE: +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) { + v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...) +} + +// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing +// of contextual validation information via context.Context. +// +// NOTE: +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) { + + if v.structLevelFuncs == nil { + v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx) + } + + for _, t := range types { + tv := reflect.ValueOf(t) + if tv.Kind() == reflect.Ptr { + t = reflect.Indirect(tv).Interface() + } + + v.structLevelFuncs[reflect.TypeOf(t)] = fn + } +} + +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types +// +// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { + + if v.customFuncs == nil { + v.customFuncs = make(map[reflect.Type]CustomTypeFunc) + } + + for _, t := range types { + v.customFuncs[reflect.TypeOf(t)] = fn + } + + v.hasCustomFuncs = true +} + +// RegisterTranslation registers translations against the provided tag. +func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) { + + if v.transTagFunc == nil { + v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc) + } + + if err = registerFn(trans); err != nil { + return + } + + m, ok := v.transTagFunc[trans] + if !ok { + m = make(map[string]TranslationFunc) + v.transTagFunc[trans] = m + } + + m[tag] = translationFn + + return +} + +// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) Struct(s interface{}) error { + return v.StructCtx(context.Background(), s) +} + +// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified +// and also allows passing of context.Context for contextual validation information. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) { + + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = false + // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept + + vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates +// nested structs, unless otherwise specified. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error { + return v.StructFilteredCtx(context.Background(), s, fn) +} + +// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates +// nested structs, unless otherwise specified and also allows passing of contextual validation information via +// context.Context +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = fn + // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept + + vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructPartial validates the fields passed in only, ignoring all others. +// Fields may be provided in a namespaced fashion relative to the struct provided +// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructPartial(s interface{}, fields ...string) error { + return v.StructPartialCtx(context.Background(), s, fields...) +} + +// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual +// validation validation information via context.Context +// Fields may be provided in a namespaced fashion relative to the struct provided +// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = nil + vd.hasExcludes = false + vd.includeExclude = make(map[string]struct{}) + + typ := val.Type() + name := typ.Name() + + for _, k := range fields { + + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { + + vd.misc = append(vd.misc[0:0], name...) + vd.misc = append(vd.misc, '.') + + for _, s := range flds { + + idx := strings.Index(s, leftBracket) + + if idx != -1 { + for idx != -1 { + vd.misc = append(vd.misc, s[:idx]...) + vd.includeExclude[string(vd.misc)] = struct{}{} + + idx2 := strings.Index(s, rightBracket) + idx2++ + vd.misc = append(vd.misc, s[idx:idx2]...) + vd.includeExclude[string(vd.misc)] = struct{}{} + s = s[idx2:] + idx = strings.Index(s, leftBracket) + } + } else { + + vd.misc = append(vd.misc, s...) + vd.includeExclude[string(vd.misc)] = struct{}{} + } + + vd.misc = append(vd.misc, '.') + } + } + } + + vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructExcept validates all fields except the ones passed in. +// Fields may be provided in a namespaced fashion relative to the struct provided +// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructExcept(s interface{}, fields ...string) error { + return v.StructExceptCtx(context.Background(), s, fields...) +} + +// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual +// validation validation information via context.Context +// Fields may be provided in a namespaced fashion relative to the struct provided +// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = nil + vd.hasExcludes = true + vd.includeExclude = make(map[string]struct{}) + + typ := val.Type() + name := typ.Name() + + for _, key := range fields { + + vd.misc = vd.misc[0:0] + + if len(name) > 0 { + vd.misc = append(vd.misc, name...) + vd.misc = append(vd.misc, '.') + } + + vd.misc = append(vd.misc, key...) + vd.includeExclude[string(vd.misc)] = struct{}{} + } + + vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// Var validates a single variable using tag style validation. +// eg. +// var i int +// validate.Var(i, "gt=1,lt=10") +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) Var(field interface{}, tag string) error { + return v.VarCtx(context.Background(), field, tag) +} + +// VarCtx validates a single variable using tag style validation and allows passing of contextual +// validation validation information via context.Context. +// eg. +// var i int +// validate.Var(i, "gt=1,lt=10") +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) { + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + + ctag := v.fetchCacheTag(tag) + val := reflect.ValueOf(field) + vd := v.pool.Get().(*validate) + vd.top = val + vd.isPartial = false + vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + v.pool.Put(vd) + return +} + +// VarWithValue validates a single variable, against another variable/field's value using tag style validation +// eg. +// s1 := "abcd" +// s2 := "abcd" +// validate.VarWithValue(s1, s2, "eqcsfield") // returns true +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error { + return v.VarWithValueCtx(context.Background(), field, other, tag) +} + +// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and +// allows passing of contextual validation validation information via context.Context. +// eg. +// s1 := "abcd" +// s2 := "abcd" +// validate.VarWithValue(s1, s2, "eqcsfield") // returns true +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) { + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + ctag := v.fetchCacheTag(tag) + otherVal := reflect.ValueOf(other) + vd := v.pool.Get().(*validate) + vd.top = otherVal + vd.isPartial = false + vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + v.pool.Put(vd) + return +} diff --git a/vendor/github.com/go-redis/redis/v8/.gitignore b/vendor/github.com/go-redis/redis/v8/.gitignore new file mode 100644 index 0000000..b975a7b --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/.gitignore @@ -0,0 +1,3 @@ +*.rdb +testdata/*/ +.idea/ diff --git a/vendor/github.com/go-redis/redis/v8/.golangci.yml b/vendor/github.com/go-redis/redis/v8/.golangci.yml new file mode 100644 index 0000000..d15b5ac --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/.golangci.yml @@ -0,0 +1,26 @@ +run: + concurrency: 8 + deadline: 5m + tests: false +linters: + enable-all: true + disable: + - funlen + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - godox + - gosec + - maligned + - wsl + - gomnd + - goerr113 + - exhaustive + - nestif + - nlreturn + - exhaustivestruct + - wrapcheck + - errorlint + - cyclop + - forcetypeassert diff --git a/vendor/github.com/go-redis/redis/v8/.prettierrc b/vendor/github.com/go-redis/redis/v8/.prettierrc new file mode 100644 index 0000000..8b7f044 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/.prettierrc @@ -0,0 +1,4 @@ +semi: false +singleQuote: true +proseWrap: always +printWidth: 100 diff --git a/vendor/github.com/go-redis/redis/v8/CHANGELOG.md b/vendor/github.com/go-redis/redis/v8/CHANGELOG.md new file mode 100644 index 0000000..63aabd3 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/CHANGELOG.md @@ -0,0 +1,143 @@ +# Changelog + +> :heart: +> [**Uptrace.dev** - All-in-one tool to optimize performance and monitor errors & logs](https://uptrace.dev) + +## v8.11 + +- Remove OpenTelemetry metrics. +- Supports more redis commands and options. + +## v8.10 + +- Removed extra OpenTelemetry spans from go-redis core. Now go-redis instrumentation only adds a + single span with a Redis command (instead of 4 spans). There are multiple reasons behind this + decision: + + - Traces become smaller and less noisy. + - It may be costly to process those 3 extra spans for each query. + - go-redis no longer depends on OpenTelemetry. + + Eventually we hope to replace the information that we no longer collect with OpenTelemetry + Metrics. + +## v8.9 + +- Changed `PubSub.Channel` to only rely on `Ping` result. You can now use `WithChannelSize`, + `WithChannelHealthCheckInterval`, and `WithChannelSendTimeout` to override default settings. + +## v8.8 + +- To make updating easier, extra modules now have the same version as go-redis does. That means that + you need to update your imports: + +``` +github.com/go-redis/redis/extra/redisotel -> github.com/go-redis/redis/extra/redisotel/v8 +github.com/go-redis/redis/extra/rediscensus -> github.com/go-redis/redis/extra/rediscensus/v8 +``` + +## v8.5 + +- [knadh](https://github.com/knadh) contributed long-awaited ability to scan Redis Hash into a + struct: + +```go +err := rdb.HGetAll(ctx, "hash").Scan(&data) + +err := rdb.MGet(ctx, "key1", "key2").Scan(&data) +``` + +- Please check [redismock](https://github.com/go-redis/redismock) by + [monkey92t](https://github.com/monkey92t) if you are looking for mocking Redis Client. + +## v8 + +- All commands require `context.Context` as a first argument, e.g. `rdb.Ping(ctx)`. If you are not + using `context.Context` yet, the simplest option is to define global package variable + `var ctx = context.TODO()` and use it when `ctx` is required. + +- Full support for `context.Context` canceling. + +- Added `redis.NewFailoverClusterClient` that supports routing read-only commands to a slave node. + +- Added `redisext.OpenTemetryHook` that adds + [Redis OpenTelemetry instrumentation](https://redis.uptrace.dev/tracing/). + +- Redis slow log support. + +- Ring uses Rendezvous Hashing by default which provides better distribution. You need to move + existing keys to a new location or keys will be inaccessible / lost. To use old hashing scheme: + +```go +import "github.com/golang/groupcache/consistenthash" + +ring := redis.NewRing(&redis.RingOptions{ + NewConsistentHash: func() { + return consistenthash.New(100, crc32.ChecksumIEEE) + }, +}) +``` + +- `ClusterOptions.MaxRedirects` default value is changed from 8 to 3. +- `Options.MaxRetries` default value is changed from 0 to 3. + +- `Cluster.ForEachNode` is renamed to `ForEachShard` for consistency with `Ring`. + +## v7.3 + +- New option `Options.Username` which causes client to use `AuthACL`. Be aware if your connection + URL contains username. + +## v7.2 + +- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users. + +## v7.1 + +- Existing `Cmd.String` is renamed to `Cmd.Text`. New `Cmd.String` implements `fmt.Stringer` + interface. + +## v7 + +- _Important_. Tx.Pipeline now returns a non-transactional pipeline. Use Tx.TxPipeline for a + transactional pipeline. +- WrapProcess is replaced with more convenient AddHook that has access to context.Context. +- WithContext now can not be used to create a shallow copy of the client. +- New methods ProcessContext, DoContext, and ExecContext. +- Client respects Context.Deadline when setting net.Conn deadline. +- Client listens on Context.Done while waiting for a connection from the pool and returns an error + when context context is cancelled. +- Add PubSub.ChannelWithSubscriptions that sends `*Subscription` in addition to `*Message` to allow + detecting reconnections. +- `time.Time` is now marshalled in RFC3339 format. `rdb.Get("foo").Time()` helper is added to parse + the time. +- `SetLimiter` is removed and added `Options.Limiter` instead. +- `HMSet` is deprecated as of Redis v4. + +## v6.15 + +- Cluster and Ring pipelines process commands for each node in its own goroutine. + +## 6.14 + +- Added Options.MinIdleConns. +- Added Options.MaxConnAge. +- PoolStats.FreeConns is renamed to PoolStats.IdleConns. +- Add Client.Do to simplify creating custom commands. +- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers. +- Lower memory usage. + +## v6.13 + +- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set + `HashReplicas = 1000` for better keys distribution between shards. +- Cluster client was optimized to use much less memory when reloading cluster state. +- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout + occurres. In most cases it is recommended to use PubSub.Channel instead. +- Dialer.KeepAlive is set to 5 minutes by default. + +## v6.12 + +- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis + Servers that don't have cluster mode enabled. See + https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup diff --git a/vendor/github.com/go-redis/redis/v8/LICENSE b/vendor/github.com/go-redis/redis/v8/LICENSE new file mode 100644 index 0000000..298bed9 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2013 The github.com/go-redis/redis Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/go-redis/redis/v8/Makefile b/vendor/github.com/go-redis/redis/v8/Makefile new file mode 100644 index 0000000..b16709c --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/Makefile @@ -0,0 +1,26 @@ +test: testdeps + go test ./... + go test ./... -short -race + go test ./... -run=NONE -bench=. -benchmem + env GOOS=linux GOARCH=386 go test ./... + go vet + +testdeps: testdata/redis/src/redis-server + +bench: testdeps + go test ./... -test.run=NONE -test.bench=. -test.benchmem + +.PHONY: all test testdeps bench + +testdata/redis: + mkdir -p $@ + wget -qO- https://download.redis.io/releases/redis-6.2.5.tar.gz | tar xvz --strip-components=1 -C $@ + +testdata/redis/src/redis-server: testdata/redis + cd $< && make all + +tag: + git tag $(VERSION) + git tag extra/rediscmd/$(VERSION) + git tag extra/redisotel/$(VERSION) + git tag extra/rediscensus/$(VERSION) diff --git a/vendor/github.com/go-redis/redis/v8/README.md b/vendor/github.com/go-redis/redis/v8/README.md new file mode 100644 index 0000000..2c36f78 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/README.md @@ -0,0 +1,176 @@ +

+ + All-in-one tool to optimize performance and monitor errors & logs + +

+ +# Redis client for Golang + +![build workflow](https://github.com/go-redis/redis/actions/workflows/build.yml/badge.svg) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/go-redis/redis/v8)](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc) +[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/) +[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj) + +- Join [Discord](https://discord.gg/rWtp5Aj) to ask questions. +- [Documentation](https://redis.uptrace.dev) +- [Reference](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc) +- [Examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples) +- [RealWorld example app](https://github.com/uptrace/go-treemux-realworld-example-app) + +My other projects: + +- [Bun](https://bun.uptrace.dev) - fast and simple SQL client for PostgreSQL, MySQL, and SQLite. +- [treemux](https://github.com/vmihailenco/treemux) - high-speed, flexible, tree-based HTTP router + for Go. + +## Ecosystem + +- [Redis Mock](https://github.com/go-redis/redismock). +- [Distributed Locks](https://github.com/bsm/redislock). +- [Redis Cache](https://github.com/go-redis/cache). +- [Rate limiting](https://github.com/go-redis/redis_rate). + +## Features + +- Redis 3 commands except QUIT, MONITOR, and SYNC. +- Automatic connection pooling with + [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support. +- [Pub/Sub](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#PubSub). +- [Transactions](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline). +- [Pipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-Pipeline) and + [TxPipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline). +- [Scripting](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Script). +- [Timeouts](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Options). +- [Redis Sentinel](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewFailoverClient). +- [Redis Cluster](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewClusterClient). +- [Cluster of Redis Servers](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-NewClusterClient--ManualSetup) + without using cluster mode and Redis Sentinel. +- [Ring](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewRing). +- [Instrumentation](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#ex-package--Instrumentation). + +## Installation + +go-redis supports 2 last Go versions and requires a Go version with +[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go +module: + +```shell +go mod init github.com/my/repo +``` + +And then install go-redis/v8 (note _v8_ in the import; omitting it is a popular mistake): + +```shell +go get github.com/go-redis/redis/v8 +``` + +## Quickstart + +```go +import ( + "context" + "github.com/go-redis/redis/v8" +) + +var ctx = context.Background() + +func ExampleClient() { + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + err := rdb.Set(ctx, "key", "value", 0).Err() + if err != nil { + panic(err) + } + + val, err := rdb.Get(ctx, "key").Result() + if err != nil { + panic(err) + } + fmt.Println("key", val) + + val2, err := rdb.Get(ctx, "key2").Result() + if err == redis.Nil { + fmt.Println("key2 does not exist") + } else if err != nil { + panic(err) + } else { + fmt.Println("key2", val2) + } + // Output: key value + // key2 does not exist +} +``` + +## Look and feel + +Some corner cases: + +```go +// SET key value EX 10 NX +set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result() + +// SET key value keepttl NX +set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result() + +// SORT list LIMIT 0 2 ASC +vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() + +// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 +vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "-inf", + Max: "+inf", + Offset: 0, + Count: 2, +}).Result() + +// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM +vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{ + Keys: []string{"zset1", "zset2"}, + Weights: []int64{2, 3} +}).Result() + +// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" +vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() + +// custom command +res, err := rdb.Do(ctx, "set", "key", "value").Result() +``` + +## Run the test + +go-redis will start a redis-server and run the test cases. + +The paths of redis-server bin file and redis config file are defined in `main_test.go`: + +``` +var ( + redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server")) + redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf")) +) +``` + +For local testing, you can change the variables to refer to your local files, or create a soft link +to the corresponding folder for redis-server and copy the config file to `testdata/redis/`: + +``` +ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src +cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/ +``` + +Lastly, run: + +``` +go test +``` + +## Contributors + +Thanks to all the people who already contributed! + + + + diff --git a/vendor/github.com/go-redis/redis/v8/RELEASING.md b/vendor/github.com/go-redis/redis/v8/RELEASING.md new file mode 100644 index 0000000..9e50c10 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/RELEASING.md @@ -0,0 +1,21 @@ +# Releasing + +1. Run `release.sh` script which updates versions in go.mod files and pushes a new branch to GitHub: + +```shell +./scripts/release.sh -t v1.0.0 +``` + +2. Open a pull request and wait for the build to finish. + +3. Merge the pull request and run `tag.sh` to create tags for packages: + +```shell +./scripts/tag.sh -t v1.0.0 +``` + +4. Push the tags: + +```shell +git push origin --tags +``` diff --git a/vendor/github.com/go-redis/redis/v8/cluster.go b/vendor/github.com/go-redis/redis/v8/cluster.go new file mode 100644 index 0000000..8d93b36 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/cluster.go @@ -0,0 +1,1748 @@ +package redis + +import ( + "context" + "crypto/tls" + "fmt" + "math" + "net" + "runtime" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/go-redis/redis/v8/internal" + "github.com/go-redis/redis/v8/internal/hashtag" + "github.com/go-redis/redis/v8/internal/pool" + "github.com/go-redis/redis/v8/internal/proto" + "github.com/go-redis/redis/v8/internal/rand" +) + +var errClusterNoNodes = fmt.Errorf("redis: cluster has no nodes") + +// ClusterOptions are used to configure a cluster client and should be +// passed to NewClusterClient. +type ClusterOptions struct { + // A seed list of host:port addresses of cluster nodes. + Addrs []string + + // NewClient creates a cluster node client with provided name and options. + NewClient func(opt *Options) *Client + + // The maximum number of retries before giving up. Command is retried + // on network errors and MOVED/ASK redirects. + // Default is 3 retries. + MaxRedirects int + + // Enables read-only commands on slave nodes. + ReadOnly bool + // Allows routing read-only commands to the closest master or slave node. + // It automatically enables ReadOnly. + RouteByLatency bool + // Allows routing read-only commands to the random master or slave node. + // It automatically enables ReadOnly. + RouteRandomly bool + + // Optional function that returns cluster slots information. + // It is useful to manually create cluster of standalone Redis servers + // and load-balance read/write operations between master and slaves. + // It can use service like ZooKeeper to maintain configuration information + // and Cluster.ReloadState to manually trigger state reloading. + ClusterSlots func(context.Context) ([]ClusterSlot, error) + + // Following options are copied from Options struct. + + Dialer func(ctx context.Context, network, addr string) (net.Conn, error) + + OnConnect func(ctx context.Context, cn *Conn) error + + Username string + Password string + + MaxRetries int + MinRetryBackoff time.Duration + MaxRetryBackoff time.Duration + + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + + // PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO). + PoolFIFO bool + + // PoolSize applies per cluster node and not for the whole cluster. + PoolSize int + MinIdleConns int + MaxConnAge time.Duration + PoolTimeout time.Duration + IdleTimeout time.Duration + IdleCheckFrequency time.Duration + + TLSConfig *tls.Config +} + +func (opt *ClusterOptions) init() { + if opt.MaxRedirects == -1 { + opt.MaxRedirects = 0 + } else if opt.MaxRedirects == 0 { + opt.MaxRedirects = 3 + } + + if opt.RouteByLatency || opt.RouteRandomly { + opt.ReadOnly = true + } + + if opt.PoolSize == 0 { + opt.PoolSize = 5 * runtime.GOMAXPROCS(0) + } + + switch opt.ReadTimeout { + case -1: + opt.ReadTimeout = 0 + case 0: + opt.ReadTimeout = 3 * time.Second + } + switch opt.WriteTimeout { + case -1: + opt.WriteTimeout = 0 + case 0: + opt.WriteTimeout = opt.ReadTimeout + } + + if opt.MaxRetries == 0 { + opt.MaxRetries = -1 + } + switch opt.MinRetryBackoff { + case -1: + opt.MinRetryBackoff = 0 + case 0: + opt.MinRetryBackoff = 8 * time.Millisecond + } + switch opt.MaxRetryBackoff { + case -1: + opt.MaxRetryBackoff = 0 + case 0: + opt.MaxRetryBackoff = 512 * time.Millisecond + } + + if opt.NewClient == nil { + opt.NewClient = NewClient + } +} + +func (opt *ClusterOptions) clientOptions() *Options { + const disableIdleCheck = -1 + + return &Options{ + Dialer: opt.Dialer, + OnConnect: opt.OnConnect, + + Username: opt.Username, + Password: opt.Password, + + MaxRetries: opt.MaxRetries, + MinRetryBackoff: opt.MinRetryBackoff, + MaxRetryBackoff: opt.MaxRetryBackoff, + + DialTimeout: opt.DialTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + + PoolFIFO: opt.PoolFIFO, + PoolSize: opt.PoolSize, + MinIdleConns: opt.MinIdleConns, + MaxConnAge: opt.MaxConnAge, + PoolTimeout: opt.PoolTimeout, + IdleTimeout: opt.IdleTimeout, + IdleCheckFrequency: disableIdleCheck, + + TLSConfig: opt.TLSConfig, + // If ClusterSlots is populated, then we probably have an artificial + // cluster whose nodes are not in clustering mode (otherwise there isn't + // much use for ClusterSlots config). This means we cannot execute the + // READONLY command against that node -- setting readOnly to false in such + // situations in the options below will prevent that from happening. + readOnly: opt.ReadOnly && opt.ClusterSlots == nil, + } +} + +//------------------------------------------------------------------------------ + +type clusterNode struct { + Client *Client + + latency uint32 // atomic + generation uint32 // atomic + failing uint32 // atomic +} + +func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode { + opt := clOpt.clientOptions() + opt.Addr = addr + node := clusterNode{ + Client: clOpt.NewClient(opt), + } + + node.latency = math.MaxUint32 + if clOpt.RouteByLatency { + go node.updateLatency() + } + + return &node +} + +func (n *clusterNode) String() string { + return n.Client.String() +} + +func (n *clusterNode) Close() error { + return n.Client.Close() +} + +func (n *clusterNode) updateLatency() { + const numProbe = 10 + var dur uint64 + + for i := 0; i < numProbe; i++ { + time.Sleep(time.Duration(10+rand.Intn(10)) * time.Millisecond) + + start := time.Now() + n.Client.Ping(context.TODO()) + dur += uint64(time.Since(start) / time.Microsecond) + } + + latency := float64(dur) / float64(numProbe) + atomic.StoreUint32(&n.latency, uint32(latency+0.5)) +} + +func (n *clusterNode) Latency() time.Duration { + latency := atomic.LoadUint32(&n.latency) + return time.Duration(latency) * time.Microsecond +} + +func (n *clusterNode) MarkAsFailing() { + atomic.StoreUint32(&n.failing, uint32(time.Now().Unix())) +} + +func (n *clusterNode) Failing() bool { + const timeout = 15 // 15 seconds + + failing := atomic.LoadUint32(&n.failing) + if failing == 0 { + return false + } + if time.Now().Unix()-int64(failing) < timeout { + return true + } + atomic.StoreUint32(&n.failing, 0) + return false +} + +func (n *clusterNode) Generation() uint32 { + return atomic.LoadUint32(&n.generation) +} + +func (n *clusterNode) SetGeneration(gen uint32) { + for { + v := atomic.LoadUint32(&n.generation) + if gen < v || atomic.CompareAndSwapUint32(&n.generation, v, gen) { + break + } + } +} + +//------------------------------------------------------------------------------ + +type clusterNodes struct { + opt *ClusterOptions + + mu sync.RWMutex + addrs []string + nodes map[string]*clusterNode + activeAddrs []string + closed bool + + _generation uint32 // atomic +} + +func newClusterNodes(opt *ClusterOptions) *clusterNodes { + return &clusterNodes{ + opt: opt, + + addrs: opt.Addrs, + nodes: make(map[string]*clusterNode), + } +} + +func (c *clusterNodes) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + + if c.closed { + return nil + } + c.closed = true + + var firstErr error + for _, node := range c.nodes { + if err := node.Client.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + + c.nodes = nil + c.activeAddrs = nil + + return firstErr +} + +func (c *clusterNodes) Addrs() ([]string, error) { + var addrs []string + + c.mu.RLock() + closed := c.closed //nolint:ifshort + if !closed { + if len(c.activeAddrs) > 0 { + addrs = c.activeAddrs + } else { + addrs = c.addrs + } + } + c.mu.RUnlock() + + if closed { + return nil, pool.ErrClosed + } + if len(addrs) == 0 { + return nil, errClusterNoNodes + } + return addrs, nil +} + +func (c *clusterNodes) NextGeneration() uint32 { + return atomic.AddUint32(&c._generation, 1) +} + +// GC removes unused nodes. +func (c *clusterNodes) GC(generation uint32) { + //nolint:prealloc + var collected []*clusterNode + + c.mu.Lock() + + c.activeAddrs = c.activeAddrs[:0] + for addr, node := range c.nodes { + if node.Generation() >= generation { + c.activeAddrs = append(c.activeAddrs, addr) + if c.opt.RouteByLatency { + go node.updateLatency() + } + continue + } + + delete(c.nodes, addr) + collected = append(collected, node) + } + + c.mu.Unlock() + + for _, node := range collected { + _ = node.Client.Close() + } +} + +func (c *clusterNodes) Get(addr string) (*clusterNode, error) { + node, err := c.get(addr) + if err != nil { + return nil, err + } + if node != nil { + return node, nil + } + + c.mu.Lock() + defer c.mu.Unlock() + + if c.closed { + return nil, pool.ErrClosed + } + + node, ok := c.nodes[addr] + if ok { + return node, nil + } + + node = newClusterNode(c.opt, addr) + + c.addrs = appendIfNotExists(c.addrs, addr) + c.nodes[addr] = node + + return node, nil +} + +func (c *clusterNodes) get(addr string) (*clusterNode, error) { + var node *clusterNode + var err error + c.mu.RLock() + if c.closed { + err = pool.ErrClosed + } else { + node = c.nodes[addr] + } + c.mu.RUnlock() + return node, err +} + +func (c *clusterNodes) All() ([]*clusterNode, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + if c.closed { + return nil, pool.ErrClosed + } + + cp := make([]*clusterNode, 0, len(c.nodes)) + for _, node := range c.nodes { + cp = append(cp, node) + } + return cp, nil +} + +func (c *clusterNodes) Random() (*clusterNode, error) { + addrs, err := c.Addrs() + if err != nil { + return nil, err + } + + n := rand.Intn(len(addrs)) + return c.Get(addrs[n]) +} + +//------------------------------------------------------------------------------ + +type clusterSlot struct { + start, end int + nodes []*clusterNode +} + +type clusterSlotSlice []*clusterSlot + +func (p clusterSlotSlice) Len() int { + return len(p) +} + +func (p clusterSlotSlice) Less(i, j int) bool { + return p[i].start < p[j].start +} + +func (p clusterSlotSlice) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +type clusterState struct { + nodes *clusterNodes + Masters []*clusterNode + Slaves []*clusterNode + + slots []*clusterSlot + + generation uint32 + createdAt time.Time +} + +func newClusterState( + nodes *clusterNodes, slots []ClusterSlot, origin string, +) (*clusterState, error) { + c := clusterState{ + nodes: nodes, + + slots: make([]*clusterSlot, 0, len(slots)), + + generation: nodes.NextGeneration(), + createdAt: time.Now(), + } + + originHost, _, _ := net.SplitHostPort(origin) + isLoopbackOrigin := isLoopback(originHost) + + for _, slot := range slots { + var nodes []*clusterNode + for i, slotNode := range slot.Nodes { + addr := slotNode.Addr + if !isLoopbackOrigin { + addr = replaceLoopbackHost(addr, originHost) + } + + node, err := c.nodes.Get(addr) + if err != nil { + return nil, err + } + + node.SetGeneration(c.generation) + nodes = append(nodes, node) + + if i == 0 { + c.Masters = appendUniqueNode(c.Masters, node) + } else { + c.Slaves = appendUniqueNode(c.Slaves, node) + } + } + + c.slots = append(c.slots, &clusterSlot{ + start: slot.Start, + end: slot.End, + nodes: nodes, + }) + } + + sort.Sort(clusterSlotSlice(c.slots)) + + time.AfterFunc(time.Minute, func() { + nodes.GC(c.generation) + }) + + return &c, nil +} + +func replaceLoopbackHost(nodeAddr, originHost string) string { + nodeHost, nodePort, err := net.SplitHostPort(nodeAddr) + if err != nil { + return nodeAddr + } + + nodeIP := net.ParseIP(nodeHost) + if nodeIP == nil { + return nodeAddr + } + + if !nodeIP.IsLoopback() { + return nodeAddr + } + + // Use origin host which is not loopback and node port. + return net.JoinHostPort(originHost, nodePort) +} + +func isLoopback(host string) bool { + ip := net.ParseIP(host) + if ip == nil { + return true + } + return ip.IsLoopback() +} + +func (c *clusterState) slotMasterNode(slot int) (*clusterNode, error) { + nodes := c.slotNodes(slot) + if len(nodes) > 0 { + return nodes[0], nil + } + return c.nodes.Random() +} + +func (c *clusterState) slotSlaveNode(slot int) (*clusterNode, error) { + nodes := c.slotNodes(slot) + switch len(nodes) { + case 0: + return c.nodes.Random() + case 1: + return nodes[0], nil + case 2: + if slave := nodes[1]; !slave.Failing() { + return slave, nil + } + return nodes[0], nil + default: + var slave *clusterNode + for i := 0; i < 10; i++ { + n := rand.Intn(len(nodes)-1) + 1 + slave = nodes[n] + if !slave.Failing() { + return slave, nil + } + } + + // All slaves are loading - use master. + return nodes[0], nil + } +} + +func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) { + nodes := c.slotNodes(slot) + if len(nodes) == 0 { + return c.nodes.Random() + } + + var node *clusterNode + for _, n := range nodes { + if n.Failing() { + continue + } + if node == nil || n.Latency() < node.Latency() { + node = n + } + } + if node != nil { + return node, nil + } + + // If all nodes are failing - return random node + return c.nodes.Random() +} + +func (c *clusterState) slotRandomNode(slot int) (*clusterNode, error) { + nodes := c.slotNodes(slot) + if len(nodes) == 0 { + return c.nodes.Random() + } + if len(nodes) == 1 { + return nodes[0], nil + } + randomNodes := rand.Perm(len(nodes)) + for _, idx := range randomNodes { + if node := nodes[idx]; !node.Failing() { + return node, nil + } + } + return nodes[randomNodes[0]], nil +} + +func (c *clusterState) slotNodes(slot int) []*clusterNode { + i := sort.Search(len(c.slots), func(i int) bool { + return c.slots[i].end >= slot + }) + if i >= len(c.slots) { + return nil + } + x := c.slots[i] + if slot >= x.start && slot <= x.end { + return x.nodes + } + return nil +} + +//------------------------------------------------------------------------------ + +type clusterStateHolder struct { + load func(ctx context.Context) (*clusterState, error) + + state atomic.Value + reloading uint32 // atomic +} + +func newClusterStateHolder(fn func(ctx context.Context) (*clusterState, error)) *clusterStateHolder { + return &clusterStateHolder{ + load: fn, + } +} + +func (c *clusterStateHolder) Reload(ctx context.Context) (*clusterState, error) { + state, err := c.load(ctx) + if err != nil { + return nil, err + } + c.state.Store(state) + return state, nil +} + +func (c *clusterStateHolder) LazyReload() { + if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) { + return + } + go func() { + defer atomic.StoreUint32(&c.reloading, 0) + + _, err := c.Reload(context.Background()) + if err != nil { + return + } + time.Sleep(200 * time.Millisecond) + }() +} + +func (c *clusterStateHolder) Get(ctx context.Context) (*clusterState, error) { + v := c.state.Load() + if v == nil { + return c.Reload(ctx) + } + + state := v.(*clusterState) + if time.Since(state.createdAt) > 10*time.Second { + c.LazyReload() + } + return state, nil +} + +func (c *clusterStateHolder) ReloadOrGet(ctx context.Context) (*clusterState, error) { + state, err := c.Reload(ctx) + if err == nil { + return state, nil + } + return c.Get(ctx) +} + +//------------------------------------------------------------------------------ + +type clusterClient struct { + opt *ClusterOptions + nodes *clusterNodes + state *clusterStateHolder //nolint:structcheck + cmdsInfoCache *cmdsInfoCache //nolint:structcheck +} + +// ClusterClient is a Redis Cluster client representing a pool of zero +// or more underlying connections. It's safe for concurrent use by +// multiple goroutines. +type ClusterClient struct { + *clusterClient + cmdable + hooks + ctx context.Context +} + +// NewClusterClient returns a Redis Cluster client as described in +// http://redis.io/topics/cluster-spec. +func NewClusterClient(opt *ClusterOptions) *ClusterClient { + opt.init() + + c := &ClusterClient{ + clusterClient: &clusterClient{ + opt: opt, + nodes: newClusterNodes(opt), + }, + ctx: context.Background(), + } + c.state = newClusterStateHolder(c.loadState) + c.cmdsInfoCache = newCmdsInfoCache(c.cmdsInfo) + c.cmdable = c.Process + + if opt.IdleCheckFrequency > 0 { + go c.reaper(opt.IdleCheckFrequency) + } + + return c +} + +func (c *ClusterClient) Context() context.Context { + return c.ctx +} + +func (c *ClusterClient) WithContext(ctx context.Context) *ClusterClient { + if ctx == nil { + panic("nil context") + } + clone := *c + clone.cmdable = clone.Process + clone.hooks.lock() + clone.ctx = ctx + return &clone +} + +// Options returns read-only Options that were used to create the client. +func (c *ClusterClient) Options() *ClusterOptions { + return c.opt +} + +// ReloadState reloads cluster state. If available it calls ClusterSlots func +// to get cluster slots information. +func (c *ClusterClient) ReloadState(ctx context.Context) { + c.state.LazyReload() +} + +// Close closes the cluster client, releasing any open resources. +// +// It is rare to Close a ClusterClient, as the ClusterClient is meant +// to be long-lived and shared between many goroutines. +func (c *ClusterClient) Close() error { + return c.nodes.Close() +} + +// Do creates a Cmd from the args and processes the cmd. +func (c *ClusterClient) Do(ctx context.Context, args ...interface{}) *Cmd { + cmd := NewCmd(ctx, args...) + _ = c.Process(ctx, cmd) + return cmd +} + +func (c *ClusterClient) Process(ctx context.Context, cmd Cmder) error { + return c.hooks.process(ctx, cmd, c.process) +} + +func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error { + cmdInfo := c.cmdInfo(cmd.Name()) + slot := c.cmdSlot(cmd) + + var node *clusterNode + var ask bool + var lastErr error + for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ { + if attempt > 0 { + if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil { + return err + } + } + + if node == nil { + var err error + node, err = c.cmdNode(ctx, cmdInfo, slot) + if err != nil { + return err + } + } + + if ask { + pipe := node.Client.Pipeline() + _ = pipe.Process(ctx, NewCmd(ctx, "asking")) + _ = pipe.Process(ctx, cmd) + _, lastErr = pipe.Exec(ctx) + _ = pipe.Close() + ask = false + } else { + lastErr = node.Client.Process(ctx, cmd) + } + + // If there is no error - we are done. + if lastErr == nil { + return nil + } + if isReadOnly := isReadOnlyError(lastErr); isReadOnly || lastErr == pool.ErrClosed { + if isReadOnly { + c.state.LazyReload() + } + node = nil + continue + } + + // If slave is loading - pick another node. + if c.opt.ReadOnly && isLoadingError(lastErr) { + node.MarkAsFailing() + node = nil + continue + } + + var moved bool + var addr string + moved, ask, addr = isMovedError(lastErr) + if moved || ask { + var err error + node, err = c.nodes.Get(addr) + if err != nil { + return err + } + continue + } + + if shouldRetry(lastErr, cmd.readTimeout() == nil) { + // First retry the same node. + if attempt == 0 { + continue + } + + // Second try another node. + node.MarkAsFailing() + node = nil + continue + } + + return lastErr + } + return lastErr +} + +// ForEachMaster concurrently calls the fn on each master node in the cluster. +// It returns the first error if any. +func (c *ClusterClient) ForEachMaster( + ctx context.Context, + fn func(ctx context.Context, client *Client) error, +) error { + state, err := c.state.ReloadOrGet(ctx) + if err != nil { + return err + } + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for _, master := range state.Masters { + wg.Add(1) + go func(node *clusterNode) { + defer wg.Done() + err := fn(ctx, node.Client) + if err != nil { + select { + case errCh <- err: + default: + } + } + }(master) + } + + wg.Wait() + + select { + case err := <-errCh: + return err + default: + return nil + } +} + +// ForEachSlave concurrently calls the fn on each slave node in the cluster. +// It returns the first error if any. +func (c *ClusterClient) ForEachSlave( + ctx context.Context, + fn func(ctx context.Context, client *Client) error, +) error { + state, err := c.state.ReloadOrGet(ctx) + if err != nil { + return err + } + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for _, slave := range state.Slaves { + wg.Add(1) + go func(node *clusterNode) { + defer wg.Done() + err := fn(ctx, node.Client) + if err != nil { + select { + case errCh <- err: + default: + } + } + }(slave) + } + + wg.Wait() + + select { + case err := <-errCh: + return err + default: + return nil + } +} + +// ForEachShard concurrently calls the fn on each known node in the cluster. +// It returns the first error if any. +func (c *ClusterClient) ForEachShard( + ctx context.Context, + fn func(ctx context.Context, client *Client) error, +) error { + state, err := c.state.ReloadOrGet(ctx) + if err != nil { + return err + } + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + worker := func(node *clusterNode) { + defer wg.Done() + err := fn(ctx, node.Client) + if err != nil { + select { + case errCh <- err: + default: + } + } + } + + for _, node := range state.Masters { + wg.Add(1) + go worker(node) + } + for _, node := range state.Slaves { + wg.Add(1) + go worker(node) + } + + wg.Wait() + + select { + case err := <-errCh: + return err + default: + return nil + } +} + +// PoolStats returns accumulated connection pool stats. +func (c *ClusterClient) PoolStats() *PoolStats { + var acc PoolStats + + state, _ := c.state.Get(context.TODO()) + if state == nil { + return &acc + } + + for _, node := range state.Masters { + s := node.Client.connPool.Stats() + acc.Hits += s.Hits + acc.Misses += s.Misses + acc.Timeouts += s.Timeouts + + acc.TotalConns += s.TotalConns + acc.IdleConns += s.IdleConns + acc.StaleConns += s.StaleConns + } + + for _, node := range state.Slaves { + s := node.Client.connPool.Stats() + acc.Hits += s.Hits + acc.Misses += s.Misses + acc.Timeouts += s.Timeouts + + acc.TotalConns += s.TotalConns + acc.IdleConns += s.IdleConns + acc.StaleConns += s.StaleConns + } + + return &acc +} + +func (c *ClusterClient) loadState(ctx context.Context) (*clusterState, error) { + if c.opt.ClusterSlots != nil { + slots, err := c.opt.ClusterSlots(ctx) + if err != nil { + return nil, err + } + return newClusterState(c.nodes, slots, "") + } + + addrs, err := c.nodes.Addrs() + if err != nil { + return nil, err + } + + var firstErr error + + for _, idx := range rand.Perm(len(addrs)) { + addr := addrs[idx] + + node, err := c.nodes.Get(addr) + if err != nil { + if firstErr == nil { + firstErr = err + } + continue + } + + slots, err := node.Client.ClusterSlots(ctx).Result() + if err != nil { + if firstErr == nil { + firstErr = err + } + continue + } + + return newClusterState(c.nodes, slots, node.Client.opt.Addr) + } + + /* + * No node is connectable. It's possible that all nodes' IP has changed. + * Clear activeAddrs to let client be able to re-connect using the initial + * setting of the addresses (e.g. [redis-cluster-0:6379, redis-cluster-1:6379]), + * which might have chance to resolve domain name and get updated IP address. + */ + c.nodes.mu.Lock() + c.nodes.activeAddrs = nil + c.nodes.mu.Unlock() + + return nil, firstErr +} + +// reaper closes idle connections to the cluster. +func (c *ClusterClient) reaper(idleCheckFrequency time.Duration) { + ticker := time.NewTicker(idleCheckFrequency) + defer ticker.Stop() + + for range ticker.C { + nodes, err := c.nodes.All() + if err != nil { + break + } + + for _, node := range nodes { + _, err := node.Client.connPool.(*pool.ConnPool).ReapStaleConns() + if err != nil { + internal.Logger.Printf(c.Context(), "ReapStaleConns failed: %s", err) + } + } + } +} + +func (c *ClusterClient) Pipeline() Pipeliner { + pipe := Pipeline{ + ctx: c.ctx, + exec: c.processPipeline, + } + pipe.init() + return &pipe +} + +func (c *ClusterClient) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) { + return c.Pipeline().Pipelined(ctx, fn) +} + +func (c *ClusterClient) processPipeline(ctx context.Context, cmds []Cmder) error { + return c.hooks.processPipeline(ctx, cmds, c._processPipeline) +} + +func (c *ClusterClient) _processPipeline(ctx context.Context, cmds []Cmder) error { + cmdsMap := newCmdsMap() + err := c.mapCmdsByNode(ctx, cmdsMap, cmds) + if err != nil { + setCmdsErr(cmds, err) + return err + } + + for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ { + if attempt > 0 { + if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil { + setCmdsErr(cmds, err) + return err + } + } + + failedCmds := newCmdsMap() + var wg sync.WaitGroup + + for node, cmds := range cmdsMap.m { + wg.Add(1) + go func(node *clusterNode, cmds []Cmder) { + defer wg.Done() + + err := c._processPipelineNode(ctx, node, cmds, failedCmds) + if err == nil { + return + } + if attempt < c.opt.MaxRedirects { + if err := c.mapCmdsByNode(ctx, failedCmds, cmds); err != nil { + setCmdsErr(cmds, err) + } + } else { + setCmdsErr(cmds, err) + } + }(node, cmds) + } + + wg.Wait() + if len(failedCmds.m) == 0 { + break + } + cmdsMap = failedCmds + } + + return cmdsFirstErr(cmds) +} + +func (c *ClusterClient) mapCmdsByNode(ctx context.Context, cmdsMap *cmdsMap, cmds []Cmder) error { + state, err := c.state.Get(ctx) + if err != nil { + return err + } + + if c.opt.ReadOnly && c.cmdsAreReadOnly(cmds) { + for _, cmd := range cmds { + slot := c.cmdSlot(cmd) + node, err := c.slotReadOnlyNode(state, slot) + if err != nil { + return err + } + cmdsMap.Add(node, cmd) + } + return nil + } + + for _, cmd := range cmds { + slot := c.cmdSlot(cmd) + node, err := state.slotMasterNode(slot) + if err != nil { + return err + } + cmdsMap.Add(node, cmd) + } + return nil +} + +func (c *ClusterClient) cmdsAreReadOnly(cmds []Cmder) bool { + for _, cmd := range cmds { + cmdInfo := c.cmdInfo(cmd.Name()) + if cmdInfo == nil || !cmdInfo.ReadOnly { + return false + } + } + return true +} + +func (c *ClusterClient) _processPipelineNode( + ctx context.Context, node *clusterNode, cmds []Cmder, failedCmds *cmdsMap, +) error { + return node.Client.hooks.processPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error { + return node.Client.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error { + err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error { + return writeCmds(wr, cmds) + }) + if err != nil { + return err + } + + return cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error { + return c.pipelineReadCmds(ctx, node, rd, cmds, failedCmds) + }) + }) + }) +} + +func (c *ClusterClient) pipelineReadCmds( + ctx context.Context, + node *clusterNode, + rd *proto.Reader, + cmds []Cmder, + failedCmds *cmdsMap, +) error { + for _, cmd := range cmds { + err := cmd.readReply(rd) + cmd.SetErr(err) + + if err == nil { + continue + } + + if c.checkMovedErr(ctx, cmd, err, failedCmds) { + continue + } + + if c.opt.ReadOnly && isLoadingError(err) { + node.MarkAsFailing() + return err + } + if isRedisError(err) { + continue + } + return err + } + return nil +} + +func (c *ClusterClient) checkMovedErr( + ctx context.Context, cmd Cmder, err error, failedCmds *cmdsMap, +) bool { + moved, ask, addr := isMovedError(err) + if !moved && !ask { + return false + } + + node, err := c.nodes.Get(addr) + if err != nil { + return false + } + + if moved { + c.state.LazyReload() + failedCmds.Add(node, cmd) + return true + } + + if ask { + failedCmds.Add(node, NewCmd(ctx, "asking"), cmd) + return true + } + + panic("not reached") +} + +// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC. +func (c *ClusterClient) TxPipeline() Pipeliner { + pipe := Pipeline{ + ctx: c.ctx, + exec: c.processTxPipeline, + } + pipe.init() + return &pipe +} + +func (c *ClusterClient) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) { + return c.TxPipeline().Pipelined(ctx, fn) +} + +func (c *ClusterClient) processTxPipeline(ctx context.Context, cmds []Cmder) error { + return c.hooks.processTxPipeline(ctx, cmds, c._processTxPipeline) +} + +func (c *ClusterClient) _processTxPipeline(ctx context.Context, cmds []Cmder) error { + // Trim multi .. exec. + cmds = cmds[1 : len(cmds)-1] + + state, err := c.state.Get(ctx) + if err != nil { + setCmdsErr(cmds, err) + return err + } + + cmdsMap := c.mapCmdsBySlot(cmds) + for slot, cmds := range cmdsMap { + node, err := state.slotMasterNode(slot) + if err != nil { + setCmdsErr(cmds, err) + continue + } + + cmdsMap := map[*clusterNode][]Cmder{node: cmds} + for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ { + if attempt > 0 { + if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil { + setCmdsErr(cmds, err) + return err + } + } + + failedCmds := newCmdsMap() + var wg sync.WaitGroup + + for node, cmds := range cmdsMap { + wg.Add(1) + go func(node *clusterNode, cmds []Cmder) { + defer wg.Done() + + err := c._processTxPipelineNode(ctx, node, cmds, failedCmds) + if err == nil { + return + } + + if attempt < c.opt.MaxRedirects { + if err := c.mapCmdsByNode(ctx, failedCmds, cmds); err != nil { + setCmdsErr(cmds, err) + } + } else { + setCmdsErr(cmds, err) + } + }(node, cmds) + } + + wg.Wait() + if len(failedCmds.m) == 0 { + break + } + cmdsMap = failedCmds.m + } + } + + return cmdsFirstErr(cmds) +} + +func (c *ClusterClient) mapCmdsBySlot(cmds []Cmder) map[int][]Cmder { + cmdsMap := make(map[int][]Cmder) + for _, cmd := range cmds { + slot := c.cmdSlot(cmd) + cmdsMap[slot] = append(cmdsMap[slot], cmd) + } + return cmdsMap +} + +func (c *ClusterClient) _processTxPipelineNode( + ctx context.Context, node *clusterNode, cmds []Cmder, failedCmds *cmdsMap, +) error { + return node.Client.hooks.processTxPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error { + return node.Client.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error { + err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error { + return writeCmds(wr, cmds) + }) + if err != nil { + return err + } + + return cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error { + statusCmd := cmds[0].(*StatusCmd) + // Trim multi and exec. + cmds = cmds[1 : len(cmds)-1] + + err := c.txPipelineReadQueued(ctx, rd, statusCmd, cmds, failedCmds) + if err != nil { + moved, ask, addr := isMovedError(err) + if moved || ask { + return c.cmdsMoved(ctx, cmds, moved, ask, addr, failedCmds) + } + return err + } + + return pipelineReadCmds(rd, cmds) + }) + }) + }) +} + +func (c *ClusterClient) txPipelineReadQueued( + ctx context.Context, + rd *proto.Reader, + statusCmd *StatusCmd, + cmds []Cmder, + failedCmds *cmdsMap, +) error { + // Parse queued replies. + if err := statusCmd.readReply(rd); err != nil { + return err + } + + for _, cmd := range cmds { + err := statusCmd.readReply(rd) + if err == nil || c.checkMovedErr(ctx, cmd, err, failedCmds) || isRedisError(err) { + continue + } + return err + } + + // Parse number of replies. + line, err := rd.ReadLine() + if err != nil { + if err == Nil { + err = TxFailedErr + } + return err + } + + switch line[0] { + case proto.ErrorReply: + return proto.ParseErrorReply(line) + case proto.ArrayReply: + // ok + default: + return fmt.Errorf("redis: expected '*', but got line %q", line) + } + + return nil +} + +func (c *ClusterClient) cmdsMoved( + ctx context.Context, cmds []Cmder, + moved, ask bool, + addr string, + failedCmds *cmdsMap, +) error { + node, err := c.nodes.Get(addr) + if err != nil { + return err + } + + if moved { + c.state.LazyReload() + for _, cmd := range cmds { + failedCmds.Add(node, cmd) + } + return nil + } + + if ask { + for _, cmd := range cmds { + failedCmds.Add(node, NewCmd(ctx, "asking"), cmd) + } + return nil + } + + return nil +} + +func (c *ClusterClient) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error { + if len(keys) == 0 { + return fmt.Errorf("redis: Watch requires at least one key") + } + + slot := hashtag.Slot(keys[0]) + for _, key := range keys[1:] { + if hashtag.Slot(key) != slot { + err := fmt.Errorf("redis: Watch requires all keys to be in the same slot") + return err + } + } + + node, err := c.slotMasterNode(ctx, slot) + if err != nil { + return err + } + + for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ { + if attempt > 0 { + if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil { + return err + } + } + + err = node.Client.Watch(ctx, fn, keys...) + if err == nil { + break + } + + moved, ask, addr := isMovedError(err) + if moved || ask { + node, err = c.nodes.Get(addr) + if err != nil { + return err + } + continue + } + + if isReadOnly := isReadOnlyError(err); isReadOnly || err == pool.ErrClosed { + if isReadOnly { + c.state.LazyReload() + } + node, err = c.slotMasterNode(ctx, slot) + if err != nil { + return err + } + continue + } + + if shouldRetry(err, true) { + continue + } + + return err + } + + return err +} + +func (c *ClusterClient) pubSub() *PubSub { + var node *clusterNode + pubsub := &PubSub{ + opt: c.opt.clientOptions(), + + newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) { + if node != nil { + panic("node != nil") + } + + var err error + if len(channels) > 0 { + slot := hashtag.Slot(channels[0]) + node, err = c.slotMasterNode(ctx, slot) + } else { + node, err = c.nodes.Random() + } + if err != nil { + return nil, err + } + + cn, err := node.Client.newConn(context.TODO()) + if err != nil { + node = nil + + return nil, err + } + + return cn, nil + }, + closeConn: func(cn *pool.Conn) error { + err := node.Client.connPool.CloseConn(cn) + node = nil + return err + }, + } + pubsub.init() + + return pubsub +} + +// Subscribe subscribes the client to the specified channels. +// Channels can be omitted to create empty subscription. +func (c *ClusterClient) Subscribe(ctx context.Context, channels ...string) *PubSub { + pubsub := c.pubSub() + if len(channels) > 0 { + _ = pubsub.Subscribe(ctx, channels...) + } + return pubsub +} + +// PSubscribe subscribes the client to the given patterns. +// Patterns can be omitted to create empty subscription. +func (c *ClusterClient) PSubscribe(ctx context.Context, channels ...string) *PubSub { + pubsub := c.pubSub() + if len(channels) > 0 { + _ = pubsub.PSubscribe(ctx, channels...) + } + return pubsub +} + +func (c *ClusterClient) retryBackoff(attempt int) time.Duration { + return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff) +} + +func (c *ClusterClient) cmdsInfo(ctx context.Context) (map[string]*CommandInfo, error) { + // Try 3 random nodes. + const nodeLimit = 3 + + addrs, err := c.nodes.Addrs() + if err != nil { + return nil, err + } + + var firstErr error + + perm := rand.Perm(len(addrs)) + if len(perm) > nodeLimit { + perm = perm[:nodeLimit] + } + + for _, idx := range perm { + addr := addrs[idx] + + node, err := c.nodes.Get(addr) + if err != nil { + if firstErr == nil { + firstErr = err + } + continue + } + + info, err := node.Client.Command(ctx).Result() + if err == nil { + return info, nil + } + if firstErr == nil { + firstErr = err + } + } + + if firstErr == nil { + panic("not reached") + } + return nil, firstErr +} + +func (c *ClusterClient) cmdInfo(name string) *CommandInfo { + cmdsInfo, err := c.cmdsInfoCache.Get(c.ctx) + if err != nil { + return nil + } + + info := cmdsInfo[name] + if info == nil { + internal.Logger.Printf(c.Context(), "info for cmd=%s not found", name) + } + return info +} + +func (c *ClusterClient) cmdSlot(cmd Cmder) int { + args := cmd.Args() + if args[0] == "cluster" && args[1] == "getkeysinslot" { + return args[2].(int) + } + + cmdInfo := c.cmdInfo(cmd.Name()) + return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo)) +} + +func cmdSlot(cmd Cmder, pos int) int { + if pos == 0 { + return hashtag.RandomSlot() + } + firstKey := cmd.stringArg(pos) + return hashtag.Slot(firstKey) +} + +func (c *ClusterClient) cmdNode( + ctx context.Context, + cmdInfo *CommandInfo, + slot int, +) (*clusterNode, error) { + state, err := c.state.Get(ctx) + if err != nil { + return nil, err + } + + if c.opt.ReadOnly && cmdInfo != nil && cmdInfo.ReadOnly { + return c.slotReadOnlyNode(state, slot) + } + return state.slotMasterNode(slot) +} + +func (c *clusterClient) slotReadOnlyNode(state *clusterState, slot int) (*clusterNode, error) { + if c.opt.RouteByLatency { + return state.slotClosestNode(slot) + } + if c.opt.RouteRandomly { + return state.slotRandomNode(slot) + } + return state.slotSlaveNode(slot) +} + +func (c *ClusterClient) slotMasterNode(ctx context.Context, slot int) (*clusterNode, error) { + state, err := c.state.Get(ctx) + if err != nil { + return nil, err + } + return state.slotMasterNode(slot) +} + +// SlaveForKey gets a client for a replica node to run any command on it. +// This is especially useful if we want to run a particular lua script which has +// only read only commands on the replica. +// This is because other redis commands generally have a flag that points that +// they are read only and automatically run on the replica nodes +// if ClusterOptions.ReadOnly flag is set to true. +func (c *ClusterClient) SlaveForKey(ctx context.Context, key string) (*Client, error) { + state, err := c.state.Get(ctx) + if err != nil { + return nil, err + } + slot := hashtag.Slot(key) + node, err := c.slotReadOnlyNode(state, slot) + if err != nil { + return nil, err + } + return node.Client, err +} + +// MasterForKey return a client to the master node for a particular key. +func (c *ClusterClient) MasterForKey(ctx context.Context, key string) (*Client, error) { + slot := hashtag.Slot(key) + node, err := c.slotMasterNode(ctx, slot) + if err != nil { + return nil, err + } + return node.Client, err +} + +func appendUniqueNode(nodes []*clusterNode, node *clusterNode) []*clusterNode { + for _, n := range nodes { + if n == node { + return nodes + } + } + return append(nodes, node) +} + +func appendIfNotExists(ss []string, es ...string) []string { +loop: + for _, e := range es { + for _, s := range ss { + if s == e { + continue loop + } + } + ss = append(ss, e) + } + return ss +} + +//------------------------------------------------------------------------------ + +type cmdsMap struct { + mu sync.Mutex + m map[*clusterNode][]Cmder +} + +func newCmdsMap() *cmdsMap { + return &cmdsMap{ + m: make(map[*clusterNode][]Cmder), + } +} + +func (m *cmdsMap) Add(node *clusterNode, cmds ...Cmder) { + m.mu.Lock() + m.m[node] = append(m.m[node], cmds...) + m.mu.Unlock() +} diff --git a/vendor/github.com/go-redis/redis/v8/cluster_commands.go b/vendor/github.com/go-redis/redis/v8/cluster_commands.go new file mode 100644 index 0000000..085bce8 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/cluster_commands.go @@ -0,0 +1,109 @@ +package redis + +import ( + "context" + "sync" + "sync/atomic" +) + +func (c *ClusterClient) DBSize(ctx context.Context) *IntCmd { + cmd := NewIntCmd(ctx, "dbsize") + _ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error { + var size int64 + err := c.ForEachMaster(ctx, func(ctx context.Context, master *Client) error { + n, err := master.DBSize(ctx).Result() + if err != nil { + return err + } + atomic.AddInt64(&size, n) + return nil + }) + if err != nil { + cmd.SetErr(err) + } else { + cmd.val = size + } + return nil + }) + return cmd +} + +func (c *ClusterClient) ScriptLoad(ctx context.Context, script string) *StringCmd { + cmd := NewStringCmd(ctx, "script", "load", script) + _ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error { + mu := &sync.Mutex{} + err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error { + val, err := shard.ScriptLoad(ctx, script).Result() + if err != nil { + return err + } + + mu.Lock() + if cmd.Val() == "" { + cmd.val = val + } + mu.Unlock() + + return nil + }) + if err != nil { + cmd.SetErr(err) + } + return nil + }) + return cmd +} + +func (c *ClusterClient) ScriptFlush(ctx context.Context) *StatusCmd { + cmd := NewStatusCmd(ctx, "script", "flush") + _ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error { + err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error { + return shard.ScriptFlush(ctx).Err() + }) + if err != nil { + cmd.SetErr(err) + } + return nil + }) + return cmd +} + +func (c *ClusterClient) ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd { + args := make([]interface{}, 2+len(hashes)) + args[0] = "script" + args[1] = "exists" + for i, hash := range hashes { + args[2+i] = hash + } + cmd := NewBoolSliceCmd(ctx, args...) + + result := make([]bool, len(hashes)) + for i := range result { + result[i] = true + } + + _ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error { + mu := &sync.Mutex{} + err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error { + val, err := shard.ScriptExists(ctx, hashes...).Result() + if err != nil { + return err + } + + mu.Lock() + for i, v := range val { + result[i] = result[i] && v + } + mu.Unlock() + + return nil + }) + if err != nil { + cmd.SetErr(err) + } else { + cmd.val = result + } + return nil + }) + return cmd +} diff --git a/vendor/github.com/go-redis/redis/v8/command.go b/vendor/github.com/go-redis/redis/v8/command.go new file mode 100644 index 0000000..6f77258 --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/command.go @@ -0,0 +1,3191 @@ +package redis + +import ( + "context" + "fmt" + "net" + "strconv" + "time" + + "github.com/go-redis/redis/v8/internal" + "github.com/go-redis/redis/v8/internal/hscan" + "github.com/go-redis/redis/v8/internal/proto" + "github.com/go-redis/redis/v8/internal/util" +) + +type Cmder interface { + Name() string + FullName() string + Args() []interface{} + String() string + stringArg(int) string + firstKeyPos() int8 + setFirstKeyPos(int8) + + readTimeout() *time.Duration + readReply(rd *proto.Reader) error + + SetErr(error) + Err() error +} + +func setCmdsErr(cmds []Cmder, e error) { + for _, cmd := range cmds { + if cmd.Err() == nil { + cmd.SetErr(e) + } + } +} + +func cmdsFirstErr(cmds []Cmder) error { + for _, cmd := range cmds { + if err := cmd.Err(); err != nil { + return err + } + } + return nil +} + +func writeCmds(wr *proto.Writer, cmds []Cmder) error { + for _, cmd := range cmds { + if err := writeCmd(wr, cmd); err != nil { + return err + } + } + return nil +} + +func writeCmd(wr *proto.Writer, cmd Cmder) error { + return wr.WriteArgs(cmd.Args()) +} + +func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { + if pos := cmd.firstKeyPos(); pos != 0 { + return int(pos) + } + + switch cmd.Name() { + case "eval", "evalsha": + if cmd.stringArg(2) != "0" { + return 3 + } + + return 0 + case "publish": + return 1 + case "memory": + // https://github.com/redis/redis/issues/7493 + if cmd.stringArg(1) == "usage" { + return 2 + } + } + + if info != nil { + return int(info.FirstKeyPos) + } + return 0 +} + +func cmdString(cmd Cmder, val interface{}) string { + b := make([]byte, 0, 64) + + for i, arg := range cmd.Args() { + if i > 0 { + b = append(b, ' ') + } + b = internal.AppendArg(b, arg) + } + + if err := cmd.Err(); err != nil { + b = append(b, ": "...) + b = append(b, err.Error()...) + } else if val != nil { + b = append(b, ": "...) + b = internal.AppendArg(b, val) + } + + return internal.String(b) +} + +//------------------------------------------------------------------------------ + +type baseCmd struct { + ctx context.Context + args []interface{} + err error + keyPos int8 + + _readTimeout *time.Duration +} + +var _ Cmder = (*Cmd)(nil) + +func (cmd *baseCmd) Name() string { + if len(cmd.args) == 0 { + return "" + } + // Cmd name must be lower cased. + return internal.ToLower(cmd.stringArg(0)) +} + +func (cmd *baseCmd) FullName() string { + switch name := cmd.Name(); name { + case "cluster", "command": + if len(cmd.args) == 1 { + return name + } + if s2, ok := cmd.args[1].(string); ok { + return name + " " + s2 + } + return name + default: + return name + } +} + +func (cmd *baseCmd) Args() []interface{} { + return cmd.args +} + +func (cmd *baseCmd) stringArg(pos int) string { + if pos < 0 || pos >= len(cmd.args) { + return "" + } + s, _ := cmd.args[pos].(string) + return s +} + +func (cmd *baseCmd) firstKeyPos() int8 { + return cmd.keyPos +} + +func (cmd *baseCmd) setFirstKeyPos(keyPos int8) { + cmd.keyPos = keyPos +} + +func (cmd *baseCmd) SetErr(e error) { + cmd.err = e +} + +func (cmd *baseCmd) Err() error { + return cmd.err +} + +func (cmd *baseCmd) readTimeout() *time.Duration { + return cmd._readTimeout +} + +func (cmd *baseCmd) setReadTimeout(d time.Duration) { + cmd._readTimeout = &d +} + +//------------------------------------------------------------------------------ + +type Cmd struct { + baseCmd + + val interface{} +} + +func NewCmd(ctx context.Context, args ...interface{}) *Cmd { + return &Cmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *Cmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *Cmd) Val() interface{} { + return cmd.val +} + +func (cmd *Cmd) Result() (interface{}, error) { + return cmd.val, cmd.err +} + +func (cmd *Cmd) Text() (string, error) { + if cmd.err != nil { + return "", cmd.err + } + switch val := cmd.val.(type) { + case string: + return val, nil + default: + err := fmt.Errorf("redis: unexpected type=%T for String", val) + return "", err + } +} + +func (cmd *Cmd) Int() (int, error) { + if cmd.err != nil { + return 0, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return int(val), nil + case string: + return strconv.Atoi(val) + default: + err := fmt.Errorf("redis: unexpected type=%T for Int", val) + return 0, err + } +} + +func (cmd *Cmd) Int64() (int64, error) { + if cmd.err != nil { + return 0, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return val, nil + case string: + return strconv.ParseInt(val, 10, 64) + default: + err := fmt.Errorf("redis: unexpected type=%T for Int64", val) + return 0, err + } +} + +func (cmd *Cmd) Uint64() (uint64, error) { + if cmd.err != nil { + return 0, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return uint64(val), nil + case string: + return strconv.ParseUint(val, 10, 64) + default: + err := fmt.Errorf("redis: unexpected type=%T for Uint64", val) + return 0, err + } +} + +func (cmd *Cmd) Float32() (float32, error) { + if cmd.err != nil { + return 0, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return float32(val), nil + case string: + f, err := strconv.ParseFloat(val, 32) + if err != nil { + return 0, err + } + return float32(f), nil + default: + err := fmt.Errorf("redis: unexpected type=%T for Float32", val) + return 0, err + } +} + +func (cmd *Cmd) Float64() (float64, error) { + if cmd.err != nil { + return 0, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return float64(val), nil + case string: + return strconv.ParseFloat(val, 64) + default: + err := fmt.Errorf("redis: unexpected type=%T for Float64", val) + return 0, err + } +} + +func (cmd *Cmd) Bool() (bool, error) { + if cmd.err != nil { + return false, cmd.err + } + switch val := cmd.val.(type) { + case int64: + return val != 0, nil + case string: + return strconv.ParseBool(val) + default: + err := fmt.Errorf("redis: unexpected type=%T for Bool", val) + return false, err + } +} + +func (cmd *Cmd) readReply(rd *proto.Reader) (err error) { + cmd.val, err = rd.ReadReply(sliceParser) + return err +} + +// sliceParser implements proto.MultiBulkParse. +func sliceParser(rd *proto.Reader, n int64) (interface{}, error) { + vals := make([]interface{}, n) + for i := 0; i < len(vals); i++ { + v, err := rd.ReadReply(sliceParser) + if err != nil { + if err == Nil { + vals[i] = nil + continue + } + if err, ok := err.(proto.RedisError); ok { + vals[i] = err + continue + } + return nil, err + } + vals[i] = v + } + return vals, nil +} + +//------------------------------------------------------------------------------ + +type SliceCmd struct { + baseCmd + + val []interface{} +} + +var _ Cmder = (*SliceCmd)(nil) + +func NewSliceCmd(ctx context.Context, args ...interface{}) *SliceCmd { + return &SliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *SliceCmd) Val() []interface{} { + return cmd.val +} + +func (cmd *SliceCmd) Result() ([]interface{}, error) { + return cmd.val, cmd.err +} + +func (cmd *SliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +// Scan scans the results from the map into a destination struct. The map keys +// are matched in the Redis struct fields by the `redis:"field"` tag. +func (cmd *SliceCmd) Scan(dst interface{}) error { + if cmd.err != nil { + return cmd.err + } + + // Pass the list of keys and values. + // Skip the first two args for: HMGET key + var args []interface{} + if cmd.args[0] == "hmget" { + args = cmd.args[2:] + } else { + // Otherwise, it's: MGET field field ... + args = cmd.args[1:] + } + + return hscan.Scan(dst, args, cmd.val) +} + +func (cmd *SliceCmd) readReply(rd *proto.Reader) error { + v, err := rd.ReadArrayReply(sliceParser) + if err != nil { + return err + } + cmd.val = v.([]interface{}) + return nil +} + +//------------------------------------------------------------------------------ + +type StatusCmd struct { + baseCmd + + val string +} + +var _ Cmder = (*StatusCmd)(nil) + +func NewStatusCmd(ctx context.Context, args ...interface{}) *StatusCmd { + return &StatusCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StatusCmd) Val() string { + return cmd.val +} + +func (cmd *StatusCmd) Result() (string, error) { + return cmd.val, cmd.err +} + +func (cmd *StatusCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StatusCmd) readReply(rd *proto.Reader) (err error) { + cmd.val, err = rd.ReadString() + return err +} + +//------------------------------------------------------------------------------ + +type IntCmd struct { + baseCmd + + val int64 +} + +var _ Cmder = (*IntCmd)(nil) + +func NewIntCmd(ctx context.Context, args ...interface{}) *IntCmd { + return &IntCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *IntCmd) Val() int64 { + return cmd.val +} + +func (cmd *IntCmd) Result() (int64, error) { + return cmd.val, cmd.err +} + +func (cmd *IntCmd) Uint64() (uint64, error) { + return uint64(cmd.val), cmd.err +} + +func (cmd *IntCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *IntCmd) readReply(rd *proto.Reader) (err error) { + cmd.val, err = rd.ReadIntReply() + return err +} + +//------------------------------------------------------------------------------ + +type IntSliceCmd struct { + baseCmd + + val []int64 +} + +var _ Cmder = (*IntSliceCmd)(nil) + +func NewIntSliceCmd(ctx context.Context, args ...interface{}) *IntSliceCmd { + return &IntSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *IntSliceCmd) Val() []int64 { + return cmd.val +} + +func (cmd *IntSliceCmd) Result() ([]int64, error) { + return cmd.val, cmd.err +} + +func (cmd *IntSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]int64, n) + for i := 0; i < len(cmd.val); i++ { + num, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.val[i] = num + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type DurationCmd struct { + baseCmd + + val time.Duration + precision time.Duration +} + +var _ Cmder = (*DurationCmd)(nil) + +func NewDurationCmd(ctx context.Context, precision time.Duration, args ...interface{}) *DurationCmd { + return &DurationCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + precision: precision, + } +} + +func (cmd *DurationCmd) Val() time.Duration { + return cmd.val +} + +func (cmd *DurationCmd) Result() (time.Duration, error) { + return cmd.val, cmd.err +} + +func (cmd *DurationCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *DurationCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadIntReply() + if err != nil { + return err + } + switch n { + // -2 if the key does not exist + // -1 if the key exists but has no associated expire + case -2, -1: + cmd.val = time.Duration(n) + default: + cmd.val = time.Duration(n) * cmd.precision + } + return nil +} + +//------------------------------------------------------------------------------ + +type TimeCmd struct { + baseCmd + + val time.Time +} + +var _ Cmder = (*TimeCmd)(nil) + +func NewTimeCmd(ctx context.Context, args ...interface{}) *TimeCmd { + return &TimeCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *TimeCmd) Val() time.Time { + return cmd.val +} + +func (cmd *TimeCmd) Result() (time.Time, error) { + return cmd.val, cmd.err +} + +func (cmd *TimeCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *TimeCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 2 { + return nil, fmt.Errorf("got %d elements, expected 2", n) + } + + sec, err := rd.ReadInt() + if err != nil { + return nil, err + } + + microsec, err := rd.ReadInt() + if err != nil { + return nil, err + } + + cmd.val = time.Unix(sec, microsec*1000) + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type BoolCmd struct { + baseCmd + + val bool +} + +var _ Cmder = (*BoolCmd)(nil) + +func NewBoolCmd(ctx context.Context, args ...interface{}) *BoolCmd { + return &BoolCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *BoolCmd) Val() bool { + return cmd.val +} + +func (cmd *BoolCmd) Result() (bool, error) { + return cmd.val, cmd.err +} + +func (cmd *BoolCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *BoolCmd) readReply(rd *proto.Reader) error { + v, err := rd.ReadReply(nil) + // `SET key value NX` returns nil when key already exists. But + // `SETNX key value` returns bool (0/1). So convert nil to bool. + if err == Nil { + cmd.val = false + return nil + } + if err != nil { + return err + } + switch v := v.(type) { + case int64: + cmd.val = v == 1 + return nil + case string: + cmd.val = v == "OK" + return nil + default: + return fmt.Errorf("got %T, wanted int64 or string", v) + } +} + +//------------------------------------------------------------------------------ + +type StringCmd struct { + baseCmd + + val string +} + +var _ Cmder = (*StringCmd)(nil) + +func NewStringCmd(ctx context.Context, args ...interface{}) *StringCmd { + return &StringCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StringCmd) Val() string { + return cmd.val +} + +func (cmd *StringCmd) Result() (string, error) { + return cmd.Val(), cmd.err +} + +func (cmd *StringCmd) Bytes() ([]byte, error) { + return util.StringToBytes(cmd.val), cmd.err +} + +func (cmd *StringCmd) Bool() (bool, error) { + if cmd.err != nil { + return false, cmd.err + } + return strconv.ParseBool(cmd.val) +} + +func (cmd *StringCmd) Int() (int, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.Atoi(cmd.Val()) +} + +func (cmd *StringCmd) Int64() (int64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseInt(cmd.Val(), 10, 64) +} + +func (cmd *StringCmd) Uint64() (uint64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseUint(cmd.Val(), 10, 64) +} + +func (cmd *StringCmd) Float32() (float32, error) { + if cmd.err != nil { + return 0, cmd.err + } + f, err := strconv.ParseFloat(cmd.Val(), 32) + if err != nil { + return 0, err + } + return float32(f), nil +} + +func (cmd *StringCmd) Float64() (float64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseFloat(cmd.Val(), 64) +} + +func (cmd *StringCmd) Time() (time.Time, error) { + if cmd.err != nil { + return time.Time{}, cmd.err + } + return time.Parse(time.RFC3339Nano, cmd.Val()) +} + +func (cmd *StringCmd) Scan(val interface{}) error { + if cmd.err != nil { + return cmd.err + } + return proto.Scan([]byte(cmd.val), val) +} + +func (cmd *StringCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringCmd) readReply(rd *proto.Reader) (err error) { + cmd.val, err = rd.ReadString() + return err +} + +//------------------------------------------------------------------------------ + +type FloatCmd struct { + baseCmd + + val float64 +} + +var _ Cmder = (*FloatCmd)(nil) + +func NewFloatCmd(ctx context.Context, args ...interface{}) *FloatCmd { + return &FloatCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *FloatCmd) Val() float64 { + return cmd.val +} + +func (cmd *FloatCmd) Result() (float64, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *FloatCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *FloatCmd) readReply(rd *proto.Reader) (err error) { + cmd.val, err = rd.ReadFloatReply() + return err +} + +//------------------------------------------------------------------------------ + +type FloatSliceCmd struct { + baseCmd + + val []float64 +} + +var _ Cmder = (*FloatSliceCmd)(nil) + +func NewFloatSliceCmd(ctx context.Context, args ...interface{}) *FloatSliceCmd { + return &FloatSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *FloatSliceCmd) Val() []float64 { + return cmd.val +} + +func (cmd *FloatSliceCmd) Result() ([]float64, error) { + return cmd.val, cmd.err +} + +func (cmd *FloatSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *FloatSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]float64, n) + for i := 0; i < len(cmd.val); i++ { + switch num, err := rd.ReadFloatReply(); { + case err == Nil: + cmd.val[i] = 0 + case err != nil: + return nil, err + default: + cmd.val[i] = num + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type StringSliceCmd struct { + baseCmd + + val []string +} + +var _ Cmder = (*StringSliceCmd)(nil) + +func NewStringSliceCmd(ctx context.Context, args ...interface{}) *StringSliceCmd { + return &StringSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StringSliceCmd) Val() []string { + return cmd.val +} + +func (cmd *StringSliceCmd) Result() ([]string, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *StringSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringSliceCmd) ScanSlice(container interface{}) error { + return proto.ScanSlice(cmd.Val(), container) +} + +func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]string, n) + for i := 0; i < len(cmd.val); i++ { + switch s, err := rd.ReadString(); { + case err == Nil: + cmd.val[i] = "" + case err != nil: + return nil, err + default: + cmd.val[i] = s + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type BoolSliceCmd struct { + baseCmd + + val []bool +} + +var _ Cmder = (*BoolSliceCmd)(nil) + +func NewBoolSliceCmd(ctx context.Context, args ...interface{}) *BoolSliceCmd { + return &BoolSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *BoolSliceCmd) Val() []bool { + return cmd.val +} + +func (cmd *BoolSliceCmd) Result() ([]bool, error) { + return cmd.val, cmd.err +} + +func (cmd *BoolSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]bool, n) + for i := 0; i < len(cmd.val); i++ { + n, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.val[i] = n == 1 + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type StringStringMapCmd struct { + baseCmd + + val map[string]string +} + +var _ Cmder = (*StringStringMapCmd)(nil) + +func NewStringStringMapCmd(ctx context.Context, args ...interface{}) *StringStringMapCmd { + return &StringStringMapCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StringStringMapCmd) Val() map[string]string { + return cmd.val +} + +func (cmd *StringStringMapCmd) Result() (map[string]string, error) { + return cmd.val, cmd.err +} + +func (cmd *StringStringMapCmd) String() string { + return cmdString(cmd, cmd.val) +} + +// Scan scans the results from the map into a destination struct. The map keys +// are matched in the Redis struct fields by the `redis:"field"` tag. +func (cmd *StringStringMapCmd) Scan(dst interface{}) error { + if cmd.err != nil { + return cmd.err + } + + strct, err := hscan.Struct(dst) + if err != nil { + return err + } + + for k, v := range cmd.val { + if err := strct.Scan(k, v); err != nil { + return err + } + } + + return nil +} + +func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make(map[string]string, n/2) + for i := int64(0); i < n; i += 2 { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + + value, err := rd.ReadString() + if err != nil { + return nil, err + } + + cmd.val[key] = value + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type StringIntMapCmd struct { + baseCmd + + val map[string]int64 +} + +var _ Cmder = (*StringIntMapCmd)(nil) + +func NewStringIntMapCmd(ctx context.Context, args ...interface{}) *StringIntMapCmd { + return &StringIntMapCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StringIntMapCmd) Val() map[string]int64 { + return cmd.val +} + +func (cmd *StringIntMapCmd) Result() (map[string]int64, error) { + return cmd.val, cmd.err +} + +func (cmd *StringIntMapCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make(map[string]int64, n/2) + for i := int64(0); i < n; i += 2 { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + + n, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + cmd.val[key] = n + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type StringStructMapCmd struct { + baseCmd + + val map[string]struct{} +} + +var _ Cmder = (*StringStructMapCmd)(nil) + +func NewStringStructMapCmd(ctx context.Context, args ...interface{}) *StringStructMapCmd { + return &StringStructMapCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *StringStructMapCmd) Val() map[string]struct{} { + return cmd.val +} + +func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) { + return cmd.val, cmd.err +} + +func (cmd *StringStructMapCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make(map[string]struct{}, n) + for i := int64(0); i < n; i++ { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + cmd.val[key] = struct{}{} + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XMessage struct { + ID string + Values map[string]interface{} +} + +type XMessageSliceCmd struct { + baseCmd + + val []XMessage +} + +var _ Cmder = (*XMessageSliceCmd)(nil) + +func NewXMessageSliceCmd(ctx context.Context, args ...interface{}) *XMessageSliceCmd { + return &XMessageSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XMessageSliceCmd) Val() []XMessage { + return cmd.val +} + +func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) { + return cmd.val, cmd.err +} + +func (cmd *XMessageSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) error { + var err error + cmd.val, err = readXMessageSlice(rd) + return err +} + +func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + msgs := make([]XMessage, n) + for i := 0; i < n; i++ { + var err error + msgs[i], err = readXMessage(rd) + if err != nil { + return nil, err + } + } + return msgs, nil +} + +func readXMessage(rd *proto.Reader) (XMessage, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return XMessage{}, err + } + if n != 2 { + return XMessage{}, fmt.Errorf("got %d, wanted 2", n) + } + + id, err := rd.ReadString() + if err != nil { + return XMessage{}, err + } + + var values map[string]interface{} + + v, err := rd.ReadArrayReply(stringInterfaceMapParser) + if err != nil { + if err != proto.Nil { + return XMessage{}, err + } + } else { + values = v.(map[string]interface{}) + } + + return XMessage{ + ID: id, + Values: values, + }, nil +} + +// stringInterfaceMapParser implements proto.MultiBulkParse. +func stringInterfaceMapParser(rd *proto.Reader, n int64) (interface{}, error) { + m := make(map[string]interface{}, n/2) + for i := int64(0); i < n; i += 2 { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + + value, err := rd.ReadString() + if err != nil { + return nil, err + } + + m[key] = value + } + return m, nil +} + +//------------------------------------------------------------------------------ + +type XStream struct { + Stream string + Messages []XMessage +} + +type XStreamSliceCmd struct { + baseCmd + + val []XStream +} + +var _ Cmder = (*XStreamSliceCmd)(nil) + +func NewXStreamSliceCmd(ctx context.Context, args ...interface{}) *XStreamSliceCmd { + return &XStreamSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XStreamSliceCmd) Val() []XStream { + return cmd.val +} + +func (cmd *XStreamSliceCmd) Result() ([]XStream, error) { + return cmd.val, cmd.err +} + +func (cmd *XStreamSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]XStream, n) + for i := 0; i < len(cmd.val); i++ { + i := i + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 2 { + return nil, fmt.Errorf("got %d, wanted 2", n) + } + + stream, err := rd.ReadString() + if err != nil { + return nil, err + } + + msgs, err := readXMessageSlice(rd) + if err != nil { + return nil, err + } + + cmd.val[i] = XStream{ + Stream: stream, + Messages: msgs, + } + return nil, nil + }) + if err != nil { + return nil, err + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XPending struct { + Count int64 + Lower string + Higher string + Consumers map[string]int64 +} + +type XPendingCmd struct { + baseCmd + val *XPending +} + +var _ Cmder = (*XPendingCmd)(nil) + +func NewXPendingCmd(ctx context.Context, args ...interface{}) *XPendingCmd { + return &XPendingCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XPendingCmd) Val() *XPending { + return cmd.val +} + +func (cmd *XPendingCmd) Result() (*XPending, error) { + return cmd.val, cmd.err +} + +func (cmd *XPendingCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XPendingCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 4 { + return nil, fmt.Errorf("got %d, wanted 4", n) + } + + count, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + lower, err := rd.ReadString() + if err != nil && err != Nil { + return nil, err + } + + higher, err := rd.ReadString() + if err != nil && err != Nil { + return nil, err + } + + cmd.val = &XPending{ + Count: count, + Lower: lower, + Higher: higher, + } + _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + for i := int64(0); i < n; i++ { + _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 2 { + return nil, fmt.Errorf("got %d, wanted 2", n) + } + + consumerName, err := rd.ReadString() + if err != nil { + return nil, err + } + + consumerPending, err := rd.ReadInt() + if err != nil { + return nil, err + } + + if cmd.val.Consumers == nil { + cmd.val.Consumers = make(map[string]int64) + } + cmd.val.Consumers[consumerName] = consumerPending + + return nil, nil + }) + if err != nil { + return nil, err + } + } + return nil, nil + }) + if err != nil && err != Nil { + return nil, err + } + + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XPendingExt struct { + ID string + Consumer string + Idle time.Duration + RetryCount int64 +} + +type XPendingExtCmd struct { + baseCmd + val []XPendingExt +} + +var _ Cmder = (*XPendingExtCmd)(nil) + +func NewXPendingExtCmd(ctx context.Context, args ...interface{}) *XPendingExtCmd { + return &XPendingExtCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XPendingExtCmd) Val() []XPendingExt { + return cmd.val +} + +func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) { + return cmd.val, cmd.err +} + +func (cmd *XPendingExtCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]XPendingExt, 0, n) + for i := int64(0); i < n; i++ { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 4 { + return nil, fmt.Errorf("got %d, wanted 4", n) + } + + id, err := rd.ReadString() + if err != nil { + return nil, err + } + + consumer, err := rd.ReadString() + if err != nil && err != Nil { + return nil, err + } + + idle, err := rd.ReadIntReply() + if err != nil && err != Nil { + return nil, err + } + + retryCount, err := rd.ReadIntReply() + if err != nil && err != Nil { + return nil, err + } + + cmd.val = append(cmd.val, XPendingExt{ + ID: id, + Consumer: consumer, + Idle: time.Duration(idle) * time.Millisecond, + RetryCount: retryCount, + }) + return nil, nil + }) + if err != nil { + return nil, err + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XAutoClaimCmd struct { + baseCmd + + start string + val []XMessage +} + +var _ Cmder = (*XAutoClaimCmd)(nil) + +func NewXAutoClaimCmd(ctx context.Context, args ...interface{}) *XAutoClaimCmd { + return &XAutoClaimCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XAutoClaimCmd) Val() (messages []XMessage, start string) { + return cmd.val, cmd.start +} + +func (cmd *XAutoClaimCmd) Result() (messages []XMessage, start string, err error) { + return cmd.val, cmd.start, cmd.err +} + +func (cmd *XAutoClaimCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XAutoClaimCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 2 { + return nil, fmt.Errorf("got %d, wanted 2", n) + } + var err error + + cmd.start, err = rd.ReadString() + if err != nil { + return nil, err + } + + cmd.val, err = readXMessageSlice(rd) + if err != nil { + return nil, err + } + + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XAutoClaimJustIDCmd struct { + baseCmd + + start string + val []string +} + +var _ Cmder = (*XAutoClaimJustIDCmd)(nil) + +func NewXAutoClaimJustIDCmd(ctx context.Context, args ...interface{}) *XAutoClaimJustIDCmd { + return &XAutoClaimJustIDCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XAutoClaimJustIDCmd) Val() (ids []string, start string) { + return cmd.val, cmd.start +} + +func (cmd *XAutoClaimJustIDCmd) Result() (ids []string, start string, err error) { + return cmd.val, cmd.start, cmd.err +} + +func (cmd *XAutoClaimJustIDCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XAutoClaimJustIDCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 2 { + return nil, fmt.Errorf("got %d, wanted 2", n) + } + var err error + + cmd.start, err = rd.ReadString() + if err != nil { + return nil, err + } + + nn, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + cmd.val = make([]string, nn) + for i := 0; i < nn; i++ { + cmd.val[i], err = rd.ReadString() + if err != nil { + return nil, err + } + } + + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type XInfoConsumersCmd struct { + baseCmd + val []XInfoConsumer +} + +type XInfoConsumer struct { + Name string + Pending int64 + Idle int64 +} + +var _ Cmder = (*XInfoConsumersCmd)(nil) + +func NewXInfoConsumersCmd(ctx context.Context, stream string, group string) *XInfoConsumersCmd { + return &XInfoConsumersCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: []interface{}{"xinfo", "consumers", stream, group}, + }, + } +} + +func (cmd *XInfoConsumersCmd) Val() []XInfoConsumer { + return cmd.val +} + +func (cmd *XInfoConsumersCmd) Result() ([]XInfoConsumer, error) { + return cmd.val, cmd.err +} + +func (cmd *XInfoConsumersCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XInfoConsumersCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + + cmd.val = make([]XInfoConsumer, n) + + for i := 0; i < n; i++ { + cmd.val[i], err = readXConsumerInfo(rd) + if err != nil { + return err + } + } + + return nil +} + +func readXConsumerInfo(rd *proto.Reader) (XInfoConsumer, error) { + var consumer XInfoConsumer + + n, err := rd.ReadArrayLen() + if err != nil { + return consumer, err + } + if n != 6 { + return consumer, fmt.Errorf("redis: got %d elements in XINFO CONSUMERS reply, wanted 6", n) + } + + for i := 0; i < 3; i++ { + key, err := rd.ReadString() + if err != nil { + return consumer, err + } + + val, err := rd.ReadString() + if err != nil { + return consumer, err + } + + switch key { + case "name": + consumer.Name = val + case "pending": + consumer.Pending, err = strconv.ParseInt(val, 0, 64) + if err != nil { + return consumer, err + } + case "idle": + consumer.Idle, err = strconv.ParseInt(val, 0, 64) + if err != nil { + return consumer, err + } + default: + return consumer, fmt.Errorf("redis: unexpected content %s in XINFO CONSUMERS reply", key) + } + } + + return consumer, nil +} + +//------------------------------------------------------------------------------ + +type XInfoGroupsCmd struct { + baseCmd + val []XInfoGroup +} + +type XInfoGroup struct { + Name string + Consumers int64 + Pending int64 + LastDeliveredID string +} + +var _ Cmder = (*XInfoGroupsCmd)(nil) + +func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd { + return &XInfoGroupsCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: []interface{}{"xinfo", "groups", stream}, + }, + } +} + +func (cmd *XInfoGroupsCmd) Val() []XInfoGroup { + return cmd.val +} + +func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) { + return cmd.val, cmd.err +} + +func (cmd *XInfoGroupsCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + + cmd.val = make([]XInfoGroup, n) + + for i := 0; i < n; i++ { + cmd.val[i], err = readXGroupInfo(rd) + if err != nil { + return err + } + } + + return nil +} + +func readXGroupInfo(rd *proto.Reader) (XInfoGroup, error) { + var group XInfoGroup + + n, err := rd.ReadArrayLen() + if err != nil { + return group, err + } + if n != 8 { + return group, fmt.Errorf("redis: got %d elements in XINFO GROUPS reply, wanted 8", n) + } + + for i := 0; i < 4; i++ { + key, err := rd.ReadString() + if err != nil { + return group, err + } + + val, err := rd.ReadString() + if err != nil { + return group, err + } + + switch key { + case "name": + group.Name = val + case "consumers": + group.Consumers, err = strconv.ParseInt(val, 0, 64) + if err != nil { + return group, err + } + case "pending": + group.Pending, err = strconv.ParseInt(val, 0, 64) + if err != nil { + return group, err + } + case "last-delivered-id": + group.LastDeliveredID = val + default: + return group, fmt.Errorf("redis: unexpected content %s in XINFO GROUPS reply", key) + } + } + + return group, nil +} + +//------------------------------------------------------------------------------ + +type XInfoStreamCmd struct { + baseCmd + val *XInfoStream +} + +type XInfoStream struct { + Length int64 + RadixTreeKeys int64 + RadixTreeNodes int64 + Groups int64 + LastGeneratedID string + FirstEntry XMessage + LastEntry XMessage +} + +var _ Cmder = (*XInfoStreamCmd)(nil) + +func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd { + return &XInfoStreamCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: []interface{}{"xinfo", "stream", stream}, + }, + } +} + +func (cmd *XInfoStreamCmd) Val() *XInfoStream { + return cmd.val +} + +func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) { + return cmd.val, cmd.err +} + +func (cmd *XInfoStreamCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error { + v, err := rd.ReadReply(xStreamInfoParser) + if err != nil { + return err + } + cmd.val = v.(*XInfoStream) + return nil +} + +func xStreamInfoParser(rd *proto.Reader, n int64) (interface{}, error) { + if n != 14 { + return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+ + "wanted 14", n) + } + var info XInfoStream + for i := 0; i < 7; i++ { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + switch key { + case "length": + info.Length, err = rd.ReadIntReply() + case "radix-tree-keys": + info.RadixTreeKeys, err = rd.ReadIntReply() + case "radix-tree-nodes": + info.RadixTreeNodes, err = rd.ReadIntReply() + case "groups": + info.Groups, err = rd.ReadIntReply() + case "last-generated-id": + info.LastGeneratedID, err = rd.ReadString() + case "first-entry": + info.FirstEntry, err = readXMessage(rd) + if err == Nil { + err = nil + } + case "last-entry": + info.LastEntry, err = readXMessage(rd) + if err == Nil { + err = nil + } + default: + return nil, fmt.Errorf("redis: unexpected content %s "+ + "in XINFO STREAM reply", key) + } + if err != nil { + return nil, err + } + } + return &info, nil +} + +//------------------------------------------------------------------------------ + +type XInfoStreamFullCmd struct { + baseCmd + val *XInfoStreamFull +} + +type XInfoStreamFull struct { + Length int64 + RadixTreeKeys int64 + RadixTreeNodes int64 + LastGeneratedID string + Entries []XMessage + Groups []XInfoStreamGroup +} + +type XInfoStreamGroup struct { + Name string + LastDeliveredID string + PelCount int64 + Pending []XInfoStreamGroupPending + Consumers []XInfoStreamConsumer +} + +type XInfoStreamGroupPending struct { + ID string + Consumer string + DeliveryTime time.Time + DeliveryCount int64 +} + +type XInfoStreamConsumer struct { + Name string + SeenTime time.Time + PelCount int64 + Pending []XInfoStreamConsumerPending +} + +type XInfoStreamConsumerPending struct { + ID string + DeliveryTime time.Time + DeliveryCount int64 +} + +var _ Cmder = (*XInfoStreamFullCmd)(nil) + +func NewXInfoStreamFullCmd(ctx context.Context, args ...interface{}) *XInfoStreamFullCmd { + return &XInfoStreamFullCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *XInfoStreamFullCmd) Val() *XInfoStreamFull { + return cmd.val +} + +func (cmd *XInfoStreamFullCmd) Result() (*XInfoStreamFull, error) { + return cmd.val, cmd.err +} + +func (cmd *XInfoStreamFullCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *XInfoStreamFullCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + if n != 12 { + return fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+ + "wanted 12", n) + } + + cmd.val = &XInfoStreamFull{} + + for i := 0; i < 6; i++ { + key, err := rd.ReadString() + if err != nil { + return err + } + + switch key { + case "length": + cmd.val.Length, err = rd.ReadIntReply() + case "radix-tree-keys": + cmd.val.RadixTreeKeys, err = rd.ReadIntReply() + case "radix-tree-nodes": + cmd.val.RadixTreeNodes, err = rd.ReadIntReply() + case "last-generated-id": + cmd.val.LastGeneratedID, err = rd.ReadString() + case "entries": + cmd.val.Entries, err = readXMessageSlice(rd) + case "groups": + cmd.val.Groups, err = readStreamGroups(rd) + default: + return fmt.Errorf("redis: unexpected content %s "+ + "in XINFO STREAM reply", key) + } + if err != nil { + return err + } + } + return nil +} + +func readStreamGroups(rd *proto.Reader) ([]XInfoStreamGroup, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + groups := make([]XInfoStreamGroup, 0, n) + for i := 0; i < n; i++ { + nn, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if nn != 10 { + return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+ + "wanted 10", nn) + } + + group := XInfoStreamGroup{} + + for f := 0; f < 5; f++ { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + + switch key { + case "name": + group.Name, err = rd.ReadString() + case "last-delivered-id": + group.LastDeliveredID, err = rd.ReadString() + case "pel-count": + group.PelCount, err = rd.ReadIntReply() + case "pending": + group.Pending, err = readXInfoStreamGroupPending(rd) + case "consumers": + group.Consumers, err = readXInfoStreamConsumers(rd) + default: + return nil, fmt.Errorf("redis: unexpected content %s "+ + "in XINFO STREAM reply", key) + } + + if err != nil { + return nil, err + } + } + + groups = append(groups, group) + } + + return groups, nil +} + +func readXInfoStreamGroupPending(rd *proto.Reader) ([]XInfoStreamGroupPending, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + pending := make([]XInfoStreamGroupPending, 0, n) + + for i := 0; i < n; i++ { + nn, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if nn != 4 { + return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+ + "wanted 4", nn) + } + + p := XInfoStreamGroupPending{} + + p.ID, err = rd.ReadString() + if err != nil { + return nil, err + } + + p.Consumer, err = rd.ReadString() + if err != nil { + return nil, err + } + + delivery, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond)) + + p.DeliveryCount, err = rd.ReadIntReply() + if err != nil { + return nil, err + } + + pending = append(pending, p) + } + + return pending, nil +} + +func readXInfoStreamConsumers(rd *proto.Reader) ([]XInfoStreamConsumer, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + consumers := make([]XInfoStreamConsumer, 0, n) + + for i := 0; i < n; i++ { + nn, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if nn != 8 { + return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+ + "wanted 8", nn) + } + + c := XInfoStreamConsumer{} + + for f := 0; f < 4; f++ { + cKey, err := rd.ReadString() + if err != nil { + return nil, err + } + + switch cKey { + case "name": + c.Name, err = rd.ReadString() + case "seen-time": + seen, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + c.SeenTime = time.Unix(seen/1000, seen%1000*int64(time.Millisecond)) + case "pel-count": + c.PelCount, err = rd.ReadIntReply() + case "pending": + pendingNumber, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + c.Pending = make([]XInfoStreamConsumerPending, 0, pendingNumber) + + for pn := 0; pn < pendingNumber; pn++ { + nn, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if nn != 3 { + return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+ + "wanted 3", nn) + } + + p := XInfoStreamConsumerPending{} + + p.ID, err = rd.ReadString() + if err != nil { + return nil, err + } + + delivery, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond)) + + p.DeliveryCount, err = rd.ReadIntReply() + if err != nil { + return nil, err + } + + c.Pending = append(c.Pending, p) + } + default: + return nil, fmt.Errorf("redis: unexpected content %s "+ + "in XINFO STREAM reply", cKey) + } + if err != nil { + return nil, err + } + } + consumers = append(consumers, c) + } + + return consumers, nil +} + +//------------------------------------------------------------------------------ + +type ZSliceCmd struct { + baseCmd + + val []Z +} + +var _ Cmder = (*ZSliceCmd)(nil) + +func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd { + return &ZSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *ZSliceCmd) Val() []Z { + return cmd.val +} + +func (cmd *ZSliceCmd) Result() ([]Z, error) { + return cmd.val, cmd.err +} + +func (cmd *ZSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]Z, n/2) + for i := 0; i < len(cmd.val); i++ { + member, err := rd.ReadString() + if err != nil { + return nil, err + } + + score, err := rd.ReadFloatReply() + if err != nil { + return nil, err + } + + cmd.val[i] = Z{ + Member: member, + Score: score, + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type ZWithKeyCmd struct { + baseCmd + + val *ZWithKey +} + +var _ Cmder = (*ZWithKeyCmd)(nil) + +func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd { + return &ZWithKeyCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *ZWithKeyCmd) Val() *ZWithKey { + return cmd.val +} + +func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *ZWithKeyCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + if n != 3 { + return nil, fmt.Errorf("got %d elements, expected 3", n) + } + + cmd.val = &ZWithKey{} + var err error + + cmd.val.Key, err = rd.ReadString() + if err != nil { + return nil, err + } + + cmd.val.Member, err = rd.ReadString() + if err != nil { + return nil, err + } + + cmd.val.Score, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type ScanCmd struct { + baseCmd + + page []string + cursor uint64 + + process cmdable +} + +var _ Cmder = (*ScanCmd)(nil) + +func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd { + return &ScanCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + process: process, + } +} + +func (cmd *ScanCmd) Val() (keys []string, cursor uint64) { + return cmd.page, cmd.cursor +} + +func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) { + return cmd.page, cmd.cursor, cmd.err +} + +func (cmd *ScanCmd) String() string { + return cmdString(cmd, cmd.page) +} + +func (cmd *ScanCmd) readReply(rd *proto.Reader) (err error) { + cmd.page, cmd.cursor, err = rd.ReadScanReply() + return err +} + +// Iterator creates a new ScanIterator. +func (cmd *ScanCmd) Iterator() *ScanIterator { + return &ScanIterator{ + cmd: cmd, + } +} + +//------------------------------------------------------------------------------ + +type ClusterNode struct { + ID string + Addr string +} + +type ClusterSlot struct { + Start int + End int + Nodes []ClusterNode +} + +type ClusterSlotsCmd struct { + baseCmd + + val []ClusterSlot +} + +var _ Cmder = (*ClusterSlotsCmd)(nil) + +func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd { + return &ClusterSlotsCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *ClusterSlotsCmd) Val() []ClusterSlot { + return cmd.val +} + +func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *ClusterSlotsCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]ClusterSlot, n) + for i := 0; i < len(cmd.val); i++ { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if n < 2 { + err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n) + return nil, err + } + + start, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + end, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + nodes := make([]ClusterNode, n-2) + for j := 0; j < len(nodes); j++ { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if n != 2 && n != 3 { + err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n) + return nil, err + } + + ip, err := rd.ReadString() + if err != nil { + return nil, err + } + + port, err := rd.ReadString() + if err != nil { + return nil, err + } + + nodes[j].Addr = net.JoinHostPort(ip, port) + + if n == 3 { + id, err := rd.ReadString() + if err != nil { + return nil, err + } + nodes[j].ID = id + } + } + + cmd.val[i] = ClusterSlot{ + Start: int(start), + End: int(end), + Nodes: nodes, + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +// GeoLocation is used with GeoAdd to add geospatial location. +type GeoLocation struct { + Name string + Longitude, Latitude, Dist float64 + GeoHash int64 +} + +// GeoRadiusQuery is used with GeoRadius to query geospatial index. +type GeoRadiusQuery struct { + Radius float64 + // Can be m, km, ft, or mi. Default is km. + Unit string + WithCoord bool + WithDist bool + WithGeoHash bool + Count int + // Can be ASC or DESC. Default is no sort order. + Sort string + Store string + StoreDist string +} + +type GeoLocationCmd struct { + baseCmd + + q *GeoRadiusQuery + locations []GeoLocation +} + +var _ Cmder = (*GeoLocationCmd)(nil) + +func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd { + return &GeoLocationCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: geoLocationArgs(q, args...), + }, + q: q, + } +} + +func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} { + args = append(args, q.Radius) + if q.Unit != "" { + args = append(args, q.Unit) + } else { + args = append(args, "km") + } + if q.WithCoord { + args = append(args, "withcoord") + } + if q.WithDist { + args = append(args, "withdist") + } + if q.WithGeoHash { + args = append(args, "withhash") + } + if q.Count > 0 { + args = append(args, "count", q.Count) + } + if q.Sort != "" { + args = append(args, q.Sort) + } + if q.Store != "" { + args = append(args, "store") + args = append(args, q.Store) + } + if q.StoreDist != "" { + args = append(args, "storedist") + args = append(args, q.StoreDist) + } + return args +} + +func (cmd *GeoLocationCmd) Val() []GeoLocation { + return cmd.locations +} + +func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) { + return cmd.locations, cmd.err +} + +func (cmd *GeoLocationCmd) String() string { + return cmdString(cmd, cmd.locations) +} + +func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error { + v, err := rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q)) + if err != nil { + return err + } + cmd.locations = v.([]GeoLocation) + return nil +} + +func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse { + return func(rd *proto.Reader, n int64) (interface{}, error) { + locs := make([]GeoLocation, 0, n) + for i := int64(0); i < n; i++ { + v, err := rd.ReadReply(newGeoLocationParser(q)) + if err != nil { + return nil, err + } + switch vv := v.(type) { + case string: + locs = append(locs, GeoLocation{ + Name: vv, + }) + case *GeoLocation: + // TODO: avoid copying + locs = append(locs, *vv) + default: + return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v) + } + } + return locs, nil + } +} + +func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse { + return func(rd *proto.Reader, n int64) (interface{}, error) { + var loc GeoLocation + var err error + + loc.Name, err = rd.ReadString() + if err != nil { + return nil, err + } + if q.WithDist { + loc.Dist, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + } + if q.WithGeoHash { + loc.GeoHash, err = rd.ReadIntReply() + if err != nil { + return nil, err + } + } + if q.WithCoord { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if n != 2 { + return nil, fmt.Errorf("got %d coordinates, expected 2", n) + } + + loc.Longitude, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + loc.Latitude, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + } + + return &loc, nil + } +} + +//------------------------------------------------------------------------------ + +// GeoSearchQuery is used for GEOSearch/GEOSearchStore command query. +type GeoSearchQuery struct { + Member string + + // Latitude and Longitude when using FromLonLat option. + Longitude float64 + Latitude float64 + + // Distance and unit when using ByRadius option. + // Can use m, km, ft, or mi. Default is km. + Radius float64 + RadiusUnit string + + // Height, width and unit when using ByBox option. + // Can be m, km, ft, or mi. Default is km. + BoxWidth float64 + BoxHeight float64 + BoxUnit string + + // Can be ASC or DESC. Default is no sort order. + Sort string + Count int + CountAny bool +} + +type GeoSearchLocationQuery struct { + GeoSearchQuery + + WithCoord bool + WithDist bool + WithHash bool +} + +type GeoSearchStoreQuery struct { + GeoSearchQuery + + // When using the StoreDist option, the command stores the items in a + // sorted set populated with their distance from the center of the circle or box, + // as a floating-point number, in the same unit specified for that shape. + StoreDist bool +} + +func geoSearchLocationArgs(q *GeoSearchLocationQuery, args []interface{}) []interface{} { + args = geoSearchArgs(&q.GeoSearchQuery, args) + + if q.WithCoord { + args = append(args, "withcoord") + } + if q.WithDist { + args = append(args, "withdist") + } + if q.WithHash { + args = append(args, "withhash") + } + + return args +} + +func geoSearchArgs(q *GeoSearchQuery, args []interface{}) []interface{} { + if q.Member != "" { + args = append(args, "frommember", q.Member) + } else { + args = append(args, "fromlonlat", q.Longitude, q.Latitude) + } + + if q.Radius > 0 { + if q.RadiusUnit == "" { + q.RadiusUnit = "km" + } + args = append(args, "byradius", q.Radius, q.RadiusUnit) + } else { + if q.BoxUnit == "" { + q.BoxUnit = "km" + } + args = append(args, "bybox", q.BoxWidth, q.BoxHeight, q.BoxUnit) + } + + if q.Sort != "" { + args = append(args, q.Sort) + } + + if q.Count > 0 { + args = append(args, "count", q.Count) + if q.CountAny { + args = append(args, "any") + } + } + + return args +} + +type GeoSearchLocationCmd struct { + baseCmd + + opt *GeoSearchLocationQuery + val []GeoLocation +} + +var _ Cmder = (*GeoSearchLocationCmd)(nil) + +func NewGeoSearchLocationCmd( + ctx context.Context, opt *GeoSearchLocationQuery, args ...interface{}, +) *GeoSearchLocationCmd { + return &GeoSearchLocationCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + opt: opt, + } +} + +func (cmd *GeoSearchLocationCmd) Val() []GeoLocation { + return cmd.val +} + +func (cmd *GeoSearchLocationCmd) Result() ([]GeoLocation, error) { + return cmd.val, cmd.err +} + +func (cmd *GeoSearchLocationCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *GeoSearchLocationCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + + cmd.val = make([]GeoLocation, n) + for i := 0; i < n; i++ { + _, err = rd.ReadArrayLen() + if err != nil { + return err + } + + var loc GeoLocation + + loc.Name, err = rd.ReadString() + if err != nil { + return err + } + if cmd.opt.WithDist { + loc.Dist, err = rd.ReadFloatReply() + if err != nil { + return err + } + } + if cmd.opt.WithHash { + loc.GeoHash, err = rd.ReadIntReply() + if err != nil { + return err + } + } + if cmd.opt.WithCoord { + nn, err := rd.ReadArrayLen() + if err != nil { + return err + } + if nn != 2 { + return fmt.Errorf("got %d coordinates, expected 2", nn) + } + + loc.Longitude, err = rd.ReadFloatReply() + if err != nil { + return err + } + loc.Latitude, err = rd.ReadFloatReply() + if err != nil { + return err + } + } + + cmd.val[i] = loc + } + + return nil +} + +//------------------------------------------------------------------------------ + +type GeoPos struct { + Longitude, Latitude float64 +} + +type GeoPosCmd struct { + baseCmd + + val []*GeoPos +} + +var _ Cmder = (*GeoPosCmd)(nil) + +func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd { + return &GeoPosCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *GeoPosCmd) Val() []*GeoPos { + return cmd.val +} + +func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *GeoPosCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]*GeoPos, n) + for i := 0; i < len(cmd.val); i++ { + i := i + _, err := rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { + longitude, err := rd.ReadFloatReply() + if err != nil { + return nil, err + } + + latitude, err := rd.ReadFloatReply() + if err != nil { + return nil, err + } + + cmd.val[i] = &GeoPos{ + Longitude: longitude, + Latitude: latitude, + } + return nil, nil + }) + if err != nil { + if err == Nil { + cmd.val[i] = nil + continue + } + return nil, err + } + } + return nil, nil + }) + return err +} + +//------------------------------------------------------------------------------ + +type CommandInfo struct { + Name string + Arity int8 + Flags []string + ACLFlags []string + FirstKeyPos int8 + LastKeyPos int8 + StepCount int8 + ReadOnly bool +} + +type CommandsInfoCmd struct { + baseCmd + + val map[string]*CommandInfo +} + +var _ Cmder = (*CommandsInfoCmd)(nil) + +func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd { + return &CommandsInfoCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo { + return cmd.val +} + +func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *CommandsInfoCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make(map[string]*CommandInfo, n) + for i := int64(0); i < n; i++ { + v, err := rd.ReadReply(commandInfoParser) + if err != nil { + return nil, err + } + vv := v.(*CommandInfo) + cmd.val[vv.Name] = vv + } + return nil, nil + }) + return err +} + +func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) { + const numArgRedis5 = 6 + const numArgRedis6 = 7 + + switch n { + case numArgRedis5, numArgRedis6: + // continue + default: + return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 7", n) + } + + var cmd CommandInfo + var err error + + cmd.Name, err = rd.ReadString() + if err != nil { + return nil, err + } + + arity, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.Arity = int8(arity) + + _, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.Flags = make([]string, n) + for i := 0; i < len(cmd.Flags); i++ { + switch s, err := rd.ReadString(); { + case err == Nil: + cmd.Flags[i] = "" + case err != nil: + return nil, err + default: + cmd.Flags[i] = s + } + } + return nil, nil + }) + if err != nil { + return nil, err + } + + firstKeyPos, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.FirstKeyPos = int8(firstKeyPos) + + lastKeyPos, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.LastKeyPos = int8(lastKeyPos) + + stepCount, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + cmd.StepCount = int8(stepCount) + + for _, flag := range cmd.Flags { + if flag == "readonly" { + cmd.ReadOnly = true + break + } + } + + if n == numArgRedis5 { + return &cmd, nil + } + + _, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.ACLFlags = make([]string, n) + for i := 0; i < len(cmd.ACLFlags); i++ { + switch s, err := rd.ReadString(); { + case err == Nil: + cmd.ACLFlags[i] = "" + case err != nil: + return nil, err + default: + cmd.ACLFlags[i] = s + } + } + return nil, nil + }) + if err != nil { + return nil, err + } + + return &cmd, nil +} + +//------------------------------------------------------------------------------ + +type cmdsInfoCache struct { + fn func(ctx context.Context) (map[string]*CommandInfo, error) + + once internal.Once + cmds map[string]*CommandInfo +} + +func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache { + return &cmdsInfoCache{ + fn: fn, + } +} + +func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) { + err := c.once.Do(func() error { + cmds, err := c.fn(ctx) + if err != nil { + return err + } + + // Extensions have cmd names in upper case. Convert them to lower case. + for k, v := range cmds { + lower := internal.ToLower(k) + if lower != k { + cmds[lower] = v + } + } + + c.cmds = cmds + return nil + }) + return c.cmds, err +} + +//------------------------------------------------------------------------------ + +type SlowLog struct { + ID int64 + Time time.Time + Duration time.Duration + Args []string + // These are also optional fields emitted only by Redis 4.0 or greater: + // https://redis.io/commands/slowlog#output-format + ClientAddr string + ClientName string +} + +type SlowLogCmd struct { + baseCmd + + val []SlowLog +} + +var _ Cmder = (*SlowLogCmd)(nil) + +func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd { + return &SlowLogCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *SlowLogCmd) Val() []SlowLog { + return cmd.val +} + +func (cmd *SlowLogCmd) Result() ([]SlowLog, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *SlowLogCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { + _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]SlowLog, n) + for i := 0; i < len(cmd.val); i++ { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if n < 4 { + err := fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", n) + return nil, err + } + + id, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + createdAt, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + createdAtTime := time.Unix(createdAt, 0) + + costs, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + costsDuration := time.Duration(costs) * time.Microsecond + + cmdLen, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if cmdLen < 1 { + err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen) + return nil, err + } + + cmdString := make([]string, cmdLen) + for i := 0; i < cmdLen; i++ { + cmdString[i], err = rd.ReadString() + if err != nil { + return nil, err + } + } + + var address, name string + for i := 4; i < n; i++ { + str, err := rd.ReadString() + if err != nil { + return nil, err + } + if i == 4 { + address = str + } else if i == 5 { + name = str + } + } + + cmd.val[i] = SlowLog{ + ID: id, + Time: createdAtTime, + Duration: costsDuration, + Args: cmdString, + ClientAddr: address, + ClientName: name, + } + } + return nil, nil + }) + return err +} diff --git a/vendor/github.com/go-redis/redis/v8/commands.go b/vendor/github.com/go-redis/redis/v8/commands.go new file mode 100644 index 0000000..80b2bab --- /dev/null +++ b/vendor/github.com/go-redis/redis/v8/commands.go @@ -0,0 +1,3419 @@ +package redis + +import ( + "context" + "errors" + "io" + "time" + + "github.com/go-redis/redis/v8/internal" +) + +// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, +// otherwise you will receive an error: (error) ERR syntax error. +// For example: +// +// rdb.Set(ctx, key, value, redis.KeepTTL) +const KeepTTL = -1 + +func usePrecise(dur time.Duration) bool { + return dur < time.Second || dur%time.Second != 0 +} + +func formatMs(ctx context.Context, dur time.Duration) int64 { + if dur > 0 && dur < time.Millisecond { + internal.Logger.Printf( + ctx, + "specified duration is %s, but minimal supported value is %s - truncating to 1ms", + dur, time.Millisecond, + ) + return 1 + } + return int64(dur / time.Millisecond) +} + +func formatSec(ctx context.Context, dur time.Duration) int64 { + if dur > 0 && dur < time.Second { + internal.Logger.Printf( + ctx, + "specified duration is %s, but minimal supported value is %s - truncating to 1s", + dur, time.Second, + ) + return 1 + } + return int64(dur / time.Second) +} + +func appendArgs(dst, src []interface{}) []interface{} { + if len(src) == 1 { + return appendArg(dst, src[0]) + } + + dst = append(dst, src...) + return dst +} + +func appendArg(dst []interface{}, arg interface{}) []interface{} { + switch arg := arg.(type) { + case []string: + for _, s := range arg { + dst = append(dst, s) + } + return dst + case []interface{}: + dst = append(dst, arg...) + return dst + case map[string]interface{}: + for k, v := range arg { + dst = append(dst, k, v) + } + return dst + case map[string]string: + for k, v := range arg { + dst = append(dst, k, v) + } + return dst + default: + return append(dst, arg) + } +} + +type Cmdable interface { + Pipeline() Pipeliner + Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) + + TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) + TxPipeline() Pipeliner + + Command(ctx context.Context) *CommandsInfoCmd + ClientGetName(ctx context.Context) *StringCmd + Echo(ctx context.Context, message interface{}) *StringCmd + Ping(ctx context.Context) *StatusCmd + Quit(ctx context.Context) *StatusCmd + Del(ctx context.Context, keys ...string) *IntCmd + Unlink(ctx context.Context, keys ...string) *IntCmd + Dump(ctx context.Context, key string) *StringCmd + Exists(ctx context.Context, keys ...string) *IntCmd + Expire(ctx context.Context, key string, expiration time.Duration) *BoolCmd + ExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd + Keys(ctx context.Context, pattern string) *StringSliceCmd + Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd + Move(ctx context.Context, key string, db int) *BoolCmd + ObjectRefCount(ctx context.Context, key string) *IntCmd + ObjectEncoding(ctx context.Context, key string) *StringCmd + ObjectIdleTime(ctx context.Context, key string) *DurationCmd + Persist(ctx context.Context, key string) *BoolCmd + PExpire(ctx context.Context, key string, expiration time.Duration) *BoolCmd + PExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd + PTTL(ctx context.Context, key string) *DurationCmd + RandomKey(ctx context.Context) *StringCmd + Rename(ctx context.Context, key, newkey string) *StatusCmd + RenameNX(ctx context.Context, key, newkey string) *BoolCmd + Restore(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd + RestoreReplace(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd + Sort(ctx context.Context, key string, sort *Sort) *StringSliceCmd + SortStore(ctx context.Context, key, store string, sort *Sort) *IntCmd + SortInterfaces(ctx context.Context, key string, sort *Sort) *SliceCmd + Touch(ctx context.Context, keys ...string) *IntCmd + TTL(ctx context.Context, key string) *DurationCmd + Type(ctx context.Context, key string) *StatusCmd + Append(ctx context.Context, key, value string) *IntCmd + Decr(ctx context.Context, key string) *IntCmd + DecrBy(ctx context.Context, key string, decrement int64) *IntCmd + Get(ctx context.Context, key string) *StringCmd + GetRange(ctx context.Context, key string, start, end int64) *StringCmd + GetSet(ctx context.Context, key string, value interface{}) *StringCmd + GetEx(ctx context.Context, key string, expiration time.Duration) *StringCmd + GetDel(ctx context.Context, key string) *StringCmd + Incr(ctx context.Context, key string) *IntCmd + IncrBy(ctx context.Context, key string, value int64) *IntCmd + IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd + MGet(ctx context.Context, keys ...string) *SliceCmd + MSet(ctx context.Context, values ...interface{}) *StatusCmd + MSetNX(ctx context.Context, values ...interface{}) *BoolCmd + Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd + SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd + // TODO: rename to SetEx + SetEX(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd + SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd + SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd + SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd + StrLen(ctx context.Context, key string) *IntCmd + + GetBit(ctx context.Context, key string, offset int64) *IntCmd + SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd + BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd + BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpNot(ctx context.Context, destKey string, key string) *IntCmd + BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd + BitField(ctx context.Context, key string, args ...interface{}) *IntSliceCmd + + Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd + ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd + SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd + HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd + ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd + + HDel(ctx context.Context, key string, fields ...string) *IntCmd + HExists(ctx context.Context, key, field string) *BoolCmd + HGet(ctx context.Context, key, field string) *StringCmd + HGetAll(ctx context.Context, key string) *StringStringMapCmd + HIncrBy(ctx context.Context, key, field string, incr int64) *IntCmd + HIncrByFloat(ctx context.Context, key, field string, incr float64) *FloatCmd + HKeys(ctx context.Context, key string) *StringSliceCmd + HLen(ctx context.Context, key string) *IntCmd + HMGet(ctx context.Context, key string, fields ...string) *SliceCmd + HSet(ctx context.Context, key string, values ...interface{}) *IntCmd + HMSet(ctx context.Context, key string, values ...interface{}) *BoolCmd + HSetNX(ctx context.Context, key, field string, value interface{}) *BoolCmd + HVals(ctx context.Context, key string) *StringSliceCmd + HRandField(ctx context.Context, key string, count int, withValues bool) *StringSliceCmd + + BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd + BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd + BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd + LIndex(ctx context.Context, key string, index int64) *StringCmd + LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd + LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd + LInsertAfter(ctx context.Context, key string, pivot, value interface{}) *IntCmd + LLen(ctx context.Context, key string) *IntCmd + LPop(ctx context.Context, key string) *StringCmd + LPopCount(ctx context.Context, key string, count int) *StringSliceCmd + LPos(ctx context.Context, key string, value string, args LPosArgs) *IntCmd + LPosCount(ctx context.Context, key string, value string, count int64, args LPosArgs) *IntSliceCmd + LPush(ctx context.Context, key string, values ...interface{}) *IntCmd + LPushX(ctx context.Context, key string, values ...interface{}) *IntCmd + LRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd + LRem(ctx context.Context, key string, count int64, value interface{}) *IntCmd + LSet(ctx context.Context, key string, index int64, value interface{}) *StatusCmd + LTrim(ctx context.Context, key string, start, stop int64) *StatusCmd + RPop(ctx context.Context, key string) *StringCmd + RPopCount(ctx context.Context, key string, count int) *StringSliceCmd + RPopLPush(ctx context.Context, source, destination string) *StringCmd + RPush(ctx context.Context, key string, values ...interface{}) *IntCmd + RPushX(ctx context.Context, key string, values ...interface{}) *IntCmd + LMove(ctx context.Context, source, destination, srcpos, destpos string) *StringCmd + + SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd + SCard(ctx context.Context, key string) *IntCmd + SDiff(ctx context.Context, keys ...string) *StringSliceCmd + SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd + SInter(ctx context.Context, keys ...string) *StringSliceCmd + SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd + SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd + SMIsMember(ctx context.Context, key string, members ...interface{}) *BoolSliceCmd + SMembers(ctx context.Context, key string) *StringSliceCmd + SMembersMap(ctx context.Context, key string) *StringStructMapCmd + SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd + SPop(ctx context.Context, key string) *StringCmd + SPopN(ctx context.Context, key string, count int64) *StringSliceCmd + SRandMember(ctx context.Context, key string) *StringCmd + SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd + SRem(ctx context.Context, key string, members ...interface{}) *IntCmd + SUnion(ctx context.Context, keys ...string) *StringSliceCmd + SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd + + XAdd(ctx context.Context, a *XAddArgs) *StringCmd + XDel(ctx context.Context, stream string, ids ...string) *IntCmd + XLen(ctx context.Context, stream string) *IntCmd + XRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd + XRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd + XRevRange(ctx context.Context, stream string, start, stop string) *XMessageSliceCmd + XRevRangeN(ctx context.Context, stream string, start, stop string, count int64) *XMessageSliceCmd + XRead(ctx context.Context, a *XReadArgs) *XStreamSliceCmd + XReadStreams(ctx context.Context, streams ...string) *XStreamSliceCmd + XGroupCreate(ctx context.Context, stream, group, start string) *StatusCmd + XGroupCreateMkStream(ctx context.Context, stream, group, start string) *StatusCmd + XGroupSetID(ctx context.Context, stream, group, start string) *StatusCmd + XGroupDestroy(ctx context.Context, stream, group string) *IntCmd + XGroupCreateConsumer(ctx context.Context, stream, group, consumer string) *IntCmd + XGroupDelConsumer(ctx context.Context, stream, group, consumer string) *IntCmd + XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd + XAck(ctx context.Context, stream, group string, ids ...string) *IntCmd + XPending(ctx context.Context, stream, group string) *XPendingCmd + XPendingExt(ctx context.Context, a *XPendingExtArgs) *XPendingExtCmd + XClaim(ctx context.Context, a *XClaimArgs) *XMessageSliceCmd + XClaimJustID(ctx context.Context, a *XClaimArgs) *StringSliceCmd + XAutoClaim(ctx context.Context, a *XAutoClaimArgs) *XAutoClaimCmd + XAutoClaimJustID(ctx context.Context, a *XAutoClaimArgs) *XAutoClaimJustIDCmd + + // TODO: XTrim and XTrimApprox remove in v9. + XTrim(ctx context.Context, key string, maxLen int64) *IntCmd + XTrimApprox(ctx context.Context, key string, maxLen int64) *IntCmd + XTrimMaxLen(ctx context.Context, key string, maxLen int64) *IntCmd + XTrimMaxLenApprox(ctx context.Context, key string, maxLen, limit int64) *IntCmd + XTrimMinID(ctx context.Context, key string, minID string) *IntCmd + XTrimMinIDApprox(ctx context.Context, key string, minID string, limit int64) *IntCmd + XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd + XInfoStream(ctx context.Context, key string) *XInfoStreamCmd + XInfoStreamFull(ctx context.Context, key string, count int) *XInfoStreamFullCmd + XInfoConsumers(ctx context.Context, key string, group string) *XInfoConsumersCmd + + BZPopMax(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd + BZPopMin(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd + + // TODO: remove + // ZAddCh + // ZIncr + // ZAddNXCh + // ZAddXXCh + // ZIncrNX + // ZIncrXX + // in v9. + // use ZAddArgs and ZAddArgsIncr. + + ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddNX(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddXX(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddCh(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddNXCh(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddXXCh(ctx context.Context, key string, members ...*Z) *IntCmd + ZAddArgs(ctx context.Context, key string, args ZAddArgs) *IntCmd + ZAddArgsIncr(ctx context.Context, key string, args ZAddArgs) *FloatCmd + ZIncr(ctx context.Context, key string, member *Z) *FloatCmd + ZIncrNX(ctx context.Context, key string, member *Z) *FloatCmd + ZIncrXX(ctx context.Context, key string, member *Z) *FloatCmd + ZCard(ctx context.Context, key string) *IntCmd + ZCount(ctx context.Context, key, min, max string) *IntCmd + ZLexCount(ctx context.Context, key, min, max string) *IntCmd + ZIncrBy(ctx context.Context, key string, increment float64, member string) *FloatCmd + ZInter(ctx context.Context, store *ZStore) *StringSliceCmd + ZInterWithScores(ctx context.Context, store *ZStore) *ZSliceCmd + ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd + ZMScore(ctx context.Context, key string, members ...string) *FloatSliceCmd + ZPopMax(ctx context.Context, key string, count ...int64) *ZSliceCmd + ZPopMin(ctx context.Context, key string, count ...int64) *ZSliceCmd + ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd + ZRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd + ZRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd + ZRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd + ZRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd + ZRangeArgs(ctx context.Context, z ZRangeArgs) *StringSliceCmd + ZRangeArgsWithScores(ctx context.Context, z ZRangeArgs) *ZSliceCmd + ZRangeStore(ctx context.Context, dst string, z ZRangeArgs) *IntCmd + ZRank(ctx context.Context, key, member string) *IntCmd + ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd + ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd + ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd + ZRemRangeByLex(ctx context.Context, key, min, max string) *IntCmd + ZRevRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd + ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd + ZRevRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd + ZRevRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd + ZRevRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd + ZRevRank(ctx context.Context, key, member string) *IntCmd + ZScore(ctx context.Context, key, member string) *FloatCmd + ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd + ZUnion(ctx context.Context, store ZStore) *StringSliceCmd + ZUnionWithScores(ctx context.Context, store ZStore) *ZSliceCmd + ZRandMember(ctx context.Context, key string, count int, withScores bool) *StringSliceCmd + ZDiff(ctx context.Context, keys ...string) *StringSliceCmd + ZDiffWithScores(ctx context.Context, keys ...string) *ZSliceCmd + ZDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd + + PFAdd(ctx context.Context, key string, els ...interface{}) *IntCmd + PFCount(ctx context.Context, keys ...string) *IntCmd + PFMerge(ctx context.Context, dest string, keys ...string) *StatusCmd + + BgRewriteAOF(ctx context.Context) *StatusCmd + BgSave(ctx context.Context) *StatusCmd + ClientKill(ctx context.Context, ipPort string) *StatusCmd + ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd + ClientList(ctx context.Context) *StringCmd + ClientPause(ctx context.Context, dur time.Duration) *BoolCmd + ClientID(ctx context.Context) *IntCmd + ConfigGet(ctx context.Context, parameter string) *SliceCmd + ConfigResetStat(ctx context.Context) *StatusCmd + ConfigSet(ctx context.Context, parameter, value string) *StatusCmd + ConfigRewrite(ctx context.Context) *StatusCmd + DBSize(ctx context.Context) *IntCmd + FlushAll(ctx context.Context) *StatusCmd + FlushAllAsync(ctx context.Context) *StatusCmd + FlushDB(ctx context.Context) *StatusCmd + FlushDBAsync(ctx context.Context) *StatusCmd + Info(ctx context.Context, section ...string) *StringCmd + LastSave(ctx context.Context) *IntCmd + Save(ctx context.Context) *StatusCmd + Shutdown(ctx context.Context) *StatusCmd + ShutdownSave(ctx context.Context) *StatusCmd + ShutdownNoSave(ctx context.Context) *StatusCmd + SlaveOf(ctx context.Context, host, port string) *StatusCmd + Time(ctx context.Context) *TimeCmd + DebugObject(ctx context.Context, key string) *StringCmd + ReadOnly(ctx context.Context) *StatusCmd + ReadWrite(ctx context.Context) *StatusCmd + MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd + + Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd + EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd + ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd + ScriptFlush(ctx context.Context) *StatusCmd + ScriptKill(ctx context.Context) *StatusCmd + ScriptLoad(ctx context.Context, script string) *StringCmd + + Publish(ctx context.Context, channel string, message interface{}) *IntCmd + PubSubChannels(ctx context.Context, pattern string) *StringSliceCmd + PubSubNumSub(ctx context.Context, channels ...string) *StringIntMapCmd + PubSubNumPat(ctx context.Context) *IntCmd + + ClusterSlots(ctx context.Context) *ClusterSlotsCmd + ClusterNodes(ctx context.Context) *StringCmd + ClusterMeet(ctx context.Context, host, port string) *StatusCmd + ClusterForget(ctx context.Context, nodeID string) *StatusCmd + ClusterReplicate(ctx context.Context, nodeID string) *StatusCmd + ClusterResetSoft(ctx context.Context) *StatusCmd + ClusterResetHard(ctx context.Context) *StatusCmd + ClusterInfo(ctx context.Context) *StringCmd + ClusterKeySlot(ctx context.Context, key string) *IntCmd + ClusterGetKeysInSlot(ctx context.Context, slot int, count int) *StringSliceCmd + ClusterCountFailureReports(ctx context.Context, nodeID string) *IntCmd + ClusterCountKeysInSlot(ctx context.Context, slot int) *IntCmd + ClusterDelSlots(ctx context.Context, slots ...int) *StatusCmd + ClusterDelSlotsRange(ctx context.Context, min, max int) *StatusCmd + ClusterSaveConfig(ctx context.Context) *StatusCmd + ClusterSlaves(ctx context.Context, nodeID string) *StringSliceCmd + ClusterFailover(ctx context.Context) *StatusCmd + ClusterAddSlots(ctx context.Context, slots ...int) *StatusCmd + ClusterAddSlotsRange(ctx context.Context, min, max int) *StatusCmd + + GeoAdd(ctx context.Context, key string, geoLocation ...*GeoLocation) *IntCmd + GeoPos(ctx context.Context, key string, members ...string) *GeoPosCmd + GeoRadius(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd + GeoRadiusStore(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd + GeoRadiusByMember(ctx context.Context, key, member string, query *GeoRadiusQuery) *GeoLocationCmd + GeoRadiusByMemberStore(ctx context.Context, key, member string, query *GeoRadiusQuery) *IntCmd + GeoSearch(ctx context.Context, key string, q *GeoSearchQuery) *StringSliceCmd + GeoSearchLocation(ctx context.Context, key string, q *GeoSearchLocationQuery) *GeoSearchLocationCmd + GeoSearchStore(ctx context.Context, key, store string, q *GeoSearchStoreQuery) *IntCmd + GeoDist(ctx context.Context, key string, member1, member2, unit string) *FloatCmd + GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd +} + +type StatefulCmdable interface { + Cmdable + Auth(ctx context.Context, password string) *StatusCmd + AuthACL(ctx context.Context, username, password string) *StatusCmd + Select(ctx context.Context, index int) *StatusCmd + SwapDB(ctx context.Context, index1, index2 int) *StatusCmd + ClientSetName(ctx context.Context, name string) *BoolCmd +} + +var ( + _ Cmdable = (*Client)(nil) + _ Cmdable = (*Tx)(nil) + _ Cmdable = (*Ring)(nil) + _ Cmdable = (*ClusterClient)(nil) +) + +type cmdable func(ctx context.Context, cmd Cmder) error + +type statefulCmdable func(ctx context.Context, cmd Cmder) error + +//------------------------------------------------------------------------------ + +func (c statefulCmdable) Auth(ctx context.Context, password string) *StatusCmd { + cmd := NewStatusCmd(ctx, "auth", password) + _ = c(ctx, cmd) + return cmd +} + +// AuthACL Perform an AUTH command, using the given user and pass. +// Should be used to authenticate the current connection with one of the connections defined in the ACL list +// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system. +func (c statefulCmdable) AuthACL(ctx context.Context, username, password string) *StatusCmd { + cmd := NewStatusCmd(ctx, "auth", username, password) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Wait(ctx context.Context, numSlaves int, timeout time.Duration) *IntCmd { + cmd := NewIntCmd(ctx, "wait", numSlaves, int(timeout/time.Millisecond)) + _ = c(ctx, cmd) + return cmd +} + +func (c statefulCmdable) Select(ctx context.Context, index int) *StatusCmd { + cmd := NewStatusCmd(ctx, "select", index) + _ = c(ctx, cmd) + return cmd +} + +func (c statefulCmdable) SwapDB(ctx context.Context, index1, index2 int) *StatusCmd { + cmd := NewStatusCmd(ctx, "swapdb", index1, index2) + _ = c(ctx, cmd) + return cmd +} + +// ClientSetName assigns a name to the connection. +func (c statefulCmdable) ClientSetName(ctx context.Context, name string) *BoolCmd { + cmd := NewBoolCmd(ctx, "client", "setname", name) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd { + cmd := NewCommandsInfoCmd(ctx, "command") + _ = c(ctx, cmd) + return cmd +} + +// ClientGetName returns the name of the connection. +func (c cmdable) ClientGetName(ctx context.Context) *StringCmd { + cmd := NewStringCmd(ctx, "client", "getname") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd { + cmd := NewStringCmd(ctx, "echo", message) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Ping(ctx context.Context) *StatusCmd { + cmd := NewStatusCmd(ctx, "ping") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Quit(_ context.Context) *StatusCmd { + panic("not implemented") +} + +func (c cmdable) Del(ctx context.Context, keys ...string) *IntCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "del" + for i, key := range keys { + args[1+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Unlink(ctx context.Context, keys ...string) *IntCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "unlink" + for i, key := range keys { + args[1+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Dump(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "dump", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Exists(ctx context.Context, keys ...string) *IntCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "exists" + for i, key := range keys { + args[1+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Expire(ctx context.Context, key string, expiration time.Duration) *BoolCmd { + cmd := NewBoolCmd(ctx, "expire", key, formatSec(ctx, expiration)) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd { + cmd := NewBoolCmd(ctx, "expireat", key, tm.Unix()) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Keys(ctx context.Context, pattern string) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "keys", pattern) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd { + cmd := NewStatusCmd( + ctx, + "migrate", + host, + port, + key, + db, + formatMs(ctx, timeout), + ) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Move(ctx context.Context, key string, db int) *BoolCmd { + cmd := NewBoolCmd(ctx, "move", key, db) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ObjectRefCount(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "object", "refcount", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ObjectEncoding(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "object", "encoding", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ObjectIdleTime(ctx context.Context, key string) *DurationCmd { + cmd := NewDurationCmd(ctx, time.Second, "object", "idletime", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Persist(ctx context.Context, key string) *BoolCmd { + cmd := NewBoolCmd(ctx, "persist", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) PExpire(ctx context.Context, key string, expiration time.Duration) *BoolCmd { + cmd := NewBoolCmd(ctx, "pexpire", key, formatMs(ctx, expiration)) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) PExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd { + cmd := NewBoolCmd( + ctx, + "pexpireat", + key, + tm.UnixNano()/int64(time.Millisecond), + ) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) PTTL(ctx context.Context, key string) *DurationCmd { + cmd := NewDurationCmd(ctx, time.Millisecond, "pttl", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RandomKey(ctx context.Context) *StringCmd { + cmd := NewStringCmd(ctx, "randomkey") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Rename(ctx context.Context, key, newkey string) *StatusCmd { + cmd := NewStatusCmd(ctx, "rename", key, newkey) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RenameNX(ctx context.Context, key, newkey string) *BoolCmd { + cmd := NewBoolCmd(ctx, "renamenx", key, newkey) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Restore(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd { + cmd := NewStatusCmd( + ctx, + "restore", + key, + formatMs(ctx, ttl), + value, + ) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RestoreReplace(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd { + cmd := NewStatusCmd( + ctx, + "restore", + key, + formatMs(ctx, ttl), + value, + "replace", + ) + _ = c(ctx, cmd) + return cmd +} + +type Sort struct { + By string + Offset, Count int64 + Get []string + Order string + Alpha bool +} + +func (sort *Sort) args(key string) []interface{} { + args := []interface{}{"sort", key} + if sort.By != "" { + args = append(args, "by", sort.By) + } + if sort.Offset != 0 || sort.Count != 0 { + args = append(args, "limit", sort.Offset, sort.Count) + } + for _, get := range sort.Get { + args = append(args, "get", get) + } + if sort.Order != "" { + args = append(args, sort.Order) + } + if sort.Alpha { + args = append(args, "alpha") + } + return args +} + +func (c cmdable) Sort(ctx context.Context, key string, sort *Sort) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, sort.args(key)...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SortStore(ctx context.Context, key, store string, sort *Sort) *IntCmd { + args := sort.args(key) + if store != "" { + args = append(args, "store", store) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SortInterfaces(ctx context.Context, key string, sort *Sort) *SliceCmd { + cmd := NewSliceCmd(ctx, sort.args(key)...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Touch(ctx context.Context, keys ...string) *IntCmd { + args := make([]interface{}, len(keys)+1) + args[0] = "touch" + for i, key := range keys { + args[i+1] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) TTL(ctx context.Context, key string) *DurationCmd { + cmd := NewDurationCmd(ctx, time.Second, "ttl", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Type(ctx context.Context, key string) *StatusCmd { + cmd := NewStatusCmd(ctx, "type", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Append(ctx context.Context, key, value string) *IntCmd { + cmd := NewIntCmd(ctx, "append", key, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Decr(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "decr", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) DecrBy(ctx context.Context, key string, decrement int64) *IntCmd { + cmd := NewIntCmd(ctx, "decrby", key, decrement) + _ = c(ctx, cmd) + return cmd +} + +// Get Redis `GET key` command. It returns redis.Nil error when key does not exist. +func (c cmdable) Get(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "get", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) GetRange(ctx context.Context, key string, start, end int64) *StringCmd { + cmd := NewStringCmd(ctx, "getrange", key, start, end) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) GetSet(ctx context.Context, key string, value interface{}) *StringCmd { + cmd := NewStringCmd(ctx, "getset", key, value) + _ = c(ctx, cmd) + return cmd +} + +// GetEx An expiration of zero removes the TTL associated with the key (i.e. GETEX key persist). +// Requires Redis >= 6.2.0. +func (c cmdable) GetEx(ctx context.Context, key string, expiration time.Duration) *StringCmd { + args := make([]interface{}, 0, 4) + args = append(args, "getex", key) + if expiration > 0 { + if usePrecise(expiration) { + args = append(args, "px", formatMs(ctx, expiration)) + } else { + args = append(args, "ex", formatSec(ctx, expiration)) + } + } else if expiration == 0 { + args = append(args, "persist") + } + + cmd := NewStringCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// GetDel redis-server version >= 6.2.0. +func (c cmdable) GetDel(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "getdel", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) Incr(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "incr", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) IncrBy(ctx context.Context, key string, value int64) *IntCmd { + cmd := NewIntCmd(ctx, "incrby", key, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd { + cmd := NewFloatCmd(ctx, "incrbyfloat", key, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "mget" + for i, key := range keys { + args[1+i] = key + } + cmd := NewSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// MSet is like Set but accepts multiple values: +// - MSet("key1", "value1", "key2", "value2") +// - MSet([]string{"key1", "value1", "key2", "value2"}) +// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"}) +func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd { + args := make([]interface{}, 1, 1+len(values)) + args[0] = "mset" + args = appendArgs(args, values) + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// MSetNX is like SetNX but accepts multiple values: +// - MSetNX("key1", "value1", "key2", "value2") +// - MSetNX([]string{"key1", "value1", "key2", "value2"}) +// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"}) +func (c cmdable) MSetNX(ctx context.Context, values ...interface{}) *BoolCmd { + args := make([]interface{}, 1, 1+len(values)) + args[0] = "msetnx" + args = appendArgs(args, values) + cmd := NewBoolCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// Set Redis `SET key value [expiration]` command. +// Use expiration for `SETEX`-like behavior. +// +// Zero expiration means the key has no expiration time. +// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, +// otherwise you will receive an error: (error) ERR syntax error. +func (c cmdable) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd { + args := make([]interface{}, 3, 5) + args[0] = "set" + args[1] = key + args[2] = value + if expiration > 0 { + if usePrecise(expiration) { + args = append(args, "px", formatMs(ctx, expiration)) + } else { + args = append(args, "ex", formatSec(ctx, expiration)) + } + } else if expiration == KeepTTL { + args = append(args, "keepttl") + } + + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// SetArgs provides arguments for the SetArgs function. +type SetArgs struct { + // Mode can be `NX` or `XX` or empty. + Mode string + + // Zero `TTL` or `Expiration` means that the key has no expiration time. + TTL time.Duration + ExpireAt time.Time + + // When Get is true, the command returns the old value stored at key, or nil when key did not exist. + Get bool + + // KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, + // otherwise you will receive an error: (error) ERR syntax error. + KeepTTL bool +} + +// SetArgs supports all the options that the SET command supports. +// It is the alternative to the Set function when you want +// to have more control over the options. +func (c cmdable) SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd { + args := []interface{}{"set", key, value} + + if a.KeepTTL { + args = append(args, "keepttl") + } + + if !a.ExpireAt.IsZero() { + args = append(args, "exat", a.ExpireAt.Unix()) + } + if a.TTL > 0 { + if usePrecise(a.TTL) { + args = append(args, "px", formatMs(ctx, a.TTL)) + } else { + args = append(args, "ex", formatSec(ctx, a.TTL)) + } + } + + if a.Mode != "" { + args = append(args, a.Mode) + } + + if a.Get { + args = append(args, "get") + } + + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// SetEX Redis `SETEX key expiration value` command. +func (c cmdable) SetEX(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd { + cmd := NewStatusCmd(ctx, "setex", key, formatSec(ctx, expiration), value) + _ = c(ctx, cmd) + return cmd +} + +// SetNX Redis `SET key value [expiration] NX` command. +// +// Zero expiration means the key has no expiration time. +// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, +// otherwise you will receive an error: (error) ERR syntax error. +func (c cmdable) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd { + var cmd *BoolCmd + switch expiration { + case 0: + // Use old `SETNX` to support old Redis versions. + cmd = NewBoolCmd(ctx, "setnx", key, value) + case KeepTTL: + cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "nx") + default: + if usePrecise(expiration) { + cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "nx") + } else { + cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "nx") + } + } + + _ = c(ctx, cmd) + return cmd +} + +// SetXX Redis `SET key value [expiration] XX` command. +// +// Zero expiration means the key has no expiration time. +// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, +// otherwise you will receive an error: (error) ERR syntax error. +func (c cmdable) SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd { + var cmd *BoolCmd + switch expiration { + case 0: + cmd = NewBoolCmd(ctx, "set", key, value, "xx") + case KeepTTL: + cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "xx") + default: + if usePrecise(expiration) { + cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "xx") + } else { + cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "xx") + } + } + + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd { + cmd := NewIntCmd(ctx, "setrange", key, offset, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) StrLen(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "strlen", key) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) GetBit(ctx context.Context, key string, offset int64) *IntCmd { + cmd := NewIntCmd(ctx, "getbit", key, offset) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd { + cmd := NewIntCmd( + ctx, + "setbit", + key, + offset, + value, + ) + _ = c(ctx, cmd) + return cmd +} + +type BitCount struct { + Start, End int64 +} + +func (c cmdable) BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd { + args := []interface{}{"bitcount", key} + if bitCount != nil { + args = append( + args, + bitCount.Start, + bitCount.End, + ) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) bitOp(ctx context.Context, op, destKey string, keys ...string) *IntCmd { + args := make([]interface{}, 3+len(keys)) + args[0] = "bitop" + args[1] = op + args[2] = destKey + for i, key := range keys { + args[3+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "and", destKey, keys...) +} + +func (c cmdable) BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "or", destKey, keys...) +} + +func (c cmdable) BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "xor", destKey, keys...) +} + +func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd { + return c.bitOp(ctx, "not", destKey, key) +} + +func (c cmdable) BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd { + args := make([]interface{}, 3+len(pos)) + args[0] = "bitpos" + args[1] = key + args[2] = bit + switch len(pos) { + case 0: + case 1: + args[3] = pos[0] + case 2: + args[3] = pos[0] + args[4] = pos[1] + default: + panic("too many arguments") + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) BitField(ctx context.Context, key string, args ...interface{}) *IntSliceCmd { + a := make([]interface{}, 0, 2+len(args)) + a = append(a, "bitfield") + a = append(a, key) + a = append(a, args...) + cmd := NewIntSliceCmd(ctx, a...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd { + args := []interface{}{"scan", cursor} + if match != "" { + args = append(args, "match", match) + } + if count > 0 { + args = append(args, "count", count) + } + cmd := NewScanCmd(ctx, c, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd { + args := []interface{}{"scan", cursor} + if match != "" { + args = append(args, "match", match) + } + if count > 0 { + args = append(args, "count", count) + } + if keyType != "" { + args = append(args, "type", keyType) + } + cmd := NewScanCmd(ctx, c, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd { + args := []interface{}{"sscan", key, cursor} + if match != "" { + args = append(args, "match", match) + } + if count > 0 { + args = append(args, "count", count) + } + cmd := NewScanCmd(ctx, c, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd { + args := []interface{}{"hscan", key, cursor} + if match != "" { + args = append(args, "match", match) + } + if count > 0 { + args = append(args, "count", count) + } + cmd := NewScanCmd(ctx, c, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd { + args := []interface{}{"zscan", key, cursor} + if match != "" { + args = append(args, "match", match) + } + if count > 0 { + args = append(args, "count", count) + } + cmd := NewScanCmd(ctx, c, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) HDel(ctx context.Context, key string, fields ...string) *IntCmd { + args := make([]interface{}, 2+len(fields)) + args[0] = "hdel" + args[1] = key + for i, field := range fields { + args[2+i] = field + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HExists(ctx context.Context, key, field string) *BoolCmd { + cmd := NewBoolCmd(ctx, "hexists", key, field) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HGet(ctx context.Context, key, field string) *StringCmd { + cmd := NewStringCmd(ctx, "hget", key, field) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HGetAll(ctx context.Context, key string) *StringStringMapCmd { + cmd := NewStringStringMapCmd(ctx, "hgetall", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HIncrBy(ctx context.Context, key, field string, incr int64) *IntCmd { + cmd := NewIntCmd(ctx, "hincrby", key, field, incr) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HIncrByFloat(ctx context.Context, key, field string, incr float64) *FloatCmd { + cmd := NewFloatCmd(ctx, "hincrbyfloat", key, field, incr) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HKeys(ctx context.Context, key string) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "hkeys", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HLen(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "hlen", key) + _ = c(ctx, cmd) + return cmd +} + +// HMGet returns the values for the specified fields in the hash stored at key. +// It returns an interface{} to distinguish between empty string and nil value. +func (c cmdable) HMGet(ctx context.Context, key string, fields ...string) *SliceCmd { + args := make([]interface{}, 2+len(fields)) + args[0] = "hmget" + args[1] = key + for i, field := range fields { + args[2+i] = field + } + cmd := NewSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// HSet accepts values in following formats: +// - HSet("myhash", "key1", "value1", "key2", "value2") +// - HSet("myhash", []string{"key1", "value1", "key2", "value2"}) +// - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"}) +// +// Note that it requires Redis v4 for multiple field/value pairs support. +func (c cmdable) HSet(ctx context.Context, key string, values ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "hset" + args[1] = key + args = appendArgs(args, values) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// HMSet is a deprecated version of HSet left for compatibility with Redis 3. +func (c cmdable) HMSet(ctx context.Context, key string, values ...interface{}) *BoolCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "hmset" + args[1] = key + args = appendArgs(args, values) + cmd := NewBoolCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HSetNX(ctx context.Context, key, field string, value interface{}) *BoolCmd { + cmd := NewBoolCmd(ctx, "hsetnx", key, field, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) HVals(ctx context.Context, key string) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "hvals", key) + _ = c(ctx, cmd) + return cmd +} + +// HRandField redis-server version >= 6.2.0. +func (c cmdable) HRandField(ctx context.Context, key string, count int, withValues bool) *StringSliceCmd { + args := make([]interface{}, 0, 4) + + // Although count=0 is meaningless, redis accepts count=0. + args = append(args, "hrandfield", key, count) + if withValues { + args = append(args, "withvalues") + } + + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd { + args := make([]interface{}, 1+len(keys)+1) + args[0] = "blpop" + for i, key := range keys { + args[1+i] = key + } + args[len(args)-1] = formatSec(ctx, timeout) + cmd := NewStringSliceCmd(ctx, args...) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd { + args := make([]interface{}, 1+len(keys)+1) + args[0] = "brpop" + for i, key := range keys { + args[1+i] = key + } + args[len(keys)+1] = formatSec(ctx, timeout) + cmd := NewStringSliceCmd(ctx, args...) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd { + cmd := NewStringCmd( + ctx, + "brpoplpush", + source, + destination, + formatSec(ctx, timeout), + ) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd { + cmd := NewStringCmd(ctx, "lindex", key, index) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd { + cmd := NewIntCmd(ctx, "linsert", key, op, pivot, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd { + cmd := NewIntCmd(ctx, "linsert", key, "before", pivot, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LInsertAfter(ctx context.Context, key string, pivot, value interface{}) *IntCmd { + cmd := NewIntCmd(ctx, "linsert", key, "after", pivot, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LLen(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "llen", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LPop(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "lpop", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LPopCount(ctx context.Context, key string, count int) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "lpop", key, count) + _ = c(ctx, cmd) + return cmd +} + +type LPosArgs struct { + Rank, MaxLen int64 +} + +func (c cmdable) LPos(ctx context.Context, key string, value string, a LPosArgs) *IntCmd { + args := []interface{}{"lpos", key, value} + if a.Rank != 0 { + args = append(args, "rank", a.Rank) + } + if a.MaxLen != 0 { + args = append(args, "maxlen", a.MaxLen) + } + + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LPosCount(ctx context.Context, key string, value string, count int64, a LPosArgs) *IntSliceCmd { + args := []interface{}{"lpos", key, value, "count", count} + if a.Rank != 0 { + args = append(args, "rank", a.Rank) + } + if a.MaxLen != 0 { + args = append(args, "maxlen", a.MaxLen) + } + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LPush(ctx context.Context, key string, values ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "lpush" + args[1] = key + args = appendArgs(args, values) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LPushX(ctx context.Context, key string, values ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "lpushx" + args[1] = key + args = appendArgs(args, values) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd { + cmd := NewStringSliceCmd( + ctx, + "lrange", + key, + start, + stop, + ) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LRem(ctx context.Context, key string, count int64, value interface{}) *IntCmd { + cmd := NewIntCmd(ctx, "lrem", key, count, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LSet(ctx context.Context, key string, index int64, value interface{}) *StatusCmd { + cmd := NewStatusCmd(ctx, "lset", key, index, value) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LTrim(ctx context.Context, key string, start, stop int64) *StatusCmd { + cmd := NewStatusCmd( + ctx, + "ltrim", + key, + start, + stop, + ) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RPop(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "rpop", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RPopCount(ctx context.Context, key string, count int) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "rpop", key, count) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RPopLPush(ctx context.Context, source, destination string) *StringCmd { + cmd := NewStringCmd(ctx, "rpoplpush", source, destination) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RPush(ctx context.Context, key string, values ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "rpush" + args[1] = key + args = appendArgs(args, values) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) RPushX(ctx context.Context, key string, values ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(values)) + args[0] = "rpushx" + args[1] = key + args = appendArgs(args, values) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LMove(ctx context.Context, source, destination, srcpos, destpos string) *StringCmd { + cmd := NewStringCmd(ctx, "lmove", source, destination, srcpos, destpos) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(members)) + args[0] = "sadd" + args[1] = key + args = appendArgs(args, members) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SCard(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "scard", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SDiff(ctx context.Context, keys ...string) *StringSliceCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "sdiff" + for i, key := range keys { + args[1+i] = key + } + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd { + args := make([]interface{}, 2+len(keys)) + args[0] = "sdiffstore" + args[1] = destination + for i, key := range keys { + args[2+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SInter(ctx context.Context, keys ...string) *StringSliceCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "sinter" + for i, key := range keys { + args[1+i] = key + } + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd { + args := make([]interface{}, 2+len(keys)) + args[0] = "sinterstore" + args[1] = destination + for i, key := range keys { + args[2+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd { + cmd := NewBoolCmd(ctx, "sismember", key, member) + _ = c(ctx, cmd) + return cmd +} + +// SMIsMember Redis `SMISMEMBER key member [member ...]` command. +func (c cmdable) SMIsMember(ctx context.Context, key string, members ...interface{}) *BoolSliceCmd { + args := make([]interface{}, 2, 2+len(members)) + args[0] = "smismember" + args[1] = key + args = appendArgs(args, members) + cmd := NewBoolSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// SMembers Redis `SMEMBERS key` command output as a slice. +func (c cmdable) SMembers(ctx context.Context, key string) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "smembers", key) + _ = c(ctx, cmd) + return cmd +} + +// SMembersMap Redis `SMEMBERS key` command output as a map. +func (c cmdable) SMembersMap(ctx context.Context, key string) *StringStructMapCmd { + cmd := NewStringStructMapCmd(ctx, "smembers", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd { + cmd := NewBoolCmd(ctx, "smove", source, destination, member) + _ = c(ctx, cmd) + return cmd +} + +// SPop Redis `SPOP key` command. +func (c cmdable) SPop(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "spop", key) + _ = c(ctx, cmd) + return cmd +} + +// SPopN Redis `SPOP key count` command. +func (c cmdable) SPopN(ctx context.Context, key string, count int64) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "spop", key, count) + _ = c(ctx, cmd) + return cmd +} + +// SRandMember Redis `SRANDMEMBER key` command. +func (c cmdable) SRandMember(ctx context.Context, key string) *StringCmd { + cmd := NewStringCmd(ctx, "srandmember", key) + _ = c(ctx, cmd) + return cmd +} + +// SRandMemberN Redis `SRANDMEMBER key count` command. +func (c cmdable) SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "srandmember", key, count) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SRem(ctx context.Context, key string, members ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(members)) + args[0] = "srem" + args[1] = key + args = appendArgs(args, members) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SUnion(ctx context.Context, keys ...string) *StringSliceCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "sunion" + for i, key := range keys { + args[1+i] = key + } + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd { + args := make([]interface{}, 2+len(keys)) + args[0] = "sunionstore" + args[1] = destination + for i, key := range keys { + args[2+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +// XAddArgs accepts values in the following formats: +// - XAddArgs.Values = []interface{}{"key1", "value1", "key2", "value2"} +// - XAddArgs.Values = []string("key1", "value1", "key2", "value2") +// - XAddArgs.Values = map[string]interface{}{"key1": "value1", "key2": "value2"} +// +// Note that map will not preserve the order of key-value pairs. +// MaxLen/MaxLenApprox and MinID are in conflict, only one of them can be used. +type XAddArgs struct { + Stream string + NoMkStream bool + MaxLen int64 // MAXLEN N + + // Deprecated: use MaxLen+Approx, remove in v9. + MaxLenApprox int64 // MAXLEN ~ N + + MinID string + // Approx causes MaxLen and MinID to use "~" matcher (instead of "="). + Approx bool + Limit int64 + ID string + Values interface{} +} + +// XAdd a.Limit has a bug, please confirm it and use it. +// issue: https://github.com/redis/redis/issues/9046 +func (c cmdable) XAdd(ctx context.Context, a *XAddArgs) *StringCmd { + args := make([]interface{}, 0, 11) + args = append(args, "xadd", a.Stream) + if a.NoMkStream { + args = append(args, "nomkstream") + } + switch { + case a.MaxLen > 0: + if a.Approx { + args = append(args, "maxlen", "~", a.MaxLen) + } else { + args = append(args, "maxlen", a.MaxLen) + } + case a.MaxLenApprox > 0: + // TODO remove in v9. + args = append(args, "maxlen", "~", a.MaxLenApprox) + case a.MinID != "": + if a.Approx { + args = append(args, "minid", "~", a.MinID) + } else { + args = append(args, "minid", a.MinID) + } + } + if a.Limit > 0 { + args = append(args, "limit", a.Limit) + } + if a.ID != "" { + args = append(args, a.ID) + } else { + args = append(args, "*") + } + args = appendArg(args, a.Values) + + cmd := NewStringCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XDel(ctx context.Context, stream string, ids ...string) *IntCmd { + args := []interface{}{"xdel", stream} + for _, id := range ids { + args = append(args, id) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XLen(ctx context.Context, stream string) *IntCmd { + cmd := NewIntCmd(ctx, "xlen", stream) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd { + cmd := NewXMessageSliceCmd(ctx, "xrange", stream, start, stop) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd { + cmd := NewXMessageSliceCmd(ctx, "xrange", stream, start, stop, "count", count) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XRevRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd { + cmd := NewXMessageSliceCmd(ctx, "xrevrange", stream, start, stop) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XRevRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd { + cmd := NewXMessageSliceCmd(ctx, "xrevrange", stream, start, stop, "count", count) + _ = c(ctx, cmd) + return cmd +} + +type XReadArgs struct { + Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2 + Count int64 + Block time.Duration +} + +func (c cmdable) XRead(ctx context.Context, a *XReadArgs) *XStreamSliceCmd { + args := make([]interface{}, 0, 5+len(a.Streams)) + args = append(args, "xread") + + keyPos := int8(1) + if a.Count > 0 { + args = append(args, "count") + args = append(args, a.Count) + keyPos += 2 + } + if a.Block >= 0 { + args = append(args, "block") + args = append(args, int64(a.Block/time.Millisecond)) + keyPos += 2 + } + args = append(args, "streams") + keyPos++ + for _, s := range a.Streams { + args = append(args, s) + } + + cmd := NewXStreamSliceCmd(ctx, args...) + if a.Block >= 0 { + cmd.setReadTimeout(a.Block) + } + cmd.setFirstKeyPos(keyPos) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XReadStreams(ctx context.Context, streams ...string) *XStreamSliceCmd { + return c.XRead(ctx, &XReadArgs{ + Streams: streams, + Block: -1, + }) +} + +func (c cmdable) XGroupCreate(ctx context.Context, stream, group, start string) *StatusCmd { + cmd := NewStatusCmd(ctx, "xgroup", "create", stream, group, start) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XGroupCreateMkStream(ctx context.Context, stream, group, start string) *StatusCmd { + cmd := NewStatusCmd(ctx, "xgroup", "create", stream, group, start, "mkstream") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XGroupSetID(ctx context.Context, stream, group, start string) *StatusCmd { + cmd := NewStatusCmd(ctx, "xgroup", "setid", stream, group, start) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XGroupDestroy(ctx context.Context, stream, group string) *IntCmd { + cmd := NewIntCmd(ctx, "xgroup", "destroy", stream, group) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XGroupCreateConsumer(ctx context.Context, stream, group, consumer string) *IntCmd { + cmd := NewIntCmd(ctx, "xgroup", "createconsumer", stream, group, consumer) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XGroupDelConsumer(ctx context.Context, stream, group, consumer string) *IntCmd { + cmd := NewIntCmd(ctx, "xgroup", "delconsumer", stream, group, consumer) + _ = c(ctx, cmd) + return cmd +} + +type XReadGroupArgs struct { + Group string + Consumer string + Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2 + Count int64 + Block time.Duration + NoAck bool +} + +func (c cmdable) XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd { + args := make([]interface{}, 0, 8+len(a.Streams)) + args = append(args, "xreadgroup", "group", a.Group, a.Consumer) + + keyPos := int8(4) + if a.Count > 0 { + args = append(args, "count", a.Count) + keyPos += 2 + } + if a.Block >= 0 { + args = append(args, "block", int64(a.Block/time.Millisecond)) + keyPos += 2 + } + if a.NoAck { + args = append(args, "noack") + keyPos++ + } + args = append(args, "streams") + keyPos++ + for _, s := range a.Streams { + args = append(args, s) + } + + cmd := NewXStreamSliceCmd(ctx, args...) + if a.Block >= 0 { + cmd.setReadTimeout(a.Block) + } + cmd.setFirstKeyPos(keyPos) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XAck(ctx context.Context, stream, group string, ids ...string) *IntCmd { + args := []interface{}{"xack", stream, group} + for _, id := range ids { + args = append(args, id) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XPending(ctx context.Context, stream, group string) *XPendingCmd { + cmd := NewXPendingCmd(ctx, "xpending", stream, group) + _ = c(ctx, cmd) + return cmd +} + +type XPendingExtArgs struct { + Stream string + Group string + Idle time.Duration + Start string + End string + Count int64 + Consumer string +} + +func (c cmdable) XPendingExt(ctx context.Context, a *XPendingExtArgs) *XPendingExtCmd { + args := make([]interface{}, 0, 9) + args = append(args, "xpending", a.Stream, a.Group) + if a.Idle != 0 { + args = append(args, "idle", formatMs(ctx, a.Idle)) + } + args = append(args, a.Start, a.End, a.Count) + if a.Consumer != "" { + args = append(args, a.Consumer) + } + cmd := NewXPendingExtCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +type XAutoClaimArgs struct { + Stream string + Group string + MinIdle time.Duration + Start string + Count int64 + Consumer string +} + +func (c cmdable) XAutoClaim(ctx context.Context, a *XAutoClaimArgs) *XAutoClaimCmd { + args := xAutoClaimArgs(ctx, a) + cmd := NewXAutoClaimCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XAutoClaimJustID(ctx context.Context, a *XAutoClaimArgs) *XAutoClaimJustIDCmd { + args := xAutoClaimArgs(ctx, a) + args = append(args, "justid") + cmd := NewXAutoClaimJustIDCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func xAutoClaimArgs(ctx context.Context, a *XAutoClaimArgs) []interface{} { + args := make([]interface{}, 0, 9) + args = append(args, "xautoclaim", a.Stream, a.Group, a.Consumer, formatMs(ctx, a.MinIdle), a.Start) + if a.Count > 0 { + args = append(args, "count", a.Count) + } + return args +} + +type XClaimArgs struct { + Stream string + Group string + Consumer string + MinIdle time.Duration + Messages []string +} + +func (c cmdable) XClaim(ctx context.Context, a *XClaimArgs) *XMessageSliceCmd { + args := xClaimArgs(a) + cmd := NewXMessageSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XClaimJustID(ctx context.Context, a *XClaimArgs) *StringSliceCmd { + args := xClaimArgs(a) + args = append(args, "justid") + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func xClaimArgs(a *XClaimArgs) []interface{} { + args := make([]interface{}, 0, 4+len(a.Messages)) + args = append(args, + "xclaim", + a.Stream, + a.Group, a.Consumer, + int64(a.MinIdle/time.Millisecond)) + for _, id := range a.Messages { + args = append(args, id) + } + return args +} + +// xTrim If approx is true, add the "~" parameter, otherwise it is the default "=" (redis default). +// example: +// XTRIM key MAXLEN/MINID threshold LIMIT limit. +// XTRIM key MAXLEN/MINID ~ threshold LIMIT limit. +// The redis-server version is lower than 6.2, please set limit to 0. +func (c cmdable) xTrim( + ctx context.Context, key, strategy string, + approx bool, threshold interface{}, limit int64, +) *IntCmd { + args := make([]interface{}, 0, 7) + args = append(args, "xtrim", key, strategy) + if approx { + args = append(args, "~") + } + args = append(args, threshold) + if limit > 0 { + args = append(args, "limit", limit) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// Deprecated: use XTrimMaxLen, remove in v9. +func (c cmdable) XTrim(ctx context.Context, key string, maxLen int64) *IntCmd { + return c.xTrim(ctx, key, "maxlen", false, maxLen, 0) +} + +// Deprecated: use XTrimMaxLenApprox, remove in v9. +func (c cmdable) XTrimApprox(ctx context.Context, key string, maxLen int64) *IntCmd { + return c.xTrim(ctx, key, "maxlen", true, maxLen, 0) +} + +// XTrimMaxLen No `~` rules are used, `limit` cannot be used. +// cmd: XTRIM key MAXLEN maxLen +func (c cmdable) XTrimMaxLen(ctx context.Context, key string, maxLen int64) *IntCmd { + return c.xTrim(ctx, key, "maxlen", false, maxLen, 0) +} + +// XTrimMaxLenApprox LIMIT has a bug, please confirm it and use it. +// issue: https://github.com/redis/redis/issues/9046 +// cmd: XTRIM key MAXLEN ~ maxLen LIMIT limit +func (c cmdable) XTrimMaxLenApprox(ctx context.Context, key string, maxLen, limit int64) *IntCmd { + return c.xTrim(ctx, key, "maxlen", true, maxLen, limit) +} + +// XTrimMinID No `~` rules are used, `limit` cannot be used. +// cmd: XTRIM key MINID minID +func (c cmdable) XTrimMinID(ctx context.Context, key string, minID string) *IntCmd { + return c.xTrim(ctx, key, "minid", false, minID, 0) +} + +// XTrimMinIDApprox LIMIT has a bug, please confirm it and use it. +// issue: https://github.com/redis/redis/issues/9046 +// cmd: XTRIM key MINID ~ minID LIMIT limit +func (c cmdable) XTrimMinIDApprox(ctx context.Context, key string, minID string, limit int64) *IntCmd { + return c.xTrim(ctx, key, "minid", true, minID, limit) +} + +func (c cmdable) XInfoConsumers(ctx context.Context, key string, group string) *XInfoConsumersCmd { + cmd := NewXInfoConsumersCmd(ctx, key, group) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd { + cmd := NewXInfoGroupsCmd(ctx, key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) XInfoStream(ctx context.Context, key string) *XInfoStreamCmd { + cmd := NewXInfoStreamCmd(ctx, key) + _ = c(ctx, cmd) + return cmd +} + +// XInfoStreamFull XINFO STREAM FULL [COUNT count] +// redis-server >= 6.0. +func (c cmdable) XInfoStreamFull(ctx context.Context, key string, count int) *XInfoStreamFullCmd { + args := make([]interface{}, 0, 6) + args = append(args, "xinfo", "stream", key, "full") + if count > 0 { + args = append(args, "count", count) + } + cmd := NewXInfoStreamFullCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +// Z represents sorted set member. +type Z struct { + Score float64 + Member interface{} +} + +// ZWithKey represents sorted set member including the name of the key where it was popped. +type ZWithKey struct { + Z + Key string +} + +// ZStore is used as an arg to ZInter/ZInterStore and ZUnion/ZUnionStore. +type ZStore struct { + Keys []string + Weights []float64 + // Can be SUM, MIN or MAX. + Aggregate string +} + +func (z ZStore) len() (n int) { + n = len(z.Keys) + if len(z.Weights) > 0 { + n += 1 + len(z.Weights) + } + if z.Aggregate != "" { + n += 2 + } + return n +} + +func (z ZStore) appendArgs(args []interface{}) []interface{} { + for _, key := range z.Keys { + args = append(args, key) + } + if len(z.Weights) > 0 { + args = append(args, "weights") + for _, weights := range z.Weights { + args = append(args, weights) + } + } + if z.Aggregate != "" { + args = append(args, "aggregate", z.Aggregate) + } + return args +} + +// BZPopMax Redis `BZPOPMAX key [key ...] timeout` command. +func (c cmdable) BZPopMax(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd { + args := make([]interface{}, 1+len(keys)+1) + args[0] = "bzpopmax" + for i, key := range keys { + args[1+i] = key + } + args[len(args)-1] = formatSec(ctx, timeout) + cmd := NewZWithKeyCmd(ctx, args...) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +// BZPopMin Redis `BZPOPMIN key [key ...] timeout` command. +func (c cmdable) BZPopMin(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd { + args := make([]interface{}, 1+len(keys)+1) + args[0] = "bzpopmin" + for i, key := range keys { + args[1+i] = key + } + args[len(args)-1] = formatSec(ctx, timeout) + cmd := NewZWithKeyCmd(ctx, args...) + cmd.setReadTimeout(timeout) + _ = c(ctx, cmd) + return cmd +} + +// ZAddArgs WARN: The GT, LT and NX options are mutually exclusive. +type ZAddArgs struct { + NX bool + XX bool + LT bool + GT bool + Ch bool + Members []Z +} + +func (c cmdable) zAddArgs(key string, args ZAddArgs, incr bool) []interface{} { + a := make([]interface{}, 0, 6+2*len(args.Members)) + a = append(a, "zadd", key) + + // The GT, LT and NX options are mutually exclusive. + if args.NX { + a = append(a, "nx") + } else { + if args.XX { + a = append(a, "xx") + } + if args.GT { + a = append(a, "gt") + } else if args.LT { + a = append(a, "lt") + } + } + if args.Ch { + a = append(a, "ch") + } + if incr { + a = append(a, "incr") + } + for _, m := range args.Members { + a = append(a, m.Score) + a = append(a, m.Member) + } + return a +} + +func (c cmdable) ZAddArgs(ctx context.Context, key string, args ZAddArgs) *IntCmd { + cmd := NewIntCmd(ctx, c.zAddArgs(key, args, false)...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZAddArgsIncr(ctx context.Context, key string, args ZAddArgs) *FloatCmd { + cmd := NewFloatCmd(ctx, c.zAddArgs(key, args, true)...) + _ = c(ctx, cmd) + return cmd +} + +// TODO: Compatible with v8 api, will be removed in v9. +func (c cmdable) zAdd(ctx context.Context, key string, args ZAddArgs, members ...*Z) *IntCmd { + args.Members = make([]Z, len(members)) + for i, m := range members { + args.Members[i] = *m + } + cmd := NewIntCmd(ctx, c.zAddArgs(key, args, false)...) + _ = c(ctx, cmd) + return cmd +} + +// ZAdd Redis `ZADD key score member [score member ...]` command. +func (c cmdable) ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{}, members...) +} + +// ZAddNX Redis `ZADD key NX score member [score member ...]` command. +func (c cmdable) ZAddNX(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{ + NX: true, + }, members...) +} + +// ZAddXX Redis `ZADD key XX score member [score member ...]` command. +func (c cmdable) ZAddXX(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{ + XX: true, + }, members...) +} + +// ZAddCh Redis `ZADD key CH score member [score member ...]` command. +// Deprecated: Use +// client.ZAddArgs(ctx, ZAddArgs{ +// Ch: true, +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZAddCh(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{ + Ch: true, + }, members...) +} + +// ZAddNXCh Redis `ZADD key NX CH score member [score member ...]` command. +// Deprecated: Use +// client.ZAddArgs(ctx, ZAddArgs{ +// NX: true, +// Ch: true, +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZAddNXCh(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{ + NX: true, + Ch: true, + }, members...) +} + +// ZAddXXCh Redis `ZADD key XX CH score member [score member ...]` command. +// Deprecated: Use +// client.ZAddArgs(ctx, ZAddArgs{ +// XX: true, +// Ch: true, +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZAddXXCh(ctx context.Context, key string, members ...*Z) *IntCmd { + return c.zAdd(ctx, key, ZAddArgs{ + XX: true, + Ch: true, + }, members...) +} + +// ZIncr Redis `ZADD key INCR score member` command. +// Deprecated: Use +// client.ZAddArgsIncr(ctx, ZAddArgs{ +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZIncr(ctx context.Context, key string, member *Z) *FloatCmd { + return c.ZAddArgsIncr(ctx, key, ZAddArgs{ + Members: []Z{*member}, + }) +} + +// ZIncrNX Redis `ZADD key NX INCR score member` command. +// Deprecated: Use +// client.ZAddArgsIncr(ctx, ZAddArgs{ +// NX: true, +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZIncrNX(ctx context.Context, key string, member *Z) *FloatCmd { + return c.ZAddArgsIncr(ctx, key, ZAddArgs{ + NX: true, + Members: []Z{*member}, + }) +} + +// ZIncrXX Redis `ZADD key XX INCR score member` command. +// Deprecated: Use +// client.ZAddArgsIncr(ctx, ZAddArgs{ +// XX: true, +// Members: []Z, +// }) +// remove in v9. +func (c cmdable) ZIncrXX(ctx context.Context, key string, member *Z) *FloatCmd { + return c.ZAddArgsIncr(ctx, key, ZAddArgs{ + XX: true, + Members: []Z{*member}, + }) +} + +func (c cmdable) ZCard(ctx context.Context, key string) *IntCmd { + cmd := NewIntCmd(ctx, "zcard", key) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZCount(ctx context.Context, key, min, max string) *IntCmd { + cmd := NewIntCmd(ctx, "zcount", key, min, max) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZLexCount(ctx context.Context, key, min, max string) *IntCmd { + cmd := NewIntCmd(ctx, "zlexcount", key, min, max) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZIncrBy(ctx context.Context, key string, increment float64, member string) *FloatCmd { + cmd := NewFloatCmd(ctx, "zincrby", key, increment, member) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd { + args := make([]interface{}, 0, 3+store.len()) + args = append(args, "zinterstore", destination, len(store.Keys)) + args = store.appendArgs(args) + cmd := NewIntCmd(ctx, args...) + cmd.setFirstKeyPos(3) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZInter(ctx context.Context, store *ZStore) *StringSliceCmd { + args := make([]interface{}, 0, 2+store.len()) + args = append(args, "zinter", len(store.Keys)) + args = store.appendArgs(args) + cmd := NewStringSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZInterWithScores(ctx context.Context, store *ZStore) *ZSliceCmd { + args := make([]interface{}, 0, 3+store.len()) + args = append(args, "zinter", len(store.Keys)) + args = store.appendArgs(args) + args = append(args, "withscores") + cmd := NewZSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZMScore(ctx context.Context, key string, members ...string) *FloatSliceCmd { + args := make([]interface{}, 2+len(members)) + args[0] = "zmscore" + args[1] = key + for i, member := range members { + args[2+i] = member + } + cmd := NewFloatSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZPopMax(ctx context.Context, key string, count ...int64) *ZSliceCmd { + args := []interface{}{ + "zpopmax", + key, + } + + switch len(count) { + case 0: + break + case 1: + args = append(args, count[0]) + default: + panic("too many arguments") + } + + cmd := NewZSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZPopMin(ctx context.Context, key string, count ...int64) *ZSliceCmd { + args := []interface{}{ + "zpopmin", + key, + } + + switch len(count) { + case 0: + break + case 1: + args = append(args, count[0]) + default: + panic("too many arguments") + } + + cmd := NewZSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// ZRangeArgs is all the options of the ZRange command. +// In version> 6.2.0, you can replace the(cmd): +// ZREVRANGE, +// ZRANGEBYSCORE, +// ZREVRANGEBYSCORE, +// ZRANGEBYLEX, +// ZREVRANGEBYLEX. +// Please pay attention to your redis-server version. +// +// Rev, ByScore, ByLex and Offset+Count options require redis-server 6.2.0 and higher. +type ZRangeArgs struct { + Key string + + // When the ByScore option is provided, the open interval(exclusive) can be set. + // By default, the score intervals specified by and are closed (inclusive). + // It is similar to the deprecated(6.2.0+) ZRangeByScore command. + // For example: + // ZRangeArgs{ + // Key: "example-key", + // Start: "(3", + // Stop: 8, + // ByScore: true, + // } + // cmd: "ZRange example-key (3 8 ByScore" (3 < score <= 8). + // + // For the ByLex option, it is similar to the deprecated(6.2.0+) ZRangeByLex command. + // You can set the and options as follows: + // ZRangeArgs{ + // Key: "example-key", + // Start: "[abc", + // Stop: "(def", + // ByLex: true, + // } + // cmd: "ZRange example-key [abc (def ByLex" + // + // For normal cases (ByScore==false && ByLex==false), and should be set to the index range (int). + // You can read the documentation for more information: https://redis.io/commands/zrange + Start interface{} + Stop interface{} + + // The ByScore and ByLex options are mutually exclusive. + ByScore bool + ByLex bool + + Rev bool + + // limit offset count. + Offset int64 + Count int64 +} + +func (z ZRangeArgs) appendArgs(args []interface{}) []interface{} { + // For Rev+ByScore/ByLex, we need to adjust the position of and . + if z.Rev && (z.ByScore || z.ByLex) { + args = append(args, z.Key, z.Stop, z.Start) + } else { + args = append(args, z.Key, z.Start, z.Stop) + } + + if z.ByScore { + args = append(args, "byscore") + } else if z.ByLex { + args = append(args, "bylex") + } + if z.Rev { + args = append(args, "rev") + } + if z.Offset != 0 || z.Count != 0 { + args = append(args, "limit", z.Offset, z.Count) + } + return args +} + +func (c cmdable) ZRangeArgs(ctx context.Context, z ZRangeArgs) *StringSliceCmd { + args := make([]interface{}, 0, 9) + args = append(args, "zrange") + args = z.appendArgs(args) + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRangeArgsWithScores(ctx context.Context, z ZRangeArgs) *ZSliceCmd { + args := make([]interface{}, 0, 10) + args = append(args, "zrange") + args = z.appendArgs(args) + args = append(args, "withscores") + cmd := NewZSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd { + return c.ZRangeArgs(ctx, ZRangeArgs{ + Key: key, + Start: start, + Stop: stop, + }) +} + +func (c cmdable) ZRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd { + return c.ZRangeArgsWithScores(ctx, ZRangeArgs{ + Key: key, + Start: start, + Stop: stop, + }) +} + +type ZRangeBy struct { + Min, Max string + Offset, Count int64 +} + +func (c cmdable) zRangeBy(ctx context.Context, zcmd, key string, opt *ZRangeBy, withScores bool) *StringSliceCmd { + args := []interface{}{zcmd, key, opt.Min, opt.Max} + if withScores { + args = append(args, "withscores") + } + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "limit", + opt.Offset, + opt.Count, + ) + } + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd { + return c.zRangeBy(ctx, "zrangebyscore", key, opt, false) +} + +func (c cmdable) ZRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd { + return c.zRangeBy(ctx, "zrangebylex", key, opt, false) +} + +func (c cmdable) ZRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd { + args := []interface{}{"zrangebyscore", key, opt.Min, opt.Max, "withscores"} + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "limit", + opt.Offset, + opt.Count, + ) + } + cmd := NewZSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRangeStore(ctx context.Context, dst string, z ZRangeArgs) *IntCmd { + args := make([]interface{}, 0, 10) + args = append(args, "zrangestore", dst) + args = z.appendArgs(args) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRank(ctx context.Context, key, member string) *IntCmd { + cmd := NewIntCmd(ctx, "zrank", key, member) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(members)) + args[0] = "zrem" + args[1] = key + args = appendArgs(args, members) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd { + cmd := NewIntCmd( + ctx, + "zremrangebyrank", + key, + start, + stop, + ) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd { + cmd := NewIntCmd(ctx, "zremrangebyscore", key, min, max) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRemRangeByLex(ctx context.Context, key, min, max string) *IntCmd { + cmd := NewIntCmd(ctx, "zremrangebylex", key, min, max) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRevRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd { + cmd := NewStringSliceCmd(ctx, "zrevrange", key, start, stop) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd { + cmd := NewZSliceCmd(ctx, "zrevrange", key, start, stop, "withscores") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) zRevRangeBy(ctx context.Context, zcmd, key string, opt *ZRangeBy) *StringSliceCmd { + args := []interface{}{zcmd, key, opt.Max, opt.Min} + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "limit", + opt.Offset, + opt.Count, + ) + } + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRevRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd { + return c.zRevRangeBy(ctx, "zrevrangebyscore", key, opt) +} + +func (c cmdable) ZRevRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd { + return c.zRevRangeBy(ctx, "zrevrangebylex", key, opt) +} + +func (c cmdable) ZRevRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd { + args := []interface{}{"zrevrangebyscore", key, opt.Max, opt.Min, "withscores"} + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "limit", + opt.Offset, + opt.Count, + ) + } + cmd := NewZSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZRevRank(ctx context.Context, key, member string) *IntCmd { + cmd := NewIntCmd(ctx, "zrevrank", key, member) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZScore(ctx context.Context, key, member string) *FloatCmd { + cmd := NewFloatCmd(ctx, "zscore", key, member) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZUnion(ctx context.Context, store ZStore) *StringSliceCmd { + args := make([]interface{}, 0, 2+store.len()) + args = append(args, "zunion", len(store.Keys)) + args = store.appendArgs(args) + cmd := NewStringSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZUnionWithScores(ctx context.Context, store ZStore) *ZSliceCmd { + args := make([]interface{}, 0, 3+store.len()) + args = append(args, "zunion", len(store.Keys)) + args = store.appendArgs(args) + args = append(args, "withscores") + cmd := NewZSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd { + args := make([]interface{}, 0, 3+store.len()) + args = append(args, "zunionstore", dest, len(store.Keys)) + args = store.appendArgs(args) + cmd := NewIntCmd(ctx, args...) + cmd.setFirstKeyPos(3) + _ = c(ctx, cmd) + return cmd +} + +// ZRandMember redis-server version >= 6.2.0. +func (c cmdable) ZRandMember(ctx context.Context, key string, count int, withScores bool) *StringSliceCmd { + args := make([]interface{}, 0, 4) + + // Although count=0 is meaningless, redis accepts count=0. + args = append(args, "zrandmember", key, count) + if withScores { + args = append(args, "withscores") + } + + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// ZDiff redis-server version >= 6.2.0. +func (c cmdable) ZDiff(ctx context.Context, keys ...string) *StringSliceCmd { + args := make([]interface{}, 2+len(keys)) + args[0] = "zdiff" + args[1] = len(keys) + for i, key := range keys { + args[i+2] = key + } + + cmd := NewStringSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +// ZDiffWithScores redis-server version >= 6.2.0. +func (c cmdable) ZDiffWithScores(ctx context.Context, keys ...string) *ZSliceCmd { + args := make([]interface{}, 3+len(keys)) + args[0] = "zdiff" + args[1] = len(keys) + for i, key := range keys { + args[i+2] = key + } + args[len(keys)+2] = "withscores" + + cmd := NewZSliceCmd(ctx, args...) + cmd.setFirstKeyPos(2) + _ = c(ctx, cmd) + return cmd +} + +// ZDiffStore redis-server version >=6.2.0. +func (c cmdable) ZDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd { + args := make([]interface{}, 0, 3+len(keys)) + args = append(args, "zdiffstore", destination, len(keys)) + for _, key := range keys { + args = append(args, key) + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) PFAdd(ctx context.Context, key string, els ...interface{}) *IntCmd { + args := make([]interface{}, 2, 2+len(els)) + args[0] = "pfadd" + args[1] = key + args = appendArgs(args, els) + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) PFCount(ctx context.Context, keys ...string) *IntCmd { + args := make([]interface{}, 1+len(keys)) + args[0] = "pfcount" + for i, key := range keys { + args[1+i] = key + } + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) PFMerge(ctx context.Context, dest string, keys ...string) *StatusCmd { + args := make([]interface{}, 2+len(keys)) + args[0] = "pfmerge" + args[1] = dest + for i, key := range keys { + args[2+i] = key + } + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c cmdable) BgRewriteAOF(ctx context.Context) *StatusCmd { + cmd := NewStatusCmd(ctx, "bgrewriteaof") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) BgSave(ctx context.Context) *StatusCmd { + cmd := NewStatusCmd(ctx, "bgsave") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) ClientKill(ctx context.Context, ipPort string) *StatusCmd { + cmd := NewStatusCmd(ctx, "client", "kill", ipPort) + _ = c(ctx, cmd) + return cmd +} + +// ClientKillByFilter is new style syntax, while the ClientKill is old +// +// CLIENT KILL