new project
This commit is contained in:
commit
9d9fe262f4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
config.yml
|
17
config.sample.yml
Normal file
17
config.sample.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
port: 10231
|
||||||
|
|
||||||
|
line:
|
||||||
|
access:
|
||||||
|
secret:
|
||||||
|
|
||||||
|
database:
|
||||||
|
host: localhost
|
||||||
|
port: 5432
|
||||||
|
user: 'postgres'
|
||||||
|
pass:
|
||||||
|
db: mtgbot
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
prefix:
|
52
go.mod
Normal file
52
go.mod
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
module git.trj.tw/golang/mtgbot
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.trj.tw/golang/utils v0.0.0-20181219032659-b1a8bce04189
|
||||||
|
github.com/alecthomas/gometalinter v2.0.12+incompatible // indirect
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
|
||||||
|
github.com/cosiner/argv v0.0.1 // indirect
|
||||||
|
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d // indirect
|
||||||
|
github.com/derekparker/delve v1.1.0 // indirect
|
||||||
|
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 // indirect
|
||||||
|
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 // indirect
|
||||||
|
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
|
||||||
|
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b // indirect
|
||||||
|
github.com/gin-gonic/gin v1.3.0
|
||||||
|
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect
|
||||||
|
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 // indirect
|
||||||
|
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.5 // indirect
|
||||||
|
github.com/jstemmer/gotags v1.4.1 // indirect
|
||||||
|
github.com/kisielk/errcheck v1.1.0 // indirect
|
||||||
|
github.com/klauspost/asmfmt v1.2.0 // indirect
|
||||||
|
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 // indirect
|
||||||
|
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||||
|
github.com/mdempsky/gocode v0.0.0-20181212191131-9c77a290fde2 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/nicksnyder/go-i18n v1.10.0 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.2.0 // indirect
|
||||||
|
github.com/peterh/liner v1.1.0 // indirect
|
||||||
|
github.com/rogpeppe/godef v1.1.1 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.2.0 // indirect
|
||||||
|
github.com/spf13/cobra v0.0.3 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.3 // indirect
|
||||||
|
github.com/stamblerre/gocode v0.0.0-20181212030458-2f9d39d8f31d // indirect
|
||||||
|
github.com/thinkerou/favicon v0.1.0 // indirect
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 // indirect
|
||||||
|
github.com/zmb3/gogetdoc v0.0.0-20181208215853-c5ca8f4d4936 // indirect
|
||||||
|
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect
|
||||||
|
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6 // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 // indirect
|
||||||
|
google.golang.org/grpc v1.17.0 // indirect
|
||||||
|
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
|
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3 // indirect
|
||||||
|
)
|
136
go.sum
Normal file
136
go.sum
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
9fans.net/go v0.0.0-20181112161441-237454027057 h1:OcHlKWkAMJEF1ndWLGxp5dnJQkYM/YImUOvsBoz6h5E=
|
||||||
|
9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY=
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
git.trj.tw/golang/utils v0.0.0-20181219032659-b1a8bce04189 h1:KHFlWRVFd5NIsnnFSy090+k6kT7KRotxUPTsho6F9lk=
|
||||||
|
git.trj.tw/golang/utils v0.0.0-20181219032659-b1a8bce04189/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0=
|
||||||
|
github.com/alecthomas/gometalinter v2.0.12+incompatible h1:RBUbc8pKtqRoVCymENDl7cpWS9Ht5XNnwwk0cKjpteI=
|
||||||
|
github.com/alecthomas/gometalinter v2.0.12+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cosiner/argv v0.0.1 h1:2iAFN+sWPktbZ4tvxm33Ei8VY66FPCxdOxpncUGpAXE=
|
||||||
|
github.com/cosiner/argv v0.0.1/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d h1:aRvyac5PN1NEfcANJ1tfs8GMs5I9OXsVeg0FJkpXOys=
|
||||||
|
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d/go.mod h1:8o/GRMvsb9VyFbSEZGXfa0dkSXml4G23W0D/h9FksWM=
|
||||||
|
github.com/derekparker/delve v1.1.0 h1:icd65nMp7s2HiLz6y/6RCVXBdoED3xxYLwX09EMaRCc=
|
||||||
|
github.com/derekparker/delve v1.1.0/go.mod h1:pMSZMfp0Nhbm8qdZJkuE/yPGOkLpGXLS1I4poXQpuJU=
|
||||||
|
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY=
|
||||||
|
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU=
|
||||||
|
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
|
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 h1:iXJdM8Uob6EPOG/PFr5q0J124ysiZdJfACHqICBb3b8=
|
||||||
|
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y=
|
||||||
|
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238 h1:Qo4RxRMFag+fvDqQ6A3MblYBormptQUZ1ssOtV+EeQ8=
|
||||||
|
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238/go.mod h1:pseIrV+t9A4+po+KJ1LheSnYH8m1qs6WhKx2zFiGi9I=
|
||||||
|
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
|
||||||
|
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
|
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8Jc6zMvyRz3PCQrTTCXnVRvEzyBcM890=
|
||||||
|
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
|
||||||
|
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
||||||
|
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||||
|
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/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
|
||||||
|
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||||
|
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
|
||||||
|
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
|
||||||
|
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4 h1:gmIVMdGlVf5e6Yo6+ZklxdOrvtOvyrAjJyXAbmOznyo=
|
||||||
|
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4/go.mod h1:t4Tr0tn92eq5ISef4cS5plFAMYAqZlAXtgUcKE6y8nw=
|
||||||
|
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||||
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/gotags v1.4.1 h1:aWIyXsU3lTDqhsEC49MP85p2cUUWr2ptvdGNqqGA3r4=
|
||||||
|
github.com/jstemmer/gotags v1.4.1/go.mod h1:b6J3X0bsLbR4C5SgSx3V3KjuWTtmRzcmWPbTkWZ49PA=
|
||||||
|
github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/asmfmt v1.2.0 h1:zwsyBYgEdabg32alMful/5pRtMTcR5C5w1LKNg9OD78=
|
||||||
|
github.com/klauspost/asmfmt v1.2.0/go.mod h1:RAoUvqkWr2rUa2I19qKMEVZQe4BVtcHGTMCUOcCU2Lg=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 h1:hunjgdb3b21ZdRmzDPXii0EcnHpjH7uCP+kODoE1JH0=
|
||||||
|
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91/go.mod h1:C2tFh8w3I6i4lnUJfoBx2Hwku3mgu4wPNTtUNp1i5KI=
|
||||||
|
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo=
|
||||||
|
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0=
|
||||||
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mdempsky/gocode v0.0.0-20181212191131-9c77a290fde2 h1:OqIVo8a2x2U2cn0+W044cmL3kSdCX5Rc7kRnK3ZRyAI=
|
||||||
|
github.com/mdempsky/gocode v0.0.0-20181212191131-9c77a290fde2/go.mod h1:hltEC42XzfMNgg0S1v6JTywwra2Mu6F6cLR03debVQ8=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/nicksnyder/go-i18n v1.10.0 h1:5AzlPKvXBH4qBzmZ09Ua9Gipyruv6uApMcrNZdo96+Q=
|
||||||
|
github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=
|
||||||
|
github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/godef v1.1.1 h1:NujOtt9q9vIClRTB3sCZpavac+NMRaIayzrcz1h4fSE=
|
||||||
|
github.com/rogpeppe/godef v1.1.1/go.mod h1:oEo1eMy1VUEHUzUIX4F7IqvMJRiz9UId44mvnR8oPlQ=
|
||||||
|
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stamblerre/gocode v0.0.0-20181212030458-2f9d39d8f31d h1:Bpu5DolLksGPpggDvoP5l9aruCElc6a47pHOSWwL74A=
|
||||||
|
github.com/stamblerre/gocode v0.0.0-20181212030458-2f9d39d8f31d/go.mod h1:EM2T8YDoTCvGXbEpFHxarbpv7VE26QD1++Cb1Pbh7Gs=
|
||||||
|
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/thinkerou/favicon v0.1.0 h1:eWMISKTpHq2G8HOuKn7ydD55j5DDehx94b0C2y8ABMs=
|
||||||
|
github.com/thinkerou/favicon v0.1.0/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/zmb3/gogetdoc v0.0.0-20181208215853-c5ca8f4d4936 h1:+We2eeE8UuACEPcT7Ez1/yK0MN6SAqzy6S2JPxJTycQ=
|
||||||
|
github.com/zmb3/gogetdoc v0.0.0-20181208215853-c5ca8f4d4936/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY=
|
||||||
|
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU=
|
||||||
|
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1 h1:rJm0LuqUjoDhSk2zO9ISMSToQxGz7Os2jRiOL8AWu4c=
|
||||||
|
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6 h1:IcgEB62HYgAhX0Nd/QrVgZlxlcyxbGQHElLUhW2X4Fo=
|
||||||
|
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181130195746-895048a75ecf/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 h1:zWPUEY/PjVHT+zO3L8OfkjrtIjf55joTxn/RQP/AjOI=
|
||||||
|
golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
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/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c h1:vTxShRUnK60yd8DZU+f95p1zSLj814+5CuEh7NjF2/Y=
|
||||||
|
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3 h1:LyX67rVB0kBUFoROrQfzKwdrYLH1cRzHibxdJW85J1c=
|
||||||
|
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
37
main.go
Normal file
37
main.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.trj.tw/golang/mtgbot/modules/config"
|
||||||
|
"git.trj.tw/golang/mtgbot/modules/options"
|
||||||
|
"git.trj.tw/golang/mtgbot/router/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
options.RegFlag()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
opts := options.GetOpts()
|
||||||
|
|
||||||
|
// Load config file
|
||||||
|
err := config.LoadConfig(opts.Config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
conf := config.GetConf()
|
||||||
|
|
||||||
|
// register session object types
|
||||||
|
regTypes()
|
||||||
|
|
||||||
|
engine := routes.NewEngine()
|
||||||
|
routes.SetRoutes(engine)
|
||||||
|
engine.Run(fmt.Sprintf(":%d", conf.Port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func regTypes() {
|
||||||
|
gob.Register(map[string]interface{}{})
|
||||||
|
}
|
1
modules/cmd/cmd.go
Normal file
1
modules/cmd/cmd.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package cmd
|
72
modules/config/config.go
Normal file
72
modules/config/config.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"git.trj.tw/golang/utils"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config -
|
||||||
|
type Config struct {
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Database struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
User string `yaml:"user"`
|
||||||
|
Pass string `yaml:"pass"`
|
||||||
|
DB string `yaml:"db"`
|
||||||
|
} `yaml:"database"`
|
||||||
|
Line struct {
|
||||||
|
Access string `yaml:"access"`
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
|
} `yaml:"line"`
|
||||||
|
Redis struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Prefix string `yaml:"prefix"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf *Config
|
||||||
|
|
||||||
|
// LoadConfig -
|
||||||
|
func LoadConfig(p ...string) error {
|
||||||
|
var fp string
|
||||||
|
|
||||||
|
if len(p) > 0 && len(p[0]) > 0 {
|
||||||
|
fp = p[0]
|
||||||
|
} else {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fp = path.Join(wd, "config.yml")
|
||||||
|
}
|
||||||
|
|
||||||
|
exists := utils.CheckExists(fp, false)
|
||||||
|
if !exists {
|
||||||
|
return errors.New("config file not exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = &Config{}
|
||||||
|
err = yaml.Unmarshal(data, conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConf -
|
||||||
|
func GetConf() *Config {
|
||||||
|
return conf
|
||||||
|
}
|
33
modules/context/context.go
Normal file
33
modules/context/context.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
import "github.com/gin-gonic/gin/binding"
|
||||||
|
|
||||||
|
// Context - custom http context
|
||||||
|
type Context struct {
|
||||||
|
*gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomMiddle -
|
||||||
|
type CustomMiddle func(*Context)
|
||||||
|
|
||||||
|
// PatchCtx - change custom middle to gin middle
|
||||||
|
func PatchCtx(handler CustomMiddle) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
ctx := &Context{
|
||||||
|
Context: c,
|
||||||
|
}
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindData - binding data
|
||||||
|
func (c *Context) BindData(i interface{}) error {
|
||||||
|
b := binding.Default(c.Request.Method, c.ContentType())
|
||||||
|
return c.ShouldBindWith(i, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomRes -
|
||||||
|
func (c *Context) CustomRes(status int, msg interface{}) {
|
||||||
|
c.AbortWithStatusJSON(status, msg)
|
||||||
|
}
|
25
modules/options/options.go
Normal file
25
modules/options/options.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import "flag"
|
||||||
|
|
||||||
|
// Options -
|
||||||
|
type Options struct {
|
||||||
|
Config string
|
||||||
|
DBTool bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var opts *Options
|
||||||
|
|
||||||
|
// RegFlag -
|
||||||
|
func RegFlag() {
|
||||||
|
opts = &Options{}
|
||||||
|
flag.StringVar(&opts.Config, "config", "", "config file path (default {PWD}/config.yml)")
|
||||||
|
flag.StringVar(&opts.Config, "f", "", "config file path (short) (default {PWD}/config.yml")
|
||||||
|
flag.BoolVar(&opts.DBTool, "dbtool", false, "run deploy database script")
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOpts -
|
||||||
|
func GetOpts() *Options {
|
||||||
|
return opts
|
||||||
|
}
|
19
router/routes/routes.go
Normal file
19
router/routes/routes.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
var r *gin.Engine
|
||||||
|
|
||||||
|
// NewEngine - new gin engine
|
||||||
|
func NewEngine() *gin.Engine {
|
||||||
|
r = gin.Default()
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoutes -
|
||||||
|
func SetRoutes(r *gin.Engine) {
|
||||||
|
r.GET("/", func(c *gin.Context) {
|
||||||
|
c.String(200, "Hello")
|
||||||
|
})
|
||||||
|
}
|
1
vendor/git.trj.tw/golang/utils/go.mod
vendored
Normal file
1
vendor/git.trj.tw/golang/utils/go.mod
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module git.trj.tw/golang/utils
|
171
vendor/git.trj.tw/golang/utils/utils.go
vendored
Normal file
171
vendor/git.trj.tw/golang/utils/utils.go
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageObject -
|
||||||
|
type PageObject struct {
|
||||||
|
Page int `json:"page" cc:"page"`
|
||||||
|
Total int `json:"total" cc:"total"`
|
||||||
|
Offset int `json:"offset" cc:"offset"`
|
||||||
|
Limit int `json:"limit" cc:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalcPage -
|
||||||
|
func CalcPage(count, page, max int) (po PageObject) {
|
||||||
|
if count < 0 {
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if max < 1 {
|
||||||
|
max = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
total := int(math.Ceil(float64(count) / float64(max)))
|
||||||
|
if total < 1 {
|
||||||
|
total = 1
|
||||||
|
}
|
||||||
|
if page > total {
|
||||||
|
page = total
|
||||||
|
}
|
||||||
|
offset := (page - 1) * max
|
||||||
|
if offset > count {
|
||||||
|
offset = count
|
||||||
|
}
|
||||||
|
limit := max
|
||||||
|
|
||||||
|
po = PageObject{}
|
||||||
|
po.Limit = limit
|
||||||
|
po.Page = page
|
||||||
|
po.Offset = offset
|
||||||
|
po.Total = total
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap struct to map[string]interface{}
|
||||||
|
func ToMap(ss interface{}) map[string]interface{} {
|
||||||
|
t := reflect.ValueOf(ss)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
smap := make(map[string]interface{})
|
||||||
|
mtag := regexp.MustCompile(`cc:\"(.+)\"`)
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`^[A-Z]`)
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
tag := string(t.Type().Field(i).Tag)
|
||||||
|
str := mtag.FindStringSubmatch(tag)
|
||||||
|
name := t.Type().Field(i).Name
|
||||||
|
if !re.Match([]byte(name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
leveling := false
|
||||||
|
if len(str) > 1 {
|
||||||
|
strArr := strings.Split(str[1], ",")
|
||||||
|
name = strArr[0]
|
||||||
|
if len(strArr) > 1 && strArr[1] == "<<" {
|
||||||
|
leveling = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Kind() == reflect.Slice {
|
||||||
|
if name == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if f.Len() > 0 && f.Index(0).Kind() != reflect.Struct {
|
||||||
|
smap[name] = f.Interface()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := make([]map[string]interface{}, f.Len())
|
||||||
|
for i := 0; i < f.Len(); i++ {
|
||||||
|
tmp[i] = ToMap(f.Index(i).Interface())
|
||||||
|
}
|
||||||
|
smap[name] = tmp
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Kind() == reflect.Struct {
|
||||||
|
if name == "-" && leveling == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if leveling == true {
|
||||||
|
tmp := ToMap(f.Interface())
|
||||||
|
for k, v := range tmp {
|
||||||
|
smap[k] = v
|
||||||
|
}
|
||||||
|
} else if name != "-" && leveling == false {
|
||||||
|
smap[name] = f.Interface()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "-" {
|
||||||
|
smap[name] = f.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return smap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePath - parse file path to absPath
|
||||||
|
func ParsePath(dst string) string {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
wd = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if []rune(dst)[0] == '~' {
|
||||||
|
home := UserHomeDir()
|
||||||
|
if len(home) > 0 {
|
||||||
|
dst = strings.Replace(dst, "~", home, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.IsAbs(dst) {
|
||||||
|
dst = path.Clean(dst)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
str := path.Join(wd, dst)
|
||||||
|
str = path.Clean(str)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserHomeDir - get user home directory
|
||||||
|
func UserHomeDir() string {
|
||||||
|
env := "HOME"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
env = "USERPROFILE"
|
||||||
|
} else if runtime.GOOS == "plan9" {
|
||||||
|
env = "home"
|
||||||
|
}
|
||||||
|
return os.Getenv(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckExists - check file exists
|
||||||
|
func CheckExists(filePath string, allowDir bool) bool {
|
||||||
|
filePath = ParsePath(filePath)
|
||||||
|
stat, err := os.Stat(filePath)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !allowDir && stat.IsDir() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
go:
|
||||||
|
- 1.6.4
|
||||||
|
- 1.7.4
|
||||||
|
- tip
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: 3
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -covermode=count -coverprofile=coverage.out
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
Normal file
@ -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.
|
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
Normal file
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
Normal file
@ -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.
|
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
Normal file
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
Normal file
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
Normal file
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
Normal file
@ -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}
|
||||||
|
}
|
||||||
|
}
|
5
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
Normal file
5
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
vendor/*
|
||||||
|
!vendor/vendor.json
|
||||||
|
coverage.out
|
||||||
|
count.out
|
||||||
|
test
|
35
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
Normal file
35
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
go:
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
- master
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: 10
|
||||||
|
|
||||||
|
install:
|
||||||
|
- make install
|
||||||
|
|
||||||
|
go_import_path: github.com/gin-gonic/gin
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make vet
|
||||||
|
- make fmt-check
|
||||||
|
- make embedmd
|
||||||
|
- 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
|
231
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
Normal file
231
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
**@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
|
604
vendor/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
Normal file
604
vendor/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
Normal file
@ -0,0 +1,604 @@
|
|||||||
|
|
||||||
|
## Benchmark System
|
||||||
|
|
||||||
|
**VM HOST:** DigitalOcean
|
||||||
|
**Machine:** 4 CPU, 8 GB RAM. Ubuntu 16.04.2 x64
|
||||||
|
**Date:** July 19th, 2017
|
||||||
|
**Go Version:** 1.8.3 linux/amd64
|
||||||
|
**Source:** [Go HTTP Router Benchmark](https://github.com/julienschmidt/go-http-routing-benchmark)
|
||||||
|
|
||||||
|
## Static Routes: 157
|
||||||
|
|
||||||
|
```
|
||||||
|
Gin: 30512 Bytes
|
||||||
|
|
||||||
|
HttpServeMux: 17344 Bytes
|
||||||
|
Ace: 30080 Bytes
|
||||||
|
Bear: 30472 Bytes
|
||||||
|
Beego: 96408 Bytes
|
||||||
|
Bone: 37904 Bytes
|
||||||
|
Denco: 10464 Bytes
|
||||||
|
Echo: 73680 Bytes
|
||||||
|
GocraftWeb: 55720 Bytes
|
||||||
|
Goji: 27200 Bytes
|
||||||
|
Gojiv2: 104464 Bytes
|
||||||
|
GoJsonRest: 136472 Bytes
|
||||||
|
GoRestful: 914904 Bytes
|
||||||
|
GorillaMux: 675568 Bytes
|
||||||
|
HttpRouter: 21128 Bytes
|
||||||
|
HttpTreeMux: 73448 Bytes
|
||||||
|
Kocha: 115072 Bytes
|
||||||
|
LARS: 30120 Bytes
|
||||||
|
Macaron: 37984 Bytes
|
||||||
|
Martini: 310832 Bytes
|
||||||
|
Pat: 20464 Bytes
|
||||||
|
Possum: 91328 Bytes
|
||||||
|
R2router: 23712 Bytes
|
||||||
|
Rivet: 23880 Bytes
|
||||||
|
Tango: 28008 Bytes
|
||||||
|
TigerTonic: 80368 Bytes
|
||||||
|
Traffic: 626480 Bytes
|
||||||
|
Vulcan: 369064 Bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## GithubAPI Routes: 203
|
||||||
|
|
||||||
|
```
|
||||||
|
Gin: 52672 Bytes
|
||||||
|
|
||||||
|
Ace: 48992 Bytes
|
||||||
|
Bear: 161592 Bytes
|
||||||
|
Beego: 147992 Bytes
|
||||||
|
Bone: 97728 Bytes
|
||||||
|
Denco: 36440 Bytes
|
||||||
|
Echo: 95672 Bytes
|
||||||
|
GocraftWeb: 95640 Bytes
|
||||||
|
Goji: 86088 Bytes
|
||||||
|
Gojiv2: 144392 Bytes
|
||||||
|
GoJsonRest: 134648 Bytes
|
||||||
|
GoRestful: 1410760 Bytes
|
||||||
|
GorillaMux: 1509488 Bytes
|
||||||
|
HttpRouter: 37464 Bytes
|
||||||
|
HttpTreeMux: 78800 Bytes
|
||||||
|
Kocha: 785408 Bytes
|
||||||
|
LARS: 49032 Bytes
|
||||||
|
Macaron: 132712 Bytes
|
||||||
|
Martini: 564352 Bytes
|
||||||
|
Pat: 21200 Bytes
|
||||||
|
Possum: 83888 Bytes
|
||||||
|
R2router: 47104 Bytes
|
||||||
|
Rivet: 42840 Bytes
|
||||||
|
Tango: 54584 Bytes
|
||||||
|
TigerTonic: 96384 Bytes
|
||||||
|
Traffic: 1061920 Bytes
|
||||||
|
Vulcan: 465296 Bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## GPlusAPI Routes: 13
|
||||||
|
|
||||||
|
```
|
||||||
|
Gin: 3968 Bytes
|
||||||
|
|
||||||
|
Ace: 3600 Bytes
|
||||||
|
Bear: 7112 Bytes
|
||||||
|
Beego: 10048 Bytes
|
||||||
|
Bone: 6480 Bytes
|
||||||
|
Denco: 3256 Bytes
|
||||||
|
Echo: 9000 Bytes
|
||||||
|
GocraftWeb: 7496 Bytes
|
||||||
|
Goji: 2912 Bytes
|
||||||
|
Gojiv2: 7376 Bytes
|
||||||
|
GoJsonRest: 11544 Bytes
|
||||||
|
GoRestful: 88776 Bytes
|
||||||
|
GorillaMux: 71488 Bytes
|
||||||
|
HttpRouter: 2712 Bytes
|
||||||
|
HttpTreeMux: 7440 Bytes
|
||||||
|
Kocha: 128880 Bytes
|
||||||
|
LARS: 3640 Bytes
|
||||||
|
Macaron: 8656 Bytes
|
||||||
|
Martini: 23936 Bytes
|
||||||
|
Pat: 1856 Bytes
|
||||||
|
Possum: 7248 Bytes
|
||||||
|
R2router: 3928 Bytes
|
||||||
|
Rivet: 3064 Bytes
|
||||||
|
Tango: 4912 Bytes
|
||||||
|
TigerTonic: 9408 Bytes
|
||||||
|
Traffic: 49472 Bytes
|
||||||
|
Vulcan: 25496 Bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## ParseAPI Routes: 26
|
||||||
|
|
||||||
|
```
|
||||||
|
Gin: 6928 Bytes
|
||||||
|
|
||||||
|
Ace: 6592 Bytes
|
||||||
|
Bear: 12320 Bytes
|
||||||
|
Beego: 18960 Bytes
|
||||||
|
Bone: 11024 Bytes
|
||||||
|
Denco: 4184 Bytes
|
||||||
|
Echo: 11168 Bytes
|
||||||
|
GocraftWeb: 12800 Bytes
|
||||||
|
Goji: 5232 Bytes
|
||||||
|
Gojiv2: 14464 Bytes
|
||||||
|
GoJsonRest: 14216 Bytes
|
||||||
|
GoRestful: 127368 Bytes
|
||||||
|
GorillaMux: 123016 Bytes
|
||||||
|
HttpRouter: 4976 Bytes
|
||||||
|
HttpTreeMux: 7848 Bytes
|
||||||
|
Kocha: 181712 Bytes
|
||||||
|
LARS: 6632 Bytes
|
||||||
|
Macaron: 13648 Bytes
|
||||||
|
Martini: 45952 Bytes
|
||||||
|
Pat: 2560 Bytes
|
||||||
|
Possum: 9200 Bytes
|
||||||
|
R2router: 7056 Bytes
|
||||||
|
Rivet: 5680 Bytes
|
||||||
|
Tango: 8664 Bytes
|
||||||
|
TigerTonic: 9840 Bytes
|
||||||
|
Traffic: 93480 Bytes
|
||||||
|
Vulcan: 44504 Bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static Routes
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGin_StaticAll 50000 34506 ns/op 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkAce_StaticAll 30000 49657 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkHttpServeMux_StaticAll 2000 1183737 ns/op 96 B/op 8 allocs/op
|
||||||
|
BenchmarkBeego_StaticAll 5000 412621 ns/op 57776 B/op 628 allocs/op
|
||||||
|
BenchmarkBear_StaticAll 10000 149242 ns/op 20336 B/op 461 allocs/op
|
||||||
|
BenchmarkBone_StaticAll 10000 118583 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkDenco_StaticAll 100000 13247 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkEcho_StaticAll 20000 79914 ns/op 5024 B/op 157 allocs/op
|
||||||
|
BenchmarkGocraftWeb_StaticAll 10000 211823 ns/op 46440 B/op 785 allocs/op
|
||||||
|
BenchmarkGoji_StaticAll 10000 109390 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGojiv2_StaticAll 3000 415533 ns/op 145696 B/op 1099 allocs/op
|
||||||
|
BenchmarkGoJsonRest_StaticAll 5000 364403 ns/op 51653 B/op 1727 allocs/op
|
||||||
|
BenchmarkGoRestful_StaticAll 500 2578579 ns/op 314936 B/op 3144 allocs/op
|
||||||
|
BenchmarkGorillaMux_StaticAll 500 2704856 ns/op 115648 B/op 1578 allocs/op
|
||||||
|
BenchmarkHttpRouter_StaticAll 100000 18541 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_StaticAll 100000 22332 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkKocha_StaticAll 50000 31176 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkLARS_StaticAll 50000 40840 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_StaticAll 5000 517656 ns/op 120576 B/op 1413 allocs/op
|
||||||
|
BenchmarkMartini_StaticAll 300 4462289 ns/op 125442 B/op 1717 allocs/op
|
||||||
|
BenchmarkPat_StaticAll 500 2157275 ns/op 533904 B/op 11123 allocs/op
|
||||||
|
BenchmarkPossum_StaticAll 10000 254701 ns/op 65312 B/op 471 allocs/op
|
||||||
|
BenchmarkR2router_StaticAll 10000 133956 ns/op 22608 B/op 628 allocs/op
|
||||||
|
BenchmarkRivet_StaticAll 30000 46812 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkTango_StaticAll 5000 390613 ns/op 39225 B/op 1256 allocs/op
|
||||||
|
BenchmarkTigerTonic_StaticAll 20000 88060 ns/op 7504 B/op 157 allocs/op
|
||||||
|
BenchmarkTraffic_StaticAll 500 2910236 ns/op 729736 B/op 14287 allocs/op
|
||||||
|
BenchmarkVulcan_StaticAll 5000 277366 ns/op 15386 B/op 471 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
## Micro Benchmarks
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGin_Param 20000000 113 ns/op 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkAce_Param 5000000 375 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_Param 1000000 1709 ns/op 456 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_Param 1000000 2484 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_Param 1000000 2391 ns/op 688 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_Param 10000000 240 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_Param 5000000 366 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGocraftWeb_Param 1000000 2343 ns/op 648 B/op 8 allocs/op
|
||||||
|
BenchmarkGoji_Param 1000000 1197 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_Param 1000000 2771 ns/op 944 B/op 8 allocs/op
|
||||||
|
BenchmarkGoJsonRest_Param 1000000 2993 ns/op 649 B/op 13 allocs/op
|
||||||
|
BenchmarkGoRestful_Param 200000 8860 ns/op 2296 B/op 21 allocs/op
|
||||||
|
BenchmarkGorillaMux_Param 500000 4461 ns/op 1056 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_Param 10000000 175 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_Param 1000000 1167 ns/op 352 B/op 3 allocs/op
|
||||||
|
BenchmarkKocha_Param 3000000 429 ns/op 56 B/op 3 allocs/op
|
||||||
|
BenchmarkLARS_Param 10000000 134 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_Param 500000 4635 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_Param 200000 9933 ns/op 1072 B/op 10 allocs/op
|
||||||
|
BenchmarkPat_Param 1000000 2929 ns/op 648 B/op 12 allocs/op
|
||||||
|
BenchmarkPossum_Param 1000000 2503 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_Param 1000000 1507 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_Param 5000000 297 ns/op 48 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_Param 1000000 1862 ns/op 248 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_Param 500000 5660 ns/op 992 B/op 17 allocs/op
|
||||||
|
BenchmarkTraffic_Param 200000 8408 ns/op 1960 B/op 21 allocs/op
|
||||||
|
BenchmarkVulcan_Param 2000000 963 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_Param5 2000000 740 ns/op 160 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_Param5 1000000 2777 ns/op 501 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_Param5 1000000 3740 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_Param5 1000000 2950 ns/op 736 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_Param5 2000000 644 ns/op 160 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_Param5 3000000 558 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_Param5 10000000 198 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_Param5 500000 3870 ns/op 920 B/op 11 allocs/op
|
||||||
|
BenchmarkGoji_Param5 1000000 1746 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_Param5 1000000 3214 ns/op 1008 B/op 8 allocs/op
|
||||||
|
BenchmarkGoJsonRest_Param5 500000 5509 ns/op 1097 B/op 16 allocs/op
|
||||||
|
BenchmarkGoRestful_Param5 200000 11232 ns/op 2392 B/op 21 allocs/op
|
||||||
|
BenchmarkGorillaMux_Param5 300000 7777 ns/op 1184 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_Param5 3000000 631 ns/op 160 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_Param5 1000000 2800 ns/op 576 B/op 6 allocs/op
|
||||||
|
BenchmarkKocha_Param5 1000000 2053 ns/op 440 B/op 10 allocs/op
|
||||||
|
BenchmarkLARS_Param5 10000000 232 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_Param5 500000 5888 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_Param5 200000 12807 ns/op 1232 B/op 11 allocs/op
|
||||||
|
BenchmarkPat_Param5 300000 7320 ns/op 964 B/op 32 allocs/op
|
||||||
|
BenchmarkPossum_Param5 1000000 2495 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_Param5 1000000 1844 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_Param5 2000000 935 ns/op 240 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_Param5 1000000 2327 ns/op 360 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_Param5 100000 18514 ns/op 2551 B/op 43 allocs/op
|
||||||
|
BenchmarkTraffic_Param5 200000 11997 ns/op 2248 B/op 25 allocs/op
|
||||||
|
BenchmarkVulcan_Param5 1000000 1333 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_Param20 1000000 2031 ns/op 640 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_Param20 200000 7285 ns/op 1664 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_Param20 300000 6224 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_Param20 200000 8023 ns/op 1903 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_Param20 1000000 2262 ns/op 640 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_Param20 1000000 1387 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_Param20 3000000 503 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_Param20 100000 14408 ns/op 3795 B/op 15 allocs/op
|
||||||
|
BenchmarkGoji_Param20 500000 5272 ns/op 1247 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_Param20 1000000 4163 ns/op 1248 B/op 8 allocs/op
|
||||||
|
BenchmarkGoJsonRest_Param20 100000 17866 ns/op 4485 B/op 20 allocs/op
|
||||||
|
BenchmarkGoRestful_Param20 100000 21022 ns/op 4724 B/op 23 allocs/op
|
||||||
|
BenchmarkGorillaMux_Param20 100000 17055 ns/op 3547 B/op 13 allocs/op
|
||||||
|
BenchmarkHttpRouter_Param20 1000000 1748 ns/op 640 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_Param20 200000 12246 ns/op 3196 B/op 10 allocs/op
|
||||||
|
BenchmarkKocha_Param20 300000 6861 ns/op 1808 B/op 27 allocs/op
|
||||||
|
BenchmarkLARS_Param20 3000000 526 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_Param20 100000 13069 ns/op 2906 B/op 12 allocs/op
|
||||||
|
BenchmarkMartini_Param20 100000 23602 ns/op 3597 B/op 13 allocs/op
|
||||||
|
BenchmarkPat_Param20 50000 32143 ns/op 4688 B/op 111 allocs/op
|
||||||
|
BenchmarkPossum_Param20 1000000 2396 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_Param20 200000 8907 ns/op 2283 B/op 7 allocs/op
|
||||||
|
BenchmarkRivet_Param20 1000000 3280 ns/op 1024 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_Param20 500000 4640 ns/op 856 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_Param20 20000 67581 ns/op 10532 B/op 138 allocs/op
|
||||||
|
BenchmarkTraffic_Param20 50000 40313 ns/op 7941 B/op 45 allocs/op
|
||||||
|
BenchmarkVulcan_Param20 1000000 2264 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_ParamWrite 3000000 532 ns/op 40 B/op 2 allocs/op
|
||||||
|
BenchmarkBear_ParamWrite 1000000 1778 ns/op 456 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_ParamWrite 1000000 2596 ns/op 376 B/op 5 allocs/op
|
||||||
|
BenchmarkBone_ParamWrite 1000000 2519 ns/op 688 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_ParamWrite 5000000 411 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_ParamWrite 2000000 718 ns/op 40 B/op 2 allocs/op
|
||||||
|
BenchmarkGin_ParamWrite 5000000 283 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_ParamWrite 1000000 2561 ns/op 656 B/op 9 allocs/op
|
||||||
|
BenchmarkGoji_ParamWrite 1000000 1378 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_ParamWrite 1000000 3128 ns/op 976 B/op 10 allocs/op
|
||||||
|
BenchmarkGoJsonRest_ParamWrite 500000 4446 ns/op 1128 B/op 18 allocs/op
|
||||||
|
BenchmarkGoRestful_ParamWrite 200000 10291 ns/op 2304 B/op 22 allocs/op
|
||||||
|
BenchmarkGorillaMux_ParamWrite 500000 5153 ns/op 1064 B/op 12 allocs/op
|
||||||
|
BenchmarkHttpRouter_ParamWrite 5000000 263 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_ParamWrite 1000000 1351 ns/op 352 B/op 3 allocs/op
|
||||||
|
BenchmarkKocha_ParamWrite 3000000 538 ns/op 56 B/op 3 allocs/op
|
||||||
|
BenchmarkLARS_ParamWrite 5000000 316 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_ParamWrite 500000 5756 ns/op 1160 B/op 14 allocs/op
|
||||||
|
BenchmarkMartini_ParamWrite 200000 13097 ns/op 1176 B/op 14 allocs/op
|
||||||
|
BenchmarkPat_ParamWrite 500000 4954 ns/op 1072 B/op 17 allocs/op
|
||||||
|
BenchmarkPossum_ParamWrite 1000000 2499 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_ParamWrite 1000000 1531 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_ParamWrite 3000000 570 ns/op 112 B/op 2 allocs/op
|
||||||
|
BenchmarkTango_ParamWrite 2000000 957 ns/op 136 B/op 4 allocs/op
|
||||||
|
BenchmarkTigerTonic_ParamWrite 200000 7025 ns/op 1424 B/op 23 allocs/op
|
||||||
|
BenchmarkTraffic_ParamWrite 200000 10112 ns/op 2384 B/op 25 allocs/op
|
||||||
|
BenchmarkVulcan_ParamWrite 1000000 1006 ns/op 98 B/op 3 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
## GitHub
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGin_GithubStatic 10000000 156 ns/op 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkAce_GithubStatic 5000000 294 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkBear_GithubStatic 2000000 893 ns/op 120 B/op 3 allocs/op
|
||||||
|
BenchmarkBeego_GithubStatic 1000000 2491 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_GithubStatic 50000 25300 ns/op 2880 B/op 60 allocs/op
|
||||||
|
BenchmarkDenco_GithubStatic 20000000 76.0 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkEcho_GithubStatic 2000000 516 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GithubStatic 1000000 1448 ns/op 296 B/op 5 allocs/op
|
||||||
|
BenchmarkGoji_GithubStatic 3000000 496 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGojiv2_GithubStatic 1000000 2941 ns/op 928 B/op 7 allocs/op
|
||||||
|
BenchmarkGoRestful_GithubStatic 100000 27256 ns/op 3224 B/op 22 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GithubStatic 1000000 2196 ns/op 329 B/op 11 allocs/op
|
||||||
|
BenchmarkGorillaMux_GithubStatic 50000 31617 ns/op 736 B/op 10 allocs/op
|
||||||
|
BenchmarkHttpRouter_GithubStatic 20000000 88.4 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GithubStatic 10000000 134 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkKocha_GithubStatic 20000000 113 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkLARS_GithubStatic 10000000 195 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GithubStatic 500000 3740 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkMartini_GithubStatic 50000 27673 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkPat_GithubStatic 100000 19470 ns/op 3648 B/op 76 allocs/op
|
||||||
|
BenchmarkPossum_GithubStatic 1000000 1729 ns/op 416 B/op 3 allocs/op
|
||||||
|
BenchmarkR2router_GithubStatic 2000000 879 ns/op 144 B/op 4 allocs/op
|
||||||
|
BenchmarkRivet_GithubStatic 10000000 231 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkTango_GithubStatic 1000000 2325 ns/op 248 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_GithubStatic 3000000 610 ns/op 48 B/op 1 allocs/op
|
||||||
|
BenchmarkTraffic_GithubStatic 20000 62973 ns/op 18904 B/op 148 allocs/op
|
||||||
|
BenchmarkVulcan_GithubStatic 1000000 1447 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_GithubParam 2000000 686 ns/op 96 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_GithubParam 1000000 2155 ns/op 496 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_GithubParam 1000000 2713 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_GithubParam 100000 15088 ns/op 1760 B/op 18 allocs/op
|
||||||
|
BenchmarkDenco_GithubParam 2000000 629 ns/op 128 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_GithubParam 2000000 653 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_GithubParam 5000000 255 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GithubParam 1000000 3145 ns/op 712 B/op 9 allocs/op
|
||||||
|
BenchmarkGoji_GithubParam 1000000 1916 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_GithubParam 1000000 3975 ns/op 1024 B/op 10 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GithubParam 300000 4134 ns/op 713 B/op 14 allocs/op
|
||||||
|
BenchmarkGoRestful_GithubParam 50000 30782 ns/op 2360 B/op 21 allocs/op
|
||||||
|
BenchmarkGorillaMux_GithubParam 100000 17148 ns/op 1088 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_GithubParam 3000000 523 ns/op 96 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GithubParam 1000000 1671 ns/op 384 B/op 4 allocs/op
|
||||||
|
BenchmarkKocha_GithubParam 1000000 1021 ns/op 128 B/op 5 allocs/op
|
||||||
|
BenchmarkLARS_GithubParam 5000000 283 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GithubParam 500000 4270 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_GithubParam 100000 21728 ns/op 1152 B/op 11 allocs/op
|
||||||
|
BenchmarkPat_GithubParam 200000 11208 ns/op 2464 B/op 48 allocs/op
|
||||||
|
BenchmarkPossum_GithubParam 1000000 2334 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_GithubParam 1000000 1487 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_GithubParam 2000000 782 ns/op 96 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_GithubParam 1000000 2653 ns/op 344 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_GithubParam 300000 14073 ns/op 1440 B/op 24 allocs/op
|
||||||
|
BenchmarkTraffic_GithubParam 50000 29164 ns/op 5992 B/op 52 allocs/op
|
||||||
|
BenchmarkVulcan_GithubParam 1000000 2529 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_GithubAll 10000 134059 ns/op 13792 B/op 167 allocs/op
|
||||||
|
BenchmarkBear_GithubAll 5000 534445 ns/op 86448 B/op 943 allocs/op
|
||||||
|
BenchmarkBeego_GithubAll 3000 592444 ns/op 74705 B/op 812 allocs/op
|
||||||
|
BenchmarkBone_GithubAll 200 6957308 ns/op 698784 B/op 8453 allocs/op
|
||||||
|
BenchmarkDenco_GithubAll 10000 158819 ns/op 20224 B/op 167 allocs/op
|
||||||
|
BenchmarkEcho_GithubAll 10000 154700 ns/op 6496 B/op 203 allocs/op
|
||||||
|
BenchmarkGin_GithubAll 30000 48375 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GithubAll 3000 570806 ns/op 131656 B/op 1686 allocs/op
|
||||||
|
BenchmarkGoji_GithubAll 2000 818034 ns/op 56112 B/op 334 allocs/op
|
||||||
|
BenchmarkGojiv2_GithubAll 2000 1213973 ns/op 274768 B/op 3712 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GithubAll 2000 785796 ns/op 134371 B/op 2737 allocs/op
|
||||||
|
BenchmarkGoRestful_GithubAll 300 5238188 ns/op 689672 B/op 4519 allocs/op
|
||||||
|
BenchmarkGorillaMux_GithubAll 100 10257726 ns/op 211840 B/op 2272 allocs/op
|
||||||
|
BenchmarkHttpRouter_GithubAll 20000 105414 ns/op 13792 B/op 167 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GithubAll 10000 319934 ns/op 65856 B/op 671 allocs/op
|
||||||
|
BenchmarkKocha_GithubAll 10000 209442 ns/op 23304 B/op 843 allocs/op
|
||||||
|
BenchmarkLARS_GithubAll 20000 62565 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GithubAll 2000 1161270 ns/op 204194 B/op 2000 allocs/op
|
||||||
|
BenchmarkMartini_GithubAll 200 9991713 ns/op 226549 B/op 2325 allocs/op
|
||||||
|
BenchmarkPat_GithubAll 200 5590793 ns/op 1499568 B/op 27435 allocs/op
|
||||||
|
BenchmarkPossum_GithubAll 10000 319768 ns/op 84448 B/op 609 allocs/op
|
||||||
|
BenchmarkR2router_GithubAll 10000 305134 ns/op 77328 B/op 979 allocs/op
|
||||||
|
BenchmarkRivet_GithubAll 10000 132134 ns/op 16272 B/op 167 allocs/op
|
||||||
|
BenchmarkTango_GithubAll 3000 552754 ns/op 63826 B/op 1618 allocs/op
|
||||||
|
BenchmarkTigerTonic_GithubAll 1000 1439483 ns/op 239104 B/op 5374 allocs/op
|
||||||
|
BenchmarkTraffic_GithubAll 100 11383067 ns/op 2659329 B/op 21848 allocs/op
|
||||||
|
BenchmarkVulcan_GithubAll 5000 394253 ns/op 19894 B/op 609 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
## Google+
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGin_GPlusStatic 10000000 183 ns/op 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkAce_GPlusStatic 5000000 276 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkBear_GPlusStatic 2000000 652 ns/op 104 B/op 3 allocs/op
|
||||||
|
BenchmarkBeego_GPlusStatic 1000000 2239 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_GPlusStatic 5000000 380 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkDenco_GPlusStatic 30000000 45.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkEcho_GPlusStatic 5000000 338 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GPlusStatic 1000000 1158 ns/op 280 B/op 5 allocs/op
|
||||||
|
BenchmarkGoji_GPlusStatic 5000000 331 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGojiv2_GPlusStatic 1000000 2106 ns/op 928 B/op 7 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GPlusStatic 1000000 1626 ns/op 329 B/op 11 allocs/op
|
||||||
|
BenchmarkGoRestful_GPlusStatic 300000 7598 ns/op 1976 B/op 20 allocs/op
|
||||||
|
BenchmarkGorillaMux_GPlusStatic 1000000 2629 ns/op 736 B/op 10 allocs/op
|
||||||
|
BenchmarkHttpRouter_GPlusStatic 30000000 52.5 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GPlusStatic 20000000 85.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkKocha_GPlusStatic 20000000 89.2 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkLARS_GPlusStatic 10000000 162 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GPlusStatic 500000 3479 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkMartini_GPlusStatic 200000 9092 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkPat_GPlusStatic 3000000 493 ns/op 96 B/op 2 allocs/op
|
||||||
|
BenchmarkPossum_GPlusStatic 1000000 1467 ns/op 416 B/op 3 allocs/op
|
||||||
|
BenchmarkR2router_GPlusStatic 2000000 788 ns/op 144 B/op 4 allocs/op
|
||||||
|
BenchmarkRivet_GPlusStatic 20000000 114 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkTango_GPlusStatic 1000000 1534 ns/op 200 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_GPlusStatic 5000000 282 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkTraffic_GPlusStatic 500000 3798 ns/op 1192 B/op 15 allocs/op
|
||||||
|
BenchmarkVulcan_GPlusStatic 2000000 1125 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_GPlusParam 3000000 528 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_GPlusParam 1000000 1570 ns/op 480 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_GPlusParam 1000000 2369 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_GPlusParam 1000000 2028 ns/op 688 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_GPlusParam 5000000 385 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_GPlusParam 3000000 441 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_GPlusParam 10000000 174 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GPlusParam 1000000 2033 ns/op 648 B/op 8 allocs/op
|
||||||
|
BenchmarkGoji_GPlusParam 1000000 1399 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_GPlusParam 1000000 2641 ns/op 944 B/op 8 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GPlusParam 1000000 2824 ns/op 649 B/op 13 allocs/op
|
||||||
|
BenchmarkGoRestful_GPlusParam 200000 8875 ns/op 2296 B/op 21 allocs/op
|
||||||
|
BenchmarkGorillaMux_GPlusParam 200000 6291 ns/op 1056 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_GPlusParam 5000000 316 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GPlusParam 1000000 1129 ns/op 352 B/op 3 allocs/op
|
||||||
|
BenchmarkKocha_GPlusParam 3000000 538 ns/op 56 B/op 3 allocs/op
|
||||||
|
BenchmarkLARS_GPlusParam 10000000 198 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GPlusParam 500000 3554 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_GPlusParam 200000 9831 ns/op 1072 B/op 10 allocs/op
|
||||||
|
BenchmarkPat_GPlusParam 1000000 2706 ns/op 688 B/op 12 allocs/op
|
||||||
|
BenchmarkPossum_GPlusParam 1000000 2297 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_GPlusParam 1000000 1318 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_GPlusParam 5000000 399 ns/op 48 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_GPlusParam 1000000 2070 ns/op 264 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_GPlusParam 500000 4853 ns/op 1056 B/op 17 allocs/op
|
||||||
|
BenchmarkTraffic_GPlusParam 200000 8278 ns/op 1976 B/op 21 allocs/op
|
||||||
|
BenchmarkVulcan_GPlusParam 1000000 1243 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_GPlus2Params 3000000 549 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_GPlus2Params 1000000 2112 ns/op 496 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_GPlus2Params 500000 2750 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_GPlus2Params 300000 7032 ns/op 1040 B/op 9 allocs/op
|
||||||
|
BenchmarkDenco_GPlus2Params 3000000 502 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_GPlus2Params 3000000 641 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_GPlus2Params 5000000 250 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GPlus2Params 1000000 2681 ns/op 712 B/op 9 allocs/op
|
||||||
|
BenchmarkGoji_GPlus2Params 1000000 1926 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_GPlus2Params 500000 3996 ns/op 1024 B/op 11 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GPlus2Params 500000 3886 ns/op 713 B/op 14 allocs/op
|
||||||
|
BenchmarkGoRestful_GPlus2Params 200000 10376 ns/op 2360 B/op 21 allocs/op
|
||||||
|
BenchmarkGorillaMux_GPlus2Params 100000 14162 ns/op 1088 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_GPlus2Params 5000000 336 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GPlus2Params 1000000 1523 ns/op 384 B/op 4 allocs/op
|
||||||
|
BenchmarkKocha_GPlus2Params 2000000 970 ns/op 128 B/op 5 allocs/op
|
||||||
|
BenchmarkLARS_GPlus2Params 5000000 238 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GPlus2Params 500000 4016 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_GPlus2Params 100000 21253 ns/op 1200 B/op 13 allocs/op
|
||||||
|
BenchmarkPat_GPlus2Params 200000 8632 ns/op 2256 B/op 34 allocs/op
|
||||||
|
BenchmarkPossum_GPlus2Params 1000000 2171 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_GPlus2Params 1000000 1340 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_GPlus2Params 3000000 557 ns/op 96 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_GPlus2Params 1000000 2186 ns/op 344 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_GPlus2Params 200000 9060 ns/op 1488 B/op 24 allocs/op
|
||||||
|
BenchmarkTraffic_GPlus2Params 100000 20324 ns/op 3272 B/op 31 allocs/op
|
||||||
|
BenchmarkVulcan_GPlus2Params 1000000 2039 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_GPlusAll 300000 6603 ns/op 640 B/op 11 allocs/op
|
||||||
|
BenchmarkBear_GPlusAll 100000 22363 ns/op 5488 B/op 61 allocs/op
|
||||||
|
BenchmarkBeego_GPlusAll 50000 38757 ns/op 4784 B/op 52 allocs/op
|
||||||
|
BenchmarkBone_GPlusAll 20000 54916 ns/op 10336 B/op 98 allocs/op
|
||||||
|
BenchmarkDenco_GPlusAll 300000 4959 ns/op 672 B/op 11 allocs/op
|
||||||
|
BenchmarkEcho_GPlusAll 200000 6558 ns/op 416 B/op 13 allocs/op
|
||||||
|
BenchmarkGin_GPlusAll 500000 2757 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_GPlusAll 50000 34615 ns/op 8040 B/op 103 allocs/op
|
||||||
|
BenchmarkGoji_GPlusAll 100000 16002 ns/op 3696 B/op 22 allocs/op
|
||||||
|
BenchmarkGojiv2_GPlusAll 50000 35060 ns/op 12624 B/op 115 allocs/op
|
||||||
|
BenchmarkGoJsonRest_GPlusAll 50000 41479 ns/op 8117 B/op 170 allocs/op
|
||||||
|
BenchmarkGoRestful_GPlusAll 10000 131653 ns/op 32024 B/op 275 allocs/op
|
||||||
|
BenchmarkGorillaMux_GPlusAll 10000 101380 ns/op 13296 B/op 142 allocs/op
|
||||||
|
BenchmarkHttpRouter_GPlusAll 500000 3711 ns/op 640 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_GPlusAll 100000 14438 ns/op 4032 B/op 38 allocs/op
|
||||||
|
BenchmarkKocha_GPlusAll 200000 8039 ns/op 976 B/op 43 allocs/op
|
||||||
|
BenchmarkLARS_GPlusAll 500000 2630 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_GPlusAll 30000 51123 ns/op 13152 B/op 128 allocs/op
|
||||||
|
BenchmarkMartini_GPlusAll 10000 176157 ns/op 14016 B/op 145 allocs/op
|
||||||
|
BenchmarkPat_GPlusAll 20000 69911 ns/op 16576 B/op 298 allocs/op
|
||||||
|
BenchmarkPossum_GPlusAll 100000 20716 ns/op 5408 B/op 39 allocs/op
|
||||||
|
BenchmarkR2router_GPlusAll 100000 17463 ns/op 5040 B/op 63 allocs/op
|
||||||
|
BenchmarkRivet_GPlusAll 300000 5142 ns/op 768 B/op 11 allocs/op
|
||||||
|
BenchmarkTango_GPlusAll 50000 27321 ns/op 3656 B/op 104 allocs/op
|
||||||
|
BenchmarkTigerTonic_GPlusAll 20000 77597 ns/op 14512 B/op 288 allocs/op
|
||||||
|
BenchmarkTraffic_GPlusAll 10000 151406 ns/op 37360 B/op 392 allocs/op
|
||||||
|
BenchmarkVulcan_GPlusAll 100000 18555 ns/op 1274 B/op 39 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parse.com
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGin_ParseStatic 10000000 133 ns/op 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkAce_ParseStatic 5000000 241 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkBear_ParseStatic 2000000 728 ns/op 120 B/op 3 allocs/op
|
||||||
|
BenchmarkBeego_ParseStatic 1000000 2623 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_ParseStatic 1000000 1285 ns/op 144 B/op 3 allocs/op
|
||||||
|
BenchmarkDenco_ParseStatic 30000000 57.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkEcho_ParseStatic 5000000 342 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGocraftWeb_ParseStatic 1000000 1478 ns/op 296 B/op 5 allocs/op
|
||||||
|
BenchmarkGoji_ParseStatic 3000000 415 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGojiv2_ParseStatic 1000000 2087 ns/op 928 B/op 7 allocs/op
|
||||||
|
BenchmarkGoJsonRest_ParseStatic 1000000 1712 ns/op 329 B/op 11 allocs/op
|
||||||
|
BenchmarkGoRestful_ParseStatic 200000 11072 ns/op 3224 B/op 22 allocs/op
|
||||||
|
BenchmarkGorillaMux_ParseStatic 500000 4129 ns/op 752 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_ParseStatic 30000000 52.4 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_ParseStatic 20000000 109 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkKocha_ParseStatic 20000000 81.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkLARS_ParseStatic 10000000 150 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_ParseStatic 1000000 3288 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkMartini_ParseStatic 200000 9110 ns/op 768 B/op 9 allocs/op
|
||||||
|
BenchmarkPat_ParseStatic 1000000 1135 ns/op 240 B/op 5 allocs/op
|
||||||
|
BenchmarkPossum_ParseStatic 1000000 1557 ns/op 416 B/op 3 allocs/op
|
||||||
|
BenchmarkR2router_ParseStatic 2000000 730 ns/op 144 B/op 4 allocs/op
|
||||||
|
BenchmarkRivet_ParseStatic 10000000 121 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkTango_ParseStatic 1000000 1688 ns/op 248 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_ParseStatic 3000000 427 ns/op 48 B/op 1 allocs/op
|
||||||
|
BenchmarkTraffic_ParseStatic 500000 5962 ns/op 1816 B/op 20 allocs/op
|
||||||
|
BenchmarkVulcan_ParseStatic 2000000 969 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_ParseParam 3000000 497 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_ParseParam 1000000 1473 ns/op 467 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_ParseParam 1000000 2384 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_ParseParam 1000000 2513 ns/op 768 B/op 6 allocs/op
|
||||||
|
BenchmarkDenco_ParseParam 5000000 364 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_ParseParam 5000000 418 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_ParseParam 10000000 163 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_ParseParam 1000000 2361 ns/op 664 B/op 8 allocs/op
|
||||||
|
BenchmarkGoji_ParseParam 1000000 1590 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_ParseParam 1000000 2851 ns/op 976 B/op 9 allocs/op
|
||||||
|
BenchmarkGoJsonRest_ParseParam 1000000 2965 ns/op 649 B/op 13 allocs/op
|
||||||
|
BenchmarkGoRestful_ParseParam 200000 12207 ns/op 3544 B/op 23 allocs/op
|
||||||
|
BenchmarkGorillaMux_ParseParam 500000 5187 ns/op 1088 B/op 12 allocs/op
|
||||||
|
BenchmarkHttpRouter_ParseParam 5000000 275 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_ParseParam 1000000 1108 ns/op 352 B/op 3 allocs/op
|
||||||
|
BenchmarkKocha_ParseParam 3000000 495 ns/op 56 B/op 3 allocs/op
|
||||||
|
BenchmarkLARS_ParseParam 10000000 192 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_ParseParam 500000 4103 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_ParseParam 200000 9878 ns/op 1072 B/op 10 allocs/op
|
||||||
|
BenchmarkPat_ParseParam 500000 3657 ns/op 1120 B/op 17 allocs/op
|
||||||
|
BenchmarkPossum_ParseParam 1000000 2084 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_ParseParam 1000000 1251 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_ParseParam 5000000 335 ns/op 48 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_ParseParam 1000000 1854 ns/op 280 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_ParseParam 500000 4582 ns/op 1008 B/op 17 allocs/op
|
||||||
|
BenchmarkTraffic_ParseParam 200000 8125 ns/op 2248 B/op 23 allocs/op
|
||||||
|
BenchmarkVulcan_ParseParam 1000000 1148 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_Parse2Params 3000000 539 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkBear_Parse2Params 1000000 1778 ns/op 496 B/op 5 allocs/op
|
||||||
|
BenchmarkBeego_Parse2Params 1000000 2519 ns/op 368 B/op 4 allocs/op
|
||||||
|
BenchmarkBone_Parse2Params 1000000 2596 ns/op 720 B/op 5 allocs/op
|
||||||
|
BenchmarkDenco_Parse2Params 3000000 492 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkEcho_Parse2Params 3000000 484 ns/op 32 B/op 1 allocs/op
|
||||||
|
BenchmarkGin_Parse2Params 10000000 193 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_Parse2Params 1000000 2575 ns/op 712 B/op 9 allocs/op
|
||||||
|
BenchmarkGoji_Parse2Params 1000000 1373 ns/op 336 B/op 2 allocs/op
|
||||||
|
BenchmarkGojiv2_Parse2Params 500000 2416 ns/op 960 B/op 8 allocs/op
|
||||||
|
BenchmarkGoJsonRest_Parse2Params 300000 3452 ns/op 713 B/op 14 allocs/op
|
||||||
|
BenchmarkGoRestful_Parse2Params 100000 17719 ns/op 6008 B/op 25 allocs/op
|
||||||
|
BenchmarkGorillaMux_Parse2Params 300000 5102 ns/op 1088 B/op 11 allocs/op
|
||||||
|
BenchmarkHttpRouter_Parse2Params 5000000 303 ns/op 64 B/op 1 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_Parse2Params 1000000 1372 ns/op 384 B/op 4 allocs/op
|
||||||
|
BenchmarkKocha_Parse2Params 2000000 874 ns/op 128 B/op 5 allocs/op
|
||||||
|
BenchmarkLARS_Parse2Params 10000000 192 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_Parse2Params 500000 3871 ns/op 1056 B/op 10 allocs/op
|
||||||
|
BenchmarkMartini_Parse2Params 200000 9954 ns/op 1152 B/op 11 allocs/op
|
||||||
|
BenchmarkPat_Parse2Params 500000 4194 ns/op 832 B/op 17 allocs/op
|
||||||
|
BenchmarkPossum_Parse2Params 1000000 2121 ns/op 560 B/op 6 allocs/op
|
||||||
|
BenchmarkR2router_Parse2Params 1000000 1415 ns/op 432 B/op 5 allocs/op
|
||||||
|
BenchmarkRivet_Parse2Params 3000000 457 ns/op 96 B/op 1 allocs/op
|
||||||
|
BenchmarkTango_Parse2Params 1000000 1914 ns/op 312 B/op 8 allocs/op
|
||||||
|
BenchmarkTigerTonic_Parse2Params 300000 6895 ns/op 1408 B/op 24 allocs/op
|
||||||
|
BenchmarkTraffic_Parse2Params 200000 8317 ns/op 2040 B/op 22 allocs/op
|
||||||
|
BenchmarkVulcan_Parse2Params 1000000 1274 ns/op 98 B/op 3 allocs/op
|
||||||
|
BenchmarkAce_ParseAll 200000 10401 ns/op 640 B/op 16 allocs/op
|
||||||
|
BenchmarkBear_ParseAll 50000 37743 ns/op 8928 B/op 110 allocs/op
|
||||||
|
BenchmarkBeego_ParseAll 20000 63193 ns/op 9568 B/op 104 allocs/op
|
||||||
|
BenchmarkBone_ParseAll 20000 61767 ns/op 14160 B/op 131 allocs/op
|
||||||
|
BenchmarkDenco_ParseAll 300000 7036 ns/op 928 B/op 16 allocs/op
|
||||||
|
BenchmarkEcho_ParseAll 200000 11824 ns/op 832 B/op 26 allocs/op
|
||||||
|
BenchmarkGin_ParseAll 300000 4199 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGocraftWeb_ParseAll 30000 51758 ns/op 13728 B/op 181 allocs/op
|
||||||
|
BenchmarkGoji_ParseAll 50000 29614 ns/op 5376 B/op 32 allocs/op
|
||||||
|
BenchmarkGojiv2_ParseAll 20000 68676 ns/op 24464 B/op 199 allocs/op
|
||||||
|
BenchmarkGoJsonRest_ParseAll 20000 76135 ns/op 13866 B/op 321 allocs/op
|
||||||
|
BenchmarkGoRestful_ParseAll 5000 389487 ns/op 110928 B/op 600 allocs/op
|
||||||
|
BenchmarkGorillaMux_ParseAll 10000 221250 ns/op 24864 B/op 292 allocs/op
|
||||||
|
BenchmarkHttpRouter_ParseAll 200000 6444 ns/op 640 B/op 16 allocs/op
|
||||||
|
BenchmarkHttpTreeMux_ParseAll 50000 30702 ns/op 5728 B/op 51 allocs/op
|
||||||
|
BenchmarkKocha_ParseAll 200000 13712 ns/op 1112 B/op 54 allocs/op
|
||||||
|
BenchmarkLARS_ParseAll 300000 6925 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkMacaron_ParseAll 20000 96278 ns/op 24576 B/op 250 allocs/op
|
||||||
|
BenchmarkMartini_ParseAll 5000 271352 ns/op 25072 B/op 253 allocs/op
|
||||||
|
BenchmarkPat_ParseAll 20000 74941 ns/op 17264 B/op 343 allocs/op
|
||||||
|
BenchmarkPossum_ParseAll 50000 39947 ns/op 10816 B/op 78 allocs/op
|
||||||
|
BenchmarkR2router_ParseAll 50000 42479 ns/op 8352 B/op 120 allocs/op
|
||||||
|
BenchmarkRivet_ParseAll 200000 7726 ns/op 912 B/op 16 allocs/op
|
||||||
|
BenchmarkTango_ParseAll 30000 50014 ns/op 7168 B/op 208 allocs/op
|
||||||
|
BenchmarkTigerTonic_ParseAll 10000 106550 ns/op 19728 B/op 379 allocs/op
|
||||||
|
BenchmarkTraffic_ParseAll 10000 216037 ns/op 57776 B/op 642 allocs/op
|
||||||
|
BenchmarkVulcan_ParseAll 50000 34379 ns/op 2548 B/op 78 allocs/op
|
||||||
|
```
|
213
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
Normal file
213
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
### Gin 1.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 typeded)
|
||||||
|
- [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 shorcut 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 shorcut 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 setted 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()
|
46
vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
generated
vendored
Normal file
46
vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -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/
|
13
vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
generated
vendored
Normal file
13
vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
generated
vendored
Normal file
@ -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 integrations 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.
|
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
Normal file
@ -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.
|
62
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
Normal file
62
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
GOFMT ?= gofmt "-s"
|
||||||
|
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||||
|
VETPACKAGES ?= $(shell go list ./... | grep -v /vendor/ | grep -v /examples/)
|
||||||
|
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
|
||||||
|
|
||||||
|
all: install
|
||||||
|
|
||||||
|
install: deps
|
||||||
|
govendor sync
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
sh coverage.sh
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
$(GOFMT) -w $(GOFILES)
|
||||||
|
|
||||||
|
.PHONY: fmt-check
|
||||||
|
fmt-check:
|
||||||
|
# get all go files and run go fmt on them
|
||||||
|
@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)
|
||||||
|
|
||||||
|
deps:
|
||||||
|
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go get -u github.com/kardianos/govendor; \
|
||||||
|
fi
|
||||||
|
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go get -u github.com/campoy/embedmd; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
embedmd:
|
||||||
|
embedmd -d *.md
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go get -u github.com/golang/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)
|
1820
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
Normal file
1820
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
96
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
Normal file
96
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 pair.value == authValue {
|
||||||
|
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 {
|
||||||
|
assert1(len(accounts) > 0, "Empty list of authorized credentials")
|
||||||
|
pairs := make(authPairs, 0, len(accounts))
|
||||||
|
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([]byte(base))
|
||||||
|
}
|
||||||
|
|
||||||
|
func secureCompare(given, actual string) bool {
|
||||||
|
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||||
|
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||||
|
}
|
||||||
|
// Securely compare actual to itself to keep constant time, but always return false.
|
||||||
|
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||||
|
}
|
99
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
Normal file
99
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 reqest. 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{}
|
||||||
|
MsgPack = msgpackBinding{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 MIMEMSGPACK, MIMEMSGPACK2:
|
||||||
|
return MsgPack
|
||||||
|
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
||||||
|
return Form
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validate(obj interface{}) error {
|
||||||
|
if Validator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Validator.ValidateStruct(obj)
|
||||||
|
}
|
51
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
Normal file
51
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gopkg.in/go-playground/validator.v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type defaultValidator struct {
|
||||||
|
once sync.Once
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
value := reflect.ValueOf(obj)
|
||||||
|
valueType := value.Kind()
|
||||||
|
if valueType == reflect.Ptr {
|
||||||
|
valueType = value.Elem().Kind()
|
||||||
|
}
|
||||||
|
if valueType == reflect.Struct {
|
||||||
|
v.lazyinit()
|
||||||
|
if err := v.validate.Struct(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
config := &validator.Config{TagName: "binding"}
|
||||||
|
v.validate = validator.New(config)
|
||||||
|
})
|
||||||
|
}
|
56
vendor/github.com/gin-gonic/gin/binding/form.go
generated
vendored
Normal file
56
vendor/github.com/gin-gonic/gin/binding/form.go
generated
vendored
Normal file
@ -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 "net/http"
|
||||||
|
|
||||||
|
const defaultMemory = 32 * 1024 * 1024
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
req.ParseMultipartForm(defaultMemory)
|
||||||
|
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 := mapForm(obj, req.MultipartForm.Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
209
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
Normal file
209
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// 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"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||||
|
typ := reflect.TypeOf(ptr).Elem()
|
||||||
|
val := reflect.ValueOf(ptr).Elem()
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
typeField := typ.Field(i)
|
||||||
|
structField := val.Field(i)
|
||||||
|
if !structField.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
structFieldKind := structField.Kind()
|
||||||
|
inputFieldName := typeField.Tag.Get("form")
|
||||||
|
inputFieldNameList := strings.Split(inputFieldName, ",")
|
||||||
|
inputFieldName = inputFieldNameList[0]
|
||||||
|
var defaultValue string
|
||||||
|
if len(inputFieldNameList) > 1 {
|
||||||
|
defaultList := strings.SplitN(inputFieldNameList[1], "=", 2)
|
||||||
|
if defaultList[0] == "default" {
|
||||||
|
defaultValue = defaultList[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inputFieldName == "" {
|
||||||
|
inputFieldName = typeField.Name
|
||||||
|
|
||||||
|
// if "form" tag is nil, we inspect if the field is a struct or struct pointer.
|
||||||
|
// this would not make sense for JSON parsing but it does for a form
|
||||||
|
// since data is flatten
|
||||||
|
if structFieldKind == reflect.Ptr {
|
||||||
|
if !structField.Elem().IsValid() {
|
||||||
|
structField.Set(reflect.New(structField.Type().Elem()))
|
||||||
|
}
|
||||||
|
structField = structField.Elem()
|
||||||
|
structFieldKind = structField.Kind()
|
||||||
|
}
|
||||||
|
if structFieldKind == reflect.Struct {
|
||||||
|
err := mapForm(structField.Addr().Interface(), form)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputValue, exists := form[inputFieldName]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
if defaultValue == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inputValue = make([]string, 1)
|
||||||
|
inputValue[0] = defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
numElems := len(inputValue)
|
||||||
|
if structFieldKind == reflect.Slice && numElems > 0 {
|
||||||
|
sliceOf := structField.Type().Elem().Kind()
|
||||||
|
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||||
|
for i := 0; i < numElems; i++ {
|
||||||
|
if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val.Field(i).Set(slice)
|
||||||
|
} else {
|
||||||
|
if _, isTime := structField.Interface().(time.Time); isTime {
|
||||||
|
if err := setTimeField(inputValue[0], typeField, structField); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||||
|
switch valueKind {
|
||||||
|
case reflect.Int:
|
||||||
|
return setIntField(val, 0, structField)
|
||||||
|
case reflect.Int8:
|
||||||
|
return setIntField(val, 8, structField)
|
||||||
|
case reflect.Int16:
|
||||||
|
return setIntField(val, 16, structField)
|
||||||
|
case reflect.Int32:
|
||||||
|
return setIntField(val, 32, structField)
|
||||||
|
case reflect.Int64:
|
||||||
|
return setIntField(val, 64, structField)
|
||||||
|
case reflect.Uint:
|
||||||
|
return setUintField(val, 0, structField)
|
||||||
|
case reflect.Uint8:
|
||||||
|
return setUintField(val, 8, structField)
|
||||||
|
case reflect.Uint16:
|
||||||
|
return setUintField(val, 16, structField)
|
||||||
|
case reflect.Uint32:
|
||||||
|
return setUintField(val, 32, structField)
|
||||||
|
case reflect.Uint64:
|
||||||
|
return setUintField(val, 64, structField)
|
||||||
|
case reflect.Bool:
|
||||||
|
return setBoolField(val, structField)
|
||||||
|
case reflect.Float32:
|
||||||
|
return setFloatField(val, 32, structField)
|
||||||
|
case reflect.Float64:
|
||||||
|
return setFloatField(val, 64, structField)
|
||||||
|
case reflect.String:
|
||||||
|
structField.SetString(val)
|
||||||
|
case reflect.Ptr:
|
||||||
|
if !structField.Elem().IsValid() {
|
||||||
|
structField.Set(reflect.New(structField.Type().Elem()))
|
||||||
|
}
|
||||||
|
structFieldElem := structField.Elem()
|
||||||
|
return setWithProperType(structFieldElem.Kind(), val, structFieldElem)
|
||||||
|
default:
|
||||||
|
return errors.New("Unknown type")
|
||||||
|
}
|
||||||
|
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 == "" {
|
||||||
|
return errors.New("Blank time format")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
43
vendor/github.com/gin-gonic/gin/binding/json.go
generated
vendored
Normal file
43
vendor/github.com/gin-gonic/gin/binding/json.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/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
|
||||||
|
|
||||||
|
type jsonBinding struct{}
|
||||||
|
|
||||||
|
func (jsonBinding) Name() string {
|
||||||
|
return "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
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 err := decoder.Decode(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
35
vendor/github.com/gin-gonic/gin/binding/msgpack.go
generated
vendored
Normal file
35
vendor/github.com/gin-gonic/gin/binding/msgpack.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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 (
|
||||||
|
"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)
|
||||||
|
}
|
36
vendor/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
Normal file
36
vendor/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
Normal file
@ -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 cann't add
|
||||||
|
// `binding:""` to the struct which automatically generate by gen-proto
|
||||||
|
return nil
|
||||||
|
// return validate(obj)
|
||||||
|
}
|
21
vendor/github.com/gin-gonic/gin/binding/query.go
generated
vendored
Normal file
21
vendor/github.com/gin-gonic/gin/binding/query.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
33
vendor/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
Normal file
33
vendor/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
5
vendor/github.com/gin-gonic/gin/codecov.yml
generated
vendored
Normal file
5
vendor/github.com/gin-gonic/gin/codecov.yml
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
coverage:
|
||||||
|
notify:
|
||||||
|
gitter:
|
||||||
|
default:
|
||||||
|
url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165
|
933
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
Normal file
933
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
Normal file
@ -0,0 +1,933 @@
|
|||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"mime/multipart"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"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
|
||||||
|
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
|
||||||
|
|
||||||
|
engine *Engine
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************/
|
||||||
|
/********** CONTEXT CREATION ********/
|
||||||
|
/************************************/
|
||||||
|
|
||||||
|
func (c *Context) reset() {
|
||||||
|
c.Writer = &c.writermem
|
||||||
|
c.Params = c.Params[0:0]
|
||||||
|
c.handlers = nil
|
||||||
|
c.index = -1
|
||||||
|
c.Keys = nil
|
||||||
|
c.Errors = c.Errors[0:0]
|
||||||
|
c.Accepted = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
var cp = *c
|
||||||
|
cp.writermem.ResponseWriter = nil
|
||||||
|
cp.Writer = &cp.writermem
|
||||||
|
cp.index = abortIndex
|
||||||
|
cp.handlers = nil
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler returns the main handler.
|
||||||
|
func (c *Context) Handler() HandlerFunc {
|
||||||
|
return c.handlers.Last()
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************/
|
||||||
|
/*********** 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 s := int8(len(c.handlers)); c.index < s; c.index++ {
|
||||||
|
c.handlers[c.index](c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{}) {
|
||||||
|
if c.Keys == nil {
|
||||||
|
c.Keys = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
c.Keys[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
value, exists = c.Keys[key]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if values, ok := c.Request.URL.Query()[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) {
|
||||||
|
return c.get(c.Request.URL.Query(), 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
req := c.Request
|
||||||
|
req.ParseForm()
|
||||||
|
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
||||||
|
if values := req.PostForm[key]; len(values) > 0 {
|
||||||
|
return values, true
|
||||||
|
}
|
||||||
|
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
||||||
|
if values := req.MultipartForm.Value[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) {
|
||||||
|
req := c.Request
|
||||||
|
req.ParseForm()
|
||||||
|
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
||||||
|
dicts, exist := c.get(req.PostForm, key)
|
||||||
|
|
||||||
|
if !exist && req.MultipartForm != nil && req.MultipartForm.File != nil {
|
||||||
|
dicts, exist = c.get(req.MultipartForm.Value, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dicts, exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
_, fh, err := c.Request.FormFile(name)
|
||||||
|
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()
|
||||||
|
|
||||||
|
io.Copy(out, src)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
|
||||||
|
func (c *Context) BindQuery(obj interface{}) error {
|
||||||
|
return c.MustBindWith(obj, binding.Query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
|
// It will abort the request with HTTP 400 if any error ocurrs.
|
||||||
|
// See the binding package.
|
||||||
|
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
||||||
|
if err = c.ShouldBindWith(obj, b); err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
|
||||||
|
func (c *Context) ShouldBindQuery(obj interface{}) error {
|
||||||
|
return c.ShouldBindWith(obj, binding.Query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 parses
|
||||||
|
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
||||||
|
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
|
||||||
|
func (c *Context) ClientIP() string {
|
||||||
|
if c.engine.ForwardedByClientIP {
|
||||||
|
clientIP := c.requestHeader("X-Forwarded-For")
|
||||||
|
clientIP = strings.TrimSpace(strings.Split(clientIP, ",")[0])
|
||||||
|
if clientIP == "" {
|
||||||
|
clientIP = strings.TrimSpace(c.requestHeader("X-Real-Ip"))
|
||||||
|
}
|
||||||
|
if clientIP != "" {
|
||||||
|
return clientIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.engine.AppEngine {
|
||||||
|
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ToLower(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.writermem.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)
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 add 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})
|
||||||
|
} else {
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 a efficient way.
|
||||||
|
func (c *Context) File(filepath string) {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Stream(step func(w io.Writer) bool) {
|
||||||
|
w := c.Writer
|
||||||
|
clientGone := w.CloseNotify()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-clientGone:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
keepOpen := step(w)
|
||||||
|
w.Flush()
|
||||||
|
if !keepOpen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************/
|
||||||
|
/******** CONTENT NEGOTIATION *******/
|
||||||
|
/************************************/
|
||||||
|
|
||||||
|
type Negotiate struct {
|
||||||
|
Offered []string
|
||||||
|
HTMLName string
|
||||||
|
HTMLData interface{}
|
||||||
|
JSONData interface{}
|
||||||
|
XMLData interface{}
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
default:
|
||||||
|
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 _, offert := range offered {
|
||||||
|
if accepted == offert {
|
||||||
|
return offert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) SetAccepted(formats ...string) {
|
||||||
|
c.Accepted = formats
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************/
|
||||||
|
/***** GOLANG.ORG/X/NET/CONTEXT *****/
|
||||||
|
/************************************/
|
||||||
|
|
||||||
|
// Deadline returns the time when work done on behalf of this context
|
||||||
|
// should be canceled. Deadline returns ok==false when no deadline is
|
||||||
|
// set. Successive calls to Deadline return the same results.
|
||||||
|
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done returns a channel that's closed when work done on behalf of this
|
||||||
|
// context should be canceled. Done may return nil if this context can
|
||||||
|
// never be canceled. Successive calls to Done return the same value.
|
||||||
|
func (c *Context) Done() <-chan struct{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns a non-nil error value after Done is closed,
|
||||||
|
// successive calls to Err return the same error.
|
||||||
|
// If Done is not yet closed, Err returns nil.
|
||||||
|
// If Done is closed, Err returns a non-nil error explaining why:
|
||||||
|
// Canceled if the context was canceled
|
||||||
|
// or DeadlineExceeded if the context's deadline passed.
|
||||||
|
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
|
||||||
|
}
|
11
vendor/github.com/gin-gonic/gin/context_appengine.go
generated
vendored
Normal file
11
vendor/github.com/gin-gonic/gin/context_appengine.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
defaultAppEngine = true
|
||||||
|
}
|
13
vendor/github.com/gin-gonic/gin/coverage.sh
generated
vendored
Normal file
13
vendor/github.com/gin-gonic/gin/coverage.sh
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "mode: count" > coverage.out
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -E 'gin$|binding$|render$' | grep -v 'examples'); do
|
||||||
|
go test -v -covermode=count -coverprofile=profile.out $d
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out | grep -v "mode:" >> coverage.out
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
80
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
Normal file
80
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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"
|
||||||
|
"html/template"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||||
|
if IsDebugging() {
|
||||||
|
nuHandlers := len(handlers)
|
||||||
|
handlerName := nameOfFunction(handlers.Last())
|
||||||
|
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugPrintLoadTemplate(tmpl *template.Template) {
|
||||||
|
if IsDebugging() {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
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() {
|
||||||
|
log.Printf("[GIN-debug] "+format, values...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugPrintWARNINGDefault() {
|
||||||
|
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
|
||||||
|
|
||||||
|
`)
|
||||||
|
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 {
|
||||||
|
debugPrint("[ERROR] %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
21
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
Normal file
21
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
6
vendor/github.com/gin-gonic/gin/doc.go
generated
vendored
Normal file
6
vendor/github.com/gin-gonic/gin/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
Package gin implements a HTTP web framework called gin.
|
||||||
|
|
||||||
|
See https://gin-gonic.github.io/gin/ for more information about gin.
|
||||||
|
*/
|
||||||
|
package gin // import "github.com/gin-gonic/gin"
|
157
vendor/github.com/gin-gonic/gin/errors.go
generated
vendored
Normal file
157
vendor/github.com/gin-gonic/gin/errors.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// 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"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorType uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
|
||||||
|
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
|
||||||
|
ErrorTypePrivate ErrorType = 1 << 0
|
||||||
|
ErrorTypePublic ErrorType = 1 << 1
|
||||||
|
|
||||||
|
ErrorTypeAny ErrorType = 1<<64 - 1
|
||||||
|
ErrorTypeNu = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Err error
|
||||||
|
Type ErrorType
|
||||||
|
Meta interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorMsgs []*Error
|
||||||
|
|
||||||
|
var _ error = &Error{}
|
||||||
|
|
||||||
|
func (msg *Error) SetType(flags ErrorType) *Error {
|
||||||
|
msg.Type = flags
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Error) SetMeta(data interface{}) *Error {
|
||||||
|
msg.Meta = data
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Error) JSON() interface{} {
|
||||||
|
json := 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() {
|
||||||
|
json[key.String()] = value.MapIndex(key).Interface()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
json["meta"] = msg.Meta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := json["error"]; !ok {
|
||||||
|
json["error"] = msg.Error()
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Error) IsType(flags ErrorType) bool {
|
||||||
|
return (msg.Type & flags) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 len(a) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return a.Last().JSON()
|
||||||
|
default:
|
||||||
|
json := make([]interface{}, len(a))
|
||||||
|
for i, err := range a {
|
||||||
|
json[i] = err.JSON()
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a errorMsgs) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(a.JSON())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a errorMsgs) String() string {
|
||||||
|
if len(a) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
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()
|
||||||
|
}
|
45
vendor/github.com/gin-gonic/gin/fs.go
generated
vendored
Normal file
45
vendor/github.com/gin-gonic/gin/fs.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
443
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
Normal file
443
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
// 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 (
|
||||||
|
"html/template"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Version is Framework's version.
|
||||||
|
Version = "v1.3.0"
|
||||||
|
defaultMultipartMemory = 32 << 20 // 32 MB
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
default404Body = []byte("404 page not found")
|
||||||
|
default405Body = []byte("405 method not allowed")
|
||||||
|
defaultAppEngine bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type HandlerFunc func(*Context)
|
||||||
|
type HandlersChain []HandlerFunc
|
||||||
|
|
||||||
|
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
||||||
|
func (c HandlersChain) Last() HandlerFunc {
|
||||||
|
if length := len(c); length > 0 {
|
||||||
|
return c[length-1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RouteInfo struct {
|
||||||
|
Method string
|
||||||
|
Path string
|
||||||
|
Handler string
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
ForwardedByClientIP bool
|
||||||
|
|
||||||
|
// #726 #755 If enabled, it will thrust 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
|
||||||
|
|
||||||
|
delims render.Delims
|
||||||
|
secureJsonPrefix string
|
||||||
|
HTMLRender render.HTMLRender
|
||||||
|
FuncMap template.FuncMap
|
||||||
|
allNoRoute HandlersChain
|
||||||
|
allNoMethod HandlersChain
|
||||||
|
noRoute HandlersChain
|
||||||
|
noMethod HandlersChain
|
||||||
|
pool sync.Pool
|
||||||
|
trees methodTrees
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
AppEngine: defaultAppEngine,
|
||||||
|
UseRawPath: 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 {
|
||||||
|
return &Context{engine: engine}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 attachs 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)
|
||||||
|
engine.trees = append(engine.trees, methodTree{method: method, root: root})
|
||||||
|
}
|
||||||
|
root.addRoute(path, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
routes = append(routes, RouteInfo{
|
||||||
|
Method: method,
|
||||||
|
Path: path,
|
||||||
|
Handler: nameOfFunction(root.handlers.Last()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
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) }()
|
||||||
|
|
||||||
|
address := resolveAddress(addr)
|
||||||
|
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||||
|
err = http.ListenAndServe(address, engine)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) }()
|
||||||
|
|
||||||
|
os.Remove(file)
|
||||||
|
listener, err := net.Listen("unix", file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
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) {
|
||||||
|
c.reset()
|
||||||
|
engine.handleHTTPRequest(c)
|
||||||
|
engine.pool.Put(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||||
|
httpMethod := c.Request.Method
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
unescape := false
|
||||||
|
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
||||||
|
path = c.Request.URL.RawPath
|
||||||
|
unescape = engine.UnescapePathValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
handlers, params, tsr := root.getValue(path, c.Params, unescape)
|
||||||
|
if handlers != nil {
|
||||||
|
c.handlers = handlers
|
||||||
|
c.Params = params
|
||||||
|
c.Next()
|
||||||
|
c.writermem.WriteHeaderNow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if httpMethod != "CONNECT" && path != "/" {
|
||||||
|
if 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 handlers, _, _ := tree.root.getValue(path, nil, unescape); 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
|
||||||
|
c.Writer.Write(defaultMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.writermem.WriteHeaderNow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectTrailingSlash(c *Context) {
|
||||||
|
req := c.Request
|
||||||
|
path := req.URL.Path
|
||||||
|
code := http.StatusMovedPermanently // Permanent redirect, request with GET method
|
||||||
|
if req.Method != "GET" {
|
||||||
|
code = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
|
|
||||||
|
req.URL.Path = path + "/"
|
||||||
|
if length := len(path); length > 1 && path[length-1] == '/' {
|
||||||
|
req.URL.Path = path[:length-1]
|
||||||
|
}
|
||||||
|
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
||||||
|
http.Redirect(c.Writer, req, req.URL.String(), code)
|
||||||
|
c.writermem.WriteHeaderNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
||||||
|
req := c.Request
|
||||||
|
path := req.URL.Path
|
||||||
|
|
||||||
|
if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(path), trailingSlash); ok {
|
||||||
|
code := http.StatusMovedPermanently // Permanent redirect, request with GET method
|
||||||
|
if req.Method != "GET" {
|
||||||
|
code = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
|
req.URL.Path = string(fixedPath)
|
||||||
|
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
||||||
|
http.Redirect(c.Writer, req, req.URL.String(), code)
|
||||||
|
c.writermem.WriteHeaderNow()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
15
vendor/github.com/gin-gonic/gin/json/json.go
generated
vendored
Normal file
15
vendor/github.com/gin-gonic/gin/json/json.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !jsoniter
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Marshal = json.Marshal
|
||||||
|
MarshalIndent = json.MarshalIndent
|
||||||
|
NewDecoder = json.NewDecoder
|
||||||
|
)
|
16
vendor/github.com/gin-gonic/gin/json/jsoniter.go
generated
vendored
Normal file
16
vendor/github.com/gin-gonic/gin/json/jsoniter.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build jsoniter
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import "github.com/json-iterator/go"
|
||||||
|
|
||||||
|
var (
|
||||||
|
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
Marshal = json.Marshal
|
||||||
|
MarshalIndent = json.MarshalIndent
|
||||||
|
NewDecoder = json.NewDecoder
|
||||||
|
)
|
152
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
Normal file
152
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
||||||
|
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
||||||
|
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
||||||
|
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
||||||
|
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
|
||||||
|
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
||||||
|
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
||||||
|
reset = string([]byte{27, 91, 48, 109})
|
||||||
|
disableColor = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// DisableConsoleColor disables color output in the console.
|
||||||
|
func DisableConsoleColor() {
|
||||||
|
disableColor = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 LoggerWithWriter(DefaultWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
||||||
|
// Example: os.Stdout, a file opened in write mode, a socket...
|
||||||
|
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
|
isTerm := true
|
||||||
|
|
||||||
|
if w, ok := out.(*os.File); !ok ||
|
||||||
|
(os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) ||
|
||||||
|
disableColor {
|
||||||
|
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 {
|
||||||
|
// Stop timer
|
||||||
|
end := time.Now()
|
||||||
|
latency := end.Sub(start)
|
||||||
|
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
method := c.Request.Method
|
||||||
|
statusCode := c.Writer.Status()
|
||||||
|
var statusColor, methodColor, resetColor string
|
||||||
|
if isTerm {
|
||||||
|
statusColor = colorForStatus(statusCode)
|
||||||
|
methodColor = colorForMethod(method)
|
||||||
|
resetColor = reset
|
||||||
|
}
|
||||||
|
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||||
|
|
||||||
|
if raw != "" {
|
||||||
|
path = path + "?" + raw
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
|
||||||
|
end.Format("2006/01/02 - 15:04:05"),
|
||||||
|
statusColor, statusCode, resetColor,
|
||||||
|
latency,
|
||||||
|
clientIP,
|
||||||
|
methodColor, method, resetColor,
|
||||||
|
path,
|
||||||
|
comment,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorForStatus(code int) string {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorForMethod(method string) string {
|
||||||
|
switch method {
|
||||||
|
case "GET":
|
||||||
|
return blue
|
||||||
|
case "POST":
|
||||||
|
return cyan
|
||||||
|
case "PUT":
|
||||||
|
return yellow
|
||||||
|
case "DELETE":
|
||||||
|
return red
|
||||||
|
case "PATCH":
|
||||||
|
return green
|
||||||
|
case "HEAD":
|
||||||
|
return magenta
|
||||||
|
case "OPTIONS":
|
||||||
|
return white
|
||||||
|
default:
|
||||||
|
return reset
|
||||||
|
}
|
||||||
|
}
|
72
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
Normal file
72
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ENV_GIN_MODE = "GIN_MODE"
|
||||||
|
|
||||||
|
const (
|
||||||
|
DebugMode = "debug"
|
||||||
|
ReleaseMode = "release"
|
||||||
|
TestMode = "test"
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
debugCode = iota
|
||||||
|
releaseCode
|
||||||
|
testCode
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultWriter is the default io.Writer used the 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
|
||||||
|
var DefaultErrorWriter io.Writer = os.Stderr
|
||||||
|
|
||||||
|
var ginMode = debugCode
|
||||||
|
var modeName = DebugMode
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mode := os.Getenv(ENV_GIN_MODE)
|
||||||
|
SetMode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetMode(value string) {
|
||||||
|
switch value {
|
||||||
|
case DebugMode, "":
|
||||||
|
ginMode = debugCode
|
||||||
|
case ReleaseMode:
|
||||||
|
ginMode = releaseCode
|
||||||
|
case TestMode:
|
||||||
|
ginMode = testCode
|
||||||
|
default:
|
||||||
|
panic("gin mode unknown: " + value)
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
value = DebugMode
|
||||||
|
}
|
||||||
|
modeName = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableBindValidation() {
|
||||||
|
binding.Validator = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnableJsonDecoderUseNumber() {
|
||||||
|
binding.EnableDecoderUseNumber = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mode() string {
|
||||||
|
return modeName
|
||||||
|
}
|
123
vendor/github.com/gin-gonic/gin/path.go
generated
vendored
Normal file
123
vendor/github.com/gin-gonic/gin/path.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// 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 {
|
||||||
|
// Turn empty string into "/"
|
||||||
|
if p == "" {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(p)
|
||||||
|
var buf []byte
|
||||||
|
|
||||||
|
// 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
|
||||||
|
buf = make([]byte, 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). So in contrast to the path package this
|
||||||
|
// loop has no expensive function calls (except 1x make)
|
||||||
|
|
||||||
|
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 buf == nil {
|
||||||
|
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 buf == nil {
|
||||||
|
return p[:w]
|
||||||
|
}
|
||||||
|
return string(buf[:w])
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal helper to lazily create a buffer if necessary.
|
||||||
|
func bufApp(buf *[]byte, s string, w int, c byte) {
|
||||||
|
if *buf == nil {
|
||||||
|
if s[w] == c {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = make([]byte, len(s))
|
||||||
|
copy(*buf, s[:w])
|
||||||
|
}
|
||||||
|
(*buf)[w] = c
|
||||||
|
}
|
116
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
Normal file
116
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
Normal file
@ -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 gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dunno = []byte("???")
|
||||||
|
centerDot = []byte("·")
|
||||||
|
dot = []byte(".")
|
||||||
|
slash = []byte("/")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
|
||||||
|
func Recovery() HandlerFunc {
|
||||||
|
return RecoveryWithWriter(DefaultErrorWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) 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 {
|
||||||
|
if logger != nil {
|
||||||
|
stack := stack(3)
|
||||||
|
httprequest, _ := httputil.DumpRequest(c.Request, false)
|
||||||
|
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
||||||
|
}
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
var timeString = t.Format("2006/01/02 - 15:04:05")
|
||||||
|
return timeString
|
||||||
|
}
|
23
vendor/github.com/gin-gonic/gin/render/data.go
generated
vendored
Normal file
23
vendor/github.com/gin-gonic/gin/render/data.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Data) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, []string{r.ContentType})
|
||||||
|
}
|
80
vendor/github.com/gin-gonic/gin/render/html.go
generated
vendored
Normal file
80
vendor/github.com/gin-gonic/gin/render/html.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Delims struct {
|
||||||
|
Left string
|
||||||
|
Right string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTMLRender interface {
|
||||||
|
Instance(string, interface{}) Render
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTMLProduction struct {
|
||||||
|
Template *template.Template
|
||||||
|
Delims Delims
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTMLDebug struct {
|
||||||
|
Files []string
|
||||||
|
Glob string
|
||||||
|
Delims Delims
|
||||||
|
FuncMap template.FuncMap
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTML struct {
|
||||||
|
Template *template.Template
|
||||||
|
Name string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var htmlContentType = []string{"text/html; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
||||||
|
return HTML{
|
||||||
|
Template: r.Template,
|
||||||
|
Name: name,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r HTML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, htmlContentType)
|
||||||
|
}
|
146
vendor/github.com/gin-gonic/gin/render/json.go
generated
vendored
Normal file
146
vendor/github.com/gin-gonic/gin/render/json.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// 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/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSON struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndentedJSON struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecureJSON struct {
|
||||||
|
Prefix string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonpJSON struct {
|
||||||
|
Callback string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AsciiJSON struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecureJSONPrefix string
|
||||||
|
|
||||||
|
var jsonContentType = []string{"application/json; charset=utf-8"}
|
||||||
|
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
|
||||||
|
var jsonAsciiContentType = []string{"application/json"}
|
||||||
|
|
||||||
|
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
||||||
|
if err = WriteJSON(w, r.Data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r JSON) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, jsonContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
||||||
|
writeContentType(w, jsonContentType)
|
||||||
|
jsonBytes, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Write(jsonBytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||||
|
r.WriteContentType(w)
|
||||||
|
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Write(jsonBytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, jsonContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) {
|
||||||
|
w.Write([]byte(r.Prefix))
|
||||||
|
}
|
||||||
|
w.Write(jsonBytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, jsonContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == "" {
|
||||||
|
w.Write(ret)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
callback := template.JSEscapeString(r.Callback)
|
||||||
|
w.Write([]byte(callback))
|
||||||
|
w.Write([]byte("("))
|
||||||
|
w.Write(ret)
|
||||||
|
w.Write([]byte(")"))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, jsonpContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 string(ret) {
|
||||||
|
cvt := ""
|
||||||
|
if r < 128 {
|
||||||
|
cvt = string(r)
|
||||||
|
} else {
|
||||||
|
cvt = fmt.Sprintf("\\u%04x", int64(r))
|
||||||
|
}
|
||||||
|
buffer.WriteString(cvt)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(buffer.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, jsonAsciiContentType)
|
||||||
|
}
|
31
vendor/github.com/gin-gonic/gin/render/msgpack.go
generated
vendored
Normal file
31
vendor/github.com/gin-gonic/gin/render/msgpack.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// 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 render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgPack struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, msgpackContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r MsgPack) Render(w http.ResponseWriter) error {
|
||||||
|
return WriteMsgPack(w, r.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
||||||
|
writeContentType(w, msgpackContentType)
|
||||||
|
var h codec.Handle = new(codec.MsgpackHandle)
|
||||||
|
return codec.NewEncoder(w, h).Encode(obj)
|
||||||
|
}
|
36
vendor/github.com/gin-gonic/gin/render/reader.go
generated
vendored
Normal file
36
vendor/github.com/gin-gonic/gin/render/reader.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10)
|
||||||
|
r.writeHeaders(w, r.Headers)
|
||||||
|
_, err = io.Copy(w, r.Reader)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Reader) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, []string{r.ContentType})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
||||||
|
header := w.Header()
|
||||||
|
for k, v := range headers {
|
||||||
|
if val := header[k]; len(val) == 0 {
|
||||||
|
header[k] = []string{v}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
vendor/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
Normal file
28
vendor/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
Normal file
@ -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 (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Redirect struct {
|
||||||
|
Code int
|
||||||
|
Request *http.Request
|
||||||
|
Location string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Redirect) Render(w http.ResponseWriter) error {
|
||||||
|
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
|
||||||
|
// when we upgrade go version we can use http.StatusPermanentRedirect
|
||||||
|
if (r.Code < 300 || r.Code > 308) && r.Code != 201 {
|
||||||
|
panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
|
||||||
|
}
|
||||||
|
http.Redirect(w, r.Request, r.Location, r.Code)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Redirect) WriteContentType(http.ResponseWriter) {}
|
37
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
Normal file
37
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
type Render interface {
|
||||||
|
Render(http.ResponseWriter) error
|
||||||
|
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 = MsgPack{}
|
||||||
|
_ Render = Reader{}
|
||||||
|
_ Render = AsciiJSON{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeContentType(w http.ResponseWriter, value []string) {
|
||||||
|
header := w.Header()
|
||||||
|
if val := header["Content-Type"]; len(val) == 0 {
|
||||||
|
header["Content-Type"] = value
|
||||||
|
}
|
||||||
|
}
|
36
vendor/github.com/gin-gonic/gin/render/text.go
generated
vendored
Normal file
36
vendor/github.com/gin-gonic/gin/render/text.go
generated
vendored
Normal file
@ -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 (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type String struct {
|
||||||
|
Format string
|
||||||
|
Data []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var plainContentType = []string{"text/plain; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r String) Render(w http.ResponseWriter) error {
|
||||||
|
WriteString(w, r.Format, r.Data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r String) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, plainContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
||||||
|
writeContentType(w, plainContentType)
|
||||||
|
if len(data) > 0 {
|
||||||
|
fmt.Fprintf(w, format, data...)
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, format)
|
||||||
|
}
|
||||||
|
}
|
25
vendor/github.com/gin-gonic/gin/render/xml.go
generated
vendored
Normal file
25
vendor/github.com/gin-gonic/gin/render/xml.go
generated
vendored
Normal file
@ -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 (
|
||||||
|
"encoding/xml"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XML struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r XML) Render(w http.ResponseWriter) error {
|
||||||
|
r.WriteContentType(w)
|
||||||
|
return xml.NewEncoder(w).Encode(r.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r XML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, xmlContentType)
|
||||||
|
}
|
33
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
Normal file
33
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
Normal file
@ -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 render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type YAML struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r YAML) Render(w http.ResponseWriter) error {
|
||||||
|
r.WriteContentType(w)
|
||||||
|
|
||||||
|
bytes, err := yaml.Marshal(r.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(bytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r YAML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, yamlContentType)
|
||||||
|
}
|
115
vendor/github.com/gin-gonic/gin/response_writer.go
generated
vendored
Normal file
115
vendor/github.com/gin-gonic/gin/response_writer.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
type responseWriterBase 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
12
vendor/github.com/gin-gonic/gin/response_writer_1.7.go
generated
vendored
Normal file
12
vendor/github.com/gin-gonic/gin/response_writer_1.7.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// +build !go1.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
|
||||||
|
|
||||||
|
// ResponseWriter ...
|
||||||
|
type ResponseWriter interface {
|
||||||
|
responseWriterBase
|
||||||
|
}
|
25
vendor/github.com/gin-gonic/gin/response_writer_1.8.go
generated
vendored
Normal file
25
vendor/github.com/gin-gonic/gin/response_writer_1.8.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// +build go1.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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseWriter ...
|
||||||
|
type ResponseWriter interface {
|
||||||
|
responseWriterBase
|
||||||
|
// get the http.Pusher for server push
|
||||||
|
Pusher() http.Pusher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Pusher() (pusher http.Pusher) {
|
||||||
|
if pusher, ok := w.ResponseWriter.(http.Pusher); ok {
|
||||||
|
return pusher
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
213
vendor/github.com/gin-gonic/gin/routergroup.go
generated
vendored
Normal file
213
vendor/github.com/gin-gonic/gin/routergroup.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IRouter interface {
|
||||||
|
IRoutes
|
||||||
|
Group(string, ...HandlerFunc) *RouterGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
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 middlwares or the same path prefix.
|
||||||
|
// For example, all the routes that use a common middlware 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("POST", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET is a shortcut for router.Handle("GET", path, handle).
|
||||||
|
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("GET", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
|
||||||
|
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("DELETE", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PATCH is a shortcut for router.Handle("PATCH", path, handle).
|
||||||
|
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("PATCH", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT is a shortcut for router.Handle("PUT", path, handle).
|
||||||
|
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("PUT", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
|
||||||
|
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("OPTIONS", relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HEAD is a shortcut for router.Handle("HEAD", path, handle).
|
||||||
|
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle("HEAD", 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("GET", relativePath, handlers)
|
||||||
|
group.handle("POST", relativePath, handlers)
|
||||||
|
group.handle("PUT", relativePath, handlers)
|
||||||
|
group.handle("PATCH", relativePath, handlers)
|
||||||
|
group.handle("HEAD", relativePath, handlers)
|
||||||
|
group.handle("OPTIONS", relativePath, handlers)
|
||||||
|
group.handle("DELETE", relativePath, handlers)
|
||||||
|
group.handle("CONNECT", relativePath, handlers)
|
||||||
|
group.handle("TRACE", 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))
|
||||||
|
_, nolisting := fs.(*onlyfilesFS)
|
||||||
|
return func(c *Context) {
|
||||||
|
if nolisting {
|
||||||
|
c.Writer.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
16
vendor/github.com/gin-gonic/gin/test_helpers.go
generated
vendored
Normal file
16
vendor/github.com/gin-gonic/gin/test_helpers.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
620
vendor/github.com/gin-gonic/gin/tree.go
generated
vendored
Normal file
620
vendor/github.com/gin-gonic/gin/tree.go
generated
vendored
Normal file
@ -0,0 +1,620 @@
|
|||||||
|
// 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 (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 countParams(path string) uint8 {
|
||||||
|
var n uint
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
if path[i] != ':' && path[i] != '*' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if n >= 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
static nodeType = iota // default
|
||||||
|
root
|
||||||
|
param
|
||||||
|
catchAll
|
||||||
|
)
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
path string
|
||||||
|
indices string
|
||||||
|
children []*node
|
||||||
|
handlers HandlersChain
|
||||||
|
priority uint32
|
||||||
|
nType nodeType
|
||||||
|
maxParams uint8
|
||||||
|
wildChild bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// increments priority of the given child and reorders if necessary.
|
||||||
|
func (n *node) incrementChildPrio(pos int) int {
|
||||||
|
n.children[pos].priority++
|
||||||
|
prio := n.children[pos].priority
|
||||||
|
|
||||||
|
// adjust position (move to front)
|
||||||
|
newPos := pos
|
||||||
|
for newPos > 0 && n.children[newPos-1].priority < prio {
|
||||||
|
// swap node positions
|
||||||
|
n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1]
|
||||||
|
|
||||||
|
newPos--
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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++
|
||||||
|
numParams := countParams(path)
|
||||||
|
|
||||||
|
// non-empty tree
|
||||||
|
if len(n.path) > 0 || len(n.children) > 0 {
|
||||||
|
walk:
|
||||||
|
for {
|
||||||
|
// Update maxParams of the current node
|
||||||
|
if numParams > n.maxParams {
|
||||||
|
n.maxParams = numParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 := 0
|
||||||
|
max := min(len(path), len(n.path))
|
||||||
|
for i < max && path[i] == n.path[i] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update maxParams (max of all children)
|
||||||
|
for i := range child.children {
|
||||||
|
if child.children[i].maxParams > child.maxParams {
|
||||||
|
child.maxParams = child.children[i].maxParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.children = []*node{&child}
|
||||||
|
// []byte for proper unicode char conversion, see #65
|
||||||
|
n.indices = string([]byte{n.path[i]})
|
||||||
|
n.path = path[:i]
|
||||||
|
n.handlers = nil
|
||||||
|
n.wildChild = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make new node a child of this node
|
||||||
|
if i < len(path) {
|
||||||
|
path = path[i:]
|
||||||
|
|
||||||
|
if n.wildChild {
|
||||||
|
n = n.children[0]
|
||||||
|
n.priority++
|
||||||
|
|
||||||
|
// Update maxParams of the child node
|
||||||
|
if numParams > n.maxParams {
|
||||||
|
n.maxParams = numParams
|
||||||
|
}
|
||||||
|
numParams--
|
||||||
|
|
||||||
|
// Check if the wildcard matches
|
||||||
|
if len(path) >= len(n.path) && n.path == path[:len(n.path)] {
|
||||||
|
// check for longer wildcard, e.g. :name and :names
|
||||||
|
if len(n.path) >= len(path) || path[len(n.path)] == '/' {
|
||||||
|
continue walk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("path segment '" + path +
|
||||||
|
"' conflicts with existing wildcard '" + n.path +
|
||||||
|
"' in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := path[0]
|
||||||
|
|
||||||
|
// slash after param
|
||||||
|
if n.nType == param && c == '/' && len(n.children) == 1 {
|
||||||
|
n = n.children[0]
|
||||||
|
n.priority++
|
||||||
|
continue walk
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a child with the next path byte exists
|
||||||
|
for i := 0; i < len(n.indices); i++ {
|
||||||
|
if c == n.indices[i] {
|
||||||
|
i = n.incrementChildPrio(i)
|
||||||
|
n = n.children[i]
|
||||||
|
continue walk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise insert it
|
||||||
|
if c != ':' && c != '*' {
|
||||||
|
// []byte for proper unicode char conversion, see #65
|
||||||
|
n.indices += string([]byte{c})
|
||||||
|
child := &node{
|
||||||
|
maxParams: numParams,
|
||||||
|
}
|
||||||
|
n.children = append(n.children, child)
|
||||||
|
n.incrementChildPrio(len(n.indices) - 1)
|
||||||
|
n = child
|
||||||
|
}
|
||||||
|
n.insertChild(numParams, path, fullPath, handlers)
|
||||||
|
return
|
||||||
|
|
||||||
|
} else if i == len(path) { // Make node a (in-path) leaf
|
||||||
|
if n.handlers != nil {
|
||||||
|
panic("handlers are already registered for path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
n.handlers = handlers
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else { // Empty tree
|
||||||
|
n.insertChild(numParams, path, fullPath, handlers)
|
||||||
|
n.nType = root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
|
||||||
|
var offset int // already handled bytes of the path
|
||||||
|
|
||||||
|
// find prefix until first wildcard (beginning with ':' or '*')
|
||||||
|
for i, max := 0, len(path); numParams > 0; i++ {
|
||||||
|
c := path[i]
|
||||||
|
if c != ':' && c != '*' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// find wildcard end (either '/' or path end)
|
||||||
|
end := i + 1
|
||||||
|
for end < max && path[end] != '/' {
|
||||||
|
switch path[end] {
|
||||||
|
// the wildcard name must not contain ':' and '*'
|
||||||
|
case ':', '*':
|
||||||
|
panic("only one wildcard per path segment is allowed, has: '" +
|
||||||
|
path[i:] + "' in path '" + fullPath + "'")
|
||||||
|
default:
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this Node existing children which would be
|
||||||
|
// unreachable if we insert the wildcard here
|
||||||
|
if len(n.children) > 0 {
|
||||||
|
panic("wildcard route '" + path[i:end] +
|
||||||
|
"' conflicts with existing children in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the wildcard has a name
|
||||||
|
if end-i < 2 {
|
||||||
|
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == ':' { // param
|
||||||
|
// split path at the beginning of the wildcard
|
||||||
|
if i > 0 {
|
||||||
|
n.path = path[offset:i]
|
||||||
|
offset = i
|
||||||
|
}
|
||||||
|
|
||||||
|
child := &node{
|
||||||
|
nType: param,
|
||||||
|
maxParams: numParams,
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
n.wildChild = true
|
||||||
|
n = child
|
||||||
|
n.priority++
|
||||||
|
numParams--
|
||||||
|
|
||||||
|
// if the path doesn't end with the wildcard, then there
|
||||||
|
// will be another non-wildcard subpath starting with '/'
|
||||||
|
if end < max {
|
||||||
|
n.path = path[offset:end]
|
||||||
|
offset = end
|
||||||
|
|
||||||
|
child := &node{
|
||||||
|
maxParams: numParams,
|
||||||
|
priority: 1,
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
n = child
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // catchAll
|
||||||
|
if end != max || numParams > 1 {
|
||||||
|
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[offset:i]
|
||||||
|
|
||||||
|
// first node: catchAll node with empty path
|
||||||
|
child := &node{
|
||||||
|
wildChild: true,
|
||||||
|
nType: catchAll,
|
||||||
|
maxParams: 1,
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
n.indices = string(path[i])
|
||||||
|
n = child
|
||||||
|
n.priority++
|
||||||
|
|
||||||
|
// second node: node holding the variable
|
||||||
|
child = &node{
|
||||||
|
path: path[i:],
|
||||||
|
nType: catchAll,
|
||||||
|
maxParams: 1,
|
||||||
|
handlers: handlers,
|
||||||
|
priority: 1,
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert remaining path part and handle to the leaf
|
||||||
|
n.path = path[offset:]
|
||||||
|
n.handlers = handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// getValue 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, po Params, unescape bool) (handlers HandlersChain, p Params, tsr bool) {
|
||||||
|
p = po
|
||||||
|
walk: // Outer loop for walking the tree
|
||||||
|
for {
|
||||||
|
if len(path) > len(n.path) {
|
||||||
|
if path[:len(n.path)] == n.path {
|
||||||
|
path = path[len(n.path):]
|
||||||
|
// 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 {
|
||||||
|
c := path[0]
|
||||||
|
for i := 0; i < len(n.indices); i++ {
|
||||||
|
if c == n.indices[i] {
|
||||||
|
n = n.children[i]
|
||||||
|
continue walk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing found.
|
||||||
|
// We can recommend to redirect to the same URL without a
|
||||||
|
// trailing slash if a leaf exists for that path.
|
||||||
|
tsr = path == "/" && n.handlers != nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle wildcard child
|
||||||
|
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++
|
||||||
|
}
|
||||||
|
|
||||||
|
// save param value
|
||||||
|
if cap(p) < int(n.maxParams) {
|
||||||
|
p = make(Params, 0, n.maxParams)
|
||||||
|
}
|
||||||
|
i := len(p)
|
||||||
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
|
p[i].Key = n.path[1:]
|
||||||
|
val := path[:end]
|
||||||
|
if unescape {
|
||||||
|
var err error
|
||||||
|
if p[i].Value, err = url.QueryUnescape(val); err != nil {
|
||||||
|
p[i].Value = val // fallback, in case of error
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p[i].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
|
||||||
|
tsr = len(path) == end+1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if handlers = n.handlers; handlers != nil {
|
||||||
|
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]
|
||||||
|
tsr = n.path == "/" && n.handlers != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
case catchAll:
|
||||||
|
// save param value
|
||||||
|
if cap(p) < int(n.maxParams) {
|
||||||
|
p = make(Params, 0, n.maxParams)
|
||||||
|
}
|
||||||
|
i := len(p)
|
||||||
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
|
p[i].Key = n.path[2:]
|
||||||
|
if unescape {
|
||||||
|
var err error
|
||||||
|
if p[i].Value, err = url.QueryUnescape(path); err != nil {
|
||||||
|
p[i].Value = path // fallback, in case of error
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p[i].Value = path
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = n.handlers
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("invalid node type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if path == n.path {
|
||||||
|
// We should have reached the node containing the handle.
|
||||||
|
// Check if this node has a handle registered.
|
||||||
|
if handlers = n.handlers; handlers != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == "/" && n.wildChild && n.nType != root {
|
||||||
|
tsr = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No handle found. Check if a handle for this path + a
|
||||||
|
// trailing slash exists for trailing slash recommendation
|
||||||
|
for i := 0; i < len(n.indices); i++ {
|
||||||
|
if n.indices[i] == '/' {
|
||||||
|
n = n.children[i]
|
||||||
|
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
|
||||||
|
tsr = (path == "/") ||
|
||||||
|
(len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
|
||||||
|
path == n.path[:len(n.path)-1] && n.handlers != nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findCaseInsensitivePath 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) (ciPath []byte, found bool) {
|
||||||
|
ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory
|
||||||
|
|
||||||
|
// Outer loop for walking the tree
|
||||||
|
for len(path) >= len(n.path) && strings.ToLower(path[:len(n.path)]) == strings.ToLower(n.path) {
|
||||||
|
path = path[len(n.path):]
|
||||||
|
ciPath = append(ciPath, n.path...)
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
// 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 {
|
||||||
|
r := unicode.ToLower(rune(path[0]))
|
||||||
|
for i, index := range n.indices {
|
||||||
|
// must use recursive approach since both index and
|
||||||
|
// ToLower(index) could exist. We must check both.
|
||||||
|
if r == unicode.ToLower(index) {
|
||||||
|
out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash)
|
||||||
|
if found {
|
||||||
|
return append(ciPath, out...), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing found. We can recommend to redirect to the same URL
|
||||||
|
// without a trailing slash if a leaf exists for that path
|
||||||
|
found = fixTrailingSlash && path == "/" && n.handlers != nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.children[0]
|
||||||
|
switch n.nType {
|
||||||
|
case param:
|
||||||
|
// find param end (either '/' or path end)
|
||||||
|
k := 0
|
||||||
|
for k < len(path) && path[k] != '/' {
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
|
||||||
|
// add param value to case insensitive path
|
||||||
|
ciPath = append(ciPath, path[:k]...)
|
||||||
|
|
||||||
|
// we need to go deeper!
|
||||||
|
if k < len(path) {
|
||||||
|
if len(n.children) > 0 {
|
||||||
|
path = path[k:]
|
||||||
|
n = n.children[0]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... but we can't
|
||||||
|
if fixTrailingSlash && len(path) == k+1 {
|
||||||
|
return ciPath, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.handlers != nil {
|
||||||
|
return ciPath, true
|
||||||
|
} else 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, '/'), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case catchAll:
|
||||||
|
return append(ciPath, path...), true
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("invalid node type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We should have reached the node containing the handle.
|
||||||
|
// Check if this node has a handle registered.
|
||||||
|
if n.handlers != nil {
|
||||||
|
return ciPath, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// No handle found.
|
||||||
|
// Try to fix the path by adding a trailing slash
|
||||||
|
if fixTrailingSlash {
|
||||||
|
for i := 0; i < len(n.indices); i++ {
|
||||||
|
if n.indices[i] == '/' {
|
||||||
|
n = n.children[i]
|
||||||
|
if (len(n.path) == 1 && n.handlers != nil) ||
|
||||||
|
(n.nType == catchAll && n.children[0].handlers != nil) {
|
||||||
|
return append(ciPath, '/'), true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing found.
|
||||||
|
// Try to fix the path by adding / removing a trailing slash
|
||||||
|
if fixTrailingSlash {
|
||||||
|
if path == "/" {
|
||||||
|
return ciPath, true
|
||||||
|
}
|
||||||
|
if len(path)+1 == len(n.path) && n.path[len(path)] == '/' &&
|
||||||
|
strings.ToLower(path) == strings.ToLower(n.path[:len(path)]) &&
|
||||||
|
n.handlers != nil {
|
||||||
|
return append(ciPath, n.path...), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
151
vendor/github.com/gin-gonic/gin/utils.go
generated
vendored
Normal file
151
vendor/github.com/gin-gonic/gin/utils.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BindKey = "_gin-gonic/gin/bindkey"
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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
|
||||||
|
// 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 {
|
||||||
|
if wildcard == nil {
|
||||||
|
panic("negotiation config is invalid")
|
||||||
|
}
|
||||||
|
return wildcard
|
||||||
|
}
|
||||||
|
return custom
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAccept(acceptHeader string) []string {
|
||||||
|
parts := strings.Split(acceptHeader, ",")
|
||||||
|
out := make([]string, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
if part = strings.TrimSpace(strings.Split(part, ";")[0]); 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)
|
||||||
|
appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/'
|
||||||
|
if appendSlash {
|
||||||
|
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 much parameters")
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/gin-gonic/gin/wercker.yml
generated
vendored
Normal file
1
vendor/github.com/gin-gonic/gin/wercker.yml
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
box: wercker/default
|
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
Normal file
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
Normal file
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Copyright 2010 The Go 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.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
253
vendor/github.com/golang/protobuf/proto/clone.go
generated
vendored
Normal file
253
vendor/github.com/golang/protobuf/proto/clone.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Protocol buffer deep copy and merge.
|
||||||
|
// TODO: RawMessage.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clone returns a deep copy of a protocol buffer.
|
||||||
|
func Clone(src Message) Message {
|
||||||
|
in := reflect.ValueOf(src)
|
||||||
|
if in.IsNil() {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
out := reflect.New(in.Type().Elem())
|
||||||
|
dst := out.Interface().(Message)
|
||||||
|
Merge(dst, src)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merger is the interface representing objects that can merge messages of the same type.
|
||||||
|
type Merger interface {
|
||||||
|
// Merge merges src into this message.
|
||||||
|
// Required and optional fields that are set in src will be set to that value in dst.
|
||||||
|
// Elements of repeated fields will be appended.
|
||||||
|
//
|
||||||
|
// Merge may panic if called with a different argument type than the receiver.
|
||||||
|
Merge(src Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatedMerger is the custom merge method that generated protos will have.
|
||||||
|
// We must add this method since a generate Merge method will conflict with
|
||||||
|
// many existing protos that have a Merge data field already defined.
|
||||||
|
type generatedMerger interface {
|
||||||
|
XXX_Merge(src Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges src into dst.
|
||||||
|
// Required and optional fields that are set in src will be set to that value in dst.
|
||||||
|
// Elements of repeated fields will be appended.
|
||||||
|
// Merge panics if src and dst are not the same type, or if dst is nil.
|
||||||
|
func Merge(dst, src Message) {
|
||||||
|
if m, ok := dst.(Merger); ok {
|
||||||
|
m.Merge(src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := reflect.ValueOf(src)
|
||||||
|
out := reflect.ValueOf(dst)
|
||||||
|
if out.IsNil() {
|
||||||
|
panic("proto: nil destination")
|
||||||
|
}
|
||||||
|
if in.Type() != out.Type() {
|
||||||
|
panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src))
|
||||||
|
}
|
||||||
|
if in.IsNil() {
|
||||||
|
return // Merge from nil src is a noop
|
||||||
|
}
|
||||||
|
if m, ok := dst.(generatedMerger); ok {
|
||||||
|
m.XXX_Merge(src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mergeStruct(out.Elem(), in.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeStruct(out, in reflect.Value) {
|
||||||
|
sprop := GetProperties(in.Type())
|
||||||
|
for i := 0; i < in.NumField(); i++ {
|
||||||
|
f := in.Type().Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
||||||
|
emOut, _ := extendable(out.Addr().Interface())
|
||||||
|
mIn, muIn := emIn.extensionsRead()
|
||||||
|
if mIn != nil {
|
||||||
|
mOut := emOut.extensionsWrite()
|
||||||
|
muIn.Lock()
|
||||||
|
mergeExtension(mOut, mIn)
|
||||||
|
muIn.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uf := in.FieldByName("XXX_unrecognized")
|
||||||
|
if !uf.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uin := uf.Bytes()
|
||||||
|
if len(uin) > 0 {
|
||||||
|
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeAny performs a merge between two values of the same type.
|
||||||
|
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
|
||||||
|
// prop is set if this is a struct field (it may be nil).
|
||||||
|
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
|
||||||
|
if in.Type() == protoMessageType {
|
||||||
|
if !in.IsNil() {
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
|
||||||
|
} else {
|
||||||
|
Merge(out.Interface().(Message), in.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch in.Kind() {
|
||||||
|
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||||
|
if !viaPtr && isProto3Zero(in) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out.Set(in)
|
||||||
|
case reflect.Interface:
|
||||||
|
// Probably a oneof field; copy non-nil values.
|
||||||
|
if in.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Allocate destination if it is not set, or set to a different type.
|
||||||
|
// Otherwise we will merge as normal.
|
||||||
|
if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
|
||||||
|
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
|
||||||
|
}
|
||||||
|
mergeAny(out.Elem(), in.Elem(), false, nil)
|
||||||
|
case reflect.Map:
|
||||||
|
if in.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.MakeMap(in.Type()))
|
||||||
|
}
|
||||||
|
// For maps with value types of *T or []byte we need to deep copy each value.
|
||||||
|
elemKind := in.Type().Elem().Kind()
|
||||||
|
for _, key := range in.MapKeys() {
|
||||||
|
var val reflect.Value
|
||||||
|
switch elemKind {
|
||||||
|
case reflect.Ptr:
|
||||||
|
val = reflect.New(in.Type().Elem().Elem())
|
||||||
|
mergeAny(val, in.MapIndex(key), false, nil)
|
||||||
|
case reflect.Slice:
|
||||||
|
val = in.MapIndex(key)
|
||||||
|
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
||||||
|
default:
|
||||||
|
val = in.MapIndex(key)
|
||||||
|
}
|
||||||
|
out.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
if in.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.New(in.Elem().Type()))
|
||||||
|
}
|
||||||
|
mergeAny(out.Elem(), in.Elem(), true, nil)
|
||||||
|
case reflect.Slice:
|
||||||
|
if in.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.Type().Elem().Kind() == reflect.Uint8 {
|
||||||
|
// []byte is a scalar bytes field, not a repeated field.
|
||||||
|
|
||||||
|
// Edge case: if this is in a proto3 message, a zero length
|
||||||
|
// bytes field is considered the zero value, and should not
|
||||||
|
// be merged.
|
||||||
|
if prop != nil && prop.proto3 && in.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a deep copy.
|
||||||
|
// Append to []byte{} instead of []byte(nil) so that we never end up
|
||||||
|
// with a nil result.
|
||||||
|
out.SetBytes(append([]byte{}, in.Bytes()...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := in.Len()
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.MakeSlice(in.Type(), 0, n))
|
||||||
|
}
|
||||||
|
switch in.Type().Elem().Kind() {
|
||||||
|
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||||
|
out.Set(reflect.AppendSlice(out, in))
|
||||||
|
default:
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
x := reflect.Indirect(reflect.New(in.Type().Elem()))
|
||||||
|
mergeAny(x, in.Index(i), false, nil)
|
||||||
|
out.Set(reflect.Append(out, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
mergeStruct(out, in)
|
||||||
|
default:
|
||||||
|
// unknown type, so not a protocol buffer
|
||||||
|
log.Printf("proto: don't know how to copy %v", in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeExtension(out, in map[int32]Extension) {
|
||||||
|
for extNum, eIn := range in {
|
||||||
|
eOut := Extension{desc: eIn.desc}
|
||||||
|
if eIn.value != nil {
|
||||||
|
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
|
||||||
|
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
|
||||||
|
eOut.value = v.Interface()
|
||||||
|
}
|
||||||
|
if eIn.enc != nil {
|
||||||
|
eOut.enc = make([]byte, len(eIn.enc))
|
||||||
|
copy(eOut.enc, eIn.enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
out[extNum] = eOut
|
||||||
|
}
|
||||||
|
}
|
428
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
Normal file
428
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routines for decoding protocol buffer data to construct in-memory representations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errOverflow is returned when an integer is too large to be represented.
|
||||||
|
var errOverflow = errors.New("proto: integer overflow")
|
||||||
|
|
||||||
|
// ErrInternalBadWireType is returned by generated code when an incorrect
|
||||||
|
// wire type is encountered. It does not get returned to user code.
|
||||||
|
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
||||||
|
|
||||||
|
// DecodeVarint reads a varint-encoded integer from the slice.
|
||||||
|
// It returns the integer and the number of bytes consumed, or
|
||||||
|
// zero if there is not enough.
|
||||||
|
// This is the format for the
|
||||||
|
// int32, int64, uint32, uint64, bool, and enum
|
||||||
|
// protocol buffer types.
|
||||||
|
func DecodeVarint(buf []byte) (x uint64, n int) {
|
||||||
|
for shift := uint(0); shift < 64; shift += 7 {
|
||||||
|
if n >= len(buf) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
b := uint64(buf[n])
|
||||||
|
n++
|
||||||
|
x |= (b & 0x7F) << shift
|
||||||
|
if (b & 0x80) == 0 {
|
||||||
|
return x, n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number is too large to represent in a 64-bit value.
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
|
||||||
|
i := p.index
|
||||||
|
l := len(p.buf)
|
||||||
|
|
||||||
|
for shift := uint(0); shift < 64; shift += 7 {
|
||||||
|
if i >= l {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b := p.buf[i]
|
||||||
|
i++
|
||||||
|
x |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
p.index = i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number is too large to represent in a 64-bit value.
|
||||||
|
err = errOverflow
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeVarint reads a varint-encoded integer from the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// int32, int64, uint32, uint64, bool, and enum
|
||||||
|
// protocol buffer types.
|
||||||
|
func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
||||||
|
i := p.index
|
||||||
|
buf := p.buf
|
||||||
|
|
||||||
|
if i >= len(buf) {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
} else if buf[i] < 0x80 {
|
||||||
|
p.index++
|
||||||
|
return uint64(buf[i]), nil
|
||||||
|
} else if len(buf)-i < 10 {
|
||||||
|
return p.decodeVarintSlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
var b uint64
|
||||||
|
// we already checked the first byte
|
||||||
|
x = uint64(buf[i]) - 0x80
|
||||||
|
i++
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 7
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 7
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 14
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 14
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 21
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 21
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 28
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 28
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 35
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 35
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 42
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 42
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 49
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 49
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 56
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
x -= 0x80 << 56
|
||||||
|
|
||||||
|
b = uint64(buf[i])
|
||||||
|
i++
|
||||||
|
x += b << 63
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
// x -= 0x80 << 63 // Always zero.
|
||||||
|
|
||||||
|
return 0, errOverflow
|
||||||
|
|
||||||
|
done:
|
||||||
|
p.index = i
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFixed64 reads a 64-bit integer from the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// fixed64, sfixed64, and double protocol buffer types.
|
||||||
|
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
|
||||||
|
// x, err already 0
|
||||||
|
i := p.index + 8
|
||||||
|
if i < 0 || i > len(p.buf) {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.index = i
|
||||||
|
|
||||||
|
x = uint64(p.buf[i-8])
|
||||||
|
x |= uint64(p.buf[i-7]) << 8
|
||||||
|
x |= uint64(p.buf[i-6]) << 16
|
||||||
|
x |= uint64(p.buf[i-5]) << 24
|
||||||
|
x |= uint64(p.buf[i-4]) << 32
|
||||||
|
x |= uint64(p.buf[i-3]) << 40
|
||||||
|
x |= uint64(p.buf[i-2]) << 48
|
||||||
|
x |= uint64(p.buf[i-1]) << 56
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFixed32 reads a 32-bit integer from the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// fixed32, sfixed32, and float protocol buffer types.
|
||||||
|
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
|
||||||
|
// x, err already 0
|
||||||
|
i := p.index + 4
|
||||||
|
if i < 0 || i > len(p.buf) {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.index = i
|
||||||
|
|
||||||
|
x = uint64(p.buf[i-4])
|
||||||
|
x |= uint64(p.buf[i-3]) << 8
|
||||||
|
x |= uint64(p.buf[i-2]) << 16
|
||||||
|
x |= uint64(p.buf[i-1]) << 24
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
|
||||||
|
// from the Buffer.
|
||||||
|
// This is the format used for the sint64 protocol buffer type.
|
||||||
|
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
|
||||||
|
x, err = p.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
|
||||||
|
// from the Buffer.
|
||||||
|
// This is the format used for the sint32 protocol buffer type.
|
||||||
|
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
||||||
|
x, err = p.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
||||||
|
// This is the format used for the bytes protocol buffer
|
||||||
|
// type and for embedded messages.
|
||||||
|
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
|
||||||
|
n, err := p.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nb := int(n)
|
||||||
|
if nb < 0 {
|
||||||
|
return nil, fmt.Errorf("proto: bad byte length %d", nb)
|
||||||
|
}
|
||||||
|
end := p.index + nb
|
||||||
|
if end < p.index || end > len(p.buf) {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if !alloc {
|
||||||
|
// todo: check if can get more uses of alloc=false
|
||||||
|
buf = p.buf[p.index:end]
|
||||||
|
p.index += nb
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = make([]byte, nb)
|
||||||
|
copy(buf, p.buf[p.index:])
|
||||||
|
p.index += nb
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeStringBytes reads an encoded string from the Buffer.
|
||||||
|
// This is the format used for the proto2 string type.
|
||||||
|
func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
||||||
|
buf, err := p.DecodeRawBytes(false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshaler is the interface representing objects that can
|
||||||
|
// unmarshal themselves. The argument points to data that may be
|
||||||
|
// overwritten, so implementations should not keep references to the
|
||||||
|
// buffer.
|
||||||
|
// Unmarshal implementations should not clear the receiver.
|
||||||
|
// Any unmarshaled data should be merged into the receiver.
|
||||||
|
// Callers of Unmarshal that do not want to retain existing data
|
||||||
|
// should Reset the receiver before calling Unmarshal.
|
||||||
|
type Unmarshaler interface {
|
||||||
|
Unmarshal([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUnmarshaler is the interface representing objects that can
|
||||||
|
// unmarshal themselves. The semantics are identical to Unmarshaler.
|
||||||
|
//
|
||||||
|
// This exists to support protoc-gen-go generated messages.
|
||||||
|
// The proto package will stop type-asserting to this interface in the future.
|
||||||
|
//
|
||||||
|
// DO NOT DEPEND ON THIS.
|
||||||
|
type newUnmarshaler interface {
|
||||||
|
XXX_Unmarshal([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the protocol buffer representation in buf and places the
|
||||||
|
// decoded result in pb. If the struct underlying pb does not match
|
||||||
|
// the data in buf, the results can be unpredictable.
|
||||||
|
//
|
||||||
|
// Unmarshal resets pb before starting to unmarshal, so any
|
||||||
|
// existing data in pb is always removed. Use UnmarshalMerge
|
||||||
|
// to preserve and append to existing data.
|
||||||
|
func Unmarshal(buf []byte, pb Message) error {
|
||||||
|
pb.Reset()
|
||||||
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
return u.XXX_Unmarshal(buf)
|
||||||
|
}
|
||||||
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
return u.Unmarshal(buf)
|
||||||
|
}
|
||||||
|
return NewBuffer(buf).Unmarshal(pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMerge parses the protocol buffer representation in buf and
|
||||||
|
// writes the decoded result to pb. If the struct underlying pb does not match
|
||||||
|
// the data in buf, the results can be unpredictable.
|
||||||
|
//
|
||||||
|
// UnmarshalMerge merges into existing data in pb.
|
||||||
|
// Most code should use Unmarshal instead.
|
||||||
|
func UnmarshalMerge(buf []byte, pb Message) error {
|
||||||
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
return u.XXX_Unmarshal(buf)
|
||||||
|
}
|
||||||
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
// NOTE: The history of proto have unfortunately been inconsistent
|
||||||
|
// whether Unmarshaler should or should not implicitly clear itself.
|
||||||
|
// Some implementations do, most do not.
|
||||||
|
// Thus, calling this here may or may not do what people want.
|
||||||
|
//
|
||||||
|
// See https://github.com/golang/protobuf/issues/424
|
||||||
|
return u.Unmarshal(buf)
|
||||||
|
}
|
||||||
|
return NewBuffer(buf).Unmarshal(pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeMessage reads a count-delimited message from the Buffer.
|
||||||
|
func (p *Buffer) DecodeMessage(pb Message) error {
|
||||||
|
enc, err := p.DecodeRawBytes(false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return NewBuffer(enc).Unmarshal(pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeGroup reads a tag-delimited group from the Buffer.
|
||||||
|
// StartGroup tag is already consumed. This function consumes
|
||||||
|
// EndGroup tag.
|
||||||
|
func (p *Buffer) DecodeGroup(pb Message) error {
|
||||||
|
b := p.buf[p.index:]
|
||||||
|
x, y := findEndGroup(b)
|
||||||
|
if x < 0 {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
err := Unmarshal(b[:x], pb)
|
||||||
|
p.index += y
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the protocol buffer representation in the
|
||||||
|
// Buffer and places the decoded result in pb. If the struct
|
||||||
|
// underlying pb does not match the data in the buffer, the results can be
|
||||||
|
// unpredictable.
|
||||||
|
//
|
||||||
|
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
|
||||||
|
func (p *Buffer) Unmarshal(pb Message) error {
|
||||||
|
// If the object can unmarshal itself, let it.
|
||||||
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
err := u.XXX_Unmarshal(p.buf[p.index:])
|
||||||
|
p.index = len(p.buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
// NOTE: The history of proto have unfortunately been inconsistent
|
||||||
|
// whether Unmarshaler should or should not implicitly clear itself.
|
||||||
|
// Some implementations do, most do not.
|
||||||
|
// Thus, calling this here may or may not do what people want.
|
||||||
|
//
|
||||||
|
// See https://github.com/golang/protobuf/issues/424
|
||||||
|
err := u.Unmarshal(p.buf[p.index:])
|
||||||
|
p.index = len(p.buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow workaround for messages that aren't Unmarshalers.
|
||||||
|
// This includes some hand-coded .pb.go files and
|
||||||
|
// bootstrap protos.
|
||||||
|
// TODO: fix all of those and then add Unmarshal to
|
||||||
|
// the Message interface. Then:
|
||||||
|
// The cast above and code below can be deleted.
|
||||||
|
// The old unmarshaler can be deleted.
|
||||||
|
// Clients can call Unmarshal directly (can already do that, actually).
|
||||||
|
var info InternalMessageInfo
|
||||||
|
err := info.Unmarshal(pb, p.buf[p.index:])
|
||||||
|
p.index = len(p.buf)
|
||||||
|
return err
|
||||||
|
}
|
350
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
Normal file
350
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type generatedDiscarder interface {
|
||||||
|
XXX_DiscardUnknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardUnknown recursively discards all unknown fields from this message
|
||||||
|
// and all embedded messages.
|
||||||
|
//
|
||||||
|
// When unmarshaling a message with unrecognized fields, the tags and values
|
||||||
|
// of such fields are preserved in the Message. This allows a later call to
|
||||||
|
// marshal to be able to produce a message that continues to have those
|
||||||
|
// unrecognized fields. To avoid this, DiscardUnknown is used to
|
||||||
|
// explicitly clear the unknown fields after unmarshaling.
|
||||||
|
//
|
||||||
|
// For proto2 messages, the unknown fields of message extensions are only
|
||||||
|
// discarded from messages that have been accessed via GetExtension.
|
||||||
|
func DiscardUnknown(m Message) {
|
||||||
|
if m, ok := m.(generatedDiscarder); ok {
|
||||||
|
m.XXX_DiscardUnknown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: Dynamically populate a InternalMessageInfo for legacy messages,
|
||||||
|
// but the master branch has no implementation for InternalMessageInfo,
|
||||||
|
// so it would be more work to replicate that approach.
|
||||||
|
discardLegacy(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardUnknown recursively discards all unknown fields.
|
||||||
|
func (a *InternalMessageInfo) DiscardUnknown(m Message) {
|
||||||
|
di := atomicLoadDiscardInfo(&a.discard)
|
||||||
|
if di == nil {
|
||||||
|
di = getDiscardInfo(reflect.TypeOf(m).Elem())
|
||||||
|
atomicStoreDiscardInfo(&a.discard, di)
|
||||||
|
}
|
||||||
|
di.discard(toPointer(&m))
|
||||||
|
}
|
||||||
|
|
||||||
|
type discardInfo struct {
|
||||||
|
typ reflect.Type
|
||||||
|
|
||||||
|
initialized int32 // 0: only typ is valid, 1: everything is valid
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
fields []discardFieldInfo
|
||||||
|
unrecognized field
|
||||||
|
}
|
||||||
|
|
||||||
|
type discardFieldInfo struct {
|
||||||
|
field field // Offset of field, guaranteed to be valid
|
||||||
|
discard func(src pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
discardInfoMap = map[reflect.Type]*discardInfo{}
|
||||||
|
discardInfoLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDiscardInfo(t reflect.Type) *discardInfo {
|
||||||
|
discardInfoLock.Lock()
|
||||||
|
defer discardInfoLock.Unlock()
|
||||||
|
di := discardInfoMap[t]
|
||||||
|
if di == nil {
|
||||||
|
di = &discardInfo{typ: t}
|
||||||
|
discardInfoMap[t] = di
|
||||||
|
}
|
||||||
|
return di
|
||||||
|
}
|
||||||
|
|
||||||
|
func (di *discardInfo) discard(src pointer) {
|
||||||
|
if src.isNil() {
|
||||||
|
return // Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.LoadInt32(&di.initialized) == 0 {
|
||||||
|
di.computeDiscardInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range di.fields {
|
||||||
|
sfp := src.offset(fi.field)
|
||||||
|
fi.discard(sfp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For proto2 messages, only discard unknown fields in message extensions
|
||||||
|
// that have been accessed via GetExtension.
|
||||||
|
if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil {
|
||||||
|
// Ignore lock since DiscardUnknown is not concurrency safe.
|
||||||
|
emm, _ := em.extensionsRead()
|
||||||
|
for _, mx := range emm {
|
||||||
|
if m, ok := mx.value.(Message); ok {
|
||||||
|
DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if di.unrecognized.IsValid() {
|
||||||
|
*src.offset(di.unrecognized).toBytes() = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (di *discardInfo) computeDiscardInfo() {
|
||||||
|
di.lock.Lock()
|
||||||
|
defer di.lock.Unlock()
|
||||||
|
if di.initialized != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := di.typ
|
||||||
|
n := t.NumField()
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dfi := discardFieldInfo{field: toField(&f)}
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// Unwrap tf to get its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
di := getDiscardInfo(tf)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sps := src.getPointerSlice()
|
||||||
|
for _, sp := range sps {
|
||||||
|
if !sp.isNil() {
|
||||||
|
di.discard(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
di := getDiscardInfo(tf)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sp := src.getPointer()
|
||||||
|
if !sp.isNil() {
|
||||||
|
di.discard(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name))
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sm := src.asPointerTo(tf).Elem()
|
||||||
|
if sm.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
DiscardUnknown(val.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dfi.discard = func(pointer) {} // Noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name))
|
||||||
|
default: // E.g., interface{}
|
||||||
|
// TODO: Make this faster?
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
su := src.asPointerTo(tf).Elem()
|
||||||
|
if !su.IsNil() {
|
||||||
|
sv := su.Elem().Elem().Field(0)
|
||||||
|
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch sv.Type().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
DiscardUnknown(sv.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
di.fields = append(di.fields, dfi)
|
||||||
|
}
|
||||||
|
|
||||||
|
di.unrecognized = invalidField
|
||||||
|
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
||||||
|
if f.Type != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
di.unrecognized = toField(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreInt32(&di.initialized, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func discardLegacy(m Message) {
|
||||||
|
v := reflect.ValueOf(m)
|
||||||
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vf := v.Field(i)
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// Unwrap tf to get its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
for j := 0; j < vf.Len(); j++ {
|
||||||
|
discardLegacy(vf.Index(j).Interface().(Message))
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
discardLegacy(vf.Interface().(Message))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name))
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
tv := vf.Type().Elem()
|
||||||
|
if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T)
|
||||||
|
for _, key := range vf.MapKeys() {
|
||||||
|
val := vf.MapIndex(key)
|
||||||
|
discardLegacy(val.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name))
|
||||||
|
default: // E.g., test_proto.isCommunique_Union interface
|
||||||
|
if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" {
|
||||||
|
vf = vf.Elem() // E.g., *test_proto.Communique_Msg
|
||||||
|
if !vf.IsNil() {
|
||||||
|
vf = vf.Elem() // E.g., test_proto.Communique_Msg
|
||||||
|
vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value
|
||||||
|
if vf.Kind() == reflect.Ptr {
|
||||||
|
discardLegacy(vf.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() {
|
||||||
|
if vf.Type() != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
vf.Set(reflect.ValueOf([]byte(nil)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// For proto2 messages, only discard unknown fields in message extensions
|
||||||
|
// that have been accessed via GetExtension.
|
||||||
|
if em, err := extendable(m); err == nil {
|
||||||
|
// Ignore lock since discardLegacy is not concurrency safe.
|
||||||
|
emm, _ := em.extensionsRead()
|
||||||
|
for _, mx := range emm {
|
||||||
|
if m, ok := mx.value.(Message); ok {
|
||||||
|
discardLegacy(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
203
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
Normal file
203
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routines for encoding data into the wire format for protocol buffers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errRepeatedHasNil is the error returned if Marshal is called with
|
||||||
|
// a struct with a repeated field containing a nil element.
|
||||||
|
errRepeatedHasNil = errors.New("proto: repeated field has nil element")
|
||||||
|
|
||||||
|
// errOneofHasNil is the error returned if Marshal is called with
|
||||||
|
// a struct with a oneof field containing a nil element.
|
||||||
|
errOneofHasNil = errors.New("proto: oneof field has nil value")
|
||||||
|
|
||||||
|
// ErrNil is the error returned if Marshal is called with nil.
|
||||||
|
ErrNil = errors.New("proto: Marshal called with nil")
|
||||||
|
|
||||||
|
// ErrTooLarge is the error returned if Marshal is called with a
|
||||||
|
// message that encodes to >2GB.
|
||||||
|
ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
|
||||||
|
)
|
||||||
|
|
||||||
|
// The fundamental encoders that put bytes on the wire.
|
||||||
|
// Those that take integer types all accept uint64 and are
|
||||||
|
// therefore of type valueEncoder.
|
||||||
|
|
||||||
|
const maxVarintBytes = 10 // maximum length of a varint
|
||||||
|
|
||||||
|
// EncodeVarint returns the varint encoding of x.
|
||||||
|
// This is the format for the
|
||||||
|
// int32, int64, uint32, uint64, bool, and enum
|
||||||
|
// protocol buffer types.
|
||||||
|
// Not used by the package itself, but helpful to clients
|
||||||
|
// wishing to use the same encoding.
|
||||||
|
func EncodeVarint(x uint64) []byte {
|
||||||
|
var buf [maxVarintBytes]byte
|
||||||
|
var n int
|
||||||
|
for n = 0; x > 127; n++ {
|
||||||
|
buf[n] = 0x80 | uint8(x&0x7F)
|
||||||
|
x >>= 7
|
||||||
|
}
|
||||||
|
buf[n] = uint8(x)
|
||||||
|
n++
|
||||||
|
return buf[0:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeVarint writes a varint-encoded integer to the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// int32, int64, uint32, uint64, bool, and enum
|
||||||
|
// protocol buffer types.
|
||||||
|
func (p *Buffer) EncodeVarint(x uint64) error {
|
||||||
|
for x >= 1<<7 {
|
||||||
|
p.buf = append(p.buf, uint8(x&0x7f|0x80))
|
||||||
|
x >>= 7
|
||||||
|
}
|
||||||
|
p.buf = append(p.buf, uint8(x))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeVarint returns the varint encoding size of an integer.
|
||||||
|
func SizeVarint(x uint64) int {
|
||||||
|
switch {
|
||||||
|
case x < 1<<7:
|
||||||
|
return 1
|
||||||
|
case x < 1<<14:
|
||||||
|
return 2
|
||||||
|
case x < 1<<21:
|
||||||
|
return 3
|
||||||
|
case x < 1<<28:
|
||||||
|
return 4
|
||||||
|
case x < 1<<35:
|
||||||
|
return 5
|
||||||
|
case x < 1<<42:
|
||||||
|
return 6
|
||||||
|
case x < 1<<49:
|
||||||
|
return 7
|
||||||
|
case x < 1<<56:
|
||||||
|
return 8
|
||||||
|
case x < 1<<63:
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeFixed64 writes a 64-bit integer to the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// fixed64, sfixed64, and double protocol buffer types.
|
||||||
|
func (p *Buffer) EncodeFixed64(x uint64) error {
|
||||||
|
p.buf = append(p.buf,
|
||||||
|
uint8(x),
|
||||||
|
uint8(x>>8),
|
||||||
|
uint8(x>>16),
|
||||||
|
uint8(x>>24),
|
||||||
|
uint8(x>>32),
|
||||||
|
uint8(x>>40),
|
||||||
|
uint8(x>>48),
|
||||||
|
uint8(x>>56))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeFixed32 writes a 32-bit integer to the Buffer.
|
||||||
|
// This is the format for the
|
||||||
|
// fixed32, sfixed32, and float protocol buffer types.
|
||||||
|
func (p *Buffer) EncodeFixed32(x uint64) error {
|
||||||
|
p.buf = append(p.buf,
|
||||||
|
uint8(x),
|
||||||
|
uint8(x>>8),
|
||||||
|
uint8(x>>16),
|
||||||
|
uint8(x>>24))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeZigzag64 writes a zigzag-encoded 64-bit integer
|
||||||
|
// to the Buffer.
|
||||||
|
// This is the format used for the sint64 protocol buffer type.
|
||||||
|
func (p *Buffer) EncodeZigzag64(x uint64) error {
|
||||||
|
// use signed number to get arithmetic right shift.
|
||||||
|
return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeZigzag32 writes a zigzag-encoded 32-bit integer
|
||||||
|
// to the Buffer.
|
||||||
|
// This is the format used for the sint32 protocol buffer type.
|
||||||
|
func (p *Buffer) EncodeZigzag32(x uint64) error {
|
||||||
|
// use signed number to get arithmetic right shift.
|
||||||
|
return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeRawBytes writes a count-delimited byte buffer to the Buffer.
|
||||||
|
// This is the format used for the bytes protocol buffer
|
||||||
|
// type and for embedded messages.
|
||||||
|
func (p *Buffer) EncodeRawBytes(b []byte) error {
|
||||||
|
p.EncodeVarint(uint64(len(b)))
|
||||||
|
p.buf = append(p.buf, b...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeStringBytes writes an encoded string to the Buffer.
|
||||||
|
// This is the format used for the proto2 string type.
|
||||||
|
func (p *Buffer) EncodeStringBytes(s string) error {
|
||||||
|
p.EncodeVarint(uint64(len(s)))
|
||||||
|
p.buf = append(p.buf, s...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaler is the interface representing objects that can marshal themselves.
|
||||||
|
type Marshaler interface {
|
||||||
|
Marshal() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeMessage writes the protocol buffer to the Buffer,
|
||||||
|
// prefixed by a varint-encoded length.
|
||||||
|
func (p *Buffer) EncodeMessage(pb Message) error {
|
||||||
|
siz := Size(pb)
|
||||||
|
p.EncodeVarint(uint64(siz))
|
||||||
|
return p.Marshal(pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// All protocol buffer fields are nillable, but be careful.
|
||||||
|
func isNil(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
300
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
Normal file
300
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Protocol buffer comparison.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Equal returns true iff protocol buffers a and b are equal.
|
||||||
|
The arguments must both be pointers to protocol buffer structs.
|
||||||
|
|
||||||
|
Equality is defined in this way:
|
||||||
|
- Two messages are equal iff they are the same type,
|
||||||
|
corresponding fields are equal, unknown field sets
|
||||||
|
are equal, and extensions sets are equal.
|
||||||
|
- Two set scalar fields are equal iff their values are equal.
|
||||||
|
If the fields are of a floating-point type, remember that
|
||||||
|
NaN != x for all x, including NaN. If the message is defined
|
||||||
|
in a proto3 .proto file, fields are not "set"; specifically,
|
||||||
|
zero length proto3 "bytes" fields are equal (nil == {}).
|
||||||
|
- Two repeated fields are equal iff their lengths are the same,
|
||||||
|
and their corresponding elements are equal. Note a "bytes" field,
|
||||||
|
although represented by []byte, is not a repeated field and the
|
||||||
|
rule for the scalar fields described above applies.
|
||||||
|
- Two unset fields are equal.
|
||||||
|
- Two unknown field sets are equal if their current
|
||||||
|
encoded state is equal.
|
||||||
|
- Two extension sets are equal iff they have corresponding
|
||||||
|
elements that are pairwise equal.
|
||||||
|
- Two map fields are equal iff their lengths are the same,
|
||||||
|
and they contain the same set of elements. Zero-length map
|
||||||
|
fields are equal.
|
||||||
|
- Every other combination of things are not equal.
|
||||||
|
|
||||||
|
The return value is undefined if a and b are not protocol buffers.
|
||||||
|
*/
|
||||||
|
func Equal(a, b Message) bool {
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
|
||||||
|
if v1.Type() != v2.Type() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v1.Kind() == reflect.Ptr {
|
||||||
|
if v1.IsNil() {
|
||||||
|
return v2.IsNil()
|
||||||
|
}
|
||||||
|
if v2.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v1, v2 = v1.Elem(), v2.Elem()
|
||||||
|
}
|
||||||
|
if v1.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return equalStruct(v1, v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1 and v2 are known to have the same type.
|
||||||
|
func equalStruct(v1, v2 reflect.Value) bool {
|
||||||
|
sprop := GetProperties(v1.Type())
|
||||||
|
for i := 0; i < v1.NumField(); i++ {
|
||||||
|
f := v1.Type().Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f1, f2 := v1.Field(i), v2.Field(i)
|
||||||
|
if f.Type.Kind() == reflect.Ptr {
|
||||||
|
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
|
||||||
|
// both unset
|
||||||
|
continue
|
||||||
|
} else if n1 != n2 {
|
||||||
|
// set/unset mismatch
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f1, f2 = f1.Elem(), f2.Elem()
|
||||||
|
}
|
||||||
|
if !equalAny(f1, f2, sprop.Prop[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
|
||||||
|
em2 := v2.FieldByName("XXX_InternalExtensions")
|
||||||
|
if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
|
||||||
|
em2 := v2.FieldByName("XXX_extensions")
|
||||||
|
if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uf := v1.FieldByName("XXX_unrecognized")
|
||||||
|
if !uf.IsValid() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 := uf.Bytes()
|
||||||
|
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
||||||
|
return bytes.Equal(u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1 and v2 are known to have the same type.
|
||||||
|
// prop may be nil.
|
||||||
|
func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
|
||||||
|
if v1.Type() == protoMessageType {
|
||||||
|
m1, _ := v1.Interface().(Message)
|
||||||
|
m2, _ := v2.Interface().(Message)
|
||||||
|
return Equal(m1, m2)
|
||||||
|
}
|
||||||
|
switch v1.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return v1.Bool() == v2.Bool()
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v1.Float() == v2.Float()
|
||||||
|
case reflect.Int32, reflect.Int64:
|
||||||
|
return v1.Int() == v2.Int()
|
||||||
|
case reflect.Interface:
|
||||||
|
// Probably a oneof field; compare the inner values.
|
||||||
|
n1, n2 := v1.IsNil(), v2.IsNil()
|
||||||
|
if n1 || n2 {
|
||||||
|
return n1 == n2
|
||||||
|
}
|
||||||
|
e1, e2 := v1.Elem(), v2.Elem()
|
||||||
|
if e1.Type() != e2.Type() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return equalAny(e1, e2, nil)
|
||||||
|
case reflect.Map:
|
||||||
|
if v1.Len() != v2.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, key := range v1.MapKeys() {
|
||||||
|
val2 := v2.MapIndex(key)
|
||||||
|
if !val2.IsValid() {
|
||||||
|
// This key was not found in the second map.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !equalAny(v1.MapIndex(key), val2, nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Maps may have nil values in them, so check for nil.
|
||||||
|
if v1.IsNil() && v2.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v1.IsNil() != v2.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return equalAny(v1.Elem(), v2.Elem(), prop)
|
||||||
|
case reflect.Slice:
|
||||||
|
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
||||||
|
// short circuit: []byte
|
||||||
|
|
||||||
|
// Edge case: if this is in a proto3 message, a zero length
|
||||||
|
// bytes field is considered the zero value.
|
||||||
|
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v1.IsNil() != v2.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v1.Len() != v2.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < v1.Len(); i++ {
|
||||||
|
if !equalAny(v1.Index(i), v2.Index(i), prop) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.String:
|
||||||
|
return v1.Interface().(string) == v2.Interface().(string)
|
||||||
|
case reflect.Struct:
|
||||||
|
return equalStruct(v1, v2)
|
||||||
|
case reflect.Uint32, reflect.Uint64:
|
||||||
|
return v1.Uint() == v2.Uint()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown type, so not a protocol buffer
|
||||||
|
log.Printf("proto: don't know how to compare %v", v1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// base is the struct type that the extensions are based on.
|
||||||
|
// x1 and x2 are InternalExtensions.
|
||||||
|
func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
|
||||||
|
em1, _ := x1.extensionsRead()
|
||||||
|
em2, _ := x2.extensionsRead()
|
||||||
|
return equalExtMap(base, em1, em2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
||||||
|
if len(em1) != len(em2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for extNum, e1 := range em1 {
|
||||||
|
e2, ok := em2[extNum]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
m1, m2 := e1.value, e2.value
|
||||||
|
|
||||||
|
if m1 == nil && m2 == nil {
|
||||||
|
// Both have only encoded form.
|
||||||
|
if bytes.Equal(e1.enc, e2.enc) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The bytes are different, but the extensions might still be
|
||||||
|
// equal. We need to decode them to compare.
|
||||||
|
}
|
||||||
|
|
||||||
|
if m1 != nil && m2 != nil {
|
||||||
|
// Both are unencoded.
|
||||||
|
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// At least one is encoded. To do a semantically correct comparison
|
||||||
|
// we need to unmarshal them first.
|
||||||
|
var desc *ExtensionDesc
|
||||||
|
if m := extensionMaps[base]; m != nil {
|
||||||
|
desc = m[extNum]
|
||||||
|
}
|
||||||
|
if desc == nil {
|
||||||
|
// If both have only encoded form and the bytes are the same,
|
||||||
|
// it is handled above. We get here when the bytes are different.
|
||||||
|
// We don't know how to decode it, so just compare them as byte
|
||||||
|
// slices.
|
||||||
|
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if m1 == nil {
|
||||||
|
m1, err = decodeExtension(e1.enc, desc)
|
||||||
|
}
|
||||||
|
if m2 == nil && err == nil {
|
||||||
|
m2, err = decodeExtension(e2.enc, desc)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// The encoded form is invalid.
|
||||||
|
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
543
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
Normal file
543
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types and routines for supporting protocol buffer extensions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
|
||||||
|
var ErrMissingExtension = errors.New("proto: missing extension")
|
||||||
|
|
||||||
|
// ExtensionRange represents a range of message extensions for a protocol buffer.
|
||||||
|
// Used in code generated by the protocol compiler.
|
||||||
|
type ExtensionRange struct {
|
||||||
|
Start, End int32 // both inclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
// extendableProto is an interface implemented by any protocol buffer generated by the current
|
||||||
|
// proto compiler that may be extended.
|
||||||
|
type extendableProto interface {
|
||||||
|
Message
|
||||||
|
ExtensionRangeArray() []ExtensionRange
|
||||||
|
extensionsWrite() map[int32]Extension
|
||||||
|
extensionsRead() (map[int32]Extension, sync.Locker)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
|
||||||
|
// version of the proto compiler that may be extended.
|
||||||
|
type extendableProtoV1 interface {
|
||||||
|
Message
|
||||||
|
ExtensionRangeArray() []ExtensionRange
|
||||||
|
ExtensionMap() map[int32]Extension
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
|
||||||
|
type extensionAdapter struct {
|
||||||
|
extendableProtoV1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e extensionAdapter) extensionsWrite() map[int32]Extension {
|
||||||
|
return e.ExtensionMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
|
||||||
|
return e.ExtensionMap(), notLocker{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
|
||||||
|
type notLocker struct{}
|
||||||
|
|
||||||
|
func (n notLocker) Lock() {}
|
||||||
|
func (n notLocker) Unlock() {}
|
||||||
|
|
||||||
|
// extendable returns the extendableProto interface for the given generated proto message.
|
||||||
|
// If the proto message has the old extension format, it returns a wrapper that implements
|
||||||
|
// the extendableProto interface.
|
||||||
|
func extendable(p interface{}) (extendableProto, error) {
|
||||||
|
switch p := p.(type) {
|
||||||
|
case extendableProto:
|
||||||
|
if isNilPtr(p) {
|
||||||
|
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
case extendableProtoV1:
|
||||||
|
if isNilPtr(p) {
|
||||||
|
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
||||||
|
}
|
||||||
|
return extensionAdapter{p}, nil
|
||||||
|
}
|
||||||
|
// Don't allocate a specific error containing %T:
|
||||||
|
// this is the hot path for Clone and MarshalText.
|
||||||
|
return nil, errNotExtendable
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNotExtendable = errors.New("proto: not an extendable proto.Message")
|
||||||
|
|
||||||
|
func isNilPtr(x interface{}) bool {
|
||||||
|
v := reflect.ValueOf(x)
|
||||||
|
return v.Kind() == reflect.Ptr && v.IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_InternalExtensions is an internal representation of proto extensions.
|
||||||
|
//
|
||||||
|
// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
|
||||||
|
// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
|
||||||
|
//
|
||||||
|
// The methods of XXX_InternalExtensions are not concurrency safe in general,
|
||||||
|
// but calls to logically read-only methods such as has and get may be executed concurrently.
|
||||||
|
type XXX_InternalExtensions struct {
|
||||||
|
// The struct must be indirect so that if a user inadvertently copies a
|
||||||
|
// generated message and its embedded XXX_InternalExtensions, they
|
||||||
|
// avoid the mayhem of a copied mutex.
|
||||||
|
//
|
||||||
|
// The mutex serializes all logically read-only operations to p.extensionMap.
|
||||||
|
// It is up to the client to ensure that write operations to p.extensionMap are
|
||||||
|
// mutually exclusive with other accesses.
|
||||||
|
p *struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
extensionMap map[int32]Extension
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensionsWrite returns the extension map, creating it on first use.
|
||||||
|
func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = new(struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
extensionMap map[int32]Extension
|
||||||
|
})
|
||||||
|
e.p.extensionMap = make(map[int32]Extension)
|
||||||
|
}
|
||||||
|
return e.p.extensionMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensionsRead returns the extensions map for read-only use. It may be nil.
|
||||||
|
// The caller must hold the returned mutex's lock when accessing Elements within the map.
|
||||||
|
func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
|
||||||
|
if e.p == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return e.p.extensionMap, &e.p.mu
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionDesc represents an extension specification.
|
||||||
|
// Used in generated code from the protocol compiler.
|
||||||
|
type ExtensionDesc struct {
|
||||||
|
ExtendedType Message // nil pointer to the type that is being extended
|
||||||
|
ExtensionType interface{} // nil pointer to the extension type
|
||||||
|
Field int32 // field number
|
||||||
|
Name string // fully-qualified name of extension, for text formatting
|
||||||
|
Tag string // protobuf tag style
|
||||||
|
Filename string // name of the file in which the extension is defined
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ed *ExtensionDesc) repeated() bool {
|
||||||
|
t := reflect.TypeOf(ed.ExtensionType)
|
||||||
|
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension represents an extension in a message.
|
||||||
|
type Extension struct {
|
||||||
|
// When an extension is stored in a message using SetExtension
|
||||||
|
// only desc and value are set. When the message is marshaled
|
||||||
|
// enc will be set to the encoded form of the message.
|
||||||
|
//
|
||||||
|
// When a message is unmarshaled and contains extensions, each
|
||||||
|
// extension will have only enc set. When such an extension is
|
||||||
|
// accessed using GetExtension (or GetExtensions) desc and value
|
||||||
|
// will be set.
|
||||||
|
desc *ExtensionDesc
|
||||||
|
value interface{}
|
||||||
|
enc []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawExtension is for testing only.
|
||||||
|
func SetRawExtension(base Message, id int32, b []byte) {
|
||||||
|
epb, err := extendable(base)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
extmap := epb.extensionsWrite()
|
||||||
|
extmap[id] = Extension{enc: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExtensionField returns true iff the given field number is in an extension range.
|
||||||
|
func isExtensionField(pb extendableProto, field int32) bool {
|
||||||
|
for _, er := range pb.ExtensionRangeArray() {
|
||||||
|
if er.Start <= field && field <= er.End {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkExtensionTypes checks that the given extension is valid for pb.
|
||||||
|
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
||||||
|
var pbi interface{} = pb
|
||||||
|
// Check the extended type.
|
||||||
|
if ea, ok := pbi.(extensionAdapter); ok {
|
||||||
|
pbi = ea.extendableProtoV1
|
||||||
|
}
|
||||||
|
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
|
||||||
|
return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a)
|
||||||
|
}
|
||||||
|
// Check the range.
|
||||||
|
if !isExtensionField(pb, extension.Field) {
|
||||||
|
return errors.New("proto: bad extension number; not in declared ranges")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extPropKey is sufficient to uniquely identify an extension.
|
||||||
|
type extPropKey struct {
|
||||||
|
base reflect.Type
|
||||||
|
field int32
|
||||||
|
}
|
||||||
|
|
||||||
|
var extProp = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
m map[extPropKey]*Properties
|
||||||
|
}{
|
||||||
|
m: make(map[extPropKey]*Properties),
|
||||||
|
}
|
||||||
|
|
||||||
|
func extensionProperties(ed *ExtensionDesc) *Properties {
|
||||||
|
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
|
||||||
|
|
||||||
|
extProp.RLock()
|
||||||
|
if prop, ok := extProp.m[key]; ok {
|
||||||
|
extProp.RUnlock()
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
extProp.RUnlock()
|
||||||
|
|
||||||
|
extProp.Lock()
|
||||||
|
defer extProp.Unlock()
|
||||||
|
// Check again.
|
||||||
|
if prop, ok := extProp.m[key]; ok {
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
prop := new(Properties)
|
||||||
|
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
|
||||||
|
extProp.m[key] = prop
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasExtension returns whether the given extension is present in pb.
|
||||||
|
func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
||||||
|
// TODO: Check types, field numbers, etc.?
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
extmap, mu := epb.extensionsRead()
|
||||||
|
if extmap == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
_, ok := extmap[extension.Field]
|
||||||
|
mu.Unlock()
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearExtension removes the given extension from pb.
|
||||||
|
func ClearExtension(pb Message, extension *ExtensionDesc) {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: Check types, field numbers, etc.?
|
||||||
|
extmap := epb.extensionsWrite()
|
||||||
|
delete(extmap, extension.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExtension retrieves a proto2 extended field from pb.
|
||||||
|
//
|
||||||
|
// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
|
||||||
|
// then GetExtension parses the encoded field and returns a Go value of the specified type.
|
||||||
|
// If the field is not present, then the default value is returned (if one is specified),
|
||||||
|
// otherwise ErrMissingExtension is reported.
|
||||||
|
//
|
||||||
|
// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil),
|
||||||
|
// then GetExtension returns the raw encoded bytes of the field extension.
|
||||||
|
func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if extension.ExtendedType != nil {
|
||||||
|
// can only check type if this is a complete descriptor
|
||||||
|
if err := checkExtensionTypes(epb, extension); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emap, mu := epb.extensionsRead()
|
||||||
|
if emap == nil {
|
||||||
|
return defaultExtensionValue(extension)
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
e, ok := emap[extension.Field]
|
||||||
|
if !ok {
|
||||||
|
// defaultExtensionValue returns the default value or
|
||||||
|
// ErrMissingExtension if there is no default.
|
||||||
|
return defaultExtensionValue(extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.value != nil {
|
||||||
|
// Already decoded. Check the descriptor, though.
|
||||||
|
if e.desc != extension {
|
||||||
|
// This shouldn't happen. If it does, it means that
|
||||||
|
// GetExtension was called twice with two different
|
||||||
|
// descriptors with the same field number.
|
||||||
|
return nil, errors.New("proto: descriptor conflict")
|
||||||
|
}
|
||||||
|
return e.value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if extension.ExtensionType == nil {
|
||||||
|
// incomplete descriptor
|
||||||
|
return e.enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := decodeExtension(e.enc, extension)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember the decoded version and drop the encoded version.
|
||||||
|
// That way it is safe to mutate what we return.
|
||||||
|
e.value = v
|
||||||
|
e.desc = extension
|
||||||
|
e.enc = nil
|
||||||
|
emap[extension.Field] = e
|
||||||
|
return e.value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultExtensionValue returns the default value for extension.
|
||||||
|
// If no default for an extension is defined ErrMissingExtension is returned.
|
||||||
|
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
||||||
|
if extension.ExtensionType == nil {
|
||||||
|
// incomplete descriptor, so no default
|
||||||
|
return nil, ErrMissingExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(extension.ExtensionType)
|
||||||
|
props := extensionProperties(extension)
|
||||||
|
|
||||||
|
sf, _, err := fieldDefault(t, props)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sf == nil || sf.value == nil {
|
||||||
|
// There is no default value.
|
||||||
|
return nil, ErrMissingExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Ptr {
|
||||||
|
// We do not need to return a Ptr, we can directly return sf.value.
|
||||||
|
return sf.value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to return an interface{} that is a pointer to sf.value.
|
||||||
|
value := reflect.New(t).Elem()
|
||||||
|
value.Set(reflect.New(value.Type().Elem()))
|
||||||
|
if sf.kind == reflect.Int32 {
|
||||||
|
// We may have an int32 or an enum, but the underlying data is int32.
|
||||||
|
// Since we can't set an int32 into a non int32 reflect.value directly
|
||||||
|
// set it as a int32.
|
||||||
|
value.Elem().SetInt(int64(sf.value.(int32)))
|
||||||
|
} else {
|
||||||
|
value.Elem().Set(reflect.ValueOf(sf.value))
|
||||||
|
}
|
||||||
|
return value.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeExtension decodes an extension encoded in b.
|
||||||
|
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
||||||
|
t := reflect.TypeOf(extension.ExtensionType)
|
||||||
|
unmarshal := typeUnmarshaler(t, extension.Tag)
|
||||||
|
|
||||||
|
// t is a pointer to a struct, pointer to basic type or a slice.
|
||||||
|
// Allocate space to store the pointer/slice.
|
||||||
|
value := reflect.New(t).Elem()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
x, n := decodeVarint(b)
|
||||||
|
if n == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
wire := int(x) & 7
|
||||||
|
|
||||||
|
b, err = unmarshal(b, valToPointer(value.Addr()), wire)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
||||||
|
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
||||||
|
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extensions = make([]interface{}, len(es))
|
||||||
|
for i, e := range es {
|
||||||
|
extensions[i], err = GetExtension(epb, e)
|
||||||
|
if err == ErrMissingExtension {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
|
||||||
|
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
|
||||||
|
// just the Field field, which defines the extension's field number.
|
||||||
|
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
registeredExtensions := RegisteredExtensions(pb)
|
||||||
|
|
||||||
|
emap, mu := epb.extensionsRead()
|
||||||
|
if emap == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
extensions := make([]*ExtensionDesc, 0, len(emap))
|
||||||
|
for extid, e := range emap {
|
||||||
|
desc := e.desc
|
||||||
|
if desc == nil {
|
||||||
|
desc = registeredExtensions[extid]
|
||||||
|
if desc == nil {
|
||||||
|
desc = &ExtensionDesc{Field: extid}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions = append(extensions, desc)
|
||||||
|
}
|
||||||
|
return extensions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExtension sets the specified extension of pb to the specified value.
|
||||||
|
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := checkExtensionTypes(epb, extension); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
typ := reflect.TypeOf(extension.ExtensionType)
|
||||||
|
if typ != reflect.TypeOf(value) {
|
||||||
|
return errors.New("proto: bad extension value type")
|
||||||
|
}
|
||||||
|
// nil extension values need to be caught early, because the
|
||||||
|
// encoder can't distinguish an ErrNil due to a nil extension
|
||||||
|
// from an ErrNil due to a missing field. Extensions are
|
||||||
|
// always optional, so the encoder would just swallow the error
|
||||||
|
// and drop all the extensions from the encoded message.
|
||||||
|
if reflect.ValueOf(value).IsNil() {
|
||||||
|
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
extmap := epb.extensionsWrite()
|
||||||
|
extmap[extension.Field] = Extension{desc: extension, value: value}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAllExtensions clears all extensions from pb.
|
||||||
|
func ClearAllExtensions(pb Message) {
|
||||||
|
epb, err := extendable(pb)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m := epb.extensionsWrite()
|
||||||
|
for k := range m {
|
||||||
|
delete(m, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A global registry of extensions.
|
||||||
|
// The generated code will register the generated descriptors by calling RegisterExtension.
|
||||||
|
|
||||||
|
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
|
||||||
|
|
||||||
|
// RegisterExtension is called from the generated code.
|
||||||
|
func RegisterExtension(desc *ExtensionDesc) {
|
||||||
|
st := reflect.TypeOf(desc.ExtendedType).Elem()
|
||||||
|
m := extensionMaps[st]
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[int32]*ExtensionDesc)
|
||||||
|
extensionMaps[st] = m
|
||||||
|
}
|
||||||
|
if _, ok := m[desc.Field]; ok {
|
||||||
|
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
|
||||||
|
}
|
||||||
|
m[desc.Field] = desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisteredExtensions returns a map of the registered extensions of a
|
||||||
|
// protocol buffer struct, indexed by the extension number.
|
||||||
|
// The argument pb should be a nil pointer to the struct type.
|
||||||
|
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
||||||
|
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
||||||
|
}
|
979
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
Normal file
979
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
Normal file
@ -0,0 +1,979 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package proto converts data structures to and from the wire format of
|
||||||
|
protocol buffers. It works in concert with the Go source code generated
|
||||||
|
for .proto files by the protocol compiler.
|
||||||
|
|
||||||
|
A summary of the properties of the protocol buffer interface
|
||||||
|
for a protocol buffer variable v:
|
||||||
|
|
||||||
|
- Names are turned from camel_case to CamelCase for export.
|
||||||
|
- There are no methods on v to set fields; just treat
|
||||||
|
them as structure fields.
|
||||||
|
- There are getters that return a field's value if set,
|
||||||
|
and return the field's default value if unset.
|
||||||
|
The getters work even if the receiver is a nil message.
|
||||||
|
- The zero value for a struct is its correct initialization state.
|
||||||
|
All desired fields must be set before marshaling.
|
||||||
|
- A Reset() method will restore a protobuf struct to its zero state.
|
||||||
|
- Non-repeated fields are pointers to the values; nil means unset.
|
||||||
|
That is, optional or required field int32 f becomes F *int32.
|
||||||
|
- Repeated fields are slices.
|
||||||
|
- Helper functions are available to aid the setting of fields.
|
||||||
|
msg.Foo = proto.String("hello") // set field
|
||||||
|
- Constants are defined to hold the default values of all fields that
|
||||||
|
have them. They have the form Default_StructName_FieldName.
|
||||||
|
Because the getter methods handle defaulted values,
|
||||||
|
direct use of these constants should be rare.
|
||||||
|
- Enums are given type names and maps from names to values.
|
||||||
|
Enum values are prefixed by the enclosing message's name, or by the
|
||||||
|
enum's type name if it is a top-level enum. Enum types have a String
|
||||||
|
method, and a Enum method to assist in message construction.
|
||||||
|
- Nested messages, groups and enums have type names prefixed with the name of
|
||||||
|
the surrounding message type.
|
||||||
|
- Extensions are given descriptor names that start with E_,
|
||||||
|
followed by an underscore-delimited list of the nested messages
|
||||||
|
that contain it (if any) followed by the CamelCased name of the
|
||||||
|
extension field itself. HasExtension, ClearExtension, GetExtension
|
||||||
|
and SetExtension are functions for manipulating extensions.
|
||||||
|
- Oneof field sets are given a single field in their message,
|
||||||
|
with distinguished wrapper types for each possible field value.
|
||||||
|
- Marshal and Unmarshal are functions to encode and decode the wire format.
|
||||||
|
|
||||||
|
When the .proto file specifies `syntax="proto3"`, there are some differences:
|
||||||
|
|
||||||
|
- Non-repeated fields of non-message type are values instead of pointers.
|
||||||
|
- Enum types do not get an Enum method.
|
||||||
|
|
||||||
|
The simplest way to describe this is to see an example.
|
||||||
|
Given file test.proto, containing
|
||||||
|
|
||||||
|
package example;
|
||||||
|
|
||||||
|
enum FOO { X = 17; }
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
required string label = 1;
|
||||||
|
optional int32 type = 2 [default=77];
|
||||||
|
repeated int64 reps = 3;
|
||||||
|
optional group OptionalGroup = 4 {
|
||||||
|
required string RequiredField = 5;
|
||||||
|
}
|
||||||
|
oneof union {
|
||||||
|
int32 number = 6;
|
||||||
|
string name = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The resulting file, test.pb.go, is:
|
||||||
|
|
||||||
|
package example
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
type FOO int32
|
||||||
|
const (
|
||||||
|
FOO_X FOO = 17
|
||||||
|
)
|
||||||
|
var FOO_name = map[int32]string{
|
||||||
|
17: "X",
|
||||||
|
}
|
||||||
|
var FOO_value = map[string]int32{
|
||||||
|
"X": 17,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x FOO) Enum() *FOO {
|
||||||
|
p := new(FOO)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
func (x FOO) String() string {
|
||||||
|
return proto.EnumName(FOO_name, int32(x))
|
||||||
|
}
|
||||||
|
func (x *FOO) UnmarshalJSON(data []byte) error {
|
||||||
|
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = FOO(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
|
||||||
|
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
|
||||||
|
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
|
||||||
|
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
|
||||||
|
// Types that are valid to be assigned to Union:
|
||||||
|
// *Test_Number
|
||||||
|
// *Test_Name
|
||||||
|
Union isTest_Union `protobuf_oneof:"union"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
func (m *Test) Reset() { *m = Test{} }
|
||||||
|
func (m *Test) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Test) ProtoMessage() {}
|
||||||
|
|
||||||
|
type isTest_Union interface {
|
||||||
|
isTest_Union()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test_Number struct {
|
||||||
|
Number int32 `protobuf:"varint,6,opt,name=number"`
|
||||||
|
}
|
||||||
|
type Test_Name struct {
|
||||||
|
Name string `protobuf:"bytes,7,opt,name=name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Test_Number) isTest_Union() {}
|
||||||
|
func (*Test_Name) isTest_Union() {}
|
||||||
|
|
||||||
|
func (m *Test) GetUnion() isTest_Union {
|
||||||
|
if m != nil {
|
||||||
|
return m.Union
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
const Default_Test_Type int32 = 77
|
||||||
|
|
||||||
|
func (m *Test) GetLabel() string {
|
||||||
|
if m != nil && m.Label != nil {
|
||||||
|
return *m.Label
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Test) GetType() int32 {
|
||||||
|
if m != nil && m.Type != nil {
|
||||||
|
return *m.Type
|
||||||
|
}
|
||||||
|
return Default_Test_Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
|
||||||
|
if m != nil {
|
||||||
|
return m.Optionalgroup
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test_OptionalGroup struct {
|
||||||
|
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
|
||||||
|
}
|
||||||
|
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
|
||||||
|
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
|
||||||
|
|
||||||
|
func (m *Test_OptionalGroup) GetRequiredField() string {
|
||||||
|
if m != nil && m.RequiredField != nil {
|
||||||
|
return *m.RequiredField
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Test) GetNumber() int32 {
|
||||||
|
if x, ok := m.GetUnion().(*Test_Number); ok {
|
||||||
|
return x.Number
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Test) GetName() string {
|
||||||
|
if x, ok := m.GetUnion().(*Test_Name); ok {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
To create and play with a Test object:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
pb "./example.pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
test := &pb.Test{
|
||||||
|
Label: proto.String("hello"),
|
||||||
|
Type: proto.Int32(17),
|
||||||
|
Reps: []int64{1, 2, 3},
|
||||||
|
Optionalgroup: &pb.Test_OptionalGroup{
|
||||||
|
RequiredField: proto.String("good bye"),
|
||||||
|
},
|
||||||
|
Union: &pb.Test_Name{"fred"},
|
||||||
|
}
|
||||||
|
data, err := proto.Marshal(test)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("marshaling error: ", err)
|
||||||
|
}
|
||||||
|
newTest := &pb.Test{}
|
||||||
|
err = proto.Unmarshal(data, newTest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unmarshaling error: ", err)
|
||||||
|
}
|
||||||
|
// Now test and newTest contain the same data.
|
||||||
|
if test.GetLabel() != newTest.GetLabel() {
|
||||||
|
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
|
||||||
|
}
|
||||||
|
// Use a type switch to determine which oneof was set.
|
||||||
|
switch u := test.Union.(type) {
|
||||||
|
case *pb.Test_Number: // u.Number contains the number.
|
||||||
|
case *pb.Test_Name: // u.Name contains the string.
|
||||||
|
}
|
||||||
|
// etc.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal.
|
||||||
|
// Marshal reports this when a required field is not initialized.
|
||||||
|
// Unmarshal reports this when a required field is missing from the wire data.
|
||||||
|
type RequiredNotSetError struct{ field string }
|
||||||
|
|
||||||
|
func (e *RequiredNotSetError) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return fmt.Sprintf("proto: required field not set")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: required field %q not set", e.field)
|
||||||
|
}
|
||||||
|
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidUTF8Error struct{ field string }
|
||||||
|
|
||||||
|
func (e *invalidUTF8Error) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return "proto: invalid UTF-8 detected"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field)
|
||||||
|
}
|
||||||
|
func (e *invalidUTF8Error) InvalidUTF8() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8.
|
||||||
|
// This error should not be exposed to the external API as such errors should
|
||||||
|
// be recreated with the field information.
|
||||||
|
var errInvalidUTF8 = &invalidUTF8Error{}
|
||||||
|
|
||||||
|
// isNonFatal reports whether the error is either a RequiredNotSet error
|
||||||
|
// or a InvalidUTF8 error.
|
||||||
|
func isNonFatal(err error) bool {
|
||||||
|
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type nonFatal struct{ E error }
|
||||||
|
|
||||||
|
// Merge merges err into nf and reports whether it was successful.
|
||||||
|
// Otherwise it returns false for any fatal non-nil errors.
|
||||||
|
func (nf *nonFatal) Merge(err error) (ok bool) {
|
||||||
|
if err == nil {
|
||||||
|
return true // not an error
|
||||||
|
}
|
||||||
|
if !isNonFatal(err) {
|
||||||
|
return false // fatal error
|
||||||
|
}
|
||||||
|
if nf.E == nil {
|
||||||
|
nf.E = err // store first instance of non-fatal error
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message is implemented by generated protocol buffer messages.
|
||||||
|
type Message interface {
|
||||||
|
Reset()
|
||||||
|
String() string
|
||||||
|
ProtoMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats records allocation details about the protocol buffer encoders
|
||||||
|
// and decoders. Useful for tuning the library itself.
|
||||||
|
type Stats struct {
|
||||||
|
Emalloc uint64 // mallocs in encode
|
||||||
|
Dmalloc uint64 // mallocs in decode
|
||||||
|
Encode uint64 // number of encodes
|
||||||
|
Decode uint64 // number of decodes
|
||||||
|
Chit uint64 // number of cache hits
|
||||||
|
Cmiss uint64 // number of cache misses
|
||||||
|
Size uint64 // number of sizes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set to true to enable stats collection.
|
||||||
|
const collectStats = false
|
||||||
|
|
||||||
|
var stats Stats
|
||||||
|
|
||||||
|
// GetStats returns a copy of the global Stats structure.
|
||||||
|
func GetStats() Stats { return stats }
|
||||||
|
|
||||||
|
// A Buffer is a buffer manager for marshaling and unmarshaling
|
||||||
|
// protocol buffers. It may be reused between invocations to
|
||||||
|
// reduce memory usage. It is not necessary to use a Buffer;
|
||||||
|
// the global functions Marshal and Unmarshal create a
|
||||||
|
// temporary Buffer and are fine for most applications.
|
||||||
|
type Buffer struct {
|
||||||
|
buf []byte // encode/decode byte stream
|
||||||
|
index int // read point
|
||||||
|
|
||||||
|
deterministic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuffer allocates a new Buffer and initializes its internal data to
|
||||||
|
// the contents of the argument slice.
|
||||||
|
func NewBuffer(e []byte) *Buffer {
|
||||||
|
return &Buffer{buf: e}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
|
||||||
|
func (p *Buffer) Reset() {
|
||||||
|
p.buf = p.buf[0:0] // for reading/writing
|
||||||
|
p.index = 0 // for reading
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBuf replaces the internal buffer with the slice,
|
||||||
|
// ready for unmarshaling the contents of the slice.
|
||||||
|
func (p *Buffer) SetBuf(s []byte) {
|
||||||
|
p.buf = s
|
||||||
|
p.index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the contents of the Buffer.
|
||||||
|
func (p *Buffer) Bytes() []byte { return p.buf }
|
||||||
|
|
||||||
|
// SetDeterministic sets whether to use deterministic serialization.
|
||||||
|
//
|
||||||
|
// Deterministic serialization guarantees that for a given binary, equal
|
||||||
|
// messages will always be serialized to the same bytes. This implies:
|
||||||
|
//
|
||||||
|
// - Repeated serialization of a message will return the same bytes.
|
||||||
|
// - Different processes of the same binary (which may be executing on
|
||||||
|
// different machines) will serialize equal messages to the same bytes.
|
||||||
|
//
|
||||||
|
// Note that the deterministic serialization is NOT canonical across
|
||||||
|
// languages. It is not guaranteed to remain stable over time. It is unstable
|
||||||
|
// across different builds with schema changes due to unknown fields.
|
||||||
|
// Users who need canonical serialization (e.g., persistent storage in a
|
||||||
|
// canonical form, fingerprinting, etc.) should define their own
|
||||||
|
// canonicalization specification and implement their own serializer rather
|
||||||
|
// than relying on this API.
|
||||||
|
//
|
||||||
|
// If deterministic serialization is requested, map entries will be sorted
|
||||||
|
// by keys in lexographical order. This is an implementation detail and
|
||||||
|
// subject to change.
|
||||||
|
func (p *Buffer) SetDeterministic(deterministic bool) {
|
||||||
|
p.deterministic = deterministic
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Bool is a helper routine that allocates a new bool value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Bool(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 is a helper routine that allocates a new int32 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Int32(v int32) *int32 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is a helper routine that allocates a new int32 value
|
||||||
|
// to store v and returns a pointer to it, but unlike Int32
|
||||||
|
// its argument value is an int.
|
||||||
|
func Int(v int) *int32 {
|
||||||
|
p := new(int32)
|
||||||
|
*p = int32(v)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 is a helper routine that allocates a new int64 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Int64(v int64) *int64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 is a helper routine that allocates a new float32 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Float32(v float32) *float32 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 is a helper routine that allocates a new float64 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Float64(v float64) *float64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 is a helper routine that allocates a new uint32 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Uint32(v uint32) *uint32 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 is a helper routine that allocates a new uint64 value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Uint64(v uint64) *uint64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a helper routine that allocates a new string value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func String(v string) *string {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumName is a helper function to simplify printing protocol buffer enums
|
||||||
|
// by name. Given an enum map and a value, it returns a useful string.
|
||||||
|
func EnumName(m map[int32]string, v int32) string {
|
||||||
|
s, ok := m[v]
|
||||||
|
if ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
|
||||||
|
// from their JSON-encoded representation. Given a map from the enum's symbolic
|
||||||
|
// names to its int values, and a byte buffer containing the JSON-encoded
|
||||||
|
// value, it returns an int32 that can be cast to the enum type by the caller.
|
||||||
|
//
|
||||||
|
// The function can deal with both JSON representations, numeric and symbolic.
|
||||||
|
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
||||||
|
if data[0] == '"' {
|
||||||
|
// New style: enums are strings.
|
||||||
|
var repr string
|
||||||
|
if err := json.Unmarshal(data, &repr); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
val, ok := m[repr]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
// Old style: enums are ints.
|
||||||
|
var val int32
|
||||||
|
if err := json.Unmarshal(data, &val); err != nil {
|
||||||
|
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugPrint dumps the encoded data in b in a debugging format with a header
|
||||||
|
// including the string s. Used in testing but made available for general debugging.
|
||||||
|
func (p *Buffer) DebugPrint(s string, b []byte) {
|
||||||
|
var u uint64
|
||||||
|
|
||||||
|
obuf := p.buf
|
||||||
|
index := p.index
|
||||||
|
p.buf = b
|
||||||
|
p.index = 0
|
||||||
|
depth := 0
|
||||||
|
|
||||||
|
fmt.Printf("\n--- %s ---\n", s)
|
||||||
|
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
for i := 0; i < depth; i++ {
|
||||||
|
fmt.Print(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
index := p.index
|
||||||
|
if index == len(p.buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := p.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%3d: fetching op err %v\n", index, err)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
tag := op >> 3
|
||||||
|
wire := op & 7
|
||||||
|
|
||||||
|
switch wire {
|
||||||
|
default:
|
||||||
|
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
|
||||||
|
index, tag, wire)
|
||||||
|
break out
|
||||||
|
|
||||||
|
case WireBytes:
|
||||||
|
var r []byte
|
||||||
|
|
||||||
|
r, err = p.DecodeRawBytes(false)
|
||||||
|
if err != nil {
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
|
||||||
|
if len(r) <= 6 {
|
||||||
|
for i := 0; i < len(r); i++ {
|
||||||
|
fmt.Printf(" %.2x", r[i])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
fmt.Printf(" %.2x", r[i])
|
||||||
|
}
|
||||||
|
fmt.Printf(" ..")
|
||||||
|
for i := len(r) - 3; i < len(r); i++ {
|
||||||
|
fmt.Printf(" %.2x", r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
case WireFixed32:
|
||||||
|
u, err = p.DecodeFixed32()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
|
||||||
|
|
||||||
|
case WireFixed64:
|
||||||
|
u, err = p.DecodeFixed64()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
|
||||||
|
|
||||||
|
case WireVarint:
|
||||||
|
u, err = p.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
|
||||||
|
|
||||||
|
case WireStartGroup:
|
||||||
|
fmt.Printf("%3d: t=%3d start\n", index, tag)
|
||||||
|
depth++
|
||||||
|
|
||||||
|
case WireEndGroup:
|
||||||
|
depth--
|
||||||
|
fmt.Printf("%3d: t=%3d end\n", index, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if depth != 0 {
|
||||||
|
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
p.buf = obuf
|
||||||
|
p.index = index
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults sets unset protocol buffer fields to their default values.
|
||||||
|
// It only modifies fields that are both unset and have defined defaults.
|
||||||
|
// It recursively sets default values in any non-nil sub-messages.
|
||||||
|
func SetDefaults(pb Message) {
|
||||||
|
setDefaults(reflect.ValueOf(pb), true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v is a pointer to a struct.
|
||||||
|
func setDefaults(v reflect.Value, recur, zeros bool) {
|
||||||
|
v = v.Elem()
|
||||||
|
|
||||||
|
defaultMu.RLock()
|
||||||
|
dm, ok := defaults[v.Type()]
|
||||||
|
defaultMu.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
dm = buildDefaultMessage(v.Type())
|
||||||
|
defaultMu.Lock()
|
||||||
|
defaults[v.Type()] = dm
|
||||||
|
defaultMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sf := range dm.scalars {
|
||||||
|
f := v.Field(sf.index)
|
||||||
|
if !f.IsNil() {
|
||||||
|
// field already set
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dv := sf.value
|
||||||
|
if dv == nil && !zeros {
|
||||||
|
// no explicit default, and don't want to set zeros
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fptr := f.Addr().Interface() // **T
|
||||||
|
// TODO: Consider batching the allocations we do here.
|
||||||
|
switch sf.kind {
|
||||||
|
case reflect.Bool:
|
||||||
|
b := new(bool)
|
||||||
|
if dv != nil {
|
||||||
|
*b = dv.(bool)
|
||||||
|
}
|
||||||
|
*(fptr.(**bool)) = b
|
||||||
|
case reflect.Float32:
|
||||||
|
f := new(float32)
|
||||||
|
if dv != nil {
|
||||||
|
*f = dv.(float32)
|
||||||
|
}
|
||||||
|
*(fptr.(**float32)) = f
|
||||||
|
case reflect.Float64:
|
||||||
|
f := new(float64)
|
||||||
|
if dv != nil {
|
||||||
|
*f = dv.(float64)
|
||||||
|
}
|
||||||
|
*(fptr.(**float64)) = f
|
||||||
|
case reflect.Int32:
|
||||||
|
// might be an enum
|
||||||
|
if ft := f.Type(); ft != int32PtrType {
|
||||||
|
// enum
|
||||||
|
f.Set(reflect.New(ft.Elem()))
|
||||||
|
if dv != nil {
|
||||||
|
f.Elem().SetInt(int64(dv.(int32)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// int32 field
|
||||||
|
i := new(int32)
|
||||||
|
if dv != nil {
|
||||||
|
*i = dv.(int32)
|
||||||
|
}
|
||||||
|
*(fptr.(**int32)) = i
|
||||||
|
}
|
||||||
|
case reflect.Int64:
|
||||||
|
i := new(int64)
|
||||||
|
if dv != nil {
|
||||||
|
*i = dv.(int64)
|
||||||
|
}
|
||||||
|
*(fptr.(**int64)) = i
|
||||||
|
case reflect.String:
|
||||||
|
s := new(string)
|
||||||
|
if dv != nil {
|
||||||
|
*s = dv.(string)
|
||||||
|
}
|
||||||
|
*(fptr.(**string)) = s
|
||||||
|
case reflect.Uint8:
|
||||||
|
// exceptional case: []byte
|
||||||
|
var b []byte
|
||||||
|
if dv != nil {
|
||||||
|
db := dv.([]byte)
|
||||||
|
b = make([]byte, len(db))
|
||||||
|
copy(b, db)
|
||||||
|
} else {
|
||||||
|
b = []byte{}
|
||||||
|
}
|
||||||
|
*(fptr.(*[]byte)) = b
|
||||||
|
case reflect.Uint32:
|
||||||
|
u := new(uint32)
|
||||||
|
if dv != nil {
|
||||||
|
*u = dv.(uint32)
|
||||||
|
}
|
||||||
|
*(fptr.(**uint32)) = u
|
||||||
|
case reflect.Uint64:
|
||||||
|
u := new(uint64)
|
||||||
|
if dv != nil {
|
||||||
|
*u = dv.(uint64)
|
||||||
|
}
|
||||||
|
*(fptr.(**uint64)) = u
|
||||||
|
default:
|
||||||
|
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ni := range dm.nested {
|
||||||
|
f := v.Field(ni)
|
||||||
|
// f is *T or []*T or map[T]*T
|
||||||
|
switch f.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if f.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setDefaults(f, recur, zeros)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
for i := 0; i < f.Len(); i++ {
|
||||||
|
e := f.Index(i)
|
||||||
|
if e.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setDefaults(e, recur, zeros)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
for _, k := range f.MapKeys() {
|
||||||
|
e := f.MapIndex(k)
|
||||||
|
if e.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setDefaults(e, recur, zeros)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaults maps a protocol buffer struct type to a slice of the fields,
|
||||||
|
// with its scalar fields set to their proto-declared non-zero default values.
|
||||||
|
defaultMu sync.RWMutex
|
||||||
|
defaults = make(map[reflect.Type]defaultMessage)
|
||||||
|
|
||||||
|
int32PtrType = reflect.TypeOf((*int32)(nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultMessage represents information about the default values of a message.
|
||||||
|
type defaultMessage struct {
|
||||||
|
scalars []scalarField
|
||||||
|
nested []int // struct field index of nested messages
|
||||||
|
}
|
||||||
|
|
||||||
|
type scalarField struct {
|
||||||
|
index int // struct field index
|
||||||
|
kind reflect.Kind // element type (the T in *T or []T)
|
||||||
|
value interface{} // the proto-declared default value, or nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// t is a struct type.
|
||||||
|
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
|
||||||
|
sprop := GetProperties(t)
|
||||||
|
for _, prop := range sprop.Prop {
|
||||||
|
fi, ok := sprop.decoderTags.get(prop.Tag)
|
||||||
|
if !ok {
|
||||||
|
// XXX_unrecognized
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ft := t.Field(fi).Type
|
||||||
|
|
||||||
|
sf, nested, err := fieldDefault(ft, prop)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
log.Print(err)
|
||||||
|
case nested:
|
||||||
|
dm.nested = append(dm.nested, fi)
|
||||||
|
case sf != nil:
|
||||||
|
sf.index = fi
|
||||||
|
dm.scalars = append(dm.scalars, *sf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dm
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldDefault returns the scalarField for field type ft.
|
||||||
|
// sf will be nil if the field can not have a default.
|
||||||
|
// nestedMessage will be true if this is a nested message.
|
||||||
|
// Note that sf.index is not set on return.
|
||||||
|
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
|
||||||
|
var canHaveDefault bool
|
||||||
|
switch ft.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if ft.Elem().Kind() == reflect.Struct {
|
||||||
|
nestedMessage = true
|
||||||
|
} else {
|
||||||
|
canHaveDefault = true // proto2 scalar field
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
switch ft.Elem().Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
nestedMessage = true // repeated message
|
||||||
|
case reflect.Uint8:
|
||||||
|
canHaveDefault = true // bytes field
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
if ft.Elem().Kind() == reflect.Ptr {
|
||||||
|
nestedMessage = true // map with message values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canHaveDefault {
|
||||||
|
if nestedMessage {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now know that ft is a pointer or slice.
|
||||||
|
sf = &scalarField{kind: ft.Elem().Kind()}
|
||||||
|
|
||||||
|
// scalar fields without defaults
|
||||||
|
if !prop.HasDefault {
|
||||||
|
return sf, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// a scalar field: either *T or []byte
|
||||||
|
switch ft.Elem().Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
x, err := strconv.ParseBool(prop.Default)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = x
|
||||||
|
case reflect.Float32:
|
||||||
|
x, err := strconv.ParseFloat(prop.Default, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = float32(x)
|
||||||
|
case reflect.Float64:
|
||||||
|
x, err := strconv.ParseFloat(prop.Default, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = x
|
||||||
|
case reflect.Int32:
|
||||||
|
x, err := strconv.ParseInt(prop.Default, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = int32(x)
|
||||||
|
case reflect.Int64:
|
||||||
|
x, err := strconv.ParseInt(prop.Default, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = x
|
||||||
|
case reflect.String:
|
||||||
|
sf.value = prop.Default
|
||||||
|
case reflect.Uint8:
|
||||||
|
// []byte (not *uint8)
|
||||||
|
sf.value = []byte(prop.Default)
|
||||||
|
case reflect.Uint32:
|
||||||
|
x, err := strconv.ParseUint(prop.Default, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = uint32(x)
|
||||||
|
case reflect.Uint64:
|
||||||
|
x, err := strconv.ParseUint(prop.Default, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
|
||||||
|
}
|
||||||
|
sf.value = x
|
||||||
|
default:
|
||||||
|
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
return sf, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapKeys returns a sort.Interface to be used for sorting the map keys.
|
||||||
|
// Map fields may have key types of non-float scalars, strings and enums.
|
||||||
|
func mapKeys(vs []reflect.Value) sort.Interface {
|
||||||
|
s := mapKeySorter{vs: vs}
|
||||||
|
|
||||||
|
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps.
|
||||||
|
if len(vs) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
switch vs[0].Kind() {
|
||||||
|
case reflect.Int32, reflect.Int64:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||||
|
case reflect.Uint32, reflect.Uint64:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||||
|
case reflect.Bool:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true
|
||||||
|
case reflect.String:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapKeySorter struct {
|
||||||
|
vs []reflect.Value
|
||||||
|
less func(a, b reflect.Value) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s mapKeySorter) Len() int { return len(s.vs) }
|
||||||
|
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
||||||
|
func (s mapKeySorter) Less(i, j int) bool {
|
||||||
|
return s.less(s.vs[i], s.vs[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// isProto3Zero reports whether v is a zero proto3 value.
|
||||||
|
func isProto3Zero(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint32, reflect.Uint64:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.String:
|
||||||
|
return v.String() == ""
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
||||||
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
|
const ProtoPackageIsVersion2 = true
|
||||||
|
|
||||||
|
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||||
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
|
const ProtoPackageIsVersion1 = true
|
||||||
|
|
||||||
|
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
||||||
|
// This type is not intended to be used by non-generated code.
|
||||||
|
// This type is not subject to any compatibility guarantee.
|
||||||
|
type InternalMessageInfo struct {
|
||||||
|
marshal *marshalInfo
|
||||||
|
unmarshal *unmarshalInfo
|
||||||
|
merge *mergeInfo
|
||||||
|
discard *discardInfo
|
||||||
|
}
|
314
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
Normal file
314
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support for message sets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
||||||
|
// A message type ID is required for storing a protocol buffer in a message set.
|
||||||
|
var errNoMessageTypeID = errors.New("proto does not have a message type ID")
|
||||||
|
|
||||||
|
// The first two types (_MessageSet_Item and messageSet)
|
||||||
|
// model what the protocol compiler produces for the following protocol message:
|
||||||
|
// message MessageSet {
|
||||||
|
// repeated group Item = 1 {
|
||||||
|
// required int32 type_id = 2;
|
||||||
|
// required string message = 3;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// That is the MessageSet wire format. We can't use a proto to generate these
|
||||||
|
// because that would introduce a circular dependency between it and this package.
|
||||||
|
|
||||||
|
type _MessageSet_Item struct {
|
||||||
|
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
|
||||||
|
Message []byte `protobuf:"bytes,3,req,name=message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type messageSet struct {
|
||||||
|
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
|
||||||
|
XXX_unrecognized []byte
|
||||||
|
// TODO: caching?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure messageSet is a Message.
|
||||||
|
var _ Message = (*messageSet)(nil)
|
||||||
|
|
||||||
|
// messageTypeIder is an interface satisfied by a protocol buffer type
|
||||||
|
// that may be stored in a MessageSet.
|
||||||
|
type messageTypeIder interface {
|
||||||
|
MessageTypeId() int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *messageSet) find(pb Message) *_MessageSet_Item {
|
||||||
|
mti, ok := pb.(messageTypeIder)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
id := mti.MessageTypeId()
|
||||||
|
for _, item := range ms.Item {
|
||||||
|
if *item.TypeId == id {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *messageSet) Has(pb Message) bool {
|
||||||
|
return ms.find(pb) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *messageSet) Unmarshal(pb Message) error {
|
||||||
|
if item := ms.find(pb); item != nil {
|
||||||
|
return Unmarshal(item.Message, pb)
|
||||||
|
}
|
||||||
|
if _, ok := pb.(messageTypeIder); !ok {
|
||||||
|
return errNoMessageTypeID
|
||||||
|
}
|
||||||
|
return nil // TODO: return error instead?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *messageSet) Marshal(pb Message) error {
|
||||||
|
msg, err := Marshal(pb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if item := ms.find(pb); item != nil {
|
||||||
|
// reuse existing item
|
||||||
|
item.Message = msg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mti, ok := pb.(messageTypeIder)
|
||||||
|
if !ok {
|
||||||
|
return errNoMessageTypeID
|
||||||
|
}
|
||||||
|
|
||||||
|
mtid := mti.MessageTypeId()
|
||||||
|
ms.Item = append(ms.Item, &_MessageSet_Item{
|
||||||
|
TypeId: &mtid,
|
||||||
|
Message: msg,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *messageSet) Reset() { *ms = messageSet{} }
|
||||||
|
func (ms *messageSet) String() string { return CompactTextString(ms) }
|
||||||
|
func (*messageSet) ProtoMessage() {}
|
||||||
|
|
||||||
|
// Support for the message_set_wire_format message option.
|
||||||
|
|
||||||
|
func skipVarint(buf []byte) []byte {
|
||||||
|
i := 0
|
||||||
|
for ; buf[i]&0x80 != 0; i++ {
|
||||||
|
}
|
||||||
|
return buf[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
||||||
|
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
|
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
||||||
|
return marshalMessageSet(exts, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
|
||||||
|
func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
|
||||||
|
switch exts := exts.(type) {
|
||||||
|
case *XXX_InternalExtensions:
|
||||||
|
var u marshalInfo
|
||||||
|
siz := u.sizeMessageSet(exts)
|
||||||
|
b := make([]byte, 0, siz)
|
||||||
|
return u.appendMessageSet(b, exts, deterministic)
|
||||||
|
|
||||||
|
case map[int32]Extension:
|
||||||
|
// This is an old-style extension map.
|
||||||
|
// Wrap it in a new-style XXX_InternalExtensions.
|
||||||
|
ie := XXX_InternalExtensions{
|
||||||
|
p: &struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
extensionMap map[int32]Extension
|
||||||
|
}{
|
||||||
|
extensionMap: exts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var u marshalInfo
|
||||||
|
siz := u.sizeMessageSet(&ie)
|
||||||
|
b := make([]byte, 0, siz)
|
||||||
|
return u.appendMessageSet(b, &ie, deterministic)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("proto: not an extension map")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||||
|
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
|
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||||
|
var m map[int32]Extension
|
||||||
|
switch exts := exts.(type) {
|
||||||
|
case *XXX_InternalExtensions:
|
||||||
|
m = exts.extensionsWrite()
|
||||||
|
case map[int32]Extension:
|
||||||
|
m = exts
|
||||||
|
default:
|
||||||
|
return errors.New("proto: not an extension map")
|
||||||
|
}
|
||||||
|
|
||||||
|
ms := new(messageSet)
|
||||||
|
if err := Unmarshal(buf, ms); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, item := range ms.Item {
|
||||||
|
id := *item.TypeId
|
||||||
|
msg := item.Message
|
||||||
|
|
||||||
|
// Restore wire type and field number varint, plus length varint.
|
||||||
|
// Be careful to preserve duplicate items.
|
||||||
|
b := EncodeVarint(uint64(id)<<3 | WireBytes)
|
||||||
|
if ext, ok := m[id]; ok {
|
||||||
|
// Existing data; rip off the tag and length varint
|
||||||
|
// so we join the new data correctly.
|
||||||
|
// We can assume that ext.enc is set because we are unmarshaling.
|
||||||
|
o := ext.enc[len(b):] // skip wire type and field number
|
||||||
|
_, n := DecodeVarint(o) // calculate length of length varint
|
||||||
|
o = o[n:] // skip length varint
|
||||||
|
msg = append(o, msg...) // join old data and new data
|
||||||
|
}
|
||||||
|
b = append(b, EncodeVarint(uint64(len(msg)))...)
|
||||||
|
b = append(b, msg...)
|
||||||
|
|
||||||
|
m[id] = Extension{enc: b}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
||||||
|
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
|
func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
||||||
|
var m map[int32]Extension
|
||||||
|
switch exts := exts.(type) {
|
||||||
|
case *XXX_InternalExtensions:
|
||||||
|
var mu sync.Locker
|
||||||
|
m, mu = exts.extensionsRead()
|
||||||
|
if m != nil {
|
||||||
|
// Keep the extensions map locked until we're done marshaling to prevent
|
||||||
|
// races between marshaling and unmarshaling the lazily-{en,de}coded
|
||||||
|
// values.
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
}
|
||||||
|
case map[int32]Extension:
|
||||||
|
m = exts
|
||||||
|
default:
|
||||||
|
return nil, errors.New("proto: not an extension map")
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteByte('{')
|
||||||
|
|
||||||
|
// Process the map in key order for deterministic output.
|
||||||
|
ids := make([]int32, 0, len(m))
|
||||||
|
for id := range m {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
||||||
|
|
||||||
|
for i, id := range ids {
|
||||||
|
ext := m[id]
|
||||||
|
msd, ok := messageSetMap[id]
|
||||||
|
if !ok {
|
||||||
|
// Unknown type; we can't render it, so skip it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 0 && b.Len() > 1 {
|
||||||
|
b.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
||||||
|
|
||||||
|
x := ext.value
|
||||||
|
if x == nil {
|
||||||
|
x = reflect.New(msd.t.Elem()).Interface()
|
||||||
|
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d, err := json.Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.Write(d)
|
||||||
|
}
|
||||||
|
b.WriteByte('}')
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
||||||
|
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
|
func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
|
||||||
|
// Common-case fast path.
|
||||||
|
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is fairly tricky, and it's not clear that it is needed.
|
||||||
|
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A global registry of types that can be used in a MessageSet.
|
||||||
|
|
||||||
|
var messageSetMap = make(map[int32]messageSetDesc)
|
||||||
|
|
||||||
|
type messageSetDesc struct {
|
||||||
|
t reflect.Type // pointer to struct
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterMessageSetType is called from the generated code.
|
||||||
|
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
||||||
|
messageSetMap[fieldNum] = messageSetDesc{
|
||||||
|
t: reflect.TypeOf(m),
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
357
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
Normal file
357
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build purego appengine js
|
||||||
|
|
||||||
|
// This file contains an implementation of proto field accesses using package reflect.
|
||||||
|
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
||||||
|
// be used on App Engine.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const unsafeAllowed = false
|
||||||
|
|
||||||
|
// A field identifies a field in a struct, accessible from a pointer.
|
||||||
|
// In this implementation, a field is identified by the sequence of field indices
|
||||||
|
// passed to reflect's FieldByIndex.
|
||||||
|
type field []int
|
||||||
|
|
||||||
|
// toField returns a field equivalent to the given reflect field.
|
||||||
|
func toField(f *reflect.StructField) field {
|
||||||
|
return f.Index
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalidField is an invalid field identifier.
|
||||||
|
var invalidField = field(nil)
|
||||||
|
|
||||||
|
// zeroField is a noop when calling pointer.offset.
|
||||||
|
var zeroField = field([]int{})
|
||||||
|
|
||||||
|
// IsValid reports whether the field identifier is valid.
|
||||||
|
func (f field) IsValid() bool { return f != nil }
|
||||||
|
|
||||||
|
// The pointer type is for the table-driven decoder.
|
||||||
|
// The implementation here uses a reflect.Value of pointer type to
|
||||||
|
// create a generic pointer. In pointer_unsafe.go we use unsafe
|
||||||
|
// instead of reflect to implement the same (but faster) interface.
|
||||||
|
type pointer struct {
|
||||||
|
v reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// toPointer converts an interface of pointer type to a pointer
|
||||||
|
// that points to the same target.
|
||||||
|
func toPointer(i *Message) pointer {
|
||||||
|
return pointer{v: reflect.ValueOf(*i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
|
// the interface data.
|
||||||
|
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||||
|
v := reflect.ValueOf(*i)
|
||||||
|
u := reflect.New(v.Type())
|
||||||
|
u.Elem().Set(v)
|
||||||
|
return pointer{v: u}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||||
|
func valToPointer(v reflect.Value) pointer {
|
||||||
|
return pointer{v: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset converts from a pointer to a structure to a pointer to
|
||||||
|
// one of its fields.
|
||||||
|
func (p pointer) offset(f field) pointer {
|
||||||
|
return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) isNil() bool {
|
||||||
|
return p.v.IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// grow updates the slice s in place to make it one element longer.
|
||||||
|
// s must be addressable.
|
||||||
|
// Returns the (addressable) new element.
|
||||||
|
func grow(s reflect.Value) reflect.Value {
|
||||||
|
n, m := s.Len(), s.Cap()
|
||||||
|
if n < m {
|
||||||
|
s.SetLen(n + 1)
|
||||||
|
} else {
|
||||||
|
s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem())))
|
||||||
|
}
|
||||||
|
return s.Index(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) toInt64() *int64 {
|
||||||
|
return p.v.Interface().(*int64)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Ptr() **int64 {
|
||||||
|
return p.v.Interface().(**int64)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Slice() *[]int64 {
|
||||||
|
return p.v.Interface().(*[]int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var int32ptr = reflect.TypeOf((*int32)(nil))
|
||||||
|
|
||||||
|
func (p pointer) toInt32() *int32 {
|
||||||
|
return p.v.Convert(int32ptr).Interface().(*int32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The toInt32Ptr/Slice methods don't work because of enums.
|
||||||
|
// Instead, we must use set/get methods for the int32ptr/slice case.
|
||||||
|
/*
|
||||||
|
func (p pointer) toInt32Ptr() **int32 {
|
||||||
|
return p.v.Interface().(**int32)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32Slice() *[]int32 {
|
||||||
|
return p.v.Interface().(*[]int32)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p pointer) getInt32Ptr() *int32 {
|
||||||
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
|
// raw int32 type
|
||||||
|
return p.v.Elem().Interface().(*int32)
|
||||||
|
}
|
||||||
|
// an enum
|
||||||
|
return p.v.Elem().Convert(int32PtrType).Interface().(*int32)
|
||||||
|
}
|
||||||
|
func (p pointer) setInt32Ptr(v int32) {
|
||||||
|
// Allocate value in a *int32. Possibly convert that to a *enum.
|
||||||
|
// Then assign it to a **int32 or **enum.
|
||||||
|
// Note: we can convert *int32 to *enum, but we can't convert
|
||||||
|
// **int32 to **enum!
|
||||||
|
p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInt32Slice copies []int32 from p as a new slice.
|
||||||
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
|
func (p pointer) getInt32Slice() []int32 {
|
||||||
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
|
// raw int32 type
|
||||||
|
return p.v.Elem().Interface().([]int32)
|
||||||
|
}
|
||||||
|
// an enum
|
||||||
|
// Allocate a []int32, then assign []enum's values into it.
|
||||||
|
// Note: we can't convert []enum to []int32.
|
||||||
|
slice := p.v.Elem()
|
||||||
|
s := make([]int32, slice.Len())
|
||||||
|
for i := 0; i < slice.Len(); i++ {
|
||||||
|
s[i] = int32(slice.Index(i).Int())
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// setInt32Slice copies []int32 into p as a new slice.
|
||||||
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
|
func (p pointer) setInt32Slice(v []int32) {
|
||||||
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
|
// raw int32 type
|
||||||
|
p.v.Elem().Set(reflect.ValueOf(v))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// an enum
|
||||||
|
// Allocate a []enum, then assign []int32's values into it.
|
||||||
|
// Note: we can't convert []enum to []int32.
|
||||||
|
slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v))
|
||||||
|
for i, x := range v {
|
||||||
|
slice.Index(i).SetInt(int64(x))
|
||||||
|
}
|
||||||
|
p.v.Elem().Set(slice)
|
||||||
|
}
|
||||||
|
func (p pointer) appendInt32Slice(v int32) {
|
||||||
|
grow(p.v.Elem()).SetInt(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) toUint64() *uint64 {
|
||||||
|
return p.v.Interface().(*uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Ptr() **uint64 {
|
||||||
|
return p.v.Interface().(**uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Slice() *[]uint64 {
|
||||||
|
return p.v.Interface().(*[]uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32() *uint32 {
|
||||||
|
return p.v.Interface().(*uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Ptr() **uint32 {
|
||||||
|
return p.v.Interface().(**uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Slice() *[]uint32 {
|
||||||
|
return p.v.Interface().(*[]uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toBool() *bool {
|
||||||
|
return p.v.Interface().(*bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolPtr() **bool {
|
||||||
|
return p.v.Interface().(**bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolSlice() *[]bool {
|
||||||
|
return p.v.Interface().(*[]bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64() *float64 {
|
||||||
|
return p.v.Interface().(*float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Ptr() **float64 {
|
||||||
|
return p.v.Interface().(**float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Slice() *[]float64 {
|
||||||
|
return p.v.Interface().(*[]float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32() *float32 {
|
||||||
|
return p.v.Interface().(*float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Ptr() **float32 {
|
||||||
|
return p.v.Interface().(**float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Slice() *[]float32 {
|
||||||
|
return p.v.Interface().(*[]float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toString() *string {
|
||||||
|
return p.v.Interface().(*string)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringPtr() **string {
|
||||||
|
return p.v.Interface().(**string)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringSlice() *[]string {
|
||||||
|
return p.v.Interface().(*[]string)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytes() *[]byte {
|
||||||
|
return p.v.Interface().(*[]byte)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytesSlice() *[][]byte {
|
||||||
|
return p.v.Interface().(*[][]byte)
|
||||||
|
}
|
||||||
|
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
||||||
|
return p.v.Interface().(*XXX_InternalExtensions)
|
||||||
|
}
|
||||||
|
func (p pointer) toOldExtensions() *map[int32]Extension {
|
||||||
|
return p.v.Interface().(*map[int32]Extension)
|
||||||
|
}
|
||||||
|
func (p pointer) getPointer() pointer {
|
||||||
|
return pointer{v: p.v.Elem()}
|
||||||
|
}
|
||||||
|
func (p pointer) setPointer(q pointer) {
|
||||||
|
p.v.Elem().Set(q.v)
|
||||||
|
}
|
||||||
|
func (p pointer) appendPointer(q pointer) {
|
||||||
|
grow(p.v.Elem()).Set(q.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPointerSlice copies []*T from p as a new []pointer.
|
||||||
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
|
func (p pointer) getPointerSlice() []pointer {
|
||||||
|
if p.v.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := p.v.Elem().Len()
|
||||||
|
s := make([]pointer, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
s[i] = pointer{v: p.v.Elem().Index(i)}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPointerSlice copies []pointer into p as a new []*T.
|
||||||
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
|
func (p pointer) setPointerSlice(v []pointer) {
|
||||||
|
if v == nil {
|
||||||
|
p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v))
|
||||||
|
for _, p := range v {
|
||||||
|
s = reflect.Append(s, p.v)
|
||||||
|
}
|
||||||
|
p.v.Elem().Set(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInterfacePointer returns a pointer that points to the
|
||||||
|
// interface data of the interface pointed by p.
|
||||||
|
func (p pointer) getInterfacePointer() pointer {
|
||||||
|
if p.v.Elem().IsNil() {
|
||||||
|
return pointer{v: p.v.Elem()}
|
||||||
|
}
|
||||||
|
return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
||||||
|
// TODO: check that p.v.Type().Elem() == t?
|
||||||
|
return p.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
|
||||||
|
var atomicLock sync.Mutex
|
308
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
Normal file
308
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !purego,!appengine,!js
|
||||||
|
|
||||||
|
// This file contains the implementation of the proto field accesses using package unsafe.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const unsafeAllowed = true
|
||||||
|
|
||||||
|
// A field identifies a field in a struct, accessible from a pointer.
|
||||||
|
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
||||||
|
type field uintptr
|
||||||
|
|
||||||
|
// toField returns a field equivalent to the given reflect field.
|
||||||
|
func toField(f *reflect.StructField) field {
|
||||||
|
return field(f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalidField is an invalid field identifier.
|
||||||
|
const invalidField = ^field(0)
|
||||||
|
|
||||||
|
// zeroField is a noop when calling pointer.offset.
|
||||||
|
const zeroField = field(0)
|
||||||
|
|
||||||
|
// IsValid reports whether the field identifier is valid.
|
||||||
|
func (f field) IsValid() bool {
|
||||||
|
return f != invalidField
|
||||||
|
}
|
||||||
|
|
||||||
|
// The pointer type below is for the new table-driven encoder/decoder.
|
||||||
|
// The implementation here uses unsafe.Pointer to create a generic pointer.
|
||||||
|
// In pointer_reflect.go we use reflect instead of unsafe to implement
|
||||||
|
// the same (but slower) interface.
|
||||||
|
type pointer struct {
|
||||||
|
p unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// size of pointer
|
||||||
|
var ptrSize = unsafe.Sizeof(uintptr(0))
|
||||||
|
|
||||||
|
// toPointer converts an interface of pointer type to a pointer
|
||||||
|
// that points to the same target.
|
||||||
|
func toPointer(i *Message) pointer {
|
||||||
|
// Super-tricky - read pointer out of data word of interface value.
|
||||||
|
// Saves ~25ns over the equivalent:
|
||||||
|
// return valToPointer(reflect.ValueOf(*i))
|
||||||
|
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
|
// the interface data.
|
||||||
|
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||||
|
// Super-tricky - read or get the address of data word of interface value.
|
||||||
|
if isptr {
|
||||||
|
// The interface is of pointer type, thus it is a direct interface.
|
||||||
|
// The data word is the pointer data itself. We take its address.
|
||||||
|
return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
||||||
|
}
|
||||||
|
// The interface is not of pointer type. The data word is the pointer
|
||||||
|
// to the data.
|
||||||
|
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||||
|
func valToPointer(v reflect.Value) pointer {
|
||||||
|
return pointer{p: unsafe.Pointer(v.Pointer())}
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset converts from a pointer to a structure to a pointer to
|
||||||
|
// one of its fields.
|
||||||
|
func (p pointer) offset(f field) pointer {
|
||||||
|
// For safety, we should panic if !f.IsValid, however calling panic causes
|
||||||
|
// this to no longer be inlineable, which is a serious performance cost.
|
||||||
|
/*
|
||||||
|
if !f.IsValid() {
|
||||||
|
panic("invalid field")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) isNil() bool {
|
||||||
|
return p.p == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) toInt64() *int64 {
|
||||||
|
return (*int64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Ptr() **int64 {
|
||||||
|
return (**int64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Slice() *[]int64 {
|
||||||
|
return (*[]int64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32() *int32 {
|
||||||
|
return (*int32)(p.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist.
|
||||||
|
/*
|
||||||
|
func (p pointer) toInt32Ptr() **int32 {
|
||||||
|
return (**int32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32Slice() *[]int32 {
|
||||||
|
return (*[]int32)(p.p)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p pointer) getInt32Ptr() *int32 {
|
||||||
|
return *(**int32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) setInt32Ptr(v int32) {
|
||||||
|
*(**int32)(p.p) = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInt32Slice loads a []int32 from p.
|
||||||
|
// The value returned is aliased with the original slice.
|
||||||
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) getInt32Slice() []int32 {
|
||||||
|
return *(*[]int32)(p.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setInt32Slice stores a []int32 to p.
|
||||||
|
// The value set is aliased with the input slice.
|
||||||
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) setInt32Slice(v []int32) {
|
||||||
|
*(*[]int32)(p.p) = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead?
|
||||||
|
func (p pointer) appendInt32Slice(v int32) {
|
||||||
|
s := (*[]int32)(p.p)
|
||||||
|
*s = append(*s, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) toUint64() *uint64 {
|
||||||
|
return (*uint64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Ptr() **uint64 {
|
||||||
|
return (**uint64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Slice() *[]uint64 {
|
||||||
|
return (*[]uint64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32() *uint32 {
|
||||||
|
return (*uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Ptr() **uint32 {
|
||||||
|
return (**uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Slice() *[]uint32 {
|
||||||
|
return (*[]uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBool() *bool {
|
||||||
|
return (*bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolPtr() **bool {
|
||||||
|
return (**bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolSlice() *[]bool {
|
||||||
|
return (*[]bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64() *float64 {
|
||||||
|
return (*float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Ptr() **float64 {
|
||||||
|
return (**float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Slice() *[]float64 {
|
||||||
|
return (*[]float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32() *float32 {
|
||||||
|
return (*float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Ptr() **float32 {
|
||||||
|
return (**float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Slice() *[]float32 {
|
||||||
|
return (*[]float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toString() *string {
|
||||||
|
return (*string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringPtr() **string {
|
||||||
|
return (**string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringSlice() *[]string {
|
||||||
|
return (*[]string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytes() *[]byte {
|
||||||
|
return (*[]byte)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytesSlice() *[][]byte {
|
||||||
|
return (*[][]byte)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
||||||
|
return (*XXX_InternalExtensions)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toOldExtensions() *map[int32]Extension {
|
||||||
|
return (*map[int32]Extension)(p.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPointerSlice loads []*T from p as a []pointer.
|
||||||
|
// The value returned is aliased with the original slice.
|
||||||
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) getPointerSlice() []pointer {
|
||||||
|
// Super-tricky - p should point to a []*T where T is a
|
||||||
|
// message type. We load it as []pointer.
|
||||||
|
return *(*[]pointer)(p.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPointerSlice stores []pointer into p as a []*T.
|
||||||
|
// The value set is aliased with the input slice.
|
||||||
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) setPointerSlice(v []pointer) {
|
||||||
|
// Super-tricky - p should point to a []*T where T is a
|
||||||
|
// message type. We store it as []pointer.
|
||||||
|
*(*[]pointer)(p.p) = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPointer loads the pointer at p and returns it.
|
||||||
|
func (p pointer) getPointer() pointer {
|
||||||
|
return pointer{p: *(*unsafe.Pointer)(p.p)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPointer stores the pointer q at p.
|
||||||
|
func (p pointer) setPointer(q pointer) {
|
||||||
|
*(*unsafe.Pointer)(p.p) = q.p
|
||||||
|
}
|
||||||
|
|
||||||
|
// append q to the slice pointed to by p.
|
||||||
|
func (p pointer) appendPointer(q pointer) {
|
||||||
|
s := (*[]unsafe.Pointer)(p.p)
|
||||||
|
*s = append(*s, q.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInterfacePointer returns a pointer that points to the
|
||||||
|
// interface data of the interface pointed by p.
|
||||||
|
func (p pointer) getInterfacePointer() pointer {
|
||||||
|
// Super-tricky - read pointer out of data word of interface value.
|
||||||
|
return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asPointerTo returns a reflect.Value that is a pointer to an
|
||||||
|
// object of type t stored at p.
|
||||||
|
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
||||||
|
return reflect.NewAt(t, p.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
||||||
|
return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
|
}
|
||||||
|
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
|
}
|
||||||
|
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
||||||
|
return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
|
}
|
||||||
|
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
|
}
|
||||||
|
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
||||||
|
return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
|
}
|
||||||
|
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
|
}
|
||||||
|
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
||||||
|
return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
|
}
|
||||||
|
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
|
}
|
544
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
Normal file
544
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
Normal file
@ -0,0 +1,544 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routines for encoding data into the wire format for protocol buffers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debug bool = false
|
||||||
|
|
||||||
|
// Constants that identify the encoding of a value on the wire.
|
||||||
|
const (
|
||||||
|
WireVarint = 0
|
||||||
|
WireFixed64 = 1
|
||||||
|
WireBytes = 2
|
||||||
|
WireStartGroup = 3
|
||||||
|
WireEndGroup = 4
|
||||||
|
WireFixed32 = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// tagMap is an optimization over map[int]int for typical protocol buffer
|
||||||
|
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
||||||
|
// numbers.
|
||||||
|
type tagMap struct {
|
||||||
|
fastTags []int
|
||||||
|
slowTags map[int]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
||||||
|
// the tagMap slice rather than its map.
|
||||||
|
const tagMapFastLimit = 1024
|
||||||
|
|
||||||
|
func (p *tagMap) get(t int) (int, bool) {
|
||||||
|
if t > 0 && t < tagMapFastLimit {
|
||||||
|
if t >= len(p.fastTags) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
fi := p.fastTags[t]
|
||||||
|
return fi, fi >= 0
|
||||||
|
}
|
||||||
|
fi, ok := p.slowTags[t]
|
||||||
|
return fi, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagMap) put(t int, fi int) {
|
||||||
|
if t > 0 && t < tagMapFastLimit {
|
||||||
|
for len(p.fastTags) < t+1 {
|
||||||
|
p.fastTags = append(p.fastTags, -1)
|
||||||
|
}
|
||||||
|
p.fastTags[t] = fi
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.slowTags == nil {
|
||||||
|
p.slowTags = make(map[int]int)
|
||||||
|
}
|
||||||
|
p.slowTags[t] = fi
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructProperties represents properties for all the fields of a struct.
|
||||||
|
// decoderTags and decoderOrigNames should only be used by the decoder.
|
||||||
|
type StructProperties struct {
|
||||||
|
Prop []*Properties // properties for each field
|
||||||
|
reqCount int // required count
|
||||||
|
decoderTags tagMap // map from proto tag to struct field number
|
||||||
|
decoderOrigNames map[string]int // map from original name to struct field number
|
||||||
|
order []int // list of struct field numbers in tag order
|
||||||
|
|
||||||
|
// OneofTypes contains information about the oneof fields in this message.
|
||||||
|
// It is keyed by the original name of a field.
|
||||||
|
OneofTypes map[string]*OneofProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
// OneofProperties represents information about a specific field in a oneof.
|
||||||
|
type OneofProperties struct {
|
||||||
|
Type reflect.Type // pointer to generated struct type for this oneof field
|
||||||
|
Field int // struct field number of the containing oneof in the message
|
||||||
|
Prop *Properties
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
||||||
|
// See encode.go, (*Buffer).enc_struct.
|
||||||
|
|
||||||
|
func (sp *StructProperties) Len() int { return len(sp.order) }
|
||||||
|
func (sp *StructProperties) Less(i, j int) bool {
|
||||||
|
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
||||||
|
}
|
||||||
|
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
||||||
|
|
||||||
|
// Properties represents the protocol-specific behavior of a single struct field.
|
||||||
|
type Properties struct {
|
||||||
|
Name string // name of the field, for error messages
|
||||||
|
OrigName string // original name before protocol compiler (always set)
|
||||||
|
JSONName string // name to use for JSON; determined by protoc
|
||||||
|
Wire string
|
||||||
|
WireType int
|
||||||
|
Tag int
|
||||||
|
Required bool
|
||||||
|
Optional bool
|
||||||
|
Repeated bool
|
||||||
|
Packed bool // relevant for repeated primitives only
|
||||||
|
Enum string // set for enum types only
|
||||||
|
proto3 bool // whether this is known to be a proto3 field
|
||||||
|
oneof bool // whether this is a oneof field
|
||||||
|
|
||||||
|
Default string // default value
|
||||||
|
HasDefault bool // whether an explicit default was provided
|
||||||
|
|
||||||
|
stype reflect.Type // set for struct types only
|
||||||
|
sprop *StructProperties // set for struct types only
|
||||||
|
|
||||||
|
mtype reflect.Type // set for map types only
|
||||||
|
MapKeyProp *Properties // set for map types only
|
||||||
|
MapValProp *Properties // set for map types only
|
||||||
|
}
|
||||||
|
|
||||||
|
// String formats the properties in the protobuf struct field tag style.
|
||||||
|
func (p *Properties) String() string {
|
||||||
|
s := p.Wire
|
||||||
|
s += ","
|
||||||
|
s += strconv.Itoa(p.Tag)
|
||||||
|
if p.Required {
|
||||||
|
s += ",req"
|
||||||
|
}
|
||||||
|
if p.Optional {
|
||||||
|
s += ",opt"
|
||||||
|
}
|
||||||
|
if p.Repeated {
|
||||||
|
s += ",rep"
|
||||||
|
}
|
||||||
|
if p.Packed {
|
||||||
|
s += ",packed"
|
||||||
|
}
|
||||||
|
s += ",name=" + p.OrigName
|
||||||
|
if p.JSONName != p.OrigName {
|
||||||
|
s += ",json=" + p.JSONName
|
||||||
|
}
|
||||||
|
if p.proto3 {
|
||||||
|
s += ",proto3"
|
||||||
|
}
|
||||||
|
if p.oneof {
|
||||||
|
s += ",oneof"
|
||||||
|
}
|
||||||
|
if len(p.Enum) > 0 {
|
||||||
|
s += ",enum=" + p.Enum
|
||||||
|
}
|
||||||
|
if p.HasDefault {
|
||||||
|
s += ",def=" + p.Default
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
||||||
|
func (p *Properties) Parse(s string) {
|
||||||
|
// "bytes,49,opt,name=foo,def=hello!"
|
||||||
|
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
||||||
|
if len(fields) < 2 {
|
||||||
|
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Wire = fields[0]
|
||||||
|
switch p.Wire {
|
||||||
|
case "varint":
|
||||||
|
p.WireType = WireVarint
|
||||||
|
case "fixed32":
|
||||||
|
p.WireType = WireFixed32
|
||||||
|
case "fixed64":
|
||||||
|
p.WireType = WireFixed64
|
||||||
|
case "zigzag32":
|
||||||
|
p.WireType = WireVarint
|
||||||
|
case "zigzag64":
|
||||||
|
p.WireType = WireVarint
|
||||||
|
case "bytes", "group":
|
||||||
|
p.WireType = WireBytes
|
||||||
|
// no numeric converter for non-numeric types
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
p.Tag, err = strconv.Atoi(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for i := 2; i < len(fields); i++ {
|
||||||
|
f := fields[i]
|
||||||
|
switch {
|
||||||
|
case f == "req":
|
||||||
|
p.Required = true
|
||||||
|
case f == "opt":
|
||||||
|
p.Optional = true
|
||||||
|
case f == "rep":
|
||||||
|
p.Repeated = true
|
||||||
|
case f == "packed":
|
||||||
|
p.Packed = true
|
||||||
|
case strings.HasPrefix(f, "name="):
|
||||||
|
p.OrigName = f[5:]
|
||||||
|
case strings.HasPrefix(f, "json="):
|
||||||
|
p.JSONName = f[5:]
|
||||||
|
case strings.HasPrefix(f, "enum="):
|
||||||
|
p.Enum = f[5:]
|
||||||
|
case f == "proto3":
|
||||||
|
p.proto3 = true
|
||||||
|
case f == "oneof":
|
||||||
|
p.oneof = true
|
||||||
|
case strings.HasPrefix(f, "def="):
|
||||||
|
p.HasDefault = true
|
||||||
|
p.Default = f[4:] // rest of string
|
||||||
|
if i+1 < len(fields) {
|
||||||
|
// Commas aren't escaped, and def is always last.
|
||||||
|
p.Default += "," + strings.Join(fields[i+1:], ",")
|
||||||
|
break outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
||||||
|
|
||||||
|
// setFieldProps initializes the field properties for submessages and maps.
|
||||||
|
func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
||||||
|
switch t1 := typ; t1.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if t1.Elem().Kind() == reflect.Struct {
|
||||||
|
p.stype = t1.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct {
|
||||||
|
p.stype = t2.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
p.mtype = t1
|
||||||
|
p.MapKeyProp = &Properties{}
|
||||||
|
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||||
|
p.MapValProp = &Properties{}
|
||||||
|
vtype := p.mtype.Elem()
|
||||||
|
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||||
|
// The value type is not a message (*T) or bytes ([]byte),
|
||||||
|
// so we need encoders for the pointer to this type.
|
||||||
|
vtype = reflect.PtrTo(vtype)
|
||||||
|
}
|
||||||
|
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.stype != nil {
|
||||||
|
if lockGetProp {
|
||||||
|
p.sprop = GetProperties(p.stype)
|
||||||
|
} else {
|
||||||
|
p.sprop = getPropertiesLocked(p.stype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init populates the properties from a protocol buffer struct tag.
|
||||||
|
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
||||||
|
p.init(typ, name, tag, f, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
||||||
|
// "bytes,49,opt,def=hello!"
|
||||||
|
p.Name = name
|
||||||
|
p.OrigName = name
|
||||||
|
if tag == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Parse(tag)
|
||||||
|
p.setFieldProps(typ, f, lockGetProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
propertiesMu sync.RWMutex
|
||||||
|
propertiesMap = make(map[reflect.Type]*StructProperties)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetProperties returns the list of properties for the type represented by t.
|
||||||
|
// t must represent a generated struct type of a protocol message.
|
||||||
|
func GetProperties(t reflect.Type) *StructProperties {
|
||||||
|
if t.Kind() != reflect.Struct {
|
||||||
|
panic("proto: type must have kind struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most calls to GetProperties in a long-running program will be
|
||||||
|
// retrieving details for types we have seen before.
|
||||||
|
propertiesMu.RLock()
|
||||||
|
sprop, ok := propertiesMap[t]
|
||||||
|
propertiesMu.RUnlock()
|
||||||
|
if ok {
|
||||||
|
if collectStats {
|
||||||
|
stats.Chit++
|
||||||
|
}
|
||||||
|
return sprop
|
||||||
|
}
|
||||||
|
|
||||||
|
propertiesMu.Lock()
|
||||||
|
sprop = getPropertiesLocked(t)
|
||||||
|
propertiesMu.Unlock()
|
||||||
|
return sprop
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPropertiesLocked requires that propertiesMu is held.
|
||||||
|
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||||
|
if prop, ok := propertiesMap[t]; ok {
|
||||||
|
if collectStats {
|
||||||
|
stats.Chit++
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
if collectStats {
|
||||||
|
stats.Cmiss++
|
||||||
|
}
|
||||||
|
|
||||||
|
prop := new(StructProperties)
|
||||||
|
// in case of recursive protos, fill this in now.
|
||||||
|
propertiesMap[t] = prop
|
||||||
|
|
||||||
|
// build properties
|
||||||
|
prop.Prop = make([]*Properties, t.NumField())
|
||||||
|
prop.order = make([]int, t.NumField())
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
p := new(Properties)
|
||||||
|
name := f.Name
|
||||||
|
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
||||||
|
|
||||||
|
oneof := f.Tag.Get("protobuf_oneof") // special case
|
||||||
|
if oneof != "" {
|
||||||
|
// Oneof fields don't use the traditional protobuf tag.
|
||||||
|
p.OrigName = oneof
|
||||||
|
}
|
||||||
|
prop.Prop[i] = p
|
||||||
|
prop.order[i] = i
|
||||||
|
if debug {
|
||||||
|
print(i, " ", f.Name, " ", t.String(), " ")
|
||||||
|
if p.Tag > 0 {
|
||||||
|
print(p.String())
|
||||||
|
}
|
||||||
|
print("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-order prop.order.
|
||||||
|
sort.Sort(prop)
|
||||||
|
|
||||||
|
type oneofMessage interface {
|
||||||
|
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||||
|
}
|
||||||
|
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
||||||
|
var oots []interface{}
|
||||||
|
_, _, _, oots = om.XXX_OneofFuncs()
|
||||||
|
|
||||||
|
// Interpret oneof metadata.
|
||||||
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||||
|
for _, oot := range oots {
|
||||||
|
oop := &OneofProperties{
|
||||||
|
Type: reflect.ValueOf(oot).Type(), // *T
|
||||||
|
Prop: new(Properties),
|
||||||
|
}
|
||||||
|
sft := oop.Type.Elem().Field(0)
|
||||||
|
oop.Prop.Name = sft.Name
|
||||||
|
oop.Prop.Parse(sft.Tag.Get("protobuf"))
|
||||||
|
// There will be exactly one interface field that
|
||||||
|
// this new value is assignable to.
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if f.Type.Kind() != reflect.Interface {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !oop.Type.AssignableTo(f.Type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oop.Field = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
prop.OneofTypes[oop.Prop.OrigName] = oop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build required counts
|
||||||
|
// build tags
|
||||||
|
reqCount := 0
|
||||||
|
prop.decoderOrigNames = make(map[string]int)
|
||||||
|
for i, p := range prop.Prop {
|
||||||
|
if strings.HasPrefix(p.Name, "XXX_") {
|
||||||
|
// Internal fields should not appear in tags/origNames maps.
|
||||||
|
// They are handled specially when encoding and decoding.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.Required {
|
||||||
|
reqCount++
|
||||||
|
}
|
||||||
|
prop.decoderTags.put(p.Tag, i)
|
||||||
|
prop.decoderOrigNames[p.OrigName] = i
|
||||||
|
}
|
||||||
|
prop.reqCount = reqCount
|
||||||
|
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
// A global registry of enum types.
|
||||||
|
// The generated code will register the generated maps by calling RegisterEnum.
|
||||||
|
|
||||||
|
var enumValueMaps = make(map[string]map[string]int32)
|
||||||
|
|
||||||
|
// RegisterEnum is called from the generated code to install the enum descriptor
|
||||||
|
// maps into the global table to aid parsing text format protocol buffers.
|
||||||
|
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
||||||
|
if _, ok := enumValueMaps[typeName]; ok {
|
||||||
|
panic("proto: duplicate enum registered: " + typeName)
|
||||||
|
}
|
||||||
|
enumValueMaps[typeName] = valueMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumValueMap returns the mapping from names to integers of the
|
||||||
|
// enum type enumType, or a nil if not found.
|
||||||
|
func EnumValueMap(enumType string) map[string]int32 {
|
||||||
|
return enumValueMaps[enumType]
|
||||||
|
}
|
||||||
|
|
||||||
|
// A registry of all linked message types.
|
||||||
|
// The string is a fully-qualified proto name ("pkg.Message").
|
||||||
|
var (
|
||||||
|
protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers
|
||||||
|
protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types
|
||||||
|
revProtoTypes = make(map[reflect.Type]string)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterType is called from generated code and maps from the fully qualified
|
||||||
|
// proto name to the type (pointer to struct) of the protocol buffer.
|
||||||
|
func RegisterType(x Message, name string) {
|
||||||
|
if _, ok := protoTypedNils[name]; ok {
|
||||||
|
// TODO: Some day, make this a panic.
|
||||||
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := reflect.TypeOf(x)
|
||||||
|
if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 {
|
||||||
|
// Generated code always calls RegisterType with nil x.
|
||||||
|
// This check is just for extra safety.
|
||||||
|
protoTypedNils[name] = x
|
||||||
|
} else {
|
||||||
|
protoTypedNils[name] = reflect.Zero(t).Interface().(Message)
|
||||||
|
}
|
||||||
|
revProtoTypes[t] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterMapType is called from generated code and maps from the fully qualified
|
||||||
|
// proto name to the native map type of the proto map definition.
|
||||||
|
func RegisterMapType(x interface{}, name string) {
|
||||||
|
if reflect.TypeOf(x).Kind() != reflect.Map {
|
||||||
|
panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name))
|
||||||
|
}
|
||||||
|
if _, ok := protoMapTypes[name]; ok {
|
||||||
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := reflect.TypeOf(x)
|
||||||
|
protoMapTypes[name] = t
|
||||||
|
revProtoTypes[t] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageName returns the fully-qualified proto name for the given message type.
|
||||||
|
func MessageName(x Message) string {
|
||||||
|
type xname interface {
|
||||||
|
XXX_MessageName() string
|
||||||
|
}
|
||||||
|
if m, ok := x.(xname); ok {
|
||||||
|
return m.XXX_MessageName()
|
||||||
|
}
|
||||||
|
return revProtoTypes[reflect.TypeOf(x)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageType returns the message type (pointer to struct) for a named message.
|
||||||
|
// The type is not guaranteed to implement proto.Message if the name refers to a
|
||||||
|
// map entry.
|
||||||
|
func MessageType(name string) reflect.Type {
|
||||||
|
if t, ok := protoTypedNils[name]; ok {
|
||||||
|
return reflect.TypeOf(t)
|
||||||
|
}
|
||||||
|
return protoMapTypes[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// A registry of all linked proto files.
|
||||||
|
var (
|
||||||
|
protoFiles = make(map[string][]byte) // file name => fileDescriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterFile is called from generated code and maps from the
|
||||||
|
// full file name of a .proto file to its compressed FileDescriptorProto.
|
||||||
|
func RegisterFile(filename string, fileDescriptor []byte) {
|
||||||
|
protoFiles[filename] = fileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
|
||||||
|
func FileDescriptor(filename string) []byte { return protoFiles[filename] }
|
2767
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
Normal file
2767
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
654
vendor/github.com/golang/protobuf/proto/table_merge.go
generated
vendored
Normal file
654
vendor/github.com/golang/protobuf/proto/table_merge.go
generated
vendored
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merge merges the src message into dst.
|
||||||
|
// This assumes that dst and src of the same type and are non-nil.
|
||||||
|
func (a *InternalMessageInfo) Merge(dst, src Message) {
|
||||||
|
mi := atomicLoadMergeInfo(&a.merge)
|
||||||
|
if mi == nil {
|
||||||
|
mi = getMergeInfo(reflect.TypeOf(dst).Elem())
|
||||||
|
atomicStoreMergeInfo(&a.merge, mi)
|
||||||
|
}
|
||||||
|
mi.merge(toPointer(&dst), toPointer(&src))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mergeInfo struct {
|
||||||
|
typ reflect.Type
|
||||||
|
|
||||||
|
initialized int32 // 0: only typ is valid, 1: everything is valid
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
fields []mergeFieldInfo
|
||||||
|
unrecognized field // Offset of XXX_unrecognized
|
||||||
|
}
|
||||||
|
|
||||||
|
type mergeFieldInfo struct {
|
||||||
|
field field // Offset of field, guaranteed to be valid
|
||||||
|
|
||||||
|
// isPointer reports whether the value in the field is a pointer.
|
||||||
|
// This is true for the following situations:
|
||||||
|
// * Pointer to struct
|
||||||
|
// * Pointer to basic type (proto2 only)
|
||||||
|
// * Slice (first value in slice header is a pointer)
|
||||||
|
// * String (first value in string header is a pointer)
|
||||||
|
isPointer bool
|
||||||
|
|
||||||
|
// basicWidth reports the width of the field assuming that it is directly
|
||||||
|
// embedded in the struct (as is the case for basic types in proto3).
|
||||||
|
// The possible values are:
|
||||||
|
// 0: invalid
|
||||||
|
// 1: bool
|
||||||
|
// 4: int32, uint32, float32
|
||||||
|
// 8: int64, uint64, float64
|
||||||
|
basicWidth int
|
||||||
|
|
||||||
|
// Where dst and src are pointers to the types being merged.
|
||||||
|
merge func(dst, src pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mergeInfoMap = map[reflect.Type]*mergeInfo{}
|
||||||
|
mergeInfoLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getMergeInfo(t reflect.Type) *mergeInfo {
|
||||||
|
mergeInfoLock.Lock()
|
||||||
|
defer mergeInfoLock.Unlock()
|
||||||
|
mi := mergeInfoMap[t]
|
||||||
|
if mi == nil {
|
||||||
|
mi = &mergeInfo{typ: t}
|
||||||
|
mergeInfoMap[t] = mi
|
||||||
|
}
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge merges src into dst assuming they are both of type *mi.typ.
|
||||||
|
func (mi *mergeInfo) merge(dst, src pointer) {
|
||||||
|
if dst.isNil() {
|
||||||
|
panic("proto: nil destination")
|
||||||
|
}
|
||||||
|
if src.isNil() {
|
||||||
|
return // Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.LoadInt32(&mi.initialized) == 0 {
|
||||||
|
mi.computeMergeInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range mi.fields {
|
||||||
|
sfp := src.offset(fi.field)
|
||||||
|
|
||||||
|
// As an optimization, we can avoid the merge function call cost
|
||||||
|
// if we know for sure that the source will have no effect
|
||||||
|
// by checking if it is the zero value.
|
||||||
|
if unsafeAllowed {
|
||||||
|
if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fi.basicWidth > 0 {
|
||||||
|
switch {
|
||||||
|
case fi.basicWidth == 1 && !*sfp.toBool():
|
||||||
|
continue
|
||||||
|
case fi.basicWidth == 4 && *sfp.toUint32() == 0:
|
||||||
|
continue
|
||||||
|
case fi.basicWidth == 8 && *sfp.toUint64() == 0:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dfp := dst.offset(fi.field)
|
||||||
|
fi.merge(dfp, sfp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make this faster?
|
||||||
|
out := dst.asPointerTo(mi.typ).Elem()
|
||||||
|
in := src.asPointerTo(mi.typ).Elem()
|
||||||
|
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
||||||
|
emOut, _ := extendable(out.Addr().Interface())
|
||||||
|
mIn, muIn := emIn.extensionsRead()
|
||||||
|
if mIn != nil {
|
||||||
|
mOut := emOut.extensionsWrite()
|
||||||
|
muIn.Lock()
|
||||||
|
mergeExtension(mOut, mIn)
|
||||||
|
muIn.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mi.unrecognized.IsValid() {
|
||||||
|
if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 {
|
||||||
|
*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mi *mergeInfo) computeMergeInfo() {
|
||||||
|
mi.lock.Lock()
|
||||||
|
defer mi.lock.Unlock()
|
||||||
|
if mi.initialized != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := mi.typ
|
||||||
|
n := t.NumField()
|
||||||
|
|
||||||
|
props := GetProperties(t)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mfi := mergeFieldInfo{field: toField(&f)}
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// As an optimization, we can avoid the merge function call cost
|
||||||
|
// if we know for sure that the source will have no effect
|
||||||
|
// by checking if it is the zero value.
|
||||||
|
if unsafeAllowed {
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Ptr, reflect.Slice, reflect.String:
|
||||||
|
// As a special case, we assume slices and strings are pointers
|
||||||
|
// since we know that the first field in the SliceSlice or
|
||||||
|
// StringHeader is a data pointer.
|
||||||
|
mfi.isPointer = true
|
||||||
|
case reflect.Bool:
|
||||||
|
mfi.basicWidth = 1
|
||||||
|
case reflect.Int32, reflect.Uint32, reflect.Float32:
|
||||||
|
mfi.basicWidth = 4
|
||||||
|
case reflect.Int64, reflect.Uint64, reflect.Float64:
|
||||||
|
mfi.basicWidth = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap tf to get at its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic("both pointer and slice for basic type in " + tf.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Int32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
// NOTE: toInt32Slice is not defined (see pointer_reflect.go).
|
||||||
|
/*
|
||||||
|
sfsp := src.toInt32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toInt32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []int64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
sfs := src.getInt32Slice()
|
||||||
|
if sfs != nil {
|
||||||
|
dfs := dst.getInt32Slice()
|
||||||
|
dfs = append(dfs, sfs...)
|
||||||
|
if dfs == nil {
|
||||||
|
dfs = []int32{}
|
||||||
|
}
|
||||||
|
dst.setInt32Slice(dfs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
// NOTE: toInt32Ptr is not defined (see pointer_reflect.go).
|
||||||
|
/*
|
||||||
|
sfpp := src.toInt32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toInt32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Int32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
sfp := src.getInt32Ptr()
|
||||||
|
if sfp != nil {
|
||||||
|
dfp := dst.getInt32Ptr()
|
||||||
|
if dfp == nil {
|
||||||
|
dst.setInt32Ptr(*sfp)
|
||||||
|
} else {
|
||||||
|
*dfp = *sfp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toInt32(); v != 0 {
|
||||||
|
*dst.toInt32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toInt64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toInt64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []int64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toInt64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toInt64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Int64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toInt64(); v != 0 {
|
||||||
|
*dst.toInt64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toUint32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toUint32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []uint32{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toUint32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toUint32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Uint32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toUint32(); v != 0 {
|
||||||
|
*dst.toUint32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toUint64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toUint64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []uint64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toUint64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toUint64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Uint64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toUint64(); v != 0 {
|
||||||
|
*dst.toUint64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toFloat32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toFloat32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []float32{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toFloat32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toFloat32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Float32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toFloat32(); v != 0 {
|
||||||
|
*dst.toFloat32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toFloat64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toFloat64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []float64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toFloat64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toFloat64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Float64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toFloat64(); v != 0 {
|
||||||
|
*dst.toFloat64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toBoolSlice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toBoolSlice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []bool{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toBoolPtr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toBoolPtr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Bool(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toBool(); v {
|
||||||
|
*dst.toBool() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toStringSlice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toStringSlice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toStringPtr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toStringPtr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = String(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toString(); v != "" {
|
||||||
|
*dst.toString() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
isProto3 := props.Prop[i].proto3
|
||||||
|
switch {
|
||||||
|
case isPointer:
|
||||||
|
panic("bad pointer in byte slice case in " + tf.Name())
|
||||||
|
case tf.Elem().Kind() != reflect.Uint8:
|
||||||
|
panic("bad element kind in byte slice case in " + tf.Name())
|
||||||
|
case isSlice: // E.g., [][]byte
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sbsp := src.toBytesSlice()
|
||||||
|
if *sbsp != nil {
|
||||||
|
dbsp := dst.toBytesSlice()
|
||||||
|
for _, sb := range *sbsp {
|
||||||
|
if sb == nil {
|
||||||
|
*dbsp = append(*dbsp, nil)
|
||||||
|
} else {
|
||||||
|
*dbsp = append(*dbsp, append([]byte{}, sb...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if *dbsp == nil {
|
||||||
|
*dbsp = [][]byte{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., []byte
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sbp := src.toBytes()
|
||||||
|
if *sbp != nil {
|
||||||
|
dbp := dst.toBytes()
|
||||||
|
if !isProto3 || len(*sbp) > 0 {
|
||||||
|
*dbp = append([]byte{}, *sbp...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("message field %s without pointer", tf))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
mi := getMergeInfo(tf)
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sps := src.getPointerSlice()
|
||||||
|
if sps != nil {
|
||||||
|
dps := dst.getPointerSlice()
|
||||||
|
for _, sp := range sps {
|
||||||
|
var dp pointer
|
||||||
|
if !sp.isNil() {
|
||||||
|
dp = valToPointer(reflect.New(tf))
|
||||||
|
mi.merge(dp, sp)
|
||||||
|
}
|
||||||
|
dps = append(dps, dp)
|
||||||
|
}
|
||||||
|
if dps == nil {
|
||||||
|
dps = []pointer{}
|
||||||
|
}
|
||||||
|
dst.setPointerSlice(dps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
mi := getMergeInfo(tf)
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sp := src.getPointer()
|
||||||
|
if !sp.isNil() {
|
||||||
|
dp := dst.getPointer()
|
||||||
|
if dp.isNil() {
|
||||||
|
dp = valToPointer(reflect.New(tf))
|
||||||
|
dst.setPointer(dp)
|
||||||
|
}
|
||||||
|
mi.merge(dp, sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic("bad pointer or slice in map case in " + tf.Name())
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sm := src.asPointerTo(tf).Elem()
|
||||||
|
if sm.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dm := dst.asPointerTo(tf).Elem()
|
||||||
|
if dm.IsNil() {
|
||||||
|
dm.Set(reflect.MakeMap(tf))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Elem().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
val = reflect.ValueOf(Clone(val.Interface().(Message)))
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
default: // Basic type (e.g., string)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic("bad pointer or slice in interface case in " + tf.Name())
|
||||||
|
default: // E.g., interface{}
|
||||||
|
// TODO: Make this faster?
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
su := src.asPointerTo(tf).Elem()
|
||||||
|
if !su.IsNil() {
|
||||||
|
du := dst.asPointerTo(tf).Elem()
|
||||||
|
typ := su.Elem().Type()
|
||||||
|
if du.IsNil() || du.Elem().Type() != typ {
|
||||||
|
du.Set(reflect.New(typ.Elem())) // Initialize interface if empty
|
||||||
|
}
|
||||||
|
sv := su.Elem().Elem().Field(0)
|
||||||
|
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dv := du.Elem().Elem().Field(0)
|
||||||
|
if dv.Kind() == reflect.Ptr && dv.IsNil() {
|
||||||
|
dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty
|
||||||
|
}
|
||||||
|
switch sv.Type().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
Merge(dv.Interface().(Message), sv.Interface().(Message))
|
||||||
|
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
||||||
|
dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...)))
|
||||||
|
default: // Basic type (e.g., string)
|
||||||
|
dv.Set(sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("merger not found for type:%s", tf))
|
||||||
|
}
|
||||||
|
mi.fields = append(mi.fields, mfi)
|
||||||
|
}
|
||||||
|
|
||||||
|
mi.unrecognized = invalidField
|
||||||
|
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
||||||
|
if f.Type != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
mi.unrecognized = toField(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreInt32(&mi.initialized, 1)
|
||||||
|
}
|
2051
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
Normal file
2051
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
843
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
Normal file
843
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
Normal file
@ -0,0 +1,843 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
// Functions for writing the text protocol buffer format.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
newline = []byte("\n")
|
||||||
|
spaces = []byte(" ")
|
||||||
|
endBraceNewline = []byte("}\n")
|
||||||
|
backslashN = []byte{'\\', 'n'}
|
||||||
|
backslashR = []byte{'\\', 'r'}
|
||||||
|
backslashT = []byte{'\\', 't'}
|
||||||
|
backslashDQ = []byte{'\\', '"'}
|
||||||
|
backslashBS = []byte{'\\', '\\'}
|
||||||
|
posInf = []byte("inf")
|
||||||
|
negInf = []byte("-inf")
|
||||||
|
nan = []byte("nan")
|
||||||
|
)
|
||||||
|
|
||||||
|
type writer interface {
|
||||||
|
io.Writer
|
||||||
|
WriteByte(byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// textWriter is an io.Writer that tracks its indentation level.
|
||||||
|
type textWriter struct {
|
||||||
|
ind int
|
||||||
|
complete bool // if the current position is a complete line
|
||||||
|
compact bool // whether to write out as a one-liner
|
||||||
|
w writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) WriteString(s string) (n int, err error) {
|
||||||
|
if !strings.Contains(s, "\n") {
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
w.complete = false
|
||||||
|
return io.WriteString(w.w, s)
|
||||||
|
}
|
||||||
|
// WriteString is typically called without newlines, so this
|
||||||
|
// codepath and its copy are rare. We copy to avoid
|
||||||
|
// duplicating all of Write's logic here.
|
||||||
|
return w.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) Write(p []byte) (n int, err error) {
|
||||||
|
newlines := bytes.Count(p, newline)
|
||||||
|
if newlines == 0 {
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
n, err = w.w.Write(p)
|
||||||
|
w.complete = false
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
frags := bytes.SplitN(p, newline, newlines+1)
|
||||||
|
if w.compact {
|
||||||
|
for i, frag := range frags {
|
||||||
|
if i > 0 {
|
||||||
|
if err := w.w.WriteByte(' '); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
nn, err := w.w.Write(frag)
|
||||||
|
n += nn
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, frag := range frags {
|
||||||
|
if w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
nn, err := w.w.Write(frag)
|
||||||
|
n += nn
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if i+1 < len(frags) {
|
||||||
|
if err := w.w.WriteByte('\n'); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.complete = len(frags[len(frags)-1]) == 0
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) WriteByte(c byte) error {
|
||||||
|
if w.compact && c == '\n' {
|
||||||
|
c = ' '
|
||||||
|
}
|
||||||
|
if !w.compact && w.complete {
|
||||||
|
w.writeIndent()
|
||||||
|
}
|
||||||
|
err := w.w.WriteByte(c)
|
||||||
|
w.complete = c == '\n'
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) indent() { w.ind++ }
|
||||||
|
|
||||||
|
func (w *textWriter) unindent() {
|
||||||
|
if w.ind == 0 {
|
||||||
|
log.Print("proto: textWriter unindented too far")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.ind--
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeName(w *textWriter, props *Properties) error {
|
||||||
|
if _, err := w.WriteString(props.OrigName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if props.Wire != "group" {
|
||||||
|
return w.WriteByte(':')
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiresQuotes(u string) bool {
|
||||||
|
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
||||||
|
for _, ch := range u {
|
||||||
|
switch {
|
||||||
|
case ch == '.' || ch == '/' || ch == '_':
|
||||||
|
continue
|
||||||
|
case '0' <= ch && ch <= '9':
|
||||||
|
continue
|
||||||
|
case 'A' <= ch && ch <= 'Z':
|
||||||
|
continue
|
||||||
|
case 'a' <= ch && ch <= 'z':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAny reports whether sv is a google.protobuf.Any message
|
||||||
|
func isAny(sv reflect.Value) bool {
|
||||||
|
type wkt interface {
|
||||||
|
XXX_WellKnownType() string
|
||||||
|
}
|
||||||
|
t, ok := sv.Addr().Interface().(wkt)
|
||||||
|
return ok && t.XXX_WellKnownType() == "Any"
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeProto3Any writes an expanded google.protobuf.Any message.
|
||||||
|
//
|
||||||
|
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
||||||
|
// required messages are not linked in).
|
||||||
|
//
|
||||||
|
// It returns (true, error) when sv was written in expanded format or an error
|
||||||
|
// was encountered.
|
||||||
|
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
||||||
|
turl := sv.FieldByName("TypeUrl")
|
||||||
|
val := sv.FieldByName("Value")
|
||||||
|
if !turl.IsValid() || !val.IsValid() {
|
||||||
|
return true, errors.New("proto: invalid google.protobuf.Any message")
|
||||||
|
}
|
||||||
|
|
||||||
|
b, ok := val.Interface().([]byte)
|
||||||
|
if !ok {
|
||||||
|
return true, errors.New("proto: invalid google.protobuf.Any message")
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(turl.String(), "/")
|
||||||
|
mt := MessageType(parts[len(parts)-1])
|
||||||
|
if mt == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
m := reflect.New(mt.Elem())
|
||||||
|
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
w.Write([]byte("["))
|
||||||
|
u := turl.String()
|
||||||
|
if requiresQuotes(u) {
|
||||||
|
writeString(w, u)
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(u))
|
||||||
|
}
|
||||||
|
if w.compact {
|
||||||
|
w.Write([]byte("]:<"))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("]: <\n"))
|
||||||
|
w.ind++
|
||||||
|
}
|
||||||
|
if err := tm.writeStruct(w, m.Elem()); err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
if w.compact {
|
||||||
|
w.Write([]byte("> "))
|
||||||
|
} else {
|
||||||
|
w.ind--
|
||||||
|
w.Write([]byte(">\n"))
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||||
|
if tm.ExpandAny && isAny(sv) {
|
||||||
|
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st := sv.Type()
|
||||||
|
sprops := GetProperties(st)
|
||||||
|
for i := 0; i < sv.NumField(); i++ {
|
||||||
|
fv := sv.Field(i)
|
||||||
|
props := sprops.Prop[i]
|
||||||
|
name := st.Field(i).Name
|
||||||
|
|
||||||
|
if name == "XXX_NoUnkeyedLiteral" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(name, "XXX_") {
|
||||||
|
// There are two XXX_ fields:
|
||||||
|
// XXX_unrecognized []byte
|
||||||
|
// XXX_extensions map[int32]proto.Extension
|
||||||
|
// The first is handled here;
|
||||||
|
// the second is handled at the bottom of this function.
|
||||||
|
if name == "XXX_unrecognized" && !fv.IsNil() {
|
||||||
|
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||||
|
// Field not filled in. This could be an optional field or
|
||||||
|
// a required field that wasn't filled in. Either way, there
|
||||||
|
// isn't anything we can show for it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
||||||
|
// Repeated field that is empty, or a bytes field that is unused.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if props.Repeated && fv.Kind() == reflect.Slice {
|
||||||
|
// Repeated field.
|
||||||
|
for j := 0; j < fv.Len(); j++ {
|
||||||
|
if err := writeName(w, props); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v := fv.Index(j)
|
||||||
|
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||||
|
// A nil message in a repeated field is not valid,
|
||||||
|
// but we can handle that more gracefully than panicking.
|
||||||
|
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := tm.writeAny(w, v, props); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fv.Kind() == reflect.Map {
|
||||||
|
// Map fields are rendered as a repeated struct with key/value fields.
|
||||||
|
keys := fv.MapKeys()
|
||||||
|
sort.Sort(mapKeys(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
val := fv.MapIndex(key)
|
||||||
|
if err := writeName(w, props); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// open struct
|
||||||
|
if err := w.WriteByte('<'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.indent()
|
||||||
|
// key
|
||||||
|
if _, err := w.WriteString("key:"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// nil values aren't legal, but we can avoid panicking because of them.
|
||||||
|
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
||||||
|
// value
|
||||||
|
if _, err := w.WriteString("value:"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// close struct
|
||||||
|
w.unindent()
|
||||||
|
if err := w.WriteByte('>'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
||||||
|
// empty bytes field
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
||||||
|
// proto3 non-repeated scalar field; skip if zero value
|
||||||
|
if isProto3Zero(fv) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fv.Kind() == reflect.Interface {
|
||||||
|
// Check if it is a oneof.
|
||||||
|
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
|
||||||
|
// fv is nil, or holds a pointer to generated struct.
|
||||||
|
// That generated struct has exactly one field,
|
||||||
|
// which has a protobuf struct tag.
|
||||||
|
if fv.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inner := fv.Elem().Elem() // interface -> *T -> T
|
||||||
|
tag := inner.Type().Field(0).Tag.Get("protobuf")
|
||||||
|
props = new(Properties) // Overwrite the outer props var, but not its pointee.
|
||||||
|
props.Parse(tag)
|
||||||
|
// Write the value in the oneof, not the oneof itself.
|
||||||
|
fv = inner.Field(0)
|
||||||
|
|
||||||
|
// Special case to cope with malformed messages gracefully:
|
||||||
|
// If the value in the oneof is a nil pointer, don't panic
|
||||||
|
// in writeAny.
|
||||||
|
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||||
|
// Use errors.New so writeAny won't render quotes.
|
||||||
|
msg := errors.New("/* nil */")
|
||||||
|
fv = reflect.ValueOf(&msg).Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeName(w, props); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enums have a String method, so writeAny will work fine.
|
||||||
|
if err := tm.writeAny(w, fv, props); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extensions (the XXX_extensions field).
|
||||||
|
pv := sv.Addr()
|
||||||
|
if _, err := extendable(pv.Interface()); err == nil {
|
||||||
|
if err := tm.writeExtensions(w, pv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeAny writes an arbitrary field.
|
||||||
|
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
|
||||||
|
// Floats have special cases.
|
||||||
|
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
||||||
|
x := v.Float()
|
||||||
|
var b []byte
|
||||||
|
switch {
|
||||||
|
case math.IsInf(x, 1):
|
||||||
|
b = posInf
|
||||||
|
case math.IsInf(x, -1):
|
||||||
|
b = negInf
|
||||||
|
case math.IsNaN(x):
|
||||||
|
b = nan
|
||||||
|
}
|
||||||
|
if b != nil {
|
||||||
|
_, err := w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Other values are handled below.
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't attempt to serialise every possible value type; only those
|
||||||
|
// that can occur in protocol buffers.
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
// Should only be a []byte; repeated fields are handled in writeStruct.
|
||||||
|
if err := writeString(w, string(v.Bytes())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
if err := writeString(w, v.String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
// Required/optional group/message.
|
||||||
|
var bra, ket byte = '<', '>'
|
||||||
|
if props != nil && props.Wire == "group" {
|
||||||
|
bra, ket = '{', '}'
|
||||||
|
}
|
||||||
|
if err := w.WriteByte(bra); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.indent()
|
||||||
|
if v.CanAddr() {
|
||||||
|
// Calling v.Interface on a struct causes the reflect package to
|
||||||
|
// copy the entire struct. This is racy with the new Marshaler
|
||||||
|
// since we atomically update the XXX_sizecache.
|
||||||
|
//
|
||||||
|
// Thus, we retrieve a pointer to the struct if possible to avoid
|
||||||
|
// a race since v.Interface on the pointer doesn't copy the struct.
|
||||||
|
//
|
||||||
|
// If v is not addressable, then we are not worried about a race
|
||||||
|
// since it implies that the binary Marshaler cannot possibly be
|
||||||
|
// mutating this value.
|
||||||
|
v = v.Addr()
|
||||||
|
}
|
||||||
|
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
||||||
|
text, err := etm.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = w.Write(text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if err := tm.writeStruct(w, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.unindent()
|
||||||
|
if err := w.WriteByte(ket); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_, err := fmt.Fprint(w, v.Interface())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// equivalent to C's isprint.
|
||||||
|
func isprint(c byte) bool {
|
||||||
|
return c >= 0x20 && c < 0x7f
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeString writes a string in the protocol buffer text format.
|
||||||
|
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
||||||
|
// we treat the string as a byte sequence, and we use octal escapes.
|
||||||
|
// These differences are to maintain interoperability with the other
|
||||||
|
// languages' implementations of the text format.
|
||||||
|
func writeString(w *textWriter, s string) error {
|
||||||
|
// use WriteByte here to get any needed indent
|
||||||
|
if err := w.WriteByte('"'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Loop over the bytes, not the runes.
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
var err error
|
||||||
|
// Divergence from C++: we don't escape apostrophes.
|
||||||
|
// There's no need to escape them, and the C++ parser
|
||||||
|
// copes with a naked apostrophe.
|
||||||
|
switch c := s[i]; c {
|
||||||
|
case '\n':
|
||||||
|
_, err = w.w.Write(backslashN)
|
||||||
|
case '\r':
|
||||||
|
_, err = w.w.Write(backslashR)
|
||||||
|
case '\t':
|
||||||
|
_, err = w.w.Write(backslashT)
|
||||||
|
case '"':
|
||||||
|
_, err = w.w.Write(backslashDQ)
|
||||||
|
case '\\':
|
||||||
|
_, err = w.w.Write(backslashBS)
|
||||||
|
default:
|
||||||
|
if isprint(c) {
|
||||||
|
err = w.w.WriteByte(c)
|
||||||
|
} else {
|
||||||
|
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w.WriteByte('"')
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
||||||
|
if !w.compact {
|
||||||
|
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b := NewBuffer(data)
|
||||||
|
for b.index < len(b.buf) {
|
||||||
|
x, err := b.DecodeVarint()
|
||||||
|
if err != nil {
|
||||||
|
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wire, tag := x&7, x>>3
|
||||||
|
if wire == WireEndGroup {
|
||||||
|
w.unindent()
|
||||||
|
if _, err := w.Write(endBraceNewline); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := fmt.Fprint(w, tag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if wire != WireStartGroup {
|
||||||
|
if err := w.WriteByte(':'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !w.compact || wire == WireStartGroup {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch wire {
|
||||||
|
case WireBytes:
|
||||||
|
buf, e := b.DecodeRawBytes(false)
|
||||||
|
if e == nil {
|
||||||
|
_, err = fmt.Fprintf(w, "%q", buf)
|
||||||
|
} else {
|
||||||
|
_, err = fmt.Fprintf(w, "/* %v */", e)
|
||||||
|
}
|
||||||
|
case WireFixed32:
|
||||||
|
x, err = b.DecodeFixed32()
|
||||||
|
err = writeUnknownInt(w, x, err)
|
||||||
|
case WireFixed64:
|
||||||
|
x, err = b.DecodeFixed64()
|
||||||
|
err = writeUnknownInt(w, x, err)
|
||||||
|
case WireStartGroup:
|
||||||
|
err = w.WriteByte('{')
|
||||||
|
w.indent()
|
||||||
|
case WireVarint:
|
||||||
|
x, err = b.DecodeVarint()
|
||||||
|
err = writeUnknownInt(w, x, err)
|
||||||
|
default:
|
||||||
|
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
_, err = fmt.Fprint(w, x)
|
||||||
|
} else {
|
||||||
|
_, err = fmt.Fprintf(w, "/* %v */", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type int32Slice []int32
|
||||||
|
|
||||||
|
func (s int32Slice) Len() int { return len(s) }
|
||||||
|
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
||||||
|
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
|
// writeExtensions writes all the extensions in pv.
|
||||||
|
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
||||||
|
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
||||||
|
emap := extensionMaps[pv.Type().Elem()]
|
||||||
|
ep, _ := extendable(pv.Interface())
|
||||||
|
|
||||||
|
// Order the extensions by ID.
|
||||||
|
// This isn't strictly necessary, but it will give us
|
||||||
|
// canonical output, which will also make testing easier.
|
||||||
|
m, mu := ep.extensionsRead()
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
ids := make([]int32, 0, len(m))
|
||||||
|
for id := range m {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
sort.Sort(int32Slice(ids))
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
for _, extNum := range ids {
|
||||||
|
ext := m[extNum]
|
||||||
|
var desc *ExtensionDesc
|
||||||
|
if emap != nil {
|
||||||
|
desc = emap[extNum]
|
||||||
|
}
|
||||||
|
if desc == nil {
|
||||||
|
// Unknown extension.
|
||||||
|
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pb, err := GetExtension(ep, desc)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed getting extension: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeated extensions will appear as a slice.
|
||||||
|
if !desc.repeated() {
|
||||||
|
if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v := reflect.ValueOf(pb)
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
||||||
|
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !w.compact {
|
||||||
|
if err := w.WriteByte(' '); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *textWriter) writeIndent() {
|
||||||
|
if !w.complete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remain := w.ind * 2
|
||||||
|
for remain > 0 {
|
||||||
|
n := remain
|
||||||
|
if n > len(spaces) {
|
||||||
|
n = len(spaces)
|
||||||
|
}
|
||||||
|
w.w.Write(spaces[:n])
|
||||||
|
remain -= n
|
||||||
|
}
|
||||||
|
w.complete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMarshaler is a configurable text format marshaler.
|
||||||
|
type TextMarshaler struct {
|
||||||
|
Compact bool // use compact text format (one line).
|
||||||
|
ExpandAny bool // expand google.protobuf.Any messages of known types
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal writes a given protocol buffer in text format.
|
||||||
|
// The only errors returned are from w.
|
||||||
|
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
||||||
|
val := reflect.ValueOf(pb)
|
||||||
|
if pb == nil || val.IsNil() {
|
||||||
|
w.Write([]byte("<nil>"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var bw *bufio.Writer
|
||||||
|
ww, ok := w.(writer)
|
||||||
|
if !ok {
|
||||||
|
bw = bufio.NewWriter(w)
|
||||||
|
ww = bw
|
||||||
|
}
|
||||||
|
aw := &textWriter{
|
||||||
|
w: ww,
|
||||||
|
complete: true,
|
||||||
|
compact: tm.Compact,
|
||||||
|
}
|
||||||
|
|
||||||
|
if etm, ok := pb.(encoding.TextMarshaler); ok {
|
||||||
|
text, err := etm.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = aw.Write(text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bw != nil {
|
||||||
|
return bw.Flush()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Dereference the received pointer so we don't have outer < and >.
|
||||||
|
v := reflect.Indirect(val)
|
||||||
|
if err := tm.writeStruct(aw, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bw != nil {
|
||||||
|
return bw.Flush()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text is the same as Marshal, but returns the string directly.
|
||||||
|
func (tm *TextMarshaler) Text(pb Message) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
tm.Marshal(&buf, pb)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTextMarshaler = TextMarshaler{}
|
||||||
|
compactTextMarshaler = TextMarshaler{Compact: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: consider removing some of the Marshal functions below.
|
||||||
|
|
||||||
|
// MarshalText writes a given protocol buffer in text format.
|
||||||
|
// The only errors returned are from w.
|
||||||
|
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
||||||
|
|
||||||
|
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
||||||
|
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
||||||
|
|
||||||
|
// CompactText writes a given protocol buffer in compact text format (one line).
|
||||||
|
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
||||||
|
|
||||||
|
// CompactTextString is the same as CompactText, but returns the string directly.
|
||||||
|
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
880
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
Normal file
880
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
Normal file
@ -0,0 +1,880 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
// Functions for parsing the Text protocol buffer format.
|
||||||
|
// TODO: message sets.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error string emitted when deserializing Any and fields are already set
|
||||||
|
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
|
||||||
|
|
||||||
|
type ParseError struct {
|
||||||
|
Message string
|
||||||
|
Line int // 1-based line number
|
||||||
|
Offset int // 0-based byte offset from start of input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseError) Error() string {
|
||||||
|
if p.Line == 1 {
|
||||||
|
// show offset only for first line
|
||||||
|
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
type token struct {
|
||||||
|
value string
|
||||||
|
err *ParseError
|
||||||
|
line int // line number
|
||||||
|
offset int // byte number from start of input, not start of line
|
||||||
|
unquoted string // the unquoted version of value, if it was a quoted string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *token) String() string {
|
||||||
|
if t.err == nil {
|
||||||
|
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("parse error: %v", t.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type textParser struct {
|
||||||
|
s string // remaining input
|
||||||
|
done bool // whether the parsing is finished (success or error)
|
||||||
|
backed bool // whether back() was called
|
||||||
|
offset, line int
|
||||||
|
cur token
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTextParser(s string) *textParser {
|
||||||
|
p := new(textParser)
|
||||||
|
p.s = s
|
||||||
|
p.line = 1
|
||||||
|
p.cur.line = 1
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
||||||
|
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
||||||
|
p.cur.err = pe
|
||||||
|
p.done = true
|
||||||
|
return pe
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
|
||||||
|
func isIdentOrNumberChar(c byte) bool {
|
||||||
|
switch {
|
||||||
|
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
||||||
|
return true
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case '-', '+', '.', '_':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWhitespace(c byte) bool {
|
||||||
|
switch c {
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQuote(c byte) bool {
|
||||||
|
switch c {
|
||||||
|
case '"', '\'':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) skipWhitespace() {
|
||||||
|
i := 0
|
||||||
|
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
||||||
|
if p.s[i] == '#' {
|
||||||
|
// comment; skip to end of line or input
|
||||||
|
for i < len(p.s) && p.s[i] != '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(p.s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.s[i] == '\n' {
|
||||||
|
p.line++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
p.offset += i
|
||||||
|
p.s = p.s[i:len(p.s)]
|
||||||
|
if len(p.s) == 0 {
|
||||||
|
p.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) advance() {
|
||||||
|
// Skip whitespace
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.done {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of non-whitespace
|
||||||
|
p.cur.err = nil
|
||||||
|
p.cur.offset, p.cur.line = p.offset, p.line
|
||||||
|
p.cur.unquoted = ""
|
||||||
|
switch p.s[0] {
|
||||||
|
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
||||||
|
// Single symbol
|
||||||
|
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
||||||
|
case '"', '\'':
|
||||||
|
// Quoted string
|
||||||
|
i := 1
|
||||||
|
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
||||||
|
if p.s[i] == '\\' && i+1 < len(p.s) {
|
||||||
|
// skip escaped char
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(p.s) || p.s[i] != p.s[0] {
|
||||||
|
p.errorf("unmatched quote")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
||||||
|
if err != nil {
|
||||||
|
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
||||||
|
p.cur.unquoted = unq
|
||||||
|
default:
|
||||||
|
i := 0
|
||||||
|
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
p.errorf("unexpected byte %#x", p.s[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
||||||
|
}
|
||||||
|
p.offset += len(p.cur.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errBadUTF8 = errors.New("proto: bad UTF-8")
|
||||||
|
)
|
||||||
|
|
||||||
|
func unquoteC(s string, quote rune) (string, error) {
|
||||||
|
// This is based on C++'s tokenizer.cc.
|
||||||
|
// Despite its name, this is *not* parsing C syntax.
|
||||||
|
// For instance, "\0" is an invalid quoted string.
|
||||||
|
|
||||||
|
// Avoid allocation in trivial cases.
|
||||||
|
simple := true
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '\\' || r == quote {
|
||||||
|
simple = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if simple {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 0, 3*len(s)/2)
|
||||||
|
for len(s) > 0 {
|
||||||
|
r, n := utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && n == 1 {
|
||||||
|
return "", errBadUTF8
|
||||||
|
}
|
||||||
|
s = s[n:]
|
||||||
|
if r != '\\' {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
} else {
|
||||||
|
buf = append(buf, string(r)...)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, tail, err := unescape(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf = append(buf, ch...)
|
||||||
|
s = tail
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unescape(s string) (ch string, tail string, err error) {
|
||||||
|
r, n := utf8.DecodeRuneInString(s)
|
||||||
|
if r == utf8.RuneError && n == 1 {
|
||||||
|
return "", "", errBadUTF8
|
||||||
|
}
|
||||||
|
s = s[n:]
|
||||||
|
switch r {
|
||||||
|
case 'a':
|
||||||
|
return "\a", s, nil
|
||||||
|
case 'b':
|
||||||
|
return "\b", s, nil
|
||||||
|
case 'f':
|
||||||
|
return "\f", s, nil
|
||||||
|
case 'n':
|
||||||
|
return "\n", s, nil
|
||||||
|
case 'r':
|
||||||
|
return "\r", s, nil
|
||||||
|
case 't':
|
||||||
|
return "\t", s, nil
|
||||||
|
case 'v':
|
||||||
|
return "\v", s, nil
|
||||||
|
case '?':
|
||||||
|
return "?", s, nil // trigraph workaround
|
||||||
|
case '\'', '"', '\\':
|
||||||
|
return string(r), s, nil
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
if len(s) < 2 {
|
||||||
|
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
||||||
|
}
|
||||||
|
ss := string(r) + s[:2]
|
||||||
|
s = s[2:]
|
||||||
|
i, err := strconv.ParseUint(ss, 8, 8)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
|
||||||
|
}
|
||||||
|
return string([]byte{byte(i)}), s, nil
|
||||||
|
case 'x', 'X', 'u', 'U':
|
||||||
|
var n int
|
||||||
|
switch r {
|
||||||
|
case 'x', 'X':
|
||||||
|
n = 2
|
||||||
|
case 'u':
|
||||||
|
n = 4
|
||||||
|
case 'U':
|
||||||
|
n = 8
|
||||||
|
}
|
||||||
|
if len(s) < n {
|
||||||
|
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
|
||||||
|
}
|
||||||
|
ss := s[:n]
|
||||||
|
s = s[n:]
|
||||||
|
i, err := strconv.ParseUint(ss, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
|
||||||
|
}
|
||||||
|
if r == 'x' || r == 'X' {
|
||||||
|
return string([]byte{byte(i)}), s, nil
|
||||||
|
}
|
||||||
|
if i > utf8.MaxRune {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
||||||
|
}
|
||||||
|
return string(i), s, nil
|
||||||
|
}
|
||||||
|
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back off the parser by one token. Can only be done between calls to next().
|
||||||
|
// It makes the next advance() a no-op.
|
||||||
|
func (p *textParser) back() { p.backed = true }
|
||||||
|
|
||||||
|
// Advances the parser and returns the new current token.
|
||||||
|
func (p *textParser) next() *token {
|
||||||
|
if p.backed || p.done {
|
||||||
|
p.backed = false
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
p.advance()
|
||||||
|
if p.done {
|
||||||
|
p.cur.value = ""
|
||||||
|
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
||||||
|
// Look for multiple quoted strings separated by whitespace,
|
||||||
|
// and concatenate them.
|
||||||
|
cat := p.cur
|
||||||
|
for {
|
||||||
|
p.skipWhitespace()
|
||||||
|
if p.done || !isQuote(p.s[0]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.advance()
|
||||||
|
if p.cur.err != nil {
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
cat.value += " " + p.cur.value
|
||||||
|
cat.unquoted += p.cur.unquoted
|
||||||
|
}
|
||||||
|
p.done = false // parser may have seen EOF, but we want to return cat
|
||||||
|
p.cur = cat
|
||||||
|
}
|
||||||
|
return &p.cur
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) consumeToken(s string) error {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != s {
|
||||||
|
p.back()
|
||||||
|
return p.errorf("expected %q, found %q", s, tok.value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a RequiredNotSetError indicating which required field was not set.
|
||||||
|
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
|
||||||
|
st := sv.Type()
|
||||||
|
sprops := GetProperties(st)
|
||||||
|
for i := 0; i < st.NumField(); i++ {
|
||||||
|
if !isNil(sv.Field(i)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
props := sprops.Prop[i]
|
||||||
|
if props.Required {
|
||||||
|
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the index in the struct for the named field, as well as the parsed tag properties.
|
||||||
|
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
|
||||||
|
i, ok := sprops.decoderOrigNames[name]
|
||||||
|
if ok {
|
||||||
|
return i, sprops.Prop[i], true
|
||||||
|
}
|
||||||
|
return -1, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume a ':' from the input stream (if the next token is a colon),
|
||||||
|
// returning an error if a colon is needed but not present.
|
||||||
|
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != ":" {
|
||||||
|
// Colon is optional when the field is a group or message.
|
||||||
|
needColon := true
|
||||||
|
switch props.Wire {
|
||||||
|
case "group":
|
||||||
|
needColon = false
|
||||||
|
case "bytes":
|
||||||
|
// A "bytes" field is either a message, a string, or a repeated field;
|
||||||
|
// those three become *T, *string and []T respectively, so we can check for
|
||||||
|
// this field being a pointer to a non-string.
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
// *T or *string
|
||||||
|
if typ.Elem().Kind() == reflect.String {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if typ.Kind() == reflect.Slice {
|
||||||
|
// []T or []*T
|
||||||
|
if typ.Elem().Kind() != reflect.Ptr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if typ.Kind() == reflect.String {
|
||||||
|
// The proto3 exception is for a string field,
|
||||||
|
// which requires a colon.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
needColon = false
|
||||||
|
}
|
||||||
|
if needColon {
|
||||||
|
return p.errorf("expected ':', found %q", tok.value)
|
||||||
|
}
|
||||||
|
p.back()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||||
|
st := sv.Type()
|
||||||
|
sprops := GetProperties(st)
|
||||||
|
reqCount := sprops.reqCount
|
||||||
|
var reqFieldErr error
|
||||||
|
fieldSet := make(map[string]bool)
|
||||||
|
// A struct is a sequence of "name: value", terminated by one of
|
||||||
|
// '>' or '}', or the end of the input. A name may also be
|
||||||
|
// "[extension]" or "[type/url]".
|
||||||
|
//
|
||||||
|
// The whole struct can also be an expanded Any message, like:
|
||||||
|
// [type/url] < ... struct contents ... >
|
||||||
|
for {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value == terminator {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tok.value == "[" {
|
||||||
|
// Looks like an extension or an Any.
|
||||||
|
//
|
||||||
|
// TODO: Check whether we need to handle
|
||||||
|
// namespace rooted names (e.g. ".something.Foo").
|
||||||
|
extName, err := p.consumeExtName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := strings.LastIndex(extName, "/"); s >= 0 {
|
||||||
|
// If it contains a slash, it's an Any type URL.
|
||||||
|
messageName := extName[s+1:]
|
||||||
|
mt := MessageType(messageName)
|
||||||
|
if mt == nil {
|
||||||
|
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
||||||
|
}
|
||||||
|
tok = p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
// consume an optional colon
|
||||||
|
if tok.value == ":" {
|
||||||
|
tok = p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
default:
|
||||||
|
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
v := reflect.New(mt.Elem())
|
||||||
|
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
||||||
|
return pe
|
||||||
|
}
|
||||||
|
b, err := Marshal(v.Interface().(Message))
|
||||||
|
if err != nil {
|
||||||
|
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
||||||
|
}
|
||||||
|
if fieldSet["type_url"] {
|
||||||
|
return p.errorf(anyRepeatedlyUnpacked, "type_url")
|
||||||
|
}
|
||||||
|
if fieldSet["value"] {
|
||||||
|
return p.errorf(anyRepeatedlyUnpacked, "value")
|
||||||
|
}
|
||||||
|
sv.FieldByName("TypeUrl").SetString(extName)
|
||||||
|
sv.FieldByName("Value").SetBytes(b)
|
||||||
|
fieldSet["type_url"] = true
|
||||||
|
fieldSet["value"] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var desc *ExtensionDesc
|
||||||
|
// This could be faster, but it's functional.
|
||||||
|
// TODO: Do something smarter than a linear scan.
|
||||||
|
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
||||||
|
if d.Name == extName {
|
||||||
|
desc = d
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if desc == nil {
|
||||||
|
return p.errorf("unrecognized extension %q", extName)
|
||||||
|
}
|
||||||
|
|
||||||
|
props := &Properties{}
|
||||||
|
props.Parse(desc.Tag)
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(desc.ExtensionType)
|
||||||
|
if err := p.checkForColon(props, typ); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rep := desc.repeated()
|
||||||
|
|
||||||
|
// Read the extension structure, and set it in
|
||||||
|
// the value we're constructing.
|
||||||
|
var ext reflect.Value
|
||||||
|
if !rep {
|
||||||
|
ext = reflect.New(typ).Elem()
|
||||||
|
} else {
|
||||||
|
ext = reflect.New(typ.Elem()).Elem()
|
||||||
|
}
|
||||||
|
if err := p.readAny(ext, props); err != nil {
|
||||||
|
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reqFieldErr = err
|
||||||
|
}
|
||||||
|
ep := sv.Addr().Interface().(Message)
|
||||||
|
if !rep {
|
||||||
|
SetExtension(ep, desc, ext.Interface())
|
||||||
|
} else {
|
||||||
|
old, err := GetExtension(ep, desc)
|
||||||
|
var sl reflect.Value
|
||||||
|
if err == nil {
|
||||||
|
sl = reflect.ValueOf(old) // existing slice
|
||||||
|
} else {
|
||||||
|
sl = reflect.MakeSlice(typ, 0, 1)
|
||||||
|
}
|
||||||
|
sl = reflect.Append(sl, ext)
|
||||||
|
SetExtension(ep, desc, sl.Interface())
|
||||||
|
}
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a normal, non-extension field.
|
||||||
|
name := tok.value
|
||||||
|
var dst reflect.Value
|
||||||
|
fi, props, ok := structFieldByName(sprops, name)
|
||||||
|
if ok {
|
||||||
|
dst = sv.Field(fi)
|
||||||
|
} else if oop, ok := sprops.OneofTypes[name]; ok {
|
||||||
|
// It is a oneof.
|
||||||
|
props = oop.Prop
|
||||||
|
nv := reflect.New(oop.Type.Elem())
|
||||||
|
dst = nv.Elem().Field(0)
|
||||||
|
field := sv.Field(oop.Field)
|
||||||
|
if !field.IsNil() {
|
||||||
|
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
|
||||||
|
}
|
||||||
|
field.Set(nv)
|
||||||
|
}
|
||||||
|
if !dst.IsValid() {
|
||||||
|
return p.errorf("unknown field name %q in %v", name, st)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst.Kind() == reflect.Map {
|
||||||
|
// Consume any colon.
|
||||||
|
if err := p.checkForColon(props, dst.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the map if it doesn't already exist.
|
||||||
|
if dst.IsNil() {
|
||||||
|
dst.Set(reflect.MakeMap(dst.Type()))
|
||||||
|
}
|
||||||
|
key := reflect.New(dst.Type().Key()).Elem()
|
||||||
|
val := reflect.New(dst.Type().Elem()).Elem()
|
||||||
|
|
||||||
|
// The map entry should be this sequence of tokens:
|
||||||
|
// < key : KEY value : VALUE >
|
||||||
|
// However, implementations may omit key or value, and technically
|
||||||
|
// we should support them in any order. See b/28924776 for a time
|
||||||
|
// this went wrong.
|
||||||
|
|
||||||
|
tok := p.next()
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
default:
|
||||||
|
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value == terminator {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch tok.value {
|
||||||
|
case "key":
|
||||||
|
if err := p.consumeToken(":"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.readAny(key, props.MapKeyProp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "value":
|
||||||
|
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.readAny(val, props.MapValProp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
p.back()
|
||||||
|
return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.SetMapIndex(key, val)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that it's not already set if it's not a repeated field.
|
||||||
|
if !props.Repeated && fieldSet[name] {
|
||||||
|
return p.errorf("non-repeated field %q was repeated", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.checkForColon(props, dst.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse into the field.
|
||||||
|
fieldSet[name] = true
|
||||||
|
if err := p.readAny(dst, props); err != nil {
|
||||||
|
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reqFieldErr = err
|
||||||
|
}
|
||||||
|
if props.Required {
|
||||||
|
reqCount--
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqCount > 0 {
|
||||||
|
return p.missingRequiredFieldError(sv)
|
||||||
|
}
|
||||||
|
return reqFieldErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeExtName consumes extension name or expanded Any type URL and the
|
||||||
|
// following ']'. It returns the name or URL consumed.
|
||||||
|
func (p *textParser) consumeExtName() (string, error) {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return "", tok.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If extension name or type url is quoted, it's a single token.
|
||||||
|
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
||||||
|
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return name, p.consumeToken("]")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume everything up to "]"
|
||||||
|
var parts []string
|
||||||
|
for tok.value != "]" {
|
||||||
|
parts = append(parts, tok.value)
|
||||||
|
tok = p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
||||||
|
}
|
||||||
|
if p.done && tok.value != "]" {
|
||||||
|
return "", p.errorf("unclosed type_url or extension name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
||||||
|
// It is used in readStruct to provide backward compatibility.
|
||||||
|
func (p *textParser) consumeOptionalSeparator() error {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value != ";" && tok.value != "," {
|
||||||
|
p.back()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value == "" {
|
||||||
|
return p.errorf("unexpected EOF")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fv := v; fv.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
at := v.Type()
|
||||||
|
if at.Elem().Kind() == reflect.Uint8 {
|
||||||
|
// Special case for []byte
|
||||||
|
if tok.value[0] != '"' && tok.value[0] != '\'' {
|
||||||
|
// Deliberately written out here, as the error after
|
||||||
|
// this switch statement would write "invalid []byte: ...",
|
||||||
|
// which is not as user-friendly.
|
||||||
|
return p.errorf("invalid string: %v", tok.value)
|
||||||
|
}
|
||||||
|
bytes := []byte(tok.unquoted)
|
||||||
|
fv.Set(reflect.ValueOf(bytes))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Repeated field.
|
||||||
|
if tok.value == "[" {
|
||||||
|
// Repeated field with list notation, like [1,2,3].
|
||||||
|
for {
|
||||||
|
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
||||||
|
err := p.readAny(fv.Index(fv.Len()-1), props)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tok := p.next()
|
||||||
|
if tok.err != nil {
|
||||||
|
return tok.err
|
||||||
|
}
|
||||||
|
if tok.value == "]" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tok.value != "," {
|
||||||
|
return p.errorf("Expected ']' or ',' found %q", tok.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// One value of the repeated field.
|
||||||
|
p.back()
|
||||||
|
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
||||||
|
return p.readAny(fv.Index(fv.Len()-1), props)
|
||||||
|
case reflect.Bool:
|
||||||
|
// true/1/t/True or false/f/0/False.
|
||||||
|
switch tok.value {
|
||||||
|
case "true", "1", "t", "True":
|
||||||
|
fv.SetBool(true)
|
||||||
|
return nil
|
||||||
|
case "false", "0", "f", "False":
|
||||||
|
fv.SetBool(false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
v := tok.value
|
||||||
|
// Ignore 'f' for compatibility with output generated by C++, but don't
|
||||||
|
// remove 'f' when the value is "-inf" or "inf".
|
||||||
|
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
|
||||||
|
v = v[:len(v)-1]
|
||||||
|
}
|
||||||
|
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
|
||||||
|
fv.SetFloat(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case reflect.Int32:
|
||||||
|
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
||||||
|
fv.SetInt(x)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(props.Enum) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m, ok := enumValueMaps[props.Enum]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
x, ok := m[tok.value]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fv.SetInt(int64(x))
|
||||||
|
return nil
|
||||||
|
case reflect.Int64:
|
||||||
|
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
||||||
|
fv.SetInt(x)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// A basic field (indirected through pointer), or a repeated message/group
|
||||||
|
p.back()
|
||||||
|
fv.Set(reflect.New(fv.Type().Elem()))
|
||||||
|
return p.readAny(fv.Elem(), props)
|
||||||
|
case reflect.String:
|
||||||
|
if tok.value[0] == '"' || tok.value[0] == '\'' {
|
||||||
|
fv.SetString(tok.unquoted)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
var terminator string
|
||||||
|
switch tok.value {
|
||||||
|
case "{":
|
||||||
|
terminator = "}"
|
||||||
|
case "<":
|
||||||
|
terminator = ">"
|
||||||
|
default:
|
||||||
|
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||||
|
}
|
||||||
|
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
|
||||||
|
return p.readStruct(fv, terminator)
|
||||||
|
case reflect.Uint32:
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
||||||
|
fv.SetUint(uint64(x))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
||||||
|
fv.SetUint(x)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.errorf("invalid %v: %v", v.Type(), tok.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
|
||||||
|
// before starting to unmarshal, so any existing data in pb is always removed.
|
||||||
|
// If a required field is not set and no other error occurs,
|
||||||
|
// UnmarshalText returns *RequiredNotSetError.
|
||||||
|
func UnmarshalText(s string, pb Message) error {
|
||||||
|
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
||||||
|
return um.UnmarshalText([]byte(s))
|
||||||
|
}
|
||||||
|
pb.Reset()
|
||||||
|
v := reflect.ValueOf(pb)
|
||||||
|
return newTextParser(s).readStruct(v.Elem(), "")
|
||||||
|
}
|
3
vendor/github.com/json-iterator/go/.codecov.yml
generated
vendored
Normal file
3
vendor/github.com/json-iterator/go/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ignore:
|
||||||
|
- "output_tests/.*"
|
||||||
|
|
4
vendor/github.com/json-iterator/go/.gitignore
generated
vendored
Normal file
4
vendor/github.com/json-iterator/go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/vendor
|
||||||
|
/bug_test.go
|
||||||
|
/coverage.txt
|
||||||
|
/.idea
|
14
vendor/github.com/json-iterator/go/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/json-iterator/go/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.8.x
|
||||||
|
- 1.x
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get -t -v ./...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./test.sh
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
21
vendor/github.com/json-iterator/go/Gopkg.lock
generated
vendored
Normal file
21
vendor/github.com/json-iterator/go/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/modern-go/concurrent"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/modern-go/reflect2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||||
|
version = "1.0.1"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
26
vendor/github.com/json-iterator/go/Gopkg.toml
generated
vendored
Normal file
26
vendor/github.com/json-iterator/go/Gopkg.toml
generated
vendored
Normal file
@ -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"
|
||||||
|
|
||||||
|
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/modern-go/reflect2"
|
||||||
|
version = "1.0.1"
|
21
vendor/github.com/json-iterator/go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/json-iterator/go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 json-iterator
|
||||||
|
|
||||||
|
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.
|
91
vendor/github.com/json-iterator/go/README.md
generated
vendored
Normal file
91
vendor/github.com/json-iterator/go/README.md
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
[![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge)
|
||||||
|
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/json-iterator/go)
|
||||||
|
[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go)
|
||||||
|
[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go)
|
||||||
|
[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go)
|
||||||
|
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE)
|
||||||
|
[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
|
||||||
|
|
||||||
|
A high-performance 100% compatible drop-in replacement of "encoding/json"
|
||||||
|
|
||||||
|
You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go)
|
||||||
|
|
||||||
|
```
|
||||||
|
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
|
||||||
|
```
|
||||||
|
|
||||||
|
# Benchmark
|
||||||
|
|
||||||
|
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
|
||||||
|
|
||||||
|
Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go
|
||||||
|
|
||||||
|
Raw Result (easyjson requires static code generation)
|
||||||
|
|
||||||
|
| | ns/op | allocation bytes | allocation times |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
|
||||||
|
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
|
||||||
|
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
|
||||||
|
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
|
||||||
|
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
||||||
|
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
||||||
|
|
||||||
|
Always benchmark with your own workload.
|
||||||
|
The result depends heavily on the data input.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
100% compatibility with standard lib
|
||||||
|
|
||||||
|
Replace
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "encoding/json"
|
||||||
|
json.Marshal(&data)
|
||||||
|
```
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/json-iterator/go"
|
||||||
|
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
json.Marshal(&data)
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "encoding/json"
|
||||||
|
json.Unmarshal(input, &data)
|
||||||
|
```
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/json-iterator/go"
|
||||||
|
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
json.Unmarshal(input, &data)
|
||||||
|
```
|
||||||
|
|
||||||
|
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
|
||||||
|
|
||||||
|
# How to get
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/json-iterator/go
|
||||||
|
```
|
||||||
|
|
||||||
|
# Contribution Welcomed !
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
|
||||||
|
* [thockin](https://github.com/thockin)
|
||||||
|
* [mattn](https://github.com/mattn)
|
||||||
|
* [cch123](https://github.com/cch123)
|
||||||
|
* [Oleg Shaldybin](https://github.com/olegshaldybin)
|
||||||
|
* [Jason Toffaletti](https://github.com/toffaletti)
|
||||||
|
|
||||||
|
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
|
150
vendor/github.com/json-iterator/go/adapter.go
generated
vendored
Normal file
150
vendor/github.com/json-iterator/go/adapter.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package jsoniter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawMessage to make replace json with jsoniter
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
// Unmarshal adapts to json/encoding Unmarshal API
|
||||||
|
//
|
||||||
|
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||||
|
// Refer to https://godoc.org/encoding/json#Unmarshal for more information
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return ConfigDefault.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalFromString convenient method to read from string instead of []byte
|
||||||
|
func UnmarshalFromString(str string, v interface{}) error {
|
||||||
|
return ConfigDefault.UnmarshalFromString(str, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get quick method to get value from deeply nested JSON structure
|
||||||
|
func Get(data []byte, path ...interface{}) Any {
|
||||||
|
return ConfigDefault.Get(data, path...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal adapts to json/encoding Marshal API
|
||||||
|
//
|
||||||
|
// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API
|
||||||
|
// Refer to https://godoc.org/encoding/json#Marshal for more information
|
||||||
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return ConfigDefault.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalIndent same as json.MarshalIndent. Prefix is not supported.
|
||||||
|
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
return ConfigDefault.MarshalIndent(v, prefix, indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalToString convenient method to write as string instead of []byte
|
||||||
|
func MarshalToString(v interface{}) (string, error) {
|
||||||
|
return ConfigDefault.MarshalToString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder adapts to json/stream NewDecoder API.
|
||||||
|
//
|
||||||
|
// NewDecoder returns a new decoder that reads from r.
|
||||||
|
//
|
||||||
|
// Instead of a json/encoding Decoder, an Decoder is returned
|
||||||
|
// Refer to https://godoc.org/encoding/json#NewDecoder for more information
|
||||||
|
func NewDecoder(reader io.Reader) *Decoder {
|
||||||
|
return ConfigDefault.NewDecoder(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder reads and decodes JSON values from an input stream.
|
||||||
|
// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress)
|
||||||
|
type Decoder struct {
|
||||||
|
iter *Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decode JSON into interface{}
|
||||||
|
func (adapter *Decoder) Decode(obj interface{}) error {
|
||||||
|
if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil {
|
||||||
|
if !adapter.iter.loadMore() {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.iter.ReadVal(obj)
|
||||||
|
err := adapter.iter.Error
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return adapter.iter.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// More is there more?
|
||||||
|
func (adapter *Decoder) More() bool {
|
||||||
|
iter := adapter.iter
|
||||||
|
if iter.Error != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iter.unreadByte()
|
||||||
|
return c != ']' && c != '}'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffered remaining buffer
|
||||||
|
func (adapter *Decoder) Buffered() io.Reader {
|
||||||
|
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
|
||||||
|
return bytes.NewReader(remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||||
|
// Number instead of as a float64.
|
||||||
|
func (adapter *Decoder) UseNumber() {
|
||||||
|
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||||
|
cfg.UseNumber = true
|
||||||
|
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (adapter *Decoder) DisallowUnknownFields() {
|
||||||
|
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||||
|
cfg.DisallowUnknownFields = true
|
||||||
|
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder same as json.NewEncoder
|
||||||
|
func NewEncoder(writer io.Writer) *Encoder {
|
||||||
|
return ConfigDefault.NewEncoder(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder same as json.Encoder
|
||||||
|
type Encoder struct {
|
||||||
|
stream *Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encode interface{} as JSON to io.Writer
|
||||||
|
func (adapter *Encoder) Encode(val interface{}) error {
|
||||||
|
adapter.stream.WriteVal(val)
|
||||||
|
adapter.stream.WriteRaw("\n")
|
||||||
|
adapter.stream.Flush()
|
||||||
|
return adapter.stream.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIndent set the indention. Prefix is not supported
|
||||||
|
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
||||||
|
config := adapter.stream.cfg.configBeforeFrozen
|
||||||
|
config.IndentionStep = len(indent)
|
||||||
|
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEscapeHTML escape html by default, set to false to disable
|
||||||
|
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
||||||
|
config := adapter.stream.cfg.configBeforeFrozen
|
||||||
|
config.EscapeHTML = escapeHTML
|
||||||
|
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid reports whether data is a valid JSON encoding.
|
||||||
|
func Valid(data []byte) bool {
|
||||||
|
return ConfigDefault.Valid(data)
|
||||||
|
}
|
321
vendor/github.com/json-iterator/go/any.go
generated
vendored
Normal file
321
vendor/github.com/json-iterator/go/any.go
generated
vendored
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
package jsoniter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Any generic object representation.
|
||||||
|
// The lazy json implementation holds []byte and parse lazily.
|
||||||
|
type Any interface {
|
||||||
|
LastError() error
|
||||||
|
ValueType() ValueType
|
||||||
|
MustBeValid() Any
|
||||||
|
ToBool() bool
|
||||||
|
ToInt() int
|
||||||
|
ToInt32() int32
|
||||||
|
ToInt64() int64
|
||||||
|
ToUint() uint
|
||||||
|
ToUint32() uint32
|
||||||
|
ToUint64() uint64
|
||||||
|
ToFloat32() float32
|
||||||
|
ToFloat64() float64
|
||||||
|
ToString() string
|
||||||
|
ToVal(val interface{})
|
||||||
|
Get(path ...interface{}) Any
|
||||||
|
Size() int
|
||||||
|
Keys() []string
|
||||||
|
GetInterface() interface{}
|
||||||
|
WriteTo(stream *Stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
type baseAny struct{}
|
||||||
|
|
||||||
|
func (any *baseAny) Get(path ...interface{}) Any {
|
||||||
|
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (any *baseAny) Size() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (any *baseAny) Keys() []string {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (any *baseAny) ToVal(obj interface{}) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInt32 turn int32 into Any interface
|
||||||
|
func WrapInt32(val int32) Any {
|
||||||
|
return &int32Any{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapInt64 turn int64 into Any interface
|
||||||
|
func WrapInt64(val int64) Any {
|
||||||
|
return &int64Any{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapUint32 turn uint32 into Any interface
|
||||||
|
func WrapUint32(val uint32) Any {
|
||||||
|
return &uint32Any{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapUint64 turn uint64 into Any interface
|
||||||
|
func WrapUint64(val uint64) Any {
|
||||||
|
return &uint64Any{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapFloat64 turn float64 into Any interface
|
||||||
|
func WrapFloat64(val float64) Any {
|
||||||
|
return &floatAny{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapString turn string into Any interface
|
||||||
|
func WrapString(val string) Any {
|
||||||
|
return &stringAny{baseAny{}, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap turn a go object into Any interface
|
||||||
|
func Wrap(val interface{}) Any {
|
||||||
|
if val == nil {
|
||||||
|
return &nilAny{}
|
||||||
|
}
|
||||||
|
asAny, isAny := val.(Any)
|
||||||
|
if isAny {
|
||||||
|
return asAny
|
||||||
|
}
|
||||||
|
typ := reflect2.TypeOf(val)
|
||||||
|
switch typ.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return wrapArray(val)
|
||||||
|
case reflect.Struct:
|
||||||
|
return wrapStruct(val)
|
||||||
|
case reflect.Map:
|
||||||
|
return wrapMap(val)
|
||||||
|
case reflect.String:
|
||||||
|
return WrapString(val.(string))
|
||||||
|
case reflect.Int:
|
||||||
|
if strconv.IntSize == 32 {
|
||||||
|
return WrapInt32(int32(val.(int)))
|
||||||
|
}
|
||||||
|
return WrapInt64(int64(val.(int)))
|
||||||
|
case reflect.Int8:
|
||||||
|
return WrapInt32(int32(val.(int8)))
|
||||||
|
case reflect.Int16:
|
||||||
|
return WrapInt32(int32(val.(int16)))
|
||||||
|
case reflect.Int32:
|
||||||
|
return WrapInt32(val.(int32))
|
||||||
|
case reflect.Int64:
|
||||||
|
return WrapInt64(val.(int64))
|
||||||
|
case reflect.Uint:
|
||||||
|
if strconv.IntSize == 32 {
|
||||||
|
return WrapUint32(uint32(val.(uint)))
|
||||||
|
}
|
||||||
|
return WrapUint64(uint64(val.(uint)))
|
||||||
|
case reflect.Uintptr:
|
||||||
|
if ptrSize == 32 {
|
||||||
|
return WrapUint32(uint32(val.(uintptr)))
|
||||||
|
}
|
||||||
|
return WrapUint64(uint64(val.(uintptr)))
|
||||||
|
case reflect.Uint8:
|
||||||
|
return WrapUint32(uint32(val.(uint8)))
|
||||||
|
case reflect.Uint16:
|
||||||
|
return WrapUint32(uint32(val.(uint16)))
|
||||||
|
case reflect.Uint32:
|
||||||
|
return WrapUint32(uint32(val.(uint32)))
|
||||||
|
case reflect.Uint64:
|
||||||
|
return WrapUint64(val.(uint64))
|
||||||
|
case reflect.Float32:
|
||||||
|
return WrapFloat64(float64(val.(float32)))
|
||||||
|
case reflect.Float64:
|
||||||
|
return WrapFloat64(val.(float64))
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.(bool) == true {
|
||||||
|
return &trueAny{}
|
||||||
|
}
|
||||||
|
return &falseAny{}
|
||||||
|
}
|
||||||
|
return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
|
||||||
|
func (iter *Iterator) ReadAny() Any {
|
||||||
|
return iter.readAny()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) readAny() Any {
|
||||||
|
c := iter.nextToken()
|
||||||
|
switch c {
|
||||||
|
case '"':
|
||||||
|
iter.unreadByte()
|
||||||
|
return &stringAny{baseAny{}, iter.ReadString()}
|
||||||
|
case 'n':
|
||||||
|
iter.skipThreeBytes('u', 'l', 'l') // null
|
||||||
|
return &nilAny{}
|
||||||
|
case 't':
|
||||||
|
iter.skipThreeBytes('r', 'u', 'e') // true
|
||||||
|
return &trueAny{}
|
||||||
|
case 'f':
|
||||||
|
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
||||||
|
return &falseAny{}
|
||||||
|
case '{':
|
||||||
|
return iter.readObjectAny()
|
||||||
|
case '[':
|
||||||
|
return iter.readArrayAny()
|
||||||
|
case '-':
|
||||||
|
return iter.readNumberAny(false)
|
||||||
|
case 0:
|
||||||
|
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
||||||
|
default:
|
||||||
|
return iter.readNumberAny(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) readNumberAny(positive bool) Any {
|
||||||
|
iter.startCapture(iter.head - 1)
|
||||||
|
iter.skipNumber()
|
||||||
|
lazyBuf := iter.stopCapture()
|
||||||
|
return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) readObjectAny() Any {
|
||||||
|
iter.startCapture(iter.head - 1)
|
||||||
|
iter.skipObject()
|
||||||
|
lazyBuf := iter.stopCapture()
|
||||||
|
return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) readArrayAny() Any {
|
||||||
|
iter.startCapture(iter.head - 1)
|
||||||
|
iter.skipArray()
|
||||||
|
lazyBuf := iter.stopCapture()
|
||||||
|
return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func locateObjectField(iter *Iterator, target string) []byte {
|
||||||
|
var found []byte
|
||||||
|
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||||
|
if field == target {
|
||||||
|
found = iter.SkipAndReturnBytes()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iter.Skip()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func locateArrayElement(iter *Iterator, target int) []byte {
|
||||||
|
var found []byte
|
||||||
|
n := 0
|
||||||
|
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||||
|
if n == target {
|
||||||
|
found = iter.SkipAndReturnBytes()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iter.Skip()
|
||||||
|
n++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func locatePath(iter *Iterator, path []interface{}) Any {
|
||||||
|
for i, pathKeyObj := range path {
|
||||||
|
switch pathKey := pathKeyObj.(type) {
|
||||||
|
case string:
|
||||||
|
valueBytes := locateObjectField(iter, pathKey)
|
||||||
|
if valueBytes == nil {
|
||||||
|
return newInvalidAny(path[i:])
|
||||||
|
}
|
||||||
|
iter.ResetBytes(valueBytes)
|
||||||
|
case int:
|
||||||
|
valueBytes := locateArrayElement(iter, pathKey)
|
||||||
|
if valueBytes == nil {
|
||||||
|
return newInvalidAny(path[i:])
|
||||||
|
}
|
||||||
|
iter.ResetBytes(valueBytes)
|
||||||
|
case int32:
|
||||||
|
if '*' == pathKey {
|
||||||
|
return iter.readAny().Get(path[i:]...)
|
||||||
|
}
|
||||||
|
return newInvalidAny(path[i:])
|
||||||
|
default:
|
||||||
|
return newInvalidAny(path[i:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
|
return &invalidAny{baseAny{}, iter.Error}
|
||||||
|
}
|
||||||
|
return iter.readAny()
|
||||||
|
}
|
||||||
|
|
||||||
|
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
|
||||||
|
|
||||||
|
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
|
||||||
|
if typ == anyType {
|
||||||
|
return &directAnyCodec{}
|
||||||
|
}
|
||||||
|
if typ.Implements(anyType) {
|
||||||
|
return &anyCodec{
|
||||||
|
valType: typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
|
||||||
|
if typ == anyType {
|
||||||
|
return &directAnyCodec{}
|
||||||
|
}
|
||||||
|
if typ.Implements(anyType) {
|
||||||
|
return &anyCodec{
|
||||||
|
valType: typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type anyCodec struct {
|
||||||
|
valType reflect2.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
obj := codec.valType.UnsafeIndirect(ptr)
|
||||||
|
any := obj.(Any)
|
||||||
|
any.WriteTo(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
|
obj := codec.valType.UnsafeIndirect(ptr)
|
||||||
|
any := obj.(Any)
|
||||||
|
return any.Size() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type directAnyCodec struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
|
*(*Any)(ptr) = iter.readAny()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
any := *(*Any)(ptr)
|
||||||
|
any.WriteTo(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
|
any := *(*Any)(ptr)
|
||||||
|
return any.Size() == 0
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user