refactor: support AbortWithStatusJSON (#200)
This commit is contained in:
parent
d94c9437f5
commit
5b2b3695e5
|
@ -17,11 +17,10 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func abortWithError(c *gin.Context, code int, message string) {
|
func abortWithError(c *gin.Context, code int, message string) {
|
||||||
c.JSON(code, gin.H{
|
c.AbortWithStatusJSON(code, gin.H{
|
||||||
"code": code,
|
"code": code,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
c.Abort()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func rootHandler(c *gin.Context) {
|
func rootHandler(c *gin.Context) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
List of all the awesome people working to make Gin the best Web Framework in Go.
|
List of all the awesome people working to make Gin the best Web Framework in Go.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## gin 0.x series authors
|
## gin 0.x series authors
|
||||||
|
|
||||||
**Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
|
**Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
|
# Gin Web Framework <img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
||||||
|
|
||||||
#Gin Web Framework
|
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) [![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin) [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Sourcegraph Badge](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
||||||
|
|
||||||
<img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
|
||||||
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
|
|
||||||
[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin)
|
|
||||||
[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
|
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
|
||||||
|
|
||||||
|
@ -15,6 +9,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||||
```sh
|
```sh
|
||||||
$ cat test.go
|
$ cat test.go
|
||||||
```
|
```
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -102,12 +97,23 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648
|
||||||
import "net/http"
|
import "net/http"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4. (Optional) Use latest changes (note: they may be broken and/or unstable):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ GIN_PATH=$GOPATH/src/gopkg.in/gin-gonic/gin.v1
|
||||||
|
$ git -C $GIN_PATH checkout develop
|
||||||
|
$ git -C $GIN_PATH pull origin develop
|
||||||
|
```
|
||||||
|
|
||||||
## API Examples
|
## API Examples
|
||||||
|
|
||||||
#### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
// Disable Console Color
|
||||||
|
// gin.DisableConsoleColor()
|
||||||
|
|
||||||
// Creates a gin router with default middleware:
|
// Creates a gin router with default middleware:
|
||||||
// logger and recovery (crash-free) middleware
|
// logger and recovery (crash-free) middleware
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
@ -127,7 +133,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Parameters in path
|
### Parameters in path
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -152,7 +158,8 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Querystring parameters
|
### Querystring parameters
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
@ -219,34 +226,66 @@ func main() {
|
||||||
id: 1234; page: 1; name: manu; message: this_is_great
|
id: 1234; page: 1; name: manu; message: this_is_great
|
||||||
```
|
```
|
||||||
|
|
||||||
### Another example: upload file
|
### Upload files
|
||||||
|
|
||||||
References issue [#548](https://github.com/gin-gonic/gin/issues/548).
|
#### Single file
|
||||||
|
|
||||||
|
References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](examples/upload-file/single).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.POST("/upload", func(c *gin.Context) {
|
router.POST("/upload", func(c *gin.Context) {
|
||||||
|
// single file
|
||||||
|
file, _ := c.FormFile("file")
|
||||||
|
log.Println(file.Filename)
|
||||||
|
|
||||||
file, header , err := c.Request.FormFile("upload")
|
c.String(http.StatusOK, fmt.Printf("'%s' uploaded!", file.Filename))
|
||||||
filename := header.Filename
|
|
||||||
fmt.Println(header.Filename)
|
|
||||||
out, err := os.Create("./tmp/"+filename+".png")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
_, err = io.Copy(out, file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Grouping routes
|
How to `curl`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/upload \
|
||||||
|
-F "file=@/Users/appleboy/test.zip" \
|
||||||
|
-H "Content-Type: multipart/form-data"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multiple files
|
||||||
|
|
||||||
|
See the detail [example code](examples/upload-file/multiple).
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
router := gin.Default()
|
||||||
|
router.POST("/upload", func(c *gin.Context) {
|
||||||
|
// Multipart form
|
||||||
|
form, _ := c.MultipartForm()
|
||||||
|
files := form.File["upload[]"]
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
log.Println(file.Filename)
|
||||||
|
}
|
||||||
|
c.String(http.StatusOK, fmt.Printf("%d files uploaded!", len(files)))
|
||||||
|
})
|
||||||
|
router.Run(":8080")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
How to `curl`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/upload \
|
||||||
|
-F "upload[]=@/Users/appleboy/test1.zip" \
|
||||||
|
-F "upload[]=@/Users/appleboy/test2.zip" \
|
||||||
|
-H "Content-Type: multipart/form-data"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grouping routes
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
@ -271,14 +310,14 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Blank Gin without middleware by default
|
||||||
#### Blank Gin without middleware by default
|
|
||||||
|
|
||||||
Use
|
Use
|
||||||
|
|
||||||
```go
|
```go
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
```
|
```
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -286,7 +325,7 @@ r := gin.Default()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Using middleware
|
### Using middleware
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
// Creates a router without any middleware by default
|
// Creates a router without any middleware by default
|
||||||
|
@ -321,7 +360,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Model binding and validation
|
### Model binding and validation
|
||||||
|
|
||||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
||||||
|
|
||||||
|
@ -371,8 +410,43 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Bind Query String
|
||||||
|
|
||||||
|
See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292).
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string `form:"name"`
|
||||||
|
Address string `form:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
route := gin.Default()
|
||||||
|
route.GET("/testing", startPage)
|
||||||
|
route.Run(":8085")
|
||||||
|
}
|
||||||
|
|
||||||
|
func startPage(c *gin.Context) {
|
||||||
|
var person Person
|
||||||
|
// If `GET`, only `Form` binding engine (`query`) used.
|
||||||
|
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
|
||||||
|
// See more at https://github.com/gin-gonic/gin/blob/develop/binding/binding.go#L45
|
||||||
|
if c.Bind(&person) == nil {
|
||||||
|
log.Println(person.Name)
|
||||||
|
log.Println(person.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(200, "Success")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Multipart/Urlencoded binding
|
### Multipart/Urlencoded binding
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -410,8 +484,7 @@ Test it with:
|
||||||
$ curl -v --form user=user --form password=password http://localhost:8080/login
|
$ curl -v --form user=user --form password=password http://localhost:8080/login
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### XML, JSON and YAML rendering
|
||||||
#### XML, JSON and YAML rendering
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -450,7 +523,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
####Serving static files
|
### Serving static files
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -464,9 +537,9 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
####HTML rendering
|
### HTML rendering
|
||||||
|
|
||||||
Using LoadHTMLTemplates()
|
Using LoadHTMLGlob() or LoadHTMLFiles()
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -481,7 +554,9 @@ func main() {
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
templates/index.tmpl
|
templates/index.tmpl
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<html>
|
<html>
|
||||||
<h1>
|
<h1>
|
||||||
|
@ -509,7 +584,9 @@ func main() {
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
templates/posts/index.tmpl
|
templates/posts/index.tmpl
|
||||||
|
|
||||||
```html
|
```html
|
||||||
{{ define "posts/index.tmpl" }}
|
{{ define "posts/index.tmpl" }}
|
||||||
<html><h1>
|
<html><h1>
|
||||||
|
@ -519,7 +596,9 @@ templates/posts/index.tmpl
|
||||||
</html>
|
</html>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
```
|
```
|
||||||
|
|
||||||
templates/users/index.tmpl
|
templates/users/index.tmpl
|
||||||
|
|
||||||
```html
|
```html
|
||||||
{{ define "users/index.tmpl" }}
|
{{ define "users/index.tmpl" }}
|
||||||
<html><h1>
|
<html><h1>
|
||||||
|
@ -543,8 +622,11 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Multitemplate
|
||||||
|
|
||||||
#### Redirects
|
Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`.
|
||||||
|
|
||||||
|
### Redirects
|
||||||
|
|
||||||
Issuing a HTTP redirect is easy:
|
Issuing a HTTP redirect is easy:
|
||||||
|
|
||||||
|
@ -556,7 +638,7 @@ r.GET("/test", func(c *gin.Context) {
|
||||||
Both internal and external locations are supported.
|
Both internal and external locations are supported.
|
||||||
|
|
||||||
|
|
||||||
#### Custom Middleware
|
### Custom Middleware
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Logger() gin.HandlerFunc {
|
func Logger() gin.HandlerFunc {
|
||||||
|
@ -596,7 +678,8 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using BasicAuth() middleware
|
### Using BasicAuth() middleware
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// simulate some private data
|
// simulate some private data
|
||||||
var secrets = gin.H{
|
var secrets = gin.H{
|
||||||
|
@ -634,8 +717,8 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Goroutines inside a middleware
|
||||||
|
|
||||||
#### Goroutines inside a middleware
|
|
||||||
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -667,7 +750,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Custom HTTP configuration
|
### Custom HTTP configuration
|
||||||
|
|
||||||
Use `http.ListenAndServe()` directly, like this:
|
Use `http.ListenAndServe()` directly, like this:
|
||||||
|
|
||||||
|
@ -694,7 +777,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Graceful restart or stop
|
### Graceful restart or stop
|
||||||
|
|
||||||
Do you want to graceful restart or stop your web server?
|
Do you want to graceful restart or stop your web server?
|
||||||
There are some ways this can be done.
|
There are some ways this can be done.
|
||||||
|
@ -711,6 +794,62 @@ endless.ListenAndServe(":4242", router)
|
||||||
An alternative to endless:
|
An alternative to endless:
|
||||||
|
|
||||||
* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.
|
* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.
|
||||||
|
* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server.
|
||||||
|
* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers.
|
||||||
|
|
||||||
|
If you are using Go 1.8, you may not need to use this library! Consider using http.Server's built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. See the full [graceful-shutdown](./examples/graceful-shutdown) example with gin.
|
||||||
|
|
||||||
|
[embedmd]:# (examples/graceful-shutdown/graceful-shutdown/server.go go)
|
||||||
|
```go
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
router := gin.Default()
|
||||||
|
router.GET("/", func(c *gin.Context) {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
c.String(http.StatusOK, "Welcome Gin Server")
|
||||||
|
})
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: ":8080",
|
||||||
|
Handler: router,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// service connections
|
||||||
|
if err := srv.ListenAndServe(); err != nil {
|
||||||
|
log.Printf("listen: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for interrupt signal to gracefully shutdown the server with
|
||||||
|
// a timeout of 5 seconds.
|
||||||
|
quit := make(chan os.Signal)
|
||||||
|
signal.Notify(quit, os.Interrupt)
|
||||||
|
<-quit
|
||||||
|
log.Println("Shutdown Server ...")
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
|
log.Fatal("Server Shutdown:", err)
|
||||||
|
}
|
||||||
|
log.Println("Server exist")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -725,7 +864,7 @@ An alternative to endless:
|
||||||
- You should add/modify tests to cover your proposed code changes.
|
- 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.
|
- If your pull request contains a new feature, please document it on the README.
|
||||||
|
|
||||||
## Example
|
## Users
|
||||||
|
|
||||||
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
|
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ const (
|
||||||
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||||
MIMEMultipartPOSTForm = "multipart/form-data"
|
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||||
MIMEPROTOBUF = "application/x-protobuf"
|
MIMEPROTOBUF = "application/x-protobuf"
|
||||||
|
MIMEMSGPACK = "application/x-msgpack"
|
||||||
|
MIMEMSGPACK2 = "application/msgpack"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Binding interface {
|
type Binding interface {
|
||||||
|
@ -40,6 +42,7 @@ var (
|
||||||
FormPost = formPostBinding{}
|
FormPost = formPostBinding{}
|
||||||
FormMultipart = formMultipartBinding{}
|
FormMultipart = formMultipartBinding{}
|
||||||
ProtoBuf = protobufBinding{}
|
ProtoBuf = protobufBinding{}
|
||||||
|
MsgPack = msgpackBinding{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Default(method, contentType string) Binding {
|
func Default(method, contentType string) Binding {
|
||||||
|
@ -53,6 +56,8 @@ func Default(method, contentType string) Binding {
|
||||||
return XML
|
return XML
|
||||||
case MIMEPROTOBUF:
|
case MIMEPROTOBUF:
|
||||||
return ProtoBuf
|
return ProtoBuf
|
||||||
|
case MIMEMSGPACK, MIMEMSGPACK2:
|
||||||
|
return MsgPack
|
||||||
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
||||||
return Form
|
return Form
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||||
|
@ -52,6 +53,12 @@ func mapForm(ptr interface{}, form map[string][]string) error {
|
||||||
}
|
}
|
||||||
val.Field(i).Set(slice)
|
val.Field(i).Set(slice)
|
||||||
} else {
|
} 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 {
|
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -140,6 +147,26 @@ func setFloatField(val string, bitSize int, field reflect.Value) error {
|
||||||
return err
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
l := time.Local
|
||||||
|
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
|
||||||
|
l = time.UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.ParseInLocation(timeFormat, val, l)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Set(reflect.ValueOf(t))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
||||||
// https://github.com/codegangsta/martini-contrib/issues/40
|
// https://github.com/codegangsta/martini-contrib/issues/40
|
||||||
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgpackBinding struct{}
|
||||||
|
|
||||||
|
func (msgpackBinding) Name() string {
|
||||||
|
return "msgpack"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
|
||||||
|
if err := codec.NewDecoder(req.Body, new(codec.MsgpackHandle)).Decode(&obj); err != nil {
|
||||||
|
//var decoder *codec.Decoder = codec.NewDecoder(req.Body, &codec.MsgpackHandle)
|
||||||
|
//if err := decoder.Decode(&obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"mime/multipart"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -16,8 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
"github.com/manucorporat/sse"
|
"gopkg.in/gin-contrib/sse.v0"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Content-Type MIME of the most common data formats
|
// Content-Type MIME of the most common data formats
|
||||||
|
@ -31,7 +31,10 @@ const (
|
||||||
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
||||||
)
|
)
|
||||||
|
|
||||||
const abortIndex int8 = math.MaxInt8 / 2
|
const (
|
||||||
|
defaultMemory = 32 << 20 // 32 MB
|
||||||
|
abortIndex int8 = math.MaxInt8 / 2
|
||||||
|
)
|
||||||
|
|
||||||
// Context is the most important part of gin. It allows us to pass variables between middleware,
|
// 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.
|
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
||||||
|
@ -50,8 +53,6 @@ type Context struct {
|
||||||
Accepted []string
|
Accepted []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ context.Context = &Context{}
|
|
||||||
|
|
||||||
/************************************/
|
/************************************/
|
||||||
/********** CONTEXT CREATION ********/
|
/********** CONTEXT CREATION ********/
|
||||||
/************************************/
|
/************************************/
|
||||||
|
@ -67,7 +68,7 @@ func (c *Context) reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of the current context that can be safely used outside the request's scope.
|
// Copy returns a copy of the current context that can be safely used outside the request's scope.
|
||||||
// This have to be used then the context has to be passed to a goroutine.
|
// This has to be used when the context has to be passed to a goroutine.
|
||||||
func (c *Context) Copy() *Context {
|
func (c *Context) Copy() *Context {
|
||||||
var cp = *c
|
var cp = *c
|
||||||
cp.writermem.ResponseWriter = nil
|
cp.writermem.ResponseWriter = nil
|
||||||
|
@ -119,6 +120,13 @@ func (c *Context) AbortWithStatus(code int) {
|
||||||
c.Abort()
|
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
|
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and
|
||||||
// pushes the specified error to `c.Errors`.
|
// pushes the specified error to `c.Errors`.
|
||||||
// See Context.Error() for more details.
|
// See Context.Error() for more details.
|
||||||
|
@ -166,9 +174,7 @@ func (c *Context) Set(key string, value interface{}) {
|
||||||
// Get returns the value for the given key, ie: (value, true).
|
// Get returns the value for the given key, ie: (value, true).
|
||||||
// If the value does not exists it returns (nil, false)
|
// If the value does not exists it returns (nil, false)
|
||||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
||||||
if c.Keys != nil {
|
|
||||||
value, exists = c.Keys[key]
|
value, exists = c.Keys[key]
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +302,7 @@ func (c *Context) PostFormArray(key string) []string {
|
||||||
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
||||||
req := c.Request
|
req := c.Request
|
||||||
req.ParseForm()
|
req.ParseForm()
|
||||||
req.ParseMultipartForm(32 << 20) // 32 MB
|
req.ParseMultipartForm(defaultMemory)
|
||||||
if values := req.PostForm[key]; len(values) > 0 {
|
if values := req.PostForm[key]; len(values) > 0 {
|
||||||
return values, true
|
return values, true
|
||||||
}
|
}
|
||||||
|
@ -308,6 +314,18 @@ func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
||||||
return []string{}, false
|
return []string{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(defaultMemory)
|
||||||
|
return c.Request.MultipartForm, err
|
||||||
|
}
|
||||||
|
|
||||||
// Bind checks the Content-Type to select a binding engine automatically,
|
// Bind checks the Content-Type to select a binding engine automatically,
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
// Depending the "Content-Type" header different bindings are used:
|
||||||
// "application/json" --> JSON binding
|
// "application/json" --> JSON binding
|
||||||
|
@ -338,13 +356,10 @@ func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||||
|
|
||||||
// ClientIP implements a best effort algorithm to return the real client IP, it parses
|
// 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.
|
// 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 {
|
func (c *Context) ClientIP() string {
|
||||||
if c.engine.ForwardedByClientIP {
|
if c.engine.ForwardedByClientIP {
|
||||||
clientIP := strings.TrimSpace(c.requestHeader("X-Real-Ip"))
|
clientIP := c.requestHeader("X-Forwarded-For")
|
||||||
if len(clientIP) > 0 {
|
|
||||||
return clientIP
|
|
||||||
}
|
|
||||||
clientIP = c.requestHeader("X-Forwarded-For")
|
|
||||||
if index := strings.IndexByte(clientIP, ','); index >= 0 {
|
if index := strings.IndexByte(clientIP, ','); index >= 0 {
|
||||||
clientIP = clientIP[0:index]
|
clientIP = clientIP[0:index]
|
||||||
}
|
}
|
||||||
|
@ -352,10 +367,22 @@ func (c *Context) ClientIP() string {
|
||||||
if len(clientIP) > 0 {
|
if len(clientIP) > 0 {
|
||||||
return clientIP
|
return clientIP
|
||||||
}
|
}
|
||||||
|
clientIP = strings.TrimSpace(c.requestHeader("X-Real-Ip"))
|
||||||
|
if len(clientIP) > 0 {
|
||||||
|
return clientIP
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.engine.AppEngine {
|
||||||
|
if addr := c.Request.Header.Get("X-Appengine-Remote-Addr"); addr != "" {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
|
if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,6 +391,16 @@ func (c *Context) ContentType() string {
|
||||||
return filterFlags(c.requestHeader("Content-Type"))
|
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 {
|
func (c *Context) requestHeader(key string) string {
|
||||||
if values, _ := c.Request.Header[key]; len(values) > 0 {
|
if values, _ := c.Request.Header[key]; len(values) > 0 {
|
||||||
return values[0]
|
return values[0]
|
||||||
|
@ -375,6 +412,19 @@ func (c *Context) requestHeader(key string) string {
|
||||||
/******** RESPONSE RENDERING ********/
|
/******** 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 == 204:
|
||||||
|
return false
|
||||||
|
case status == 304:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) Status(code int) {
|
func (c *Context) Status(code int) {
|
||||||
c.writermem.WriteHeader(code)
|
c.writermem.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
@ -390,6 +440,11 @@ func (c *Context) Header(key, value string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeader returns value from request headers
|
||||||
|
func (c *Context) GetHeader(key string) string {
|
||||||
|
return c.requestHeader(key)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) SetCookie(
|
func (c *Context) SetCookie(
|
||||||
name string,
|
name string,
|
||||||
value string,
|
value string,
|
||||||
|
@ -424,6 +479,13 @@ func (c *Context) Cookie(name string) (string, error) {
|
||||||
|
|
||||||
func (c *Context) Render(code int, r render.Render) {
|
func (c *Context) Render(code int, r render.Render) {
|
||||||
c.Status(code)
|
c.Status(code)
|
||||||
|
|
||||||
|
if !bodyAllowedForStatus(code) {
|
||||||
|
r.WriteContentType(c.Writer)
|
||||||
|
c.Writer.WriteHeaderNow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.Render(c.Writer); err != nil {
|
if err := r.Render(c.Writer); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -448,10 +510,7 @@ func (c *Context) IndentedJSON(code int, obj interface{}) {
|
||||||
// JSON serializes the given struct as JSON into the response body.
|
// JSON serializes the given struct as JSON into the response body.
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
func (c *Context) JSON(code int, obj interface{}) {
|
func (c *Context) JSON(code int, obj interface{}) {
|
||||||
c.Status(code)
|
c.Render(code, render.JSON{Data: obj})
|
||||||
if err := render.WriteJSON(c.Writer, obj); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XML serializes the given struct as XML into the response body.
|
// XML serializes the given struct as XML into the response body.
|
||||||
|
@ -467,8 +526,7 @@ func (c *Context) YAML(code int, obj interface{}) {
|
||||||
|
|
||||||
// String writes the given string into the response body.
|
// String writes the given string into the response body.
|
||||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
func (c *Context) String(code int, format string, values ...interface{}) {
|
||||||
c.Status(code)
|
c.Render(code, render.String{Format: format, Data: values})
|
||||||
render.WriteString(c.Writer, format, values)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect returns a HTTP redirect to the specific location.
|
// Redirect returns a HTTP redirect to the specific location.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package gin
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
defaultAppEngine = true
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the error interface
|
// Implements the error interface
|
||||||
func (msg *Error) Error() string {
|
func (msg Error) Error() string {
|
||||||
return msg.Err.Error()
|
return msg.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used interally
|
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
|
||||||
// in router.Static().
|
// in router.Static().
|
||||||
// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
|
// 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.
|
// a filesystem that prevents http.FileServer() to list the directory files.
|
||||||
|
|
|
@ -15,10 +15,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is Framework's version
|
// Version is Framework's version
|
||||||
const Version = "v1.0rc2"
|
const Version = "v1.1.4"
|
||||||
|
|
||||||
var default404Body = []byte("404 page not found")
|
var default404Body = []byte("404 page not found")
|
||||||
var default405Body = []byte("405 method not allowed")
|
var default405Body = []byte("405 method not allowed")
|
||||||
|
var defaultAppEngine bool
|
||||||
|
|
||||||
type HandlerFunc func(*Context)
|
type HandlerFunc func(*Context)
|
||||||
type HandlersChain []HandlerFunc
|
type HandlersChain []HandlerFunc
|
||||||
|
@ -78,6 +79,17 @@ type (
|
||||||
// handler.
|
// handler.
|
||||||
HandleMethodNotAllowed bool
|
HandleMethodNotAllowed bool
|
||||||
ForwardedByClientIP 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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -89,6 +101,8 @@ var _ IRouter = &Engine{}
|
||||||
// - RedirectFixedPath: false
|
// - RedirectFixedPath: false
|
||||||
// - HandleMethodNotAllowed: false
|
// - HandleMethodNotAllowed: false
|
||||||
// - ForwardedByClientIP: true
|
// - ForwardedByClientIP: true
|
||||||
|
// - UseRawPath: false
|
||||||
|
// - UnescapePathValues: true
|
||||||
func New() *Engine {
|
func New() *Engine {
|
||||||
debugPrintWARNINGNew()
|
debugPrintWARNINGNew()
|
||||||
engine := &Engine{
|
engine := &Engine{
|
||||||
|
@ -101,6 +115,9 @@ func New() *Engine {
|
||||||
RedirectFixedPath: false,
|
RedirectFixedPath: false,
|
||||||
HandleMethodNotAllowed: false,
|
HandleMethodNotAllowed: false,
|
||||||
ForwardedByClientIP: true,
|
ForwardedByClientIP: true,
|
||||||
|
AppEngine: defaultAppEngine,
|
||||||
|
UseRawPath: false,
|
||||||
|
UnescapePathValues: true,
|
||||||
trees: make(methodTrees, 0, 9),
|
trees: make(methodTrees, 0, 9),
|
||||||
}
|
}
|
||||||
engine.RouterGroup.engine = engine
|
engine.RouterGroup.engine = engine
|
||||||
|
@ -267,9 +284,26 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
engine.pool.Put(c)
|
engine.pool.Put(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-enter a context that has been rewritten.
|
||||||
|
// This can be done by setting c.Request.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(context *Context) {
|
func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||||
httpMethod := context.Request.Method
|
httpMethod := context.Request.Method
|
||||||
path := context.Request.URL.Path
|
var path string
|
||||||
|
var unescape bool
|
||||||
|
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
|
||||||
|
path = context.Request.URL.RawPath
|
||||||
|
unescape = engine.UnescapePathValues
|
||||||
|
} else {
|
||||||
|
path = context.Request.URL.Path
|
||||||
|
unescape = false
|
||||||
|
}
|
||||||
|
|
||||||
// Find root of the tree for the given HTTP method
|
// Find root of the tree for the given HTTP method
|
||||||
t := engine.trees
|
t := engine.trees
|
||||||
|
@ -277,7 +311,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||||
if t[i].method == httpMethod {
|
if t[i].method == httpMethod {
|
||||||
root := t[i].root
|
root := t[i].root
|
||||||
// Find route in tree
|
// Find route in tree
|
||||||
handlers, params, tsr := root.getValue(path, context.Params)
|
handlers, params, tsr := root.getValue(path, context.Params, unescape)
|
||||||
if handlers != nil {
|
if handlers != nil {
|
||||||
context.handlers = handlers
|
context.handlers = handlers
|
||||||
context.Params = params
|
context.Params = params
|
||||||
|
@ -302,7 +336,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||||
if engine.HandleMethodNotAllowed {
|
if engine.HandleMethodNotAllowed {
|
||||||
for _, tree := range engine.trees {
|
for _, tree := range engine.trees {
|
||||||
if tree.method != httpMethod {
|
if tree.method != httpMethod {
|
||||||
if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
|
if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
|
||||||
context.handlers = engine.allNoMethod
|
context.handlers = engine.allNoMethod
|
||||||
serveError(context, 405, default405Body)
|
serveError(context, 405, default405Body)
|
||||||
return
|
return
|
||||||
|
|
|
@ -22,8 +22,13 @@ var (
|
||||||
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
||||||
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
||||||
reset = string([]byte{27, 91, 48, 109})
|
reset = string([]byte{27, 91, 48, 109})
|
||||||
|
disableColor = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func DisableConsoleColor() {
|
||||||
|
disableColor = true
|
||||||
|
}
|
||||||
|
|
||||||
func ErrorLogger() HandlerFunc {
|
func ErrorLogger() HandlerFunc {
|
||||||
return ErrorLoggerT(ErrorTypeAny)
|
return ErrorLoggerT(ErrorTypeAny)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +54,9 @@ func Logger() HandlerFunc {
|
||||||
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
isTerm := true
|
isTerm := true
|
||||||
|
|
||||||
if w, ok := out.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) {
|
if w, ok := out.(*os.File); !ok ||
|
||||||
|
(os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) ||
|
||||||
|
disableColor {
|
||||||
isTerm = false
|
isTerm = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +94,12 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
}
|
}
|
||||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||||
|
|
||||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s",
|
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s",
|
||||||
end.Format("2006/01/02 - 15:04:05"),
|
end.Format("2006/01/02 - 15:04:05"),
|
||||||
statusColor, statusCode, reset,
|
statusColor, statusCode, reset,
|
||||||
latency,
|
latency,
|
||||||
clientIP,
|
clientIP,
|
||||||
methodColor, reset, method,
|
methodColor, method, reset,
|
||||||
path,
|
path,
|
||||||
comment,
|
comment,
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,10 +11,13 @@ type Data struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Data) Render(w http.ResponseWriter) error {
|
// Render (Data) writes data with custom ContentType
|
||||||
if len(r.ContentType) > 0 {
|
func (r Data) Render(w http.ResponseWriter) (err error) {
|
||||||
w.Header()["Content-Type"] = []string{r.ContentType}
|
r.WriteContentType(w)
|
||||||
|
_, err = w.Write(r.Data)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
w.Write(r.Data)
|
|
||||||
return nil
|
func (r Data) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, []string{r.ContentType})
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,9 +58,14 @@ func (r HTMLDebug) loadTemplate() *template.Template {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r HTML) Render(w http.ResponseWriter) error {
|
func (r HTML) Render(w http.ResponseWriter) error {
|
||||||
writeContentType(w, htmlContentType)
|
r.WriteContentType(w)
|
||||||
|
|
||||||
if len(r.Name) == 0 {
|
if len(r.Name) == 0 {
|
||||||
return r.Template.Execute(w, r.Data)
|
return r.Template.Execute(w, r.Data)
|
||||||
}
|
}
|
||||||
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r HTML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, htmlContentType)
|
||||||
|
}
|
||||||
|
|
|
@ -21,12 +21,29 @@ type (
|
||||||
|
|
||||||
var jsonContentType = []string{"application/json; charset=utf-8"}
|
var jsonContentType = []string{"application/json; charset=utf-8"}
|
||||||
|
|
||||||
func (r JSON) Render(w http.ResponseWriter) error {
|
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
||||||
return WriteJSON(w, r.Data)
|
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 {
|
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||||
writeContentType(w, jsonContentType)
|
r.WriteContentType(w)
|
||||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -35,7 +52,6 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
writeContentType(w, jsonContentType)
|
||||||
return json.NewEncoder(w).Encode(obj)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -22,3 +22,5 @@ func (r Redirect) Render(w http.ResponseWriter) error {
|
||||||
http.Redirect(w, r.Request, r.Location, r.Code)
|
http.Redirect(w, r.Request, r.Location, r.Code)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Redirect) WriteContentType(http.ResponseWriter) {}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "net/http"
|
||||||
|
|
||||||
type Render interface {
|
type Render interface {
|
||||||
Render(http.ResponseWriter) error
|
Render(http.ResponseWriter) error
|
||||||
|
WriteContentType(w http.ResponseWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,6 +22,8 @@ var (
|
||||||
_ HTMLRender = HTMLDebug{}
|
_ HTMLRender = HTMLDebug{}
|
||||||
_ HTMLRender = HTMLProduction{}
|
_ HTMLRender = HTMLProduction{}
|
||||||
_ Render = YAML{}
|
_ Render = YAML{}
|
||||||
|
_ Render = MsgPack{}
|
||||||
|
_ Render = MsgPack{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeContentType(w http.ResponseWriter, value []string) {
|
func writeContentType(w http.ResponseWriter, value []string) {
|
||||||
|
|
|
@ -22,9 +22,12 @@ func (r String) Render(w http.ResponseWriter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r String) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, plainContentType)
|
||||||
|
}
|
||||||
|
|
||||||
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
||||||
writeContentType(w, plainContentType)
|
writeContentType(w, plainContentType)
|
||||||
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
fmt.Fprintf(w, format, data...)
|
fmt.Fprintf(w, format, data...)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,6 +16,10 @@ type XML struct {
|
||||||
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
||||||
|
|
||||||
func (r XML) Render(w http.ResponseWriter) error {
|
func (r XML) Render(w http.ResponseWriter) error {
|
||||||
writeContentType(w, xmlContentType)
|
r.WriteContentType(w)
|
||||||
return xml.NewEncoder(w).Encode(r.Data)
|
return xml.NewEncoder(w).Encode(r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r XML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, xmlContentType)
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ type YAML struct {
|
||||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
||||||
|
|
||||||
func (r YAML) Render(w http.ResponseWriter) error {
|
func (r YAML) Render(w http.ResponseWriter) error {
|
||||||
writeContentType(w, yamlContentType)
|
r.WriteContentType(w)
|
||||||
|
|
||||||
bytes, err := yaml.Marshal(r.Data)
|
bytes, err := yaml.Marshal(r.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,3 +27,7 @@ func (r YAML) Render(w http.ResponseWriter) error {
|
||||||
w.Write(bytes)
|
w.Write(bytes)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r YAML) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, yamlContentType)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) {
|
||||||
|
r = New()
|
||||||
|
c = r.allocateContext()
|
||||||
|
c.reset()
|
||||||
|
c.writermem.reset(w)
|
||||||
|
return
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
@ -363,7 +364,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
|
||||||
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
// 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
|
// made if a handle exists with an extra (without the) trailing slash for the
|
||||||
// given path.
|
// given path.
|
||||||
func (n *node) getValue(path string, po Params) (handlers HandlersChain, p Params, tsr bool) {
|
func (n *node) getValue(path string, po Params, unescape bool) (handlers HandlersChain, p Params, tsr bool) {
|
||||||
p = po
|
p = po
|
||||||
walk: // Outer loop for walking the tree
|
walk: // Outer loop for walking the tree
|
||||||
for {
|
for {
|
||||||
|
@ -406,7 +407,15 @@ walk: // Outer loop for walking the tree
|
||||||
i := len(p)
|
i := len(p)
|
||||||
p = p[:i+1] // expand slice within preallocated capacity
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
p[i].Key = n.path[1:]
|
p[i].Key = n.path[1:]
|
||||||
p[i].Value = path[:end]
|
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!
|
// we need to go deeper!
|
||||||
if end < len(path) {
|
if end < len(path) {
|
||||||
|
@ -440,7 +449,14 @@ walk: // Outer loop for walking the tree
|
||||||
i := len(p)
|
i := len(p)
|
||||||
p = p[:i+1] // expand slice within preallocated capacity
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
p[i].Key = n.path[2:]
|
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
|
p[i].Value = path
|
||||||
|
}
|
||||||
|
|
||||||
handlers = n.handlers
|
handlers = n.handlers
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2015 Ugorji Nwoke.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
High Performance, Feature-Rich Idiomatic Go codec/encoding library for
|
||||||
|
binc, msgpack, cbor, json.
|
||||||
|
|
||||||
|
Supported Serialization formats are:
|
||||||
|
|
||||||
|
- msgpack: https://github.com/msgpack/msgpack
|
||||||
|
- binc: http://github.com/ugorji/binc
|
||||||
|
- cbor: http://cbor.io http://tools.ietf.org/html/rfc7049
|
||||||
|
- json: http://json.org http://tools.ietf.org/html/rfc7159
|
||||||
|
- simple:
|
||||||
|
|
||||||
|
To install:
|
||||||
|
|
||||||
|
go get github.com/ugorji/go/codec
|
||||||
|
|
||||||
|
This package understands the 'unsafe' tag, to allow using unsafe semantics:
|
||||||
|
|
||||||
|
- When decoding into a struct, you need to read the field name as a string
|
||||||
|
so you can find the struct field it is mapped to.
|
||||||
|
Using `unsafe` will bypass the allocation and copying overhead of []byte->string conversion.
|
||||||
|
|
||||||
|
To install using unsafe, pass the 'unsafe' tag:
|
||||||
|
|
||||||
|
go get -tags=unsafe github.com/ugorji/go/codec
|
||||||
|
|
||||||
|
For detailed usage information, read the primer at http://ugorji.net/blog/go-codec-primer .
|
||||||
|
|
||||||
|
The idiomatic Go support is as seen in other encoding packages in
|
||||||
|
the standard library (ie json, xml, gob, etc).
|
||||||
|
|
||||||
|
Rich Feature Set includes:
|
||||||
|
|
||||||
|
- Simple but extremely powerful and feature-rich API
|
||||||
|
- Very High Performance.
|
||||||
|
Our extensive benchmarks show us outperforming Gob, Json, Bson, etc by 2-4X.
|
||||||
|
- Multiple conversions:
|
||||||
|
Package coerces types where appropriate
|
||||||
|
e.g. decode an int in the stream into a float, etc.
|
||||||
|
- Corner Cases:
|
||||||
|
Overflows, nil maps/slices, nil values in streams are handled correctly
|
||||||
|
- Standard field renaming via tags
|
||||||
|
- Support for omitting empty fields during an encoding
|
||||||
|
- Encoding from any value and decoding into pointer to any value
|
||||||
|
(struct, slice, map, primitives, pointers, interface{}, etc)
|
||||||
|
- Extensions to support efficient encoding/decoding of any named types
|
||||||
|
- Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
|
||||||
|
- Decoding without a schema (into a interface{}).
|
||||||
|
Includes Options to configure what specific map or slice type to use
|
||||||
|
when decoding an encoded list or map into a nil interface{}
|
||||||
|
- Encode a struct as an array, and decode struct from an array in the data stream
|
||||||
|
- Comprehensive support for anonymous fields
|
||||||
|
- Fast (no-reflection) encoding/decoding of common maps and slices
|
||||||
|
- Code-generation for faster performance.
|
||||||
|
- Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
|
||||||
|
- Support indefinite-length formats to enable true streaming
|
||||||
|
(for formats which support it e.g. json, cbor)
|
||||||
|
- Support canonical encoding, where a value is ALWAYS encoded as same sequence of bytes.
|
||||||
|
This mostly applies to maps, where iteration order is non-deterministic.
|
||||||
|
- NIL in data stream decoded as zero value
|
||||||
|
- Never silently skip data when decoding.
|
||||||
|
User decides whether to return an error or silently skip data when keys or indexes
|
||||||
|
in the data stream do not map to fields in the struct.
|
||||||
|
- Detect and error when encoding a cyclic reference (instead of stack overflow shutdown)
|
||||||
|
- Encode/Decode from/to chan types (for iterative streaming support)
|
||||||
|
- Drop-in replacement for encoding/json. `json:` key in struct tag supported.
|
||||||
|
- Provides a RPC Server and Client Codec for net/rpc communication protocol.
|
||||||
|
- Handle unique idiosyncrasies of codecs e.g.
|
||||||
|
- For messagepack, configure how ambiguities in handling raw bytes are resolved
|
||||||
|
- For messagepack, provide rpc server/client codec to support
|
||||||
|
msgpack-rpc protocol defined at:
|
||||||
|
https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
|
||||||
|
|
||||||
|
Extension Support
|
||||||
|
|
||||||
|
Users can register a function to handle the encoding or decoding of
|
||||||
|
their custom types.
|
||||||
|
|
||||||
|
There are no restrictions on what the custom type can be. Some examples:
|
||||||
|
|
||||||
|
type BisSet []int
|
||||||
|
type BitSet64 uint64
|
||||||
|
type UUID string
|
||||||
|
type MyStructWithUnexportedFields struct { a int; b bool; c []int; }
|
||||||
|
type GifImage struct { ... }
|
||||||
|
|
||||||
|
As an illustration, MyStructWithUnexportedFields would normally be
|
||||||
|
encoded as an empty map because it has no exported fields, while UUID
|
||||||
|
would be encoded as a string. However, with extension support, you can
|
||||||
|
encode any of these however you like.
|
||||||
|
|
||||||
|
RPC
|
||||||
|
|
||||||
|
RPC Client and Server Codecs are implemented, so the codecs can be used
|
||||||
|
with the standard net/rpc package.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
The Handle is SAFE for concurrent READ, but NOT SAFE for concurrent modification.
|
||||||
|
|
||||||
|
The Encoder and Decoder are NOT safe for concurrent use.
|
||||||
|
|
||||||
|
Consequently, the usage model is basically:
|
||||||
|
|
||||||
|
- Create and initialize the Handle before any use.
|
||||||
|
Once created, DO NOT modify it.
|
||||||
|
- Multiple Encoders or Decoders can now use the Handle concurrently.
|
||||||
|
They only read information off the Handle (never write).
|
||||||
|
- However, each Encoder or Decoder MUST not be used concurrently
|
||||||
|
- To re-use an Encoder/Decoder, call Reset(...) on it first.
|
||||||
|
This allows you use state maintained on the Encoder/Decoder.
|
||||||
|
|
||||||
|
Sample usage model:
|
||||||
|
|
||||||
|
// create and configure Handle
|
||||||
|
var (
|
||||||
|
bh codec.BincHandle
|
||||||
|
mh codec.MsgpackHandle
|
||||||
|
ch codec.CborHandle
|
||||||
|
)
|
||||||
|
|
||||||
|
mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
|
||||||
|
|
||||||
|
// configure extensions
|
||||||
|
// e.g. for msgpack, define functions and enable Time support for tag 1
|
||||||
|
// mh.SetExt(reflect.TypeOf(time.Time{}), 1, myExt)
|
||||||
|
|
||||||
|
// create and use decoder/encoder
|
||||||
|
var (
|
||||||
|
r io.Reader
|
||||||
|
w io.Writer
|
||||||
|
b []byte
|
||||||
|
h = &bh // or mh to use msgpack
|
||||||
|
)
|
||||||
|
|
||||||
|
dec = codec.NewDecoder(r, h)
|
||||||
|
dec = codec.NewDecoderBytes(b, h)
|
||||||
|
err = dec.Decode(&v)
|
||||||
|
|
||||||
|
enc = codec.NewEncoder(w, h)
|
||||||
|
enc = codec.NewEncoderBytes(&b, h)
|
||||||
|
err = enc.Encode(v)
|
||||||
|
|
||||||
|
//RPC Server
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
rpcCodec := codec.GoRpc.ServerCodec(conn, h)
|
||||||
|
//OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
|
||||||
|
rpc.ServeCodec(rpcCodec)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//RPC Communication (client side)
|
||||||
|
conn, err = net.Dial("tcp", "localhost:5555")
|
||||||
|
rpcCodec := codec.GoRpc.ClientCodec(conn, h)
|
||||||
|
//OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
|
||||||
|
client := rpc.NewClientWithCodec(rpcCodec)
|
||||||
|
|
||||||
|
*/
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// Benefits of go-codec:
|
||||||
|
//
|
||||||
|
// - encoding/json always reads whole file into memory first.
|
||||||
|
// This makes it unsuitable for parsing very large files.
|
||||||
|
// - encoding/xml cannot parse into a map[string]interface{}
|
||||||
|
// I found this out on reading https://github.com/clbanning/mxj
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
//
|
||||||
|
// - optimization for codecgen:
|
||||||
|
// if len of entity is <= 3 words, then support a value receiver for encode.
|
||||||
|
// - (En|De)coder should store an error when it occurs.
|
||||||
|
// Until reset, subsequent calls return that error that was stored.
|
||||||
|
// This means that free panics must go away.
|
||||||
|
// All errors must be raised through errorf method.
|
||||||
|
// - Decoding using a chan is good, but incurs concurrency costs.
|
||||||
|
// This is because there's no fast way to use a channel without it
|
||||||
|
// having to switch goroutines constantly.
|
||||||
|
// Callback pattern is still the best. Maybe consider supporting something like:
|
||||||
|
// type X struct {
|
||||||
|
// Name string
|
||||||
|
// Ys []Y
|
||||||
|
// Ys chan <- Y
|
||||||
|
// Ys func(Y) -> call this function for each entry
|
||||||
|
// }
|
||||||
|
// - Consider adding a isZeroer interface { isZero() bool }
|
||||||
|
// It is used within isEmpty, for omitEmpty support.
|
||||||
|
// - Consider making Handle used AS-IS within the encoding/decoding session.
|
||||||
|
// This means that we don't cache Handle information within the (En|De)coder,
|
||||||
|
// except we really need it at Reset(...)
|
||||||
|
// - Consider adding math/big support
|
||||||
|
// - Consider reducing the size of the generated functions:
|
||||||
|
// Maybe use one loop, and put the conditionals in the loop.
|
||||||
|
// for ... { if cLen > 0 { if j == cLen { break } } else if dd.CheckBreak() { break } }
|
|
@ -0,0 +1,148 @@
|
||||||
|
# Codec
|
||||||
|
|
||||||
|
High Performance, Feature-Rich Idiomatic Go codec/encoding library for
|
||||||
|
binc, msgpack, cbor, json.
|
||||||
|
|
||||||
|
Supported Serialization formats are:
|
||||||
|
|
||||||
|
- msgpack: https://github.com/msgpack/msgpack
|
||||||
|
- binc: http://github.com/ugorji/binc
|
||||||
|
- cbor: http://cbor.io http://tools.ietf.org/html/rfc7049
|
||||||
|
- json: http://json.org http://tools.ietf.org/html/rfc7159
|
||||||
|
- simple:
|
||||||
|
|
||||||
|
To install:
|
||||||
|
|
||||||
|
go get github.com/ugorji/go/codec
|
||||||
|
|
||||||
|
This package understands the `unsafe` tag, to allow using unsafe semantics:
|
||||||
|
|
||||||
|
- When decoding into a struct, you need to read the field name as a string
|
||||||
|
so you can find the struct field it is mapped to.
|
||||||
|
Using `unsafe` will bypass the allocation and copying overhead of `[]byte->string` conversion.
|
||||||
|
|
||||||
|
To use it, you must pass the `unsafe` tag during install:
|
||||||
|
|
||||||
|
```
|
||||||
|
go install -tags=unsafe github.com/ugorji/go/codec
|
||||||
|
```
|
||||||
|
|
||||||
|
Online documentation: http://godoc.org/github.com/ugorji/go/codec
|
||||||
|
Detailed Usage/How-to Primer: http://ugorji.net/blog/go-codec-primer
|
||||||
|
|
||||||
|
The idiomatic Go support is as seen in other encoding packages in
|
||||||
|
the standard library (ie json, xml, gob, etc).
|
||||||
|
|
||||||
|
Rich Feature Set includes:
|
||||||
|
|
||||||
|
- Simple but extremely powerful and feature-rich API
|
||||||
|
- Very High Performance.
|
||||||
|
Our extensive benchmarks show us outperforming Gob, Json, Bson, etc by 2-4X.
|
||||||
|
- Multiple conversions:
|
||||||
|
Package coerces types where appropriate
|
||||||
|
e.g. decode an int in the stream into a float, etc.
|
||||||
|
- Corner Cases:
|
||||||
|
Overflows, nil maps/slices, nil values in streams are handled correctly
|
||||||
|
- Standard field renaming via tags
|
||||||
|
- Support for omitting empty fields during an encoding
|
||||||
|
- Encoding from any value and decoding into pointer to any value
|
||||||
|
(struct, slice, map, primitives, pointers, interface{}, etc)
|
||||||
|
- Extensions to support efficient encoding/decoding of any named types
|
||||||
|
- Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
|
||||||
|
- Decoding without a schema (into a interface{}).
|
||||||
|
Includes Options to configure what specific map or slice type to use
|
||||||
|
when decoding an encoded list or map into a nil interface{}
|
||||||
|
- Encode a struct as an array, and decode struct from an array in the data stream
|
||||||
|
- Comprehensive support for anonymous fields
|
||||||
|
- Fast (no-reflection) encoding/decoding of common maps and slices
|
||||||
|
- Code-generation for faster performance.
|
||||||
|
- Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
|
||||||
|
- Support indefinite-length formats to enable true streaming
|
||||||
|
(for formats which support it e.g. json, cbor)
|
||||||
|
- Support canonical encoding, where a value is ALWAYS encoded as same sequence of bytes.
|
||||||
|
This mostly applies to maps, where iteration order is non-deterministic.
|
||||||
|
- NIL in data stream decoded as zero value
|
||||||
|
- Never silently skip data when decoding.
|
||||||
|
User decides whether to return an error or silently skip data when keys or indexes
|
||||||
|
in the data stream do not map to fields in the struct.
|
||||||
|
- Encode/Decode from/to chan types (for iterative streaming support)
|
||||||
|
- Drop-in replacement for encoding/json. `json:` key in struct tag supported.
|
||||||
|
- Provides a RPC Server and Client Codec for net/rpc communication protocol.
|
||||||
|
- Handle unique idiosyncrasies of codecs e.g.
|
||||||
|
- For messagepack, configure how ambiguities in handling raw bytes are resolved
|
||||||
|
- For messagepack, provide rpc server/client codec to support
|
||||||
|
msgpack-rpc protocol defined at:
|
||||||
|
https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
|
||||||
|
|
||||||
|
## Extension Support
|
||||||
|
|
||||||
|
Users can register a function to handle the encoding or decoding of
|
||||||
|
their custom types.
|
||||||
|
|
||||||
|
There are no restrictions on what the custom type can be. Some examples:
|
||||||
|
|
||||||
|
type BisSet []int
|
||||||
|
type BitSet64 uint64
|
||||||
|
type UUID string
|
||||||
|
type MyStructWithUnexportedFields struct { a int; b bool; c []int; }
|
||||||
|
type GifImage struct { ... }
|
||||||
|
|
||||||
|
As an illustration, MyStructWithUnexportedFields would normally be
|
||||||
|
encoded as an empty map because it has no exported fields, while UUID
|
||||||
|
would be encoded as a string. However, with extension support, you can
|
||||||
|
encode any of these however you like.
|
||||||
|
|
||||||
|
## RPC
|
||||||
|
|
||||||
|
RPC Client and Server Codecs are implemented, so the codecs can be used
|
||||||
|
with the standard net/rpc package.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Typical usage model:
|
||||||
|
|
||||||
|
// create and configure Handle
|
||||||
|
var (
|
||||||
|
bh codec.BincHandle
|
||||||
|
mh codec.MsgpackHandle
|
||||||
|
ch codec.CborHandle
|
||||||
|
)
|
||||||
|
|
||||||
|
mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
|
||||||
|
|
||||||
|
// configure extensions
|
||||||
|
// e.g. for msgpack, define functions and enable Time support for tag 1
|
||||||
|
// mh.SetExt(reflect.TypeOf(time.Time{}), 1, myExt)
|
||||||
|
|
||||||
|
// create and use decoder/encoder
|
||||||
|
var (
|
||||||
|
r io.Reader
|
||||||
|
w io.Writer
|
||||||
|
b []byte
|
||||||
|
h = &bh // or mh to use msgpack
|
||||||
|
)
|
||||||
|
|
||||||
|
dec = codec.NewDecoder(r, h)
|
||||||
|
dec = codec.NewDecoderBytes(b, h)
|
||||||
|
err = dec.Decode(&v)
|
||||||
|
|
||||||
|
enc = codec.NewEncoder(w, h)
|
||||||
|
enc = codec.NewEncoderBytes(&b, h)
|
||||||
|
err = enc.Encode(v)
|
||||||
|
|
||||||
|
//RPC Server
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
rpcCodec := codec.GoRpc.ServerCodec(conn, h)
|
||||||
|
//OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
|
||||||
|
rpc.ServeCodec(rpcCodec)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//RPC Communication (client side)
|
||||||
|
conn, err = net.Dial("tcp", "localhost:5555")
|
||||||
|
rpcCodec := codec.GoRpc.ClientCodec(conn, h)
|
||||||
|
//OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
|
||||||
|
client := rpc.NewClientWithCodec(rpcCodec)
|
||||||
|
|
|
@ -0,0 +1,929 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const bincDoPrune = true // No longer needed. Needed before as C lib did not support pruning.
|
||||||
|
|
||||||
|
// vd as low 4 bits (there are 16 slots)
|
||||||
|
const (
|
||||||
|
bincVdSpecial byte = iota
|
||||||
|
bincVdPosInt
|
||||||
|
bincVdNegInt
|
||||||
|
bincVdFloat
|
||||||
|
|
||||||
|
bincVdString
|
||||||
|
bincVdByteArray
|
||||||
|
bincVdArray
|
||||||
|
bincVdMap
|
||||||
|
|
||||||
|
bincVdTimestamp
|
||||||
|
bincVdSmallInt
|
||||||
|
bincVdUnicodeOther
|
||||||
|
bincVdSymbol
|
||||||
|
|
||||||
|
bincVdDecimal
|
||||||
|
_ // open slot
|
||||||
|
_ // open slot
|
||||||
|
bincVdCustomExt = 0x0f
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bincSpNil byte = iota
|
||||||
|
bincSpFalse
|
||||||
|
bincSpTrue
|
||||||
|
bincSpNan
|
||||||
|
bincSpPosInf
|
||||||
|
bincSpNegInf
|
||||||
|
bincSpZeroFloat
|
||||||
|
bincSpZero
|
||||||
|
bincSpNegOne
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bincFlBin16 byte = iota
|
||||||
|
bincFlBin32
|
||||||
|
_ // bincFlBin32e
|
||||||
|
bincFlBin64
|
||||||
|
_ // bincFlBin64e
|
||||||
|
// others not currently supported
|
||||||
|
)
|
||||||
|
|
||||||
|
type bincEncDriver struct {
|
||||||
|
e *Encoder
|
||||||
|
w encWriter
|
||||||
|
m map[string]uint16 // symbols
|
||||||
|
b [scratchByteArrayLen]byte
|
||||||
|
s uint16 // symbols sequencer
|
||||||
|
encNoSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) IsBuiltinType(rt uintptr) bool {
|
||||||
|
return rt == timeTypId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {
|
||||||
|
if rt == timeTypId {
|
||||||
|
var bs []byte
|
||||||
|
switch x := v.(type) {
|
||||||
|
case time.Time:
|
||||||
|
bs = encodeTime(x)
|
||||||
|
case *time.Time:
|
||||||
|
bs = encodeTime(*x)
|
||||||
|
default:
|
||||||
|
e.e.errorf("binc error encoding builtin: expect time.Time, received %T", v)
|
||||||
|
}
|
||||||
|
e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
|
||||||
|
e.w.writeb(bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeNil() {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeBool(b bool) {
|
||||||
|
if b {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpTrue)
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeFloat32(f float32) {
|
||||||
|
if f == 0 {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.w.writen1(bincVdFloat<<4 | bincFlBin32)
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(math.Float32bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeFloat64(f float64) {
|
||||||
|
if f == 0 {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bigen.PutUint64(e.b[:8], math.Float64bits(f))
|
||||||
|
if bincDoPrune {
|
||||||
|
i := 7
|
||||||
|
for ; i >= 0 && (e.b[i] == 0); i-- {
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if i <= 6 {
|
||||||
|
e.w.writen1(bincVdFloat<<4 | 0x8 | bincFlBin64)
|
||||||
|
e.w.writen1(byte(i))
|
||||||
|
e.w.writeb(e.b[:i])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.w.writen1(bincVdFloat<<4 | bincFlBin64)
|
||||||
|
e.w.writeb(e.b[:8])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encIntegerPrune(bd byte, pos bool, v uint64, lim uint8) {
|
||||||
|
if lim == 4 {
|
||||||
|
bigen.PutUint32(e.b[:lim], uint32(v))
|
||||||
|
} else {
|
||||||
|
bigen.PutUint64(e.b[:lim], v)
|
||||||
|
}
|
||||||
|
if bincDoPrune {
|
||||||
|
i := pruneSignExt(e.b[:lim], pos)
|
||||||
|
e.w.writen1(bd | lim - 1 - byte(i))
|
||||||
|
e.w.writeb(e.b[i:lim])
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bd | lim - 1)
|
||||||
|
e.w.writeb(e.b[:lim])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeInt(v int64) {
|
||||||
|
const nbd byte = bincVdNegInt << 4
|
||||||
|
if v >= 0 {
|
||||||
|
e.encUint(bincVdPosInt<<4, true, uint64(v))
|
||||||
|
} else if v == -1 {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpNegOne)
|
||||||
|
} else {
|
||||||
|
e.encUint(bincVdNegInt<<4, false, uint64(-v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeUint(v uint64) {
|
||||||
|
e.encUint(bincVdPosInt<<4, true, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encUint(bd byte, pos bool, v uint64) {
|
||||||
|
if v == 0 {
|
||||||
|
e.w.writen1(bincVdSpecial<<4 | bincSpZero)
|
||||||
|
} else if pos && v >= 1 && v <= 16 {
|
||||||
|
e.w.writen1(bincVdSmallInt<<4 | byte(v-1))
|
||||||
|
} else if v <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bd|0x0, byte(v))
|
||||||
|
} else if v <= math.MaxUint16 {
|
||||||
|
e.w.writen1(bd | 0x01)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
|
||||||
|
} else if v <= math.MaxUint32 {
|
||||||
|
e.encIntegerPrune(bd, pos, v, 4)
|
||||||
|
} else {
|
||||||
|
e.encIntegerPrune(bd, pos, v, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, _ *Encoder) {
|
||||||
|
bs := ext.WriteExt(rv)
|
||||||
|
if bs == nil {
|
||||||
|
e.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.encodeExtPreamble(uint8(xtag), len(bs))
|
||||||
|
e.w.writeb(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
|
||||||
|
e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
|
||||||
|
e.w.writeb(re.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encodeExtPreamble(xtag byte, length int) {
|
||||||
|
e.encLen(bincVdCustomExt<<4, uint64(length))
|
||||||
|
e.w.writen1(xtag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeArrayStart(length int) {
|
||||||
|
e.encLen(bincVdArray<<4, uint64(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeMapStart(length int) {
|
||||||
|
e.encLen(bincVdMap<<4, uint64(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeString(c charEncoding, v string) {
|
||||||
|
l := uint64(len(v))
|
||||||
|
e.encBytesLen(c, l)
|
||||||
|
if l > 0 {
|
||||||
|
e.w.writestr(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeSymbol(v string) {
|
||||||
|
// if WriteSymbolsNoRefs {
|
||||||
|
// e.encodeString(c_UTF8, v)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
//symbols only offer benefit when string length > 1.
|
||||||
|
//This is because strings with length 1 take only 2 bytes to store
|
||||||
|
//(bd with embedded length, and single byte for string val).
|
||||||
|
|
||||||
|
l := len(v)
|
||||||
|
if l == 0 {
|
||||||
|
e.encBytesLen(c_UTF8, 0)
|
||||||
|
return
|
||||||
|
} else if l == 1 {
|
||||||
|
e.encBytesLen(c_UTF8, 1)
|
||||||
|
e.w.writen1(v[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.m == nil {
|
||||||
|
e.m = make(map[string]uint16, 16)
|
||||||
|
}
|
||||||
|
ui, ok := e.m[v]
|
||||||
|
if ok {
|
||||||
|
if ui <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bincVdSymbol<<4, byte(ui))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bincVdSymbol<<4 | 0x8)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(ui)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.s++
|
||||||
|
ui = e.s
|
||||||
|
//ui = uint16(atomic.AddUint32(&e.s, 1))
|
||||||
|
e.m[v] = ui
|
||||||
|
var lenprec uint8
|
||||||
|
if l <= math.MaxUint8 {
|
||||||
|
// lenprec = 0
|
||||||
|
} else if l <= math.MaxUint16 {
|
||||||
|
lenprec = 1
|
||||||
|
} else if int64(l) <= math.MaxUint32 {
|
||||||
|
lenprec = 2
|
||||||
|
} else {
|
||||||
|
lenprec = 3
|
||||||
|
}
|
||||||
|
if ui <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bincVdSymbol<<4|0x0|0x4|lenprec, byte(ui))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bincVdSymbol<<4 | 0x8 | 0x4 | lenprec)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(ui)
|
||||||
|
}
|
||||||
|
if lenprec == 0 {
|
||||||
|
e.w.writen1(byte(l))
|
||||||
|
} else if lenprec == 1 {
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(uint16(l))
|
||||||
|
} else if lenprec == 2 {
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(uint32(l))
|
||||||
|
} else {
|
||||||
|
bigenHelper{e.b[:8], e.w}.writeUint64(uint64(l))
|
||||||
|
}
|
||||||
|
e.w.writestr(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
|
||||||
|
l := uint64(len(v))
|
||||||
|
e.encBytesLen(c, l)
|
||||||
|
if l > 0 {
|
||||||
|
e.w.writeb(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encBytesLen(c charEncoding, length uint64) {
|
||||||
|
//TODO: support bincUnicodeOther (for now, just use string or bytearray)
|
||||||
|
if c == c_RAW {
|
||||||
|
e.encLen(bincVdByteArray<<4, length)
|
||||||
|
} else {
|
||||||
|
e.encLen(bincVdString<<4, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encLen(bd byte, l uint64) {
|
||||||
|
if l < 12 {
|
||||||
|
e.w.writen1(bd | uint8(l+4))
|
||||||
|
} else {
|
||||||
|
e.encLenNumber(bd, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) encLenNumber(bd byte, v uint64) {
|
||||||
|
if v <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bd, byte(v))
|
||||||
|
} else if v <= math.MaxUint16 {
|
||||||
|
e.w.writen1(bd | 0x01)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
|
||||||
|
} else if v <= math.MaxUint32 {
|
||||||
|
e.w.writen1(bd | 0x02)
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(uint32(v))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bd | 0x03)
|
||||||
|
bigenHelper{e.b[:8], e.w}.writeUint64(uint64(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
type bincDecSymbol struct {
|
||||||
|
s string
|
||||||
|
b []byte
|
||||||
|
i uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type bincDecDriver struct {
|
||||||
|
d *Decoder
|
||||||
|
h *BincHandle
|
||||||
|
r decReader
|
||||||
|
br bool // bytes reader
|
||||||
|
bdRead bool
|
||||||
|
bd byte
|
||||||
|
vd byte
|
||||||
|
vs byte
|
||||||
|
noStreamingCodec
|
||||||
|
decNoSeparator
|
||||||
|
b [scratchByteArrayLen]byte
|
||||||
|
|
||||||
|
// linear searching on this slice is ok,
|
||||||
|
// because we typically expect < 32 symbols in each stream.
|
||||||
|
s []bincDecSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) readNextBd() {
|
||||||
|
d.bd = d.r.readn1()
|
||||||
|
d.vd = d.bd >> 4
|
||||||
|
d.vs = d.bd & 0x0f
|
||||||
|
d.bdRead = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) uncacheRead() {
|
||||||
|
if d.bdRead {
|
||||||
|
d.r.unreadn1()
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) ContainerType() (vt valueType) {
|
||||||
|
if d.vd == bincVdSpecial && d.vs == bincSpNil {
|
||||||
|
return valueTypeNil
|
||||||
|
} else if d.vd == bincVdByteArray {
|
||||||
|
return valueTypeBytes
|
||||||
|
} else if d.vd == bincVdString {
|
||||||
|
return valueTypeString
|
||||||
|
} else if d.vd == bincVdArray {
|
||||||
|
return valueTypeArray
|
||||||
|
} else if d.vd == bincVdMap {
|
||||||
|
return valueTypeMap
|
||||||
|
} else {
|
||||||
|
// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
|
||||||
|
}
|
||||||
|
return valueTypeUnset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) TryDecodeAsNil() bool {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == bincVdSpecial<<4|bincSpNil {
|
||||||
|
d.bdRead = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) IsBuiltinType(rt uintptr) bool {
|
||||||
|
return rt == timeTypId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if rt == timeTypId {
|
||||||
|
if d.vd != bincVdTimestamp {
|
||||||
|
d.d.errorf("Invalid d.vd. Expecting 0x%x. Received: 0x%x", bincVdTimestamp, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tt, err := decodeTime(d.r.readx(int(d.vs)))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var vt *time.Time = v.(*time.Time)
|
||||||
|
*vt = tt
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decFloatPre(vs, defaultLen byte) {
|
||||||
|
if vs&0x8 == 0 {
|
||||||
|
d.r.readb(d.b[0:defaultLen])
|
||||||
|
} else {
|
||||||
|
l := d.r.readn1()
|
||||||
|
if l > 8 {
|
||||||
|
d.d.errorf("At most 8 bytes used to represent float. Received: %v bytes", l)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := l; i < 8; i++ {
|
||||||
|
d.b[i] = 0
|
||||||
|
}
|
||||||
|
d.r.readb(d.b[0:l])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decFloat() (f float64) {
|
||||||
|
//if true { f = math.Float64frombits(bigen.Uint64(d.r.readx(8))); break; }
|
||||||
|
if x := d.vs & 0x7; x == bincFlBin32 {
|
||||||
|
d.decFloatPre(d.vs, 4)
|
||||||
|
f = float64(math.Float32frombits(bigen.Uint32(d.b[0:4])))
|
||||||
|
} else if x == bincFlBin64 {
|
||||||
|
d.decFloatPre(d.vs, 8)
|
||||||
|
f = math.Float64frombits(bigen.Uint64(d.b[0:8]))
|
||||||
|
} else {
|
||||||
|
d.d.errorf("only float32 and float64 are supported. d.vd: 0x%x, d.vs: 0x%x", d.vd, d.vs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decUint() (v uint64) {
|
||||||
|
// need to inline the code (interface conversion and type assertion expensive)
|
||||||
|
switch d.vs {
|
||||||
|
case 0:
|
||||||
|
v = uint64(d.r.readn1())
|
||||||
|
case 1:
|
||||||
|
d.r.readb(d.b[6:8])
|
||||||
|
v = uint64(bigen.Uint16(d.b[6:8]))
|
||||||
|
case 2:
|
||||||
|
d.b[4] = 0
|
||||||
|
d.r.readb(d.b[5:8])
|
||||||
|
v = uint64(bigen.Uint32(d.b[4:8]))
|
||||||
|
case 3:
|
||||||
|
d.r.readb(d.b[4:8])
|
||||||
|
v = uint64(bigen.Uint32(d.b[4:8]))
|
||||||
|
case 4, 5, 6:
|
||||||
|
lim := int(7 - d.vs)
|
||||||
|
d.r.readb(d.b[lim:8])
|
||||||
|
for i := 0; i < lim; i++ {
|
||||||
|
d.b[i] = 0
|
||||||
|
}
|
||||||
|
v = uint64(bigen.Uint64(d.b[:8]))
|
||||||
|
case 7:
|
||||||
|
d.r.readb(d.b[:8])
|
||||||
|
v = uint64(bigen.Uint64(d.b[:8]))
|
||||||
|
default:
|
||||||
|
d.d.errorf("unsigned integers with greater than 64 bits of precision not supported")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decCheckInteger() (ui uint64, neg bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
vd, vs := d.vd, d.vs
|
||||||
|
if vd == bincVdPosInt {
|
||||||
|
ui = d.decUint()
|
||||||
|
} else if vd == bincVdNegInt {
|
||||||
|
ui = d.decUint()
|
||||||
|
neg = true
|
||||||
|
} else if vd == bincVdSmallInt {
|
||||||
|
ui = uint64(d.vs) + 1
|
||||||
|
} else if vd == bincVdSpecial {
|
||||||
|
if vs == bincSpZero {
|
||||||
|
//i = 0
|
||||||
|
} else if vs == bincSpNegOne {
|
||||||
|
neg = true
|
||||||
|
ui = 1
|
||||||
|
} else {
|
||||||
|
d.d.errorf("numeric decode fails for special value: d.vs: 0x%x", d.vs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.d.errorf("number can only be decoded from uint or int values. d.bd: 0x%x, d.vd: 0x%x", d.bd, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeInt(bitsize uint8) (i int64) {
|
||||||
|
ui, neg := d.decCheckInteger()
|
||||||
|
i, overflow := chkOvf.SignedInt(ui)
|
||||||
|
if overflow {
|
||||||
|
d.d.errorf("simple: overflow converting %v to signed integer", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if neg {
|
||||||
|
i = -i
|
||||||
|
}
|
||||||
|
if chkOvf.Int(i, bitsize) {
|
||||||
|
d.d.errorf("binc: overflow integer: %v", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
|
||||||
|
ui, neg := d.decCheckInteger()
|
||||||
|
if neg {
|
||||||
|
d.d.errorf("Assigning negative signed value to unsigned type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if chkOvf.Uint(ui, bitsize) {
|
||||||
|
d.d.errorf("binc: overflow integer: %v", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
vd, vs := d.vd, d.vs
|
||||||
|
if vd == bincVdSpecial {
|
||||||
|
d.bdRead = false
|
||||||
|
if vs == bincSpNan {
|
||||||
|
return math.NaN()
|
||||||
|
} else if vs == bincSpPosInf {
|
||||||
|
return math.Inf(1)
|
||||||
|
} else if vs == bincSpZeroFloat || vs == bincSpZero {
|
||||||
|
return
|
||||||
|
} else if vs == bincSpNegInf {
|
||||||
|
return math.Inf(-1)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid d.vs decoding float where d.vd=bincVdSpecial: %v", d.vs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if vd == bincVdFloat {
|
||||||
|
f = d.decFloat()
|
||||||
|
} else {
|
||||||
|
f = float64(d.DecodeInt(64))
|
||||||
|
}
|
||||||
|
if chkOverflow32 && chkOvf.Float32(f) {
|
||||||
|
d.d.errorf("binc: float32 overflow: %v", f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool can be decoded from bool only (single byte).
|
||||||
|
func (d *bincDecDriver) DecodeBool() (b bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if bd := d.bd; bd == (bincVdSpecial | bincSpFalse) {
|
||||||
|
// b = false
|
||||||
|
} else if bd == (bincVdSpecial | bincSpTrue) {
|
||||||
|
b = true
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) ReadMapStart() (length int) {
|
||||||
|
if d.vd != bincVdMap {
|
||||||
|
d.d.errorf("Invalid d.vd for map. Expecting 0x%x. Got: 0x%x", bincVdMap, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length = d.decLen()
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) ReadArrayStart() (length int) {
|
||||||
|
if d.vd != bincVdArray {
|
||||||
|
d.d.errorf("Invalid d.vd for array. Expecting 0x%x. Got: 0x%x", bincVdArray, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length = d.decLen()
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decLen() int {
|
||||||
|
if d.vs > 3 {
|
||||||
|
return int(d.vs - 4)
|
||||||
|
}
|
||||||
|
return int(d.decLenNumber())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decLenNumber() (v uint64) {
|
||||||
|
if x := d.vs; x == 0 {
|
||||||
|
v = uint64(d.r.readn1())
|
||||||
|
} else if x == 1 {
|
||||||
|
d.r.readb(d.b[6:8])
|
||||||
|
v = uint64(bigen.Uint16(d.b[6:8]))
|
||||||
|
} else if x == 2 {
|
||||||
|
d.r.readb(d.b[4:8])
|
||||||
|
v = uint64(bigen.Uint32(d.b[4:8]))
|
||||||
|
} else {
|
||||||
|
d.r.readb(d.b[:8])
|
||||||
|
v = bigen.Uint64(d.b[:8])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decStringAndBytes(bs []byte, withString, zerocopy bool) (bs2 []byte, s string) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == bincVdSpecial<<4|bincSpNil {
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var slen int = -1
|
||||||
|
// var ok bool
|
||||||
|
switch d.vd {
|
||||||
|
case bincVdString, bincVdByteArray:
|
||||||
|
slen = d.decLen()
|
||||||
|
if zerocopy {
|
||||||
|
if d.br {
|
||||||
|
bs2 = d.r.readx(slen)
|
||||||
|
} else if len(bs) == 0 {
|
||||||
|
bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, d.b[:])
|
||||||
|
} else {
|
||||||
|
bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
if withString {
|
||||||
|
s = string(bs2)
|
||||||
|
}
|
||||||
|
case bincVdSymbol:
|
||||||
|
// zerocopy doesn't apply for symbols,
|
||||||
|
// as the values must be stored in a table for later use.
|
||||||
|
//
|
||||||
|
//from vs: extract numSymbolBytes, containsStringVal, strLenPrecision,
|
||||||
|
//extract symbol
|
||||||
|
//if containsStringVal, read it and put in map
|
||||||
|
//else look in map for string value
|
||||||
|
var symbol uint16
|
||||||
|
vs := d.vs
|
||||||
|
if vs&0x8 == 0 {
|
||||||
|
symbol = uint16(d.r.readn1())
|
||||||
|
} else {
|
||||||
|
symbol = uint16(bigen.Uint16(d.r.readx(2)))
|
||||||
|
}
|
||||||
|
if d.s == nil {
|
||||||
|
d.s = make([]bincDecSymbol, 0, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vs&0x4 == 0 {
|
||||||
|
for i := range d.s {
|
||||||
|
j := &d.s[i]
|
||||||
|
if j.i == symbol {
|
||||||
|
bs2 = j.b
|
||||||
|
if withString {
|
||||||
|
if j.s == "" && bs2 != nil {
|
||||||
|
j.s = string(bs2)
|
||||||
|
}
|
||||||
|
s = j.s
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch vs & 0x3 {
|
||||||
|
case 0:
|
||||||
|
slen = int(d.r.readn1())
|
||||||
|
case 1:
|
||||||
|
slen = int(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case 2:
|
||||||
|
slen = int(bigen.Uint32(d.r.readx(4)))
|
||||||
|
case 3:
|
||||||
|
slen = int(bigen.Uint64(d.r.readx(8)))
|
||||||
|
}
|
||||||
|
// since using symbols, do not store any part of
|
||||||
|
// the parameter bs in the map, as it might be a shared buffer.
|
||||||
|
// bs2 = decByteSlice(d.r, slen, bs)
|
||||||
|
bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, nil)
|
||||||
|
if withString {
|
||||||
|
s = string(bs2)
|
||||||
|
}
|
||||||
|
d.s = append(d.s, bincDecSymbol{i: symbol, s: s, b: bs2})
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
d.d.errorf("Invalid d.vd. Expecting string:0x%x, bytearray:0x%x or symbol: 0x%x. Got: 0x%x",
|
||||||
|
bincVdString, bincVdByteArray, bincVdSymbol, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeString() (s string) {
|
||||||
|
// DecodeBytes does not accommodate symbols, whose impl stores string version in map.
|
||||||
|
// Use decStringAndBytes directly.
|
||||||
|
// return string(d.DecodeBytes(d.b[:], true, true))
|
||||||
|
_, s = d.decStringAndBytes(d.b[:], true, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
|
||||||
|
if isstring {
|
||||||
|
bsOut, _ = d.decStringAndBytes(bs, false, zerocopy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == bincVdSpecial<<4|bincSpNil {
|
||||||
|
d.bdRead = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var clen int
|
||||||
|
if d.vd == bincVdString || d.vd == bincVdByteArray {
|
||||||
|
clen = d.decLen()
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid d.vd for bytes. Expecting string:0x%x or bytearray:0x%x. Got: 0x%x",
|
||||||
|
bincVdString, bincVdByteArray, d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
if zerocopy {
|
||||||
|
if d.br {
|
||||||
|
return d.r.readx(clen)
|
||||||
|
} else if len(bs) == 0 {
|
||||||
|
bs = d.b[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
|
||||||
|
if xtag > 0xff {
|
||||||
|
d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
|
||||||
|
realxtag = uint64(realxtag1)
|
||||||
|
if ext == nil {
|
||||||
|
re := rv.(*RawExt)
|
||||||
|
re.Tag = realxtag
|
||||||
|
re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
|
||||||
|
} else {
|
||||||
|
ext.ReadExt(rv, xbs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.vd == bincVdCustomExt {
|
||||||
|
l := d.decLen()
|
||||||
|
xtag = d.r.readn1()
|
||||||
|
if verifyTag && xtag != tag {
|
||||||
|
d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xbs = d.r.readx(l)
|
||||||
|
} else if d.vd == bincVdByteArray {
|
||||||
|
xbs = d.DecodeBytes(nil, false, true)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid d.vd for extensions (Expecting extensions or byte array). Got: 0x%x", d.vd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) DecodeNaked() {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
|
||||||
|
n := &d.d.n
|
||||||
|
var decodeFurther bool
|
||||||
|
|
||||||
|
switch d.vd {
|
||||||
|
case bincVdSpecial:
|
||||||
|
switch d.vs {
|
||||||
|
case bincSpNil:
|
||||||
|
n.v = valueTypeNil
|
||||||
|
case bincSpFalse:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = false
|
||||||
|
case bincSpTrue:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = true
|
||||||
|
case bincSpNan:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = math.NaN()
|
||||||
|
case bincSpPosInf:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = math.Inf(1)
|
||||||
|
case bincSpNegInf:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = math.Inf(-1)
|
||||||
|
case bincSpZeroFloat:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = float64(0)
|
||||||
|
case bincSpZero:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(0) // int8(0)
|
||||||
|
case bincSpNegOne:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(-1) // int8(-1)
|
||||||
|
default:
|
||||||
|
d.d.errorf("decodeNaked: Unrecognized special value 0x%x", d.vs)
|
||||||
|
}
|
||||||
|
case bincVdSmallInt:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(int8(d.vs)) + 1 // int8(d.vs) + 1
|
||||||
|
case bincVdPosInt:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = d.decUint()
|
||||||
|
case bincVdNegInt:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = -(int64(d.decUint()))
|
||||||
|
case bincVdFloat:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = d.decFloat()
|
||||||
|
case bincVdSymbol:
|
||||||
|
n.v = valueTypeSymbol
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
case bincVdString:
|
||||||
|
n.v = valueTypeString
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
case bincVdByteArray:
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
case bincVdTimestamp:
|
||||||
|
n.v = valueTypeTimestamp
|
||||||
|
tt, err := decodeTime(d.r.readx(int(d.vs)))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
n.t = tt
|
||||||
|
case bincVdCustomExt:
|
||||||
|
n.v = valueTypeExt
|
||||||
|
l := d.decLen()
|
||||||
|
n.u = uint64(d.r.readn1())
|
||||||
|
n.l = d.r.readx(l)
|
||||||
|
case bincVdArray:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
decodeFurther = true
|
||||||
|
case bincVdMap:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
decodeFurther = true
|
||||||
|
default:
|
||||||
|
d.d.errorf("decodeNaked: Unrecognized d.vd: 0x%x", d.vd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !decodeFurther {
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
if n.v == valueTypeUint && d.h.SignedInteger {
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(n.u)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//BincHandle is a Handle for the Binc Schema-Free Encoding Format
|
||||||
|
//defined at https://github.com/ugorji/binc .
|
||||||
|
//
|
||||||
|
//BincHandle currently supports all Binc features with the following EXCEPTIONS:
|
||||||
|
// - only integers up to 64 bits of precision are supported.
|
||||||
|
// big integers are unsupported.
|
||||||
|
// - Only IEEE 754 binary32 and binary64 floats are supported (ie Go float32 and float64 types).
|
||||||
|
// extended precision and decimal IEEE 754 floats are unsupported.
|
||||||
|
// - Only UTF-8 strings supported.
|
||||||
|
// Unicode_Other Binc types (UTF16, UTF32) are currently unsupported.
|
||||||
|
//
|
||||||
|
//Note that these EXCEPTIONS are temporary and full support is possible and may happen soon.
|
||||||
|
type BincHandle struct {
|
||||||
|
BasicHandle
|
||||||
|
binaryEncodingType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BincHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
|
||||||
|
return h.SetExt(rt, tag, &setExtWrapper{b: ext})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BincHandle) newEncDriver(e *Encoder) encDriver {
|
||||||
|
return &bincEncDriver{e: e, w: e.w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BincHandle) newDecDriver(d *Decoder) decDriver {
|
||||||
|
return &bincDecDriver{d: d, h: h, r: d.r, br: d.bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *bincEncDriver) reset() {
|
||||||
|
e.w = e.e.w
|
||||||
|
e.s = 0
|
||||||
|
e.m = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bincDecDriver) reset() {
|
||||||
|
d.r, d.br = d.d.r, d.d.bytes
|
||||||
|
d.s = nil
|
||||||
|
d.bd, d.bdRead, d.vd, d.vs = 0, false, 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ decDriver = (*bincDecDriver)(nil)
|
||||||
|
var _ encDriver = (*bincEncDriver)(nil)
|
|
@ -0,0 +1,592 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cborMajorUint byte = iota
|
||||||
|
cborMajorNegInt
|
||||||
|
cborMajorBytes
|
||||||
|
cborMajorText
|
||||||
|
cborMajorArray
|
||||||
|
cborMajorMap
|
||||||
|
cborMajorTag
|
||||||
|
cborMajorOther
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cborBdFalse byte = 0xf4 + iota
|
||||||
|
cborBdTrue
|
||||||
|
cborBdNil
|
||||||
|
cborBdUndefined
|
||||||
|
cborBdExt
|
||||||
|
cborBdFloat16
|
||||||
|
cborBdFloat32
|
||||||
|
cborBdFloat64
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cborBdIndefiniteBytes byte = 0x5f
|
||||||
|
cborBdIndefiniteString = 0x7f
|
||||||
|
cborBdIndefiniteArray = 0x9f
|
||||||
|
cborBdIndefiniteMap = 0xbf
|
||||||
|
cborBdBreak = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CborStreamBytes byte = 0x5f
|
||||||
|
CborStreamString = 0x7f
|
||||||
|
CborStreamArray = 0x9f
|
||||||
|
CborStreamMap = 0xbf
|
||||||
|
CborStreamBreak = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cborBaseUint byte = 0x00
|
||||||
|
cborBaseNegInt = 0x20
|
||||||
|
cborBaseBytes = 0x40
|
||||||
|
cborBaseString = 0x60
|
||||||
|
cborBaseArray = 0x80
|
||||||
|
cborBaseMap = 0xa0
|
||||||
|
cborBaseTag = 0xc0
|
||||||
|
cborBaseSimple = 0xe0
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
type cborEncDriver struct {
|
||||||
|
noBuiltInTypes
|
||||||
|
encNoSeparator
|
||||||
|
e *Encoder
|
||||||
|
w encWriter
|
||||||
|
h *CborHandle
|
||||||
|
x [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeNil() {
|
||||||
|
e.w.writen1(cborBdNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeBool(b bool) {
|
||||||
|
if b {
|
||||||
|
e.w.writen1(cborBdTrue)
|
||||||
|
} else {
|
||||||
|
e.w.writen1(cborBdFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeFloat32(f float32) {
|
||||||
|
e.w.writen1(cborBdFloat32)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(math.Float32bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeFloat64(f float64) {
|
||||||
|
e.w.writen1(cborBdFloat64)
|
||||||
|
bigenHelper{e.x[:8], e.w}.writeUint64(math.Float64bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) encUint(v uint64, bd byte) {
|
||||||
|
if v <= 0x17 {
|
||||||
|
e.w.writen1(byte(v) + bd)
|
||||||
|
} else if v <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bd+0x18, uint8(v))
|
||||||
|
} else if v <= math.MaxUint16 {
|
||||||
|
e.w.writen1(bd + 0x19)
|
||||||
|
bigenHelper{e.x[:2], e.w}.writeUint16(uint16(v))
|
||||||
|
} else if v <= math.MaxUint32 {
|
||||||
|
e.w.writen1(bd + 0x1a)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(uint32(v))
|
||||||
|
} else { // if v <= math.MaxUint64 {
|
||||||
|
e.w.writen1(bd + 0x1b)
|
||||||
|
bigenHelper{e.x[:8], e.w}.writeUint64(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeInt(v int64) {
|
||||||
|
if v < 0 {
|
||||||
|
e.encUint(uint64(-1-v), cborBaseNegInt)
|
||||||
|
} else {
|
||||||
|
e.encUint(uint64(v), cborBaseUint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeUint(v uint64) {
|
||||||
|
e.encUint(v, cborBaseUint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) encLen(bd byte, length int) {
|
||||||
|
e.encUint(uint64(length), bd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, en *Encoder) {
|
||||||
|
e.encUint(uint64(xtag), cborBaseTag)
|
||||||
|
if v := ext.ConvertExt(rv); v == nil {
|
||||||
|
e.EncodeNil()
|
||||||
|
} else {
|
||||||
|
en.encode(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeRawExt(re *RawExt, en *Encoder) {
|
||||||
|
e.encUint(uint64(re.Tag), cborBaseTag)
|
||||||
|
if re.Data != nil {
|
||||||
|
en.encode(re.Data)
|
||||||
|
} else if re.Value == nil {
|
||||||
|
e.EncodeNil()
|
||||||
|
} else {
|
||||||
|
en.encode(re.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeArrayStart(length int) {
|
||||||
|
e.encLen(cborBaseArray, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeMapStart(length int) {
|
||||||
|
e.encLen(cborBaseMap, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeString(c charEncoding, v string) {
|
||||||
|
e.encLen(cborBaseString, len(v))
|
||||||
|
e.w.writestr(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeSymbol(v string) {
|
||||||
|
e.EncodeString(c_UTF8, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
|
||||||
|
if c == c_RAW {
|
||||||
|
e.encLen(cborBaseBytes, len(v))
|
||||||
|
} else {
|
||||||
|
e.encLen(cborBaseString, len(v))
|
||||||
|
}
|
||||||
|
e.w.writeb(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
type cborDecDriver struct {
|
||||||
|
d *Decoder
|
||||||
|
h *CborHandle
|
||||||
|
r decReader
|
||||||
|
b [scratchByteArrayLen]byte
|
||||||
|
br bool // bytes reader
|
||||||
|
bdRead bool
|
||||||
|
bd byte
|
||||||
|
noBuiltInTypes
|
||||||
|
decNoSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) readNextBd() {
|
||||||
|
d.bd = d.r.readn1()
|
||||||
|
d.bdRead = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) uncacheRead() {
|
||||||
|
if d.bdRead {
|
||||||
|
d.r.unreadn1()
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) ContainerType() (vt valueType) {
|
||||||
|
if d.bd == cborBdNil {
|
||||||
|
return valueTypeNil
|
||||||
|
} else if d.bd == cborBdIndefiniteBytes || (d.bd >= cborBaseBytes && d.bd < cborBaseString) {
|
||||||
|
return valueTypeBytes
|
||||||
|
} else if d.bd == cborBdIndefiniteString || (d.bd >= cborBaseString && d.bd < cborBaseArray) {
|
||||||
|
return valueTypeString
|
||||||
|
} else if d.bd == cborBdIndefiniteArray || (d.bd >= cborBaseArray && d.bd < cborBaseMap) {
|
||||||
|
return valueTypeArray
|
||||||
|
} else if d.bd == cborBdIndefiniteMap || (d.bd >= cborBaseMap && d.bd < cborBaseTag) {
|
||||||
|
return valueTypeMap
|
||||||
|
} else {
|
||||||
|
// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
|
||||||
|
}
|
||||||
|
return valueTypeUnset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) TryDecodeAsNil() bool {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
// treat Nil and Undefined as nil values
|
||||||
|
if d.bd == cborBdNil || d.bd == cborBdUndefined {
|
||||||
|
d.bdRead = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) CheckBreak() bool {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == cborBdBreak {
|
||||||
|
d.bdRead = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) decUint() (ui uint64) {
|
||||||
|
v := d.bd & 0x1f
|
||||||
|
if v <= 0x17 {
|
||||||
|
ui = uint64(v)
|
||||||
|
} else {
|
||||||
|
if v == 0x18 {
|
||||||
|
ui = uint64(d.r.readn1())
|
||||||
|
} else if v == 0x19 {
|
||||||
|
ui = uint64(bigen.Uint16(d.r.readx(2)))
|
||||||
|
} else if v == 0x1a {
|
||||||
|
ui = uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
} else if v == 0x1b {
|
||||||
|
ui = uint64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
} else {
|
||||||
|
d.d.errorf("decUint: Invalid descriptor: %v", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) decCheckInteger() (neg bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
major := d.bd >> 5
|
||||||
|
if major == cborMajorUint {
|
||||||
|
} else if major == cborMajorNegInt {
|
||||||
|
neg = true
|
||||||
|
} else {
|
||||||
|
d.d.errorf("invalid major: %v (bd: %v)", major, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeInt(bitsize uint8) (i int64) {
|
||||||
|
neg := d.decCheckInteger()
|
||||||
|
ui := d.decUint()
|
||||||
|
// check if this number can be converted to an int without overflow
|
||||||
|
var overflow bool
|
||||||
|
if neg {
|
||||||
|
if i, overflow = chkOvf.SignedInt(ui + 1); overflow {
|
||||||
|
d.d.errorf("cbor: overflow converting %v to signed integer", ui+1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i = -i
|
||||||
|
} else {
|
||||||
|
if i, overflow = chkOvf.SignedInt(ui); overflow {
|
||||||
|
d.d.errorf("cbor: overflow converting %v to signed integer", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if chkOvf.Int(i, bitsize) {
|
||||||
|
d.d.errorf("cbor: overflow integer: %v", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
|
||||||
|
if d.decCheckInteger() {
|
||||||
|
d.d.errorf("Assigning negative signed value to unsigned type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ui = d.decUint()
|
||||||
|
if chkOvf.Uint(ui, bitsize) {
|
||||||
|
d.d.errorf("cbor: overflow integer: %v", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if bd := d.bd; bd == cborBdFloat16 {
|
||||||
|
f = float64(math.Float32frombits(halfFloatToFloatBits(bigen.Uint16(d.r.readx(2)))))
|
||||||
|
} else if bd == cborBdFloat32 {
|
||||||
|
f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
|
||||||
|
} else if bd == cborBdFloat64 {
|
||||||
|
f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
|
||||||
|
} else if bd >= cborBaseUint && bd < cborBaseBytes {
|
||||||
|
f = float64(d.DecodeInt(64))
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Float only valid from float16/32/64: Invalid descriptor: %v", bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if chkOverflow32 && chkOvf.Float32(f) {
|
||||||
|
d.d.errorf("cbor: float32 overflow: %v", f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool can be decoded from bool only (single byte).
|
||||||
|
func (d *cborDecDriver) DecodeBool() (b bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if bd := d.bd; bd == cborBdTrue {
|
||||||
|
b = true
|
||||||
|
} else if bd == cborBdFalse {
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) ReadMapStart() (length int) {
|
||||||
|
d.bdRead = false
|
||||||
|
if d.bd == cborBdIndefiniteMap {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return d.decLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) ReadArrayStart() (length int) {
|
||||||
|
d.bdRead = false
|
||||||
|
if d.bd == cborBdIndefiniteArray {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return d.decLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) decLen() int {
|
||||||
|
return int(d.decUint())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) decAppendIndefiniteBytes(bs []byte) []byte {
|
||||||
|
d.bdRead = false
|
||||||
|
for {
|
||||||
|
if d.CheckBreak() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if major := d.bd >> 5; major != cborMajorBytes && major != cborMajorText {
|
||||||
|
d.d.errorf("cbor: expect bytes or string major type in indefinite string/bytes; got: %v, byte: %v", major, d.bd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := d.decLen()
|
||||||
|
oldLen := len(bs)
|
||||||
|
newLen := oldLen + n
|
||||||
|
if newLen > cap(bs) {
|
||||||
|
bs2 := make([]byte, newLen, 2*cap(bs)+n)
|
||||||
|
copy(bs2, bs)
|
||||||
|
bs = bs2
|
||||||
|
} else {
|
||||||
|
bs = bs[:newLen]
|
||||||
|
}
|
||||||
|
d.r.readb(bs[oldLen:newLen])
|
||||||
|
// bs = append(bs, d.r.readn()...)
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == cborBdNil || d.bd == cborBdUndefined {
|
||||||
|
d.bdRead = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if d.bd == cborBdIndefiniteBytes || d.bd == cborBdIndefiniteString {
|
||||||
|
if bs == nil {
|
||||||
|
return d.decAppendIndefiniteBytes(nil)
|
||||||
|
}
|
||||||
|
return d.decAppendIndefiniteBytes(bs[:0])
|
||||||
|
}
|
||||||
|
clen := d.decLen()
|
||||||
|
d.bdRead = false
|
||||||
|
if zerocopy {
|
||||||
|
if d.br {
|
||||||
|
return d.r.readx(clen)
|
||||||
|
} else if len(bs) == 0 {
|
||||||
|
bs = d.b[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeString() (s string) {
|
||||||
|
return string(d.DecodeBytes(d.b[:], true, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
u := d.decUint()
|
||||||
|
d.bdRead = false
|
||||||
|
realxtag = u
|
||||||
|
if ext == nil {
|
||||||
|
re := rv.(*RawExt)
|
||||||
|
re.Tag = realxtag
|
||||||
|
d.d.decode(&re.Value)
|
||||||
|
} else if xtag != realxtag {
|
||||||
|
d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", realxtag, xtag)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
var v interface{}
|
||||||
|
d.d.decode(&v)
|
||||||
|
ext.UpdateExt(rv, v)
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) DecodeNaked() {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
|
||||||
|
n := &d.d.n
|
||||||
|
var decodeFurther bool
|
||||||
|
|
||||||
|
switch d.bd {
|
||||||
|
case cborBdNil:
|
||||||
|
n.v = valueTypeNil
|
||||||
|
case cborBdFalse:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = false
|
||||||
|
case cborBdTrue:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = true
|
||||||
|
case cborBdFloat16, cborBdFloat32:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = d.DecodeFloat(true)
|
||||||
|
case cborBdFloat64:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = d.DecodeFloat(false)
|
||||||
|
case cborBdIndefiniteBytes:
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
case cborBdIndefiniteString:
|
||||||
|
n.v = valueTypeString
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
case cborBdIndefiniteArray:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
decodeFurther = true
|
||||||
|
case cborBdIndefiniteMap:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
decodeFurther = true
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case d.bd >= cborBaseUint && d.bd < cborBaseNegInt:
|
||||||
|
if d.h.SignedInteger {
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = d.DecodeInt(64)
|
||||||
|
} else {
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = d.DecodeUint(64)
|
||||||
|
}
|
||||||
|
case d.bd >= cborBaseNegInt && d.bd < cborBaseBytes:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = d.DecodeInt(64)
|
||||||
|
case d.bd >= cborBaseBytes && d.bd < cborBaseString:
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
case d.bd >= cborBaseString && d.bd < cborBaseArray:
|
||||||
|
n.v = valueTypeString
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
case d.bd >= cborBaseArray && d.bd < cborBaseMap:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
decodeFurther = true
|
||||||
|
case d.bd >= cborBaseMap && d.bd < cborBaseTag:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
decodeFurther = true
|
||||||
|
case d.bd >= cborBaseTag && d.bd < cborBaseSimple:
|
||||||
|
n.v = valueTypeExt
|
||||||
|
n.u = d.decUint()
|
||||||
|
n.l = nil
|
||||||
|
// d.bdRead = false
|
||||||
|
// d.d.decode(&re.Value) // handled by decode itself.
|
||||||
|
// decodeFurther = true
|
||||||
|
default:
|
||||||
|
d.d.errorf("decodeNaked: Unrecognized d.bd: 0x%x", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !decodeFurther {
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
|
||||||
|
// CborHandle is a Handle for the CBOR encoding format,
|
||||||
|
// defined at http://tools.ietf.org/html/rfc7049 and documented further at http://cbor.io .
|
||||||
|
//
|
||||||
|
// CBOR is comprehensively supported, including support for:
|
||||||
|
// - indefinite-length arrays/maps/bytes/strings
|
||||||
|
// - (extension) tags in range 0..0xffff (0 .. 65535)
|
||||||
|
// - half, single and double-precision floats
|
||||||
|
// - all numbers (1, 2, 4 and 8-byte signed and unsigned integers)
|
||||||
|
// - nil, true, false, ...
|
||||||
|
// - arrays and maps, bytes and text strings
|
||||||
|
//
|
||||||
|
// None of the optional extensions (with tags) defined in the spec are supported out-of-the-box.
|
||||||
|
// Users can implement them as needed (using SetExt), including spec-documented ones:
|
||||||
|
// - timestamp, BigNum, BigFloat, Decimals, Encoded Text (e.g. URL, regexp, base64, MIME Message), etc.
|
||||||
|
//
|
||||||
|
// To encode with indefinite lengths (streaming), users will use
|
||||||
|
// (Must)Encode methods of *Encoder, along with writing CborStreamXXX constants.
|
||||||
|
//
|
||||||
|
// For example, to encode "one-byte" as an indefinite length string:
|
||||||
|
// var buf bytes.Buffer
|
||||||
|
// e := NewEncoder(&buf, new(CborHandle))
|
||||||
|
// buf.WriteByte(CborStreamString)
|
||||||
|
// e.MustEncode("one-")
|
||||||
|
// e.MustEncode("byte")
|
||||||
|
// buf.WriteByte(CborStreamBreak)
|
||||||
|
// encodedBytes := buf.Bytes()
|
||||||
|
// var vv interface{}
|
||||||
|
// NewDecoderBytes(buf.Bytes(), new(CborHandle)).MustDecode(&vv)
|
||||||
|
// // Now, vv contains the same string "one-byte"
|
||||||
|
//
|
||||||
|
type CborHandle struct {
|
||||||
|
binaryEncodingType
|
||||||
|
BasicHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CborHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
|
||||||
|
return h.SetExt(rt, tag, &setExtWrapper{i: ext})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CborHandle) newEncDriver(e *Encoder) encDriver {
|
||||||
|
return &cborEncDriver{e: e, w: e.w, h: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CborHandle) newDecDriver(d *Decoder) decDriver {
|
||||||
|
return &cborDecDriver{d: d, h: h, r: d.r, br: d.bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cborEncDriver) reset() {
|
||||||
|
e.w = e.e.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *cborDecDriver) reset() {
|
||||||
|
d.r, d.br = d.d.r, d.d.bytes
|
||||||
|
d.bd, d.bdRead = 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ decDriver = (*cborDecDriver)(nil)
|
||||||
|
var _ encDriver = (*cborEncDriver)(nil)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.5
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
const reflectArrayOfSupported = true
|
||||||
|
|
||||||
|
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) {
|
||||||
|
rvn2 = reflect.New(reflect.ArrayOf(rvn.Len(), intfTyp)).Elem()
|
||||||
|
reflect.Copy(rvn2, rvn)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.5
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
const reflectArrayOfSupported = false
|
||||||
|
|
||||||
|
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) {
|
||||||
|
panic("reflect.ArrayOf unsupported")
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,527 @@
|
||||||
|
// +build !notfastpath
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// ************************************************************
|
||||||
|
// DO NOT EDIT.
|
||||||
|
// THIS FILE IS AUTO-GENERATED from fast-path.go.tmpl
|
||||||
|
// ************************************************************
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// Fast path functions try to create a fast path encode or decode implementation
|
||||||
|
// for common maps and slices.
|
||||||
|
//
|
||||||
|
// We define the functions and register then in this single file
|
||||||
|
// so as not to pollute the encode.go and decode.go, and create a dependency in there.
|
||||||
|
// This file can be omitted without causing a build failure.
|
||||||
|
//
|
||||||
|
// The advantage of fast paths is:
|
||||||
|
// - Many calls bypass reflection altogether
|
||||||
|
//
|
||||||
|
// Currently support
|
||||||
|
// - slice of all builtin types,
|
||||||
|
// - map of all builtin types to string or interface value
|
||||||
|
// - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
|
||||||
|
// This should provide adequate "typical" implementations.
|
||||||
|
//
|
||||||
|
// Note that fast track decode functions must handle values for which an address cannot be obtained.
|
||||||
|
// For example:
|
||||||
|
// m2 := map[string]int{}
|
||||||
|
// p2 := []interface{}{m2}
|
||||||
|
// // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
|
||||||
|
//
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fastpathEnabled = true
|
||||||
|
|
||||||
|
const fastpathCheckNilFalse = false // for reflect
|
||||||
|
const fastpathCheckNilTrue = true // for type switch
|
||||||
|
|
||||||
|
type fastpathT struct {}
|
||||||
|
|
||||||
|
var fastpathTV fastpathT
|
||||||
|
|
||||||
|
type fastpathE struct {
|
||||||
|
rtid uintptr
|
||||||
|
rt reflect.Type
|
||||||
|
encfn func(*encFnInfo, reflect.Value)
|
||||||
|
decfn func(*decFnInfo, reflect.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fastpathA [{{ .FastpathLen }}]fastpathE
|
||||||
|
|
||||||
|
func (x *fastpathA) index(rtid uintptr) int {
|
||||||
|
// use binary search to grab the index (adapted from sort/search.go)
|
||||||
|
h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
|
||||||
|
for i < j {
|
||||||
|
h = i + (j-i)/2
|
||||||
|
if x[h].rtid < rtid {
|
||||||
|
i = h + 1
|
||||||
|
} else {
|
||||||
|
j = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < {{ .FastpathLen }} && x[i].rtid == rtid {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
type fastpathAslice []fastpathE
|
||||||
|
|
||||||
|
func (x fastpathAslice) Len() int { return len(x) }
|
||||||
|
func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
|
||||||
|
func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
|
|
||||||
|
var fastpathAV fastpathA
|
||||||
|
|
||||||
|
// due to possible initialization loop error, make fastpath in an init()
|
||||||
|
func init() {
|
||||||
|
i := 0
|
||||||
|
fn := func(v interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) (f fastpathE) {
|
||||||
|
xrt := reflect.TypeOf(v)
|
||||||
|
xptr := reflect.ValueOf(xrt).Pointer()
|
||||||
|
fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
|
||||||
|
i++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
fn([]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
|
||||||
|
fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
sort.Sort(fastpathAslice(fastpathAV[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- encode
|
||||||
|
|
||||||
|
// -- -- fast path type switch
|
||||||
|
func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
|
||||||
|
switch v := iv.(type) {
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
case []{{ .Elem }}:{{else}}
|
||||||
|
case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e){{if not .MapKey }}
|
||||||
|
case *[]{{ .Elem }}:{{else}}
|
||||||
|
case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
|
||||||
|
{{end}}{{end}}
|
||||||
|
default:
|
||||||
|
_ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
|
||||||
|
switch v := iv.(type) {
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
case []{{ .Elem }}:
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
|
||||||
|
case *[]{{ .Elem }}:
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
default:
|
||||||
|
_ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
|
||||||
|
switch v := iv.(type) {
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
|
||||||
|
case map[{{ .MapKey }}]{{ .Elem }}:
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
|
||||||
|
case *map[{{ .MapKey }}]{{ .Elem }}:
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
default:
|
||||||
|
_ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- -- fast path functions
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
|
||||||
|
func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
|
||||||
|
if f.ti.mbs {
|
||||||
|
fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
|
||||||
|
} else {
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
|
||||||
|
ee := e.e
|
||||||
|
cr := e.cr
|
||||||
|
if checkNil && v == nil {
|
||||||
|
ee.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ee.EncodeArrayStart(len(v))
|
||||||
|
for _, v2 := range v {
|
||||||
|
if cr != nil { cr.sendContainerState(containerArrayElem) }
|
||||||
|
{{ encmd .Elem "v2"}}
|
||||||
|
}
|
||||||
|
if cr != nil { cr.sendContainerState(containerArrayEnd) }{{/* ee.EncodeEnd() */}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
|
||||||
|
ee := e.e
|
||||||
|
cr := e.cr
|
||||||
|
if checkNil && v == nil {
|
||||||
|
ee.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(v)%2 == 1 {
|
||||||
|
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ee.EncodeMapStart(len(v) / 2)
|
||||||
|
for j, v2 := range v {
|
||||||
|
if cr != nil {
|
||||||
|
if j%2 == 0 {
|
||||||
|
cr.sendContainerState(containerMapKey)
|
||||||
|
} else {
|
||||||
|
cr.sendContainerState(containerMapValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ encmd .Elem "v2"}}
|
||||||
|
}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapEnd) }
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
|
||||||
|
|
||||||
|
func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}), fastpathCheckNilFalse, f.e)
|
||||||
|
}
|
||||||
|
func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, e *Encoder) {
|
||||||
|
ee := e.e
|
||||||
|
cr := e.cr
|
||||||
|
if checkNil && v == nil {
|
||||||
|
ee.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ee.EncodeMapStart(len(v))
|
||||||
|
{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
|
||||||
|
{{end}}if e.h.Canonical {
|
||||||
|
{{if eq .MapKey "interface{}"}}{{/* out of band
|
||||||
|
*/}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
|
||||||
|
e2 := NewEncoderBytes(&mksv, e.hh)
|
||||||
|
v2 := make([]bytesI, len(v))
|
||||||
|
var i, l int
|
||||||
|
var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
|
||||||
|
for k2, _ := range v {
|
||||||
|
l = len(mksv)
|
||||||
|
e2.MustEncode(k2)
|
||||||
|
vp = &v2[i]
|
||||||
|
vp.v = mksv[l:]
|
||||||
|
vp.i = k2
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort(bytesISlice(v2))
|
||||||
|
for j := range v2 {
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapKey) }
|
||||||
|
e.asis(v2[j].v)
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapValue) }
|
||||||
|
e.encode(v[v2[j].i])
|
||||||
|
} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
|
||||||
|
var i int
|
||||||
|
for k, _ := range v {
|
||||||
|
v2[i] = {{ $x }}(k)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort({{ sorttype .MapKey false}}(v2))
|
||||||
|
for _, k2 := range v2 {
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapKey) }
|
||||||
|
{{if eq .MapKey "string"}}if asSymbols {
|
||||||
|
ee.EncodeSymbol(k2)
|
||||||
|
} else {
|
||||||
|
ee.EncodeString(c_UTF8, k2)
|
||||||
|
}{{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapValue) }
|
||||||
|
{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
|
||||||
|
} {{end}}
|
||||||
|
} else {
|
||||||
|
for k2, v2 := range v {
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapKey) }
|
||||||
|
{{if eq .MapKey "string"}}if asSymbols {
|
||||||
|
ee.EncodeSymbol(k2)
|
||||||
|
} else {
|
||||||
|
ee.EncodeString(c_UTF8, k2)
|
||||||
|
}{{else}}{{ encmd .MapKey "k2"}}{{end}}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapValue) }
|
||||||
|
{{ encmd .Elem "v2"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapEnd) }{{/* ee.EncodeEnd() */}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
// -- decode
|
||||||
|
|
||||||
|
// -- -- fast path type switch
|
||||||
|
func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
|
||||||
|
switch v := iv.(type) {
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
case []{{ .Elem }}:{{else}}
|
||||||
|
case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, d){{if not .MapKey }}
|
||||||
|
case *[]{{ .Elem }}:{{else}}
|
||||||
|
case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
|
||||||
|
v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, fastpathCheckNilFalse, true, d)
|
||||||
|
if changed2 {
|
||||||
|
*v = v2
|
||||||
|
}
|
||||||
|
{{end}}{{end}}
|
||||||
|
default:
|
||||||
|
_ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- -- fast path functions
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
|
||||||
|
{{/*
|
||||||
|
Slices can change if they
|
||||||
|
- did not come from an array
|
||||||
|
- are addressable (from a ptr)
|
||||||
|
- are settable (e.g. contained in an interface{})
|
||||||
|
*/}}
|
||||||
|
func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
|
||||||
|
array := f.seq == seqTypeArray
|
||||||
|
if !array && rv.CanAddr() { {{/* // CanSet => CanAddr + Exported */}}
|
||||||
|
vp := rv.Addr().Interface().(*[]{{ .Elem }})
|
||||||
|
v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)
|
||||||
|
if changed {
|
||||||
|
*vp = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v := rv.Interface().([]{{ .Elem }})
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, checkNil bool, d *Decoder) {
|
||||||
|
v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
|
||||||
|
if changed {
|
||||||
|
*vp = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil bool, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
|
||||||
|
dd := d.d
|
||||||
|
{{/* // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil() */}}
|
||||||
|
if checkNil && dd.TryDecodeAsNil() {
|
||||||
|
if v != nil {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
return nil, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
slh, containerLenS := d.decSliceHelperStart()
|
||||||
|
if containerLenS == 0 {
|
||||||
|
if canChange {
|
||||||
|
if v == nil {
|
||||||
|
v = []{{ .Elem }}{}
|
||||||
|
} else if len(v) != 0 {
|
||||||
|
v = v[:0]
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
slh.End()
|
||||||
|
return v, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerLenS > 0 {
|
||||||
|
x2read := containerLenS
|
||||||
|
var xtrunc bool
|
||||||
|
if containerLenS > cap(v) {
|
||||||
|
if canChange { {{/*
|
||||||
|
// fast-path is for "basic" immutable types, so no need to copy them over
|
||||||
|
// s := make([]{{ .Elem }}, decInferLen(containerLenS, d.h.MaxInitLen))
|
||||||
|
// copy(s, v[:cap(v)])
|
||||||
|
// v = s */}}
|
||||||
|
var xlen int
|
||||||
|
xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
|
||||||
|
if xtrunc {
|
||||||
|
if xlen <= cap(v) {
|
||||||
|
v = v[:xlen]
|
||||||
|
} else {
|
||||||
|
v = make([]{{ .Elem }}, xlen)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v = make([]{{ .Elem }}, xlen)
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
d.arrayCannotExpand(len(v), containerLenS)
|
||||||
|
}
|
||||||
|
x2read = len(v)
|
||||||
|
} else if containerLenS != len(v) {
|
||||||
|
if canChange {
|
||||||
|
v = v[:containerLenS]
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
} {{/* // all checks done. cannot go past len. */}}
|
||||||
|
j := 0
|
||||||
|
for ; j < x2read; j++ {
|
||||||
|
slh.ElemContainerState(j)
|
||||||
|
{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
|
||||||
|
}
|
||||||
|
if xtrunc { {{/* // means canChange=true, changed=true already. */}}
|
||||||
|
for ; j < containerLenS; j++ {
|
||||||
|
v = append(v, {{ zerocmd .Elem }})
|
||||||
|
slh.ElemContainerState(j)
|
||||||
|
{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
|
||||||
|
}
|
||||||
|
} else if !canChange {
|
||||||
|
for ; j < containerLenS; j++ {
|
||||||
|
slh.ElemContainerState(j)
|
||||||
|
d.swallow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
breakFound := dd.CheckBreak() {{/* check break first, so we can initialize v with a capacity of 4 if necessary */}}
|
||||||
|
if breakFound {
|
||||||
|
if canChange {
|
||||||
|
if v == nil {
|
||||||
|
v = []{{ .Elem }}{}
|
||||||
|
} else if len(v) != 0 {
|
||||||
|
v = v[:0]
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
slh.End()
|
||||||
|
return v, changed
|
||||||
|
}
|
||||||
|
if cap(v) == 0 {
|
||||||
|
v = make([]{{ .Elem }}, 1, 4)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
j := 0
|
||||||
|
for ; !breakFound; j++ {
|
||||||
|
if j >= len(v) {
|
||||||
|
if canChange {
|
||||||
|
v = append(v, {{ zerocmd .Elem }})
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
d.arrayCannotExpand(len(v), j+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slh.ElemContainerState(j)
|
||||||
|
if j < len(v) { {{/* // all checks done. cannot go past len. */}}
|
||||||
|
{{ if eq .Elem "interface{}" }}d.decode(&v[j])
|
||||||
|
{{ else }}v[j] = {{ decmd .Elem }}{{ end }}
|
||||||
|
} else {
|
||||||
|
d.swallow()
|
||||||
|
}
|
||||||
|
breakFound = dd.CheckBreak()
|
||||||
|
}
|
||||||
|
if canChange && j < len(v) {
|
||||||
|
v = v[:j]
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slh.End()
|
||||||
|
return v, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
|
||||||
|
{{/*
|
||||||
|
Maps can change if they are
|
||||||
|
- addressable (from a ptr)
|
||||||
|
- settable (e.g. contained in an interface{})
|
||||||
|
*/}}
|
||||||
|
func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
|
||||||
|
if rv.CanAddr() {
|
||||||
|
vp := rv.Addr().Interface().(*map[{{ .MapKey }}]{{ .Elem }})
|
||||||
|
v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, true, f.d)
|
||||||
|
if changed {
|
||||||
|
*vp = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v := rv.Interface().(map[{{ .MapKey }}]{{ .Elem }})
|
||||||
|
fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, d *Decoder) {
|
||||||
|
v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
|
||||||
|
if changed {
|
||||||
|
*vp = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, canChange bool,
|
||||||
|
d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
|
||||||
|
dd := d.d
|
||||||
|
cr := d.cr
|
||||||
|
{{/* // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil() */}}
|
||||||
|
if checkNil && dd.TryDecodeAsNil() {
|
||||||
|
if v != nil {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
return nil, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
containerLen := dd.ReadMapStart()
|
||||||
|
if canChange && v == nil {
|
||||||
|
xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
|
||||||
|
v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
{{ if eq .Elem "interface{}" }}mapGet := !d.h.MapValueReset && !d.h.InterfaceReset{{end}}
|
||||||
|
var mk {{ .MapKey }}
|
||||||
|
var mv {{ .Elem }}
|
||||||
|
if containerLen > 0 {
|
||||||
|
for j := 0; j < containerLen; j++ {
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapKey) }
|
||||||
|
{{ if eq .MapKey "interface{}" }}mk = nil
|
||||||
|
d.decode(&mk)
|
||||||
|
if bv, bok := mk.([]byte); bok {
|
||||||
|
mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||||
|
}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapValue) }
|
||||||
|
{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
|
||||||
|
d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||||
|
if v != nil {
|
||||||
|
v[mk] = mv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if containerLen < 0 {
|
||||||
|
for j := 0; !dd.CheckBreak(); j++ {
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapKey) }
|
||||||
|
{{ if eq .MapKey "interface{}" }}mk = nil
|
||||||
|
d.decode(&mk)
|
||||||
|
if bv, bok := mk.([]byte); bok {
|
||||||
|
mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||||
|
}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapValue) }
|
||||||
|
{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
|
||||||
|
d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||||
|
if v != nil {
|
||||||
|
v[mk] = mv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cr != nil { cr.sendContainerState(containerMapEnd) }
|
||||||
|
return v, changed
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}{{end}}{{end}}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// +build notfastpath
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
const fastpathEnabled = false
|
||||||
|
|
||||||
|
// The generated fast-path code is very large, and adds a few seconds to the build time.
|
||||||
|
// This causes test execution, execution of small tools which use codec, etc
|
||||||
|
// to take a long time.
|
||||||
|
//
|
||||||
|
// To mitigate, we now support the notfastpath tag.
|
||||||
|
// This tag disables fastpath during build, allowing for faster build, test execution,
|
||||||
|
// short-program runs, etc.
|
||||||
|
|
||||||
|
func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool { return false }
|
||||||
|
func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool { return false }
|
||||||
|
func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool { return false }
|
||||||
|
func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool { return false }
|
||||||
|
|
||||||
|
type fastpathT struct{}
|
||||||
|
type fastpathE struct {
|
||||||
|
rtid uintptr
|
||||||
|
rt reflect.Type
|
||||||
|
encfn func(*encFnInfo, reflect.Value)
|
||||||
|
decfn func(*decFnInfo, reflect.Value)
|
||||||
|
}
|
||||||
|
type fastpathA [0]fastpathE
|
||||||
|
|
||||||
|
func (x fastpathA) index(rtid uintptr) int { return -1 }
|
||||||
|
|
||||||
|
var fastpathAV fastpathA
|
||||||
|
var fastpathTV fastpathT
|
|
@ -0,0 +1,104 @@
|
||||||
|
{{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }}
|
||||||
|
{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}}
|
||||||
|
var {{var "c"}} bool {{/* // changed */}}
|
||||||
|
_ = {{var "c"}}{{end}}
|
||||||
|
if {{var "l"}} == 0 {
|
||||||
|
{{if isSlice }}if {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = []{{ .Typ }}{}
|
||||||
|
{{var "c"}} = true
|
||||||
|
} else if len({{var "v"}}) != 0 {
|
||||||
|
{{var "v"}} = {{var "v"}}[:0]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}} {{if isChan }}if {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = make({{ .CTyp }}, 0)
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}}
|
||||||
|
} else if {{var "l"}} > 0 {
|
||||||
|
{{if isChan }}if {{var "v"}} == nil {
|
||||||
|
{{var "rl"}}, _ = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||||
|
{{var "v"}} = make({{ .CTyp }}, {{var "rl"}})
|
||||||
|
{{var "c"}} = true
|
||||||
|
}
|
||||||
|
for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "r"}})
|
||||||
|
var {{var "t"}} {{ .Typ }}
|
||||||
|
{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
{{var "v"}} <- {{var "t"}}
|
||||||
|
}
|
||||||
|
{{ else }} var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}}
|
||||||
|
var {{var "rt"}} bool {{/* truncated */}}
|
||||||
|
_, _ = {{var "rl"}}, {{var "rt"}}
|
||||||
|
{{var "rr"}} = {{var "l"}} // len({{var "v"}})
|
||||||
|
if {{var "l"}} > cap({{var "v"}}) {
|
||||||
|
{{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}})
|
||||||
|
{{ else }}{{if not .Immutable }}
|
||||||
|
{{var "rg"}} := len({{var "v"}}) > 0
|
||||||
|
{{var "v2"}} := {{var "v"}} {{end}}
|
||||||
|
{{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||||
|
if {{var "rt"}} {
|
||||||
|
if {{var "rl"}} <= cap({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "rl"}}]
|
||||||
|
} else {
|
||||||
|
{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
|
||||||
|
}
|
||||||
|
{{var "c"}} = true
|
||||||
|
{{var "rr"}} = len({{var "v"}}) {{if not .Immutable }}
|
||||||
|
if {{var "rg"}} { copy({{var "v"}}, {{var "v2"}}) } {{end}} {{end}}{{/* end not Immutable, isArray */}}
|
||||||
|
} {{if isSlice }} else if {{var "l"}} != len({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "l"}}]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}} {{/* end isSlice:47 */}}
|
||||||
|
{{var "j"}} := 0
|
||||||
|
for ; {{var "j"}} < {{var "rr"}} ; {{var "j"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
}
|
||||||
|
{{if isArray }}for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
z.DecSwallow()
|
||||||
|
}
|
||||||
|
{{ else }}if {{var "rt"}} {
|
||||||
|
for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
|
||||||
|
{{var "v"}} = append({{var "v"}}, {{ zero}})
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
}
|
||||||
|
} {{end}} {{/* end isArray:56 */}}
|
||||||
|
{{end}} {{/* end isChan:16 */}}
|
||||||
|
} else { {{/* len < 0 */}}
|
||||||
|
{{var "j"}} := 0
|
||||||
|
for ; !r.CheckBreak(); {{var "j"}}++ {
|
||||||
|
{{if isChan }}
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
var {{var "t"}} {{ .Typ }}
|
||||||
|
{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
{{var "v"}} <- {{var "t"}}
|
||||||
|
{{ else }}
|
||||||
|
if {{var "j"}} >= len({{var "v"}}) {
|
||||||
|
{{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
|
||||||
|
{{ else }}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
|
||||||
|
{{var "c"}} = true {{end}}
|
||||||
|
}
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
if {{var "j"}} < len({{var "v"}}) {
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
} else {
|
||||||
|
z.DecSwallow()
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
{{if isSlice }}if {{var "j"}} < len({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "j"}}]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} else if {{var "j"}} == 0 && {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = []{{ .Typ }}{}
|
||||||
|
{{var "c"}} = true
|
||||||
|
}{{end}}
|
||||||
|
}
|
||||||
|
{{var "h"}}.End()
|
||||||
|
{{if not isArray }}if {{var "c"}} {
|
||||||
|
*{{ .Varname }} = {{var "v"}}
|
||||||
|
}{{end}}
|
|
@ -0,0 +1,58 @@
|
||||||
|
{{var "v"}} := *{{ .Varname }}
|
||||||
|
{{var "l"}} := r.ReadMapStart()
|
||||||
|
{{var "bh"}} := z.DecBasicHandle()
|
||||||
|
if {{var "v"}} == nil {
|
||||||
|
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
|
||||||
|
{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
|
||||||
|
*{{ .Varname }} = {{var "v"}}
|
||||||
|
}
|
||||||
|
var {{var "mk"}} {{ .KTyp }}
|
||||||
|
var {{var "mv"}} {{ .Typ }}
|
||||||
|
var {{var "mg"}} {{if decElemKindPtr}}, {{var "ms"}}, {{var "mok"}}{{end}} bool
|
||||||
|
if {{var "bh"}}.MapValueReset {
|
||||||
|
{{if decElemKindPtr}}{{var "mg"}} = true
|
||||||
|
{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
|
||||||
|
{{else if not decElemKindImmutable}}{{var "mg"}} = true
|
||||||
|
{{end}} }
|
||||||
|
if {{var "l"}} > 0 {
|
||||||
|
for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapKey{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||||
|
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||||
|
{{var "mk"}} = string({{var "bv"}})
|
||||||
|
}{{ end }}{{if decElemKindPtr}}
|
||||||
|
{{var "ms"}} = true{{end}}
|
||||||
|
if {{var "mg"}} {
|
||||||
|
{{if decElemKindPtr}}{{var "mv"}}, {{var "mok"}} = {{var "v"}}[{{var "mk"}}]
|
||||||
|
if {{var "mok"}} {
|
||||||
|
{{var "ms"}} = false
|
||||||
|
} {{else}}{{var "mv"}} = {{var "v"}}[{{var "mk"}}] {{end}}
|
||||||
|
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapValue{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
if {{if decElemKindPtr}} {{var "ms"}} && {{end}} {{var "v"}} != nil {
|
||||||
|
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if {{var "l"}} < 0 {
|
||||||
|
for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapKey{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||||
|
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||||
|
{{var "mk"}} = string({{var "bv"}})
|
||||||
|
}{{ end }}{{if decElemKindPtr}}
|
||||||
|
{{var "ms"}} = true {{ end }}
|
||||||
|
if {{var "mg"}} {
|
||||||
|
{{if decElemKindPtr}}{{var "mv"}}, {{var "mok"}} = {{var "v"}}[{{var "mk"}}]
|
||||||
|
if {{var "mok"}} {
|
||||||
|
{{var "ms"}} = false
|
||||||
|
} {{else}}{{var "mv"}} = {{var "v"}}[{{var "mk"}}] {{end}}
|
||||||
|
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapValue{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
if {{if decElemKindPtr}} {{var "ms"}} && {{end}} {{var "v"}} != nil {
|
||||||
|
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else len==0: TODO: Should we clear map entries?
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapEnd{{ .Sfx }})
|
|
@ -0,0 +1,243 @@
|
||||||
|
/* // +build ignore */
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// ************************************************************
|
||||||
|
// DO NOT EDIT.
|
||||||
|
// THIS FILE IS AUTO-GENERATED from gen-helper.go.tmpl
|
||||||
|
// ************************************************************
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file is used to generate helper code for codecgen.
|
||||||
|
// The values here i.e. genHelper(En|De)coder are not to be used directly by
|
||||||
|
// library users. They WILL change continuously and without notice.
|
||||||
|
//
|
||||||
|
// To help enforce this, we create an unexported type with exported members.
|
||||||
|
// The only way to get the type is via the one exported type that we control (somewhat).
|
||||||
|
//
|
||||||
|
// When static codecs are created for types, they will use this value
|
||||||
|
// to perform encoding or decoding of primitives or known slice or map types.
|
||||||
|
|
||||||
|
// GenHelperEncoder is exported so that it can be used externally by codecgen.
|
||||||
|
// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
|
||||||
|
func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
|
||||||
|
return genHelperEncoder{e: e}, e.e
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenHelperDecoder is exported so that it can be used externally by codecgen.
|
||||||
|
// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
|
||||||
|
func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
|
||||||
|
return genHelperDecoder{d: d}, d.d
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
type genHelperEncoder struct {
|
||||||
|
e *Encoder
|
||||||
|
F fastpathT
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
type genHelperDecoder struct {
|
||||||
|
d *Decoder
|
||||||
|
F fastpathT
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
|
||||||
|
return f.e.h
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBinary() bool {
|
||||||
|
return f.e.be // f.e.hh.isBinaryEncoding()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncFallback(iv interface{}) {
|
||||||
|
// println(">>>>>>>>> EncFallback")
|
||||||
|
f.e.encodeI(iv, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalText()
|
||||||
|
f.e.marshal(bs, fnerr, false, c_UTF8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalJSON()
|
||||||
|
f.e.marshal(bs, fnerr, true, c_UTF8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalBinary()
|
||||||
|
f.e.marshal(bs, fnerr, false, c_RAW)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncRaw(iv Raw) {
|
||||||
|
f.e.raw(iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
|
||||||
|
if _, ok := f.e.hh.(*BincHandle); ok {
|
||||||
|
return timeTypId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) IsJSONHandle() bool {
|
||||||
|
return f.e.js
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) HasExtensions() bool {
|
||||||
|
return len(f.e.h.extHandle) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
|
||||||
|
rt := reflect.TypeOf(v)
|
||||||
|
if rt.Kind() == reflect.Ptr {
|
||||||
|
rt = rt.Elem()
|
||||||
|
}
|
||||||
|
rtid := reflect.ValueOf(rt).Pointer()
|
||||||
|
if xfFn := f.e.h.getExt(rtid); xfFn != nil {
|
||||||
|
f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncSendContainerState(c containerState) {
|
||||||
|
if f.e.cr != nil {
|
||||||
|
f.e.cr.sendContainerState(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- DECODER FOLLOWS -----------------
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
|
||||||
|
return f.d.h
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBinary() bool {
|
||||||
|
return f.d.be // f.d.hh.isBinaryEncoding()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSwallow() {
|
||||||
|
f.d.swallow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecScratchBuffer() []byte {
|
||||||
|
return f.d.b[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
|
||||||
|
// println(">>>>>>>>> DecFallback")
|
||||||
|
f.d.decodeI(iv, chkPtr, false, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSliceHelperStart() (decSliceHelper, int) {
|
||||||
|
return f.d.decSliceHelperStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
|
||||||
|
f.d.structFieldNotFound(index, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
|
||||||
|
f.d.arrayCannotExpand(sliceLen, streamLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
|
||||||
|
fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
|
||||||
|
// bs := f.dd.DecodeBytes(f.d.b[:], true, true)
|
||||||
|
// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
|
||||||
|
fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
|
||||||
|
fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecRaw() []byte {
|
||||||
|
return f.d.raw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
|
||||||
|
if _, ok := f.d.hh.(*BincHandle); ok {
|
||||||
|
return timeTypId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) IsJSONHandle() bool {
|
||||||
|
return f.d.js
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) HasExtensions() bool {
|
||||||
|
return len(f.d.h.extHandle) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
|
||||||
|
rt := reflect.TypeOf(v).Elem()
|
||||||
|
rtid := reflect.ValueOf(rt).Pointer()
|
||||||
|
if xfFn := f.d.h.getExt(rtid); xfFn != nil {
|
||||||
|
f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int, truncated bool) {
|
||||||
|
return decInferLen(clen, maxlen, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSendContainerState(c containerState) {
|
||||||
|
if f.d.cr != nil {
|
||||||
|
f.d.cr.sendContainerState(c)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,372 @@
|
||||||
|
/* // +build ignore */
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// ************************************************************
|
||||||
|
// DO NOT EDIT.
|
||||||
|
// THIS FILE IS AUTO-GENERATED from gen-helper.go.tmpl
|
||||||
|
// ************************************************************
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file is used to generate helper code for codecgen.
|
||||||
|
// The values here i.e. genHelper(En|De)coder are not to be used directly by
|
||||||
|
// library users. They WILL change continuously and without notice.
|
||||||
|
//
|
||||||
|
// To help enforce this, we create an unexported type with exported members.
|
||||||
|
// The only way to get the type is via the one exported type that we control (somewhat).
|
||||||
|
//
|
||||||
|
// When static codecs are created for types, they will use this value
|
||||||
|
// to perform encoding or decoding of primitives or known slice or map types.
|
||||||
|
|
||||||
|
// GenHelperEncoder is exported so that it can be used externally by codecgen.
|
||||||
|
// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
|
||||||
|
func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
|
||||||
|
return genHelperEncoder{e:e}, e.e
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenHelperDecoder is exported so that it can be used externally by codecgen.
|
||||||
|
// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
|
||||||
|
func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
|
||||||
|
return genHelperDecoder{d:d}, d.d
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
type genHelperEncoder struct {
|
||||||
|
e *Encoder
|
||||||
|
F fastpathT
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
type genHelperDecoder struct {
|
||||||
|
d *Decoder
|
||||||
|
F fastpathT
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
|
||||||
|
return f.e.h
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBinary() bool {
|
||||||
|
return f.e.be // f.e.hh.isBinaryEncoding()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncFallback(iv interface{}) {
|
||||||
|
// println(">>>>>>>>> EncFallback")
|
||||||
|
f.e.encodeI(iv, false, false)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalText()
|
||||||
|
f.e.marshal(bs, fnerr, false, c_UTF8)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalJSON()
|
||||||
|
f.e.marshal(bs, fnerr, true, c_UTF8)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
|
||||||
|
bs, fnerr := iv.MarshalBinary()
|
||||||
|
f.e.marshal(bs, fnerr, false, c_RAW)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncRaw(iv Raw) {
|
||||||
|
f.e.raw(iv)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
|
||||||
|
if _, ok := f.e.hh.(*BincHandle); ok {
|
||||||
|
return timeTypId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) IsJSONHandle() bool {
|
||||||
|
return f.e.js
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) HasExtensions() bool {
|
||||||
|
return len(f.e.h.extHandle) != 0
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
|
||||||
|
rt := reflect.TypeOf(v)
|
||||||
|
if rt.Kind() == reflect.Ptr {
|
||||||
|
rt = rt.Elem()
|
||||||
|
}
|
||||||
|
rtid := reflect.ValueOf(rt).Pointer()
|
||||||
|
if xfFn := f.e.h.getExt(rtid); xfFn != nil {
|
||||||
|
f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncSendContainerState(c containerState) {
|
||||||
|
if f.e.cr != nil {
|
||||||
|
f.e.cr.sendContainerState(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- DECODER FOLLOWS -----------------
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
|
||||||
|
return f.d.h
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBinary() bool {
|
||||||
|
return f.d.be // f.d.hh.isBinaryEncoding()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSwallow() {
|
||||||
|
f.d.swallow()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecScratchBuffer() []byte {
|
||||||
|
return f.d.b[:]
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
|
||||||
|
// println(">>>>>>>>> DecFallback")
|
||||||
|
f.d.decodeI(iv, chkPtr, false, false, false)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSliceHelperStart() (decSliceHelper, int) {
|
||||||
|
return f.d.decSliceHelperStart()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
|
||||||
|
f.d.structFieldNotFound(index, name)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
|
||||||
|
f.d.arrayCannotExpand(sliceLen, streamLen)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
|
||||||
|
fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
|
||||||
|
// bs := f.dd.DecodeBytes(f.d.b[:], true, true)
|
||||||
|
// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
|
||||||
|
fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
|
||||||
|
fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
|
||||||
|
if fnerr != nil {
|
||||||
|
panic(fnerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecRaw() []byte {
|
||||||
|
return f.d.raw()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
|
||||||
|
if _, ok := f.d.hh.(*BincHandle); ok {
|
||||||
|
return timeTypId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) IsJSONHandle() bool {
|
||||||
|
return f.d.js
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) HasExtensions() bool {
|
||||||
|
return len(f.d.h.extHandle) != 0
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
|
||||||
|
rt := reflect.TypeOf(v).Elem()
|
||||||
|
rtid := reflect.ValueOf(rt).Pointer()
|
||||||
|
if xfFn := f.d.h.getExt(rtid); xfFn != nil {
|
||||||
|
f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int, truncated bool) {
|
||||||
|
return decInferLen(clen, maxlen, unit)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecSendContainerState(c containerState) {
|
||||||
|
if f.d.cr != nil {
|
||||||
|
f.d.cr.sendContainerState(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncDriver() encDriver {
|
||||||
|
return f.e.e
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecDriver() decDriver {
|
||||||
|
return f.d.d
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncNil() {
|
||||||
|
f.e.e.EncodeNil()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncBytes(v []byte) {
|
||||||
|
f.e.e.EncodeStringBytes(c_RAW, v)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncArrayStart(length int) {
|
||||||
|
f.e.e.EncodeArrayStart(length)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncArrayEnd() {
|
||||||
|
f.e.e.EncodeArrayEnd()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncArrayEntrySeparator() {
|
||||||
|
f.e.e.EncodeArrayEntrySeparator()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncMapStart(length int) {
|
||||||
|
f.e.e.EncodeMapStart(length)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncMapEnd() {
|
||||||
|
f.e.e.EncodeMapEnd()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncMapEntrySeparator() {
|
||||||
|
f.e.e.EncodeMapEntrySeparator()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) EncMapKVSeparator() {
|
||||||
|
f.e.e.EncodeMapKVSeparator()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------
|
||||||
|
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecBytes(v *[]byte) {
|
||||||
|
*v = f.d.d.DecodeBytes(*v)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecTryNil() bool {
|
||||||
|
return f.d.d.TryDecodeAsNil()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecContainerIsNil() (b bool) {
|
||||||
|
return f.d.d.IsContainerType(valueTypeNil)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecContainerIsMap() (b bool) {
|
||||||
|
return f.d.d.IsContainerType(valueTypeMap)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecContainerIsArray() (b bool) {
|
||||||
|
return f.d.d.IsContainerType(valueTypeArray)
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecCheckBreak() bool {
|
||||||
|
return f.d.d.CheckBreak()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecMapStart() int {
|
||||||
|
return f.d.d.ReadMapStart()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecArrayStart() int {
|
||||||
|
return f.d.d.ReadArrayStart()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecMapEnd() {
|
||||||
|
f.d.d.ReadMapEnd()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecArrayEnd() {
|
||||||
|
f.d.d.ReadArrayEnd()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecArrayEntrySeparator() {
|
||||||
|
f.d.d.ReadArrayEntrySeparator()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecMapEntrySeparator() {
|
||||||
|
f.d.d.ReadMapEntrySeparator()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) DecMapKVSeparator() {
|
||||||
|
f.d.d.ReadMapKVSeparator()
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) ReadStringAsBytes(bs []byte) []byte {
|
||||||
|
return f.d.d.DecodeStringAsBytes(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -- encode calls (primitives)
|
||||||
|
{{range .Values}}{{if .Primitive }}{{if ne .Primitive "interface{}" }}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) {{ .MethodNamePfx "Enc" true }}(v {{ .Primitive }}) {
|
||||||
|
ee := f.e.e
|
||||||
|
{{ encmd .Primitive "v" }}
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}{{ end }}
|
||||||
|
|
||||||
|
// -- decode calls (primitives)
|
||||||
|
{{range .Values}}{{if .Primitive }}{{if ne .Primitive "interface{}" }}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) {{ .MethodNamePfx "Dec" true }}(vp *{{ .Primitive }}) {
|
||||||
|
dd := f.d.d
|
||||||
|
*vp = {{ decmd .Primitive }}
|
||||||
|
}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperDecoder) {{ .MethodNamePfx "Read" true }}() (v {{ .Primitive }}) {
|
||||||
|
dd := f.d.d
|
||||||
|
v = {{ decmd .Primitive }}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
// -- encode calls (slices/maps)
|
||||||
|
{{range .Values}}{{if not .Primitive }}{{if .Slice }}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) {{ .MethodNamePfx "Enc" false }}(v []{{ .Elem }}) { {{ else }}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
func (f genHelperEncoder) {{ .MethodNamePfx "Enc" false }}(v map[{{ .MapKey }}]{{ .Elem }}) { {{end}}
|
||||||
|
f.F.{{ .MethodNamePfx "Enc" false }}V(v, false, f.e)
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}
|
||||||
|
|
||||||
|
// -- decode calls (slices/maps)
|
||||||
|
{{range .Values}}{{if not .Primitive }}
|
||||||
|
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||||
|
{{if .Slice }}func (f genHelperDecoder) {{ .MethodNamePfx "Dec" false }}(vp *[]{{ .Elem }}) {
|
||||||
|
{{else}}func (f genHelperDecoder) {{ .MethodNamePfx "Dec" false }}(vp *map[{{ .MapKey }}]{{ .Elem }}) { {{end}}
|
||||||
|
v, changed := f.F.{{ .MethodNamePfx "Dec" false }}V(*vp, false, true, f.d)
|
||||||
|
if changed {
|
||||||
|
*vp = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}
|
||||||
|
*/}}
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
|
||||||
|
|
||||||
|
const genDecMapTmpl = `
|
||||||
|
{{var "v"}} := *{{ .Varname }}
|
||||||
|
{{var "l"}} := r.ReadMapStart()
|
||||||
|
{{var "bh"}} := z.DecBasicHandle()
|
||||||
|
if {{var "v"}} == nil {
|
||||||
|
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
|
||||||
|
{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
|
||||||
|
*{{ .Varname }} = {{var "v"}}
|
||||||
|
}
|
||||||
|
var {{var "mk"}} {{ .KTyp }}
|
||||||
|
var {{var "mv"}} {{ .Typ }}
|
||||||
|
var {{var "mg"}} {{if decElemKindPtr}}, {{var "ms"}}, {{var "mok"}}{{end}} bool
|
||||||
|
if {{var "bh"}}.MapValueReset {
|
||||||
|
{{if decElemKindPtr}}{{var "mg"}} = true
|
||||||
|
{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
|
||||||
|
{{else if not decElemKindImmutable}}{{var "mg"}} = true
|
||||||
|
{{end}} }
|
||||||
|
if {{var "l"}} > 0 {
|
||||||
|
for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapKey{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||||
|
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||||
|
{{var "mk"}} = string({{var "bv"}})
|
||||||
|
}{{ end }}{{if decElemKindPtr}}
|
||||||
|
{{var "ms"}} = true{{end}}
|
||||||
|
if {{var "mg"}} {
|
||||||
|
{{if decElemKindPtr}}{{var "mv"}}, {{var "mok"}} = {{var "v"}}[{{var "mk"}}]
|
||||||
|
if {{var "mok"}} {
|
||||||
|
{{var "ms"}} = false
|
||||||
|
} {{else}}{{var "mv"}} = {{var "v"}}[{{var "mk"}}] {{end}}
|
||||||
|
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapValue{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
if {{if decElemKindPtr}} {{var "ms"}} && {{end}} {{var "v"}} != nil {
|
||||||
|
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if {{var "l"}} < 0 {
|
||||||
|
for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapKey{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||||
|
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||||
|
{{var "mk"}} = string({{var "bv"}})
|
||||||
|
}{{ end }}{{if decElemKindPtr}}
|
||||||
|
{{var "ms"}} = true {{ end }}
|
||||||
|
if {{var "mg"}} {
|
||||||
|
{{if decElemKindPtr}}{{var "mv"}}, {{var "mok"}} = {{var "v"}}[{{var "mk"}}]
|
||||||
|
if {{var "mok"}} {
|
||||||
|
{{var "ms"}} = false
|
||||||
|
} {{else}}{{var "mv"}} = {{var "v"}}[{{var "mk"}}] {{end}}
|
||||||
|
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapValue{{ .Sfx }})
|
||||||
|
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
if {{if decElemKindPtr}} {{var "ms"}} && {{end}} {{var "v"}} != nil {
|
||||||
|
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else len==0: TODO: Should we clear map entries?
|
||||||
|
z.DecSendContainerState(codecSelfer_containerMapEnd{{ .Sfx }})
|
||||||
|
`
|
||||||
|
|
||||||
|
const genDecListTmpl = `
|
||||||
|
{{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }}
|
||||||
|
{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}}
|
||||||
|
var {{var "c"}} bool {{/* // changed */}}
|
||||||
|
_ = {{var "c"}}{{end}}
|
||||||
|
if {{var "l"}} == 0 {
|
||||||
|
{{if isSlice }}if {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = []{{ .Typ }}{}
|
||||||
|
{{var "c"}} = true
|
||||||
|
} else if len({{var "v"}}) != 0 {
|
||||||
|
{{var "v"}} = {{var "v"}}[:0]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}} {{if isChan }}if {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = make({{ .CTyp }}, 0)
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}}
|
||||||
|
} else if {{var "l"}} > 0 {
|
||||||
|
{{if isChan }}if {{var "v"}} == nil {
|
||||||
|
{{var "rl"}}, _ = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||||
|
{{var "v"}} = make({{ .CTyp }}, {{var "rl"}})
|
||||||
|
{{var "c"}} = true
|
||||||
|
}
|
||||||
|
for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "r"}})
|
||||||
|
var {{var "t"}} {{ .Typ }}
|
||||||
|
{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
{{var "v"}} <- {{var "t"}}
|
||||||
|
}
|
||||||
|
{{ else }} var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}}
|
||||||
|
var {{var "rt"}} bool {{/* truncated */}}
|
||||||
|
_, _ = {{var "rl"}}, {{var "rt"}}
|
||||||
|
{{var "rr"}} = {{var "l"}} // len({{var "v"}})
|
||||||
|
if {{var "l"}} > cap({{var "v"}}) {
|
||||||
|
{{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}})
|
||||||
|
{{ else }}{{if not .Immutable }}
|
||||||
|
{{var "rg"}} := len({{var "v"}}) > 0
|
||||||
|
{{var "v2"}} := {{var "v"}} {{end}}
|
||||||
|
{{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||||
|
if {{var "rt"}} {
|
||||||
|
if {{var "rl"}} <= cap({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "rl"}}]
|
||||||
|
} else {
|
||||||
|
{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
|
||||||
|
}
|
||||||
|
{{var "c"}} = true
|
||||||
|
{{var "rr"}} = len({{var "v"}}) {{if not .Immutable }}
|
||||||
|
if {{var "rg"}} { copy({{var "v"}}, {{var "v2"}}) } {{end}} {{end}}{{/* end not Immutable, isArray */}}
|
||||||
|
} {{if isSlice }} else if {{var "l"}} != len({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "l"}}]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} {{end}} {{/* end isSlice:47 */}}
|
||||||
|
{{var "j"}} := 0
|
||||||
|
for ; {{var "j"}} < {{var "rr"}} ; {{var "j"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
}
|
||||||
|
{{if isArray }}for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
z.DecSwallow()
|
||||||
|
}
|
||||||
|
{{ else }}if {{var "rt"}} {
|
||||||
|
for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
|
||||||
|
{{var "v"}} = append({{var "v"}}, {{ zero}})
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
}
|
||||||
|
} {{end}} {{/* end isArray:56 */}}
|
||||||
|
{{end}} {{/* end isChan:16 */}}
|
||||||
|
} else { {{/* len < 0 */}}
|
||||||
|
{{var "j"}} := 0
|
||||||
|
for ; !r.CheckBreak(); {{var "j"}}++ {
|
||||||
|
{{if isChan }}
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
var {{var "t"}} {{ .Typ }}
|
||||||
|
{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
{{var "v"}} <- {{var "t"}}
|
||||||
|
{{ else }}
|
||||||
|
if {{var "j"}} >= len({{var "v"}}) {
|
||||||
|
{{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
|
||||||
|
{{ else }}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
|
||||||
|
{{var "c"}} = true {{end}}
|
||||||
|
}
|
||||||
|
{{var "h"}}.ElemContainerState({{var "j"}})
|
||||||
|
if {{var "j"}} < len({{var "v"}}) {
|
||||||
|
{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
|
||||||
|
} else {
|
||||||
|
z.DecSwallow()
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
{{if isSlice }}if {{var "j"}} < len({{var "v"}}) {
|
||||||
|
{{var "v"}} = {{var "v"}}[:{{var "j"}}]
|
||||||
|
{{var "c"}} = true
|
||||||
|
} else if {{var "j"}} == 0 && {{var "v"}} == nil {
|
||||||
|
{{var "v"}} = []{{ .Typ }}{}
|
||||||
|
{{var "c"}} = true
|
||||||
|
}{{end}}
|
||||||
|
}
|
||||||
|
{{var "h"}}.End()
|
||||||
|
{{if not isArray }}if {{var "c"}} {
|
||||||
|
*{{ .Varname }} = {{var "v"}}
|
||||||
|
}{{end}}
|
||||||
|
`
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.5,!go1.6
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") == "1"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.6
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") != "0"
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
genCheckVendor = true
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,242 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// All non-std package dependencies live in this file,
|
||||||
|
// so porting to different environment is easy (just update functions).
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func panicValToErr(panicVal interface{}, err *error) {
|
||||||
|
if panicVal == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// case nil
|
||||||
|
switch xerr := panicVal.(type) {
|
||||||
|
case error:
|
||||||
|
*err = xerr
|
||||||
|
case string:
|
||||||
|
*err = errors.New(xerr)
|
||||||
|
default:
|
||||||
|
*err = fmt.Errorf("%v", panicVal)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hIsEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Invalid:
|
||||||
|
return true
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
if deref {
|
||||||
|
if v.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return hIsEmptyValue(v.Elem(), deref, checkStruct)
|
||||||
|
} else {
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if !checkStruct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// return true if all fields are empty. else return false.
|
||||||
|
// we cannot use equality check, because some fields may be maps/slices/etc
|
||||||
|
// and consequently the structs are not comparable.
|
||||||
|
// return v.Interface() == reflect.Zero(v.Type()).Interface()
|
||||||
|
for i, n := 0, v.NumField(); i < n; i++ {
|
||||||
|
if !hIsEmptyValue(v.Field(i), deref, checkStruct) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
|
||||||
|
return hIsEmptyValue(v, deref, checkStruct)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pruneSignExt(v []byte, pos bool) (n int) {
|
||||||
|
if len(v) < 2 {
|
||||||
|
} else if pos && v[0] == 0 {
|
||||||
|
for ; v[n] == 0 && n+1 < len(v) && (v[n+1]&(1<<7) == 0); n++ {
|
||||||
|
}
|
||||||
|
} else if !pos && v[0] == 0xff {
|
||||||
|
for ; v[n] == 0xff && n+1 < len(v) && (v[n+1]&(1<<7) != 0); n++ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func implementsIntf(typ, iTyp reflect.Type) (success bool, indir int8) {
|
||||||
|
if typ == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rt := typ
|
||||||
|
// The type might be a pointer and we need to keep
|
||||||
|
// dereferencing to the base type until we find an implementation.
|
||||||
|
for {
|
||||||
|
if rt.Implements(iTyp) {
|
||||||
|
return true, indir
|
||||||
|
}
|
||||||
|
if p := rt; p.Kind() == reflect.Ptr {
|
||||||
|
indir++
|
||||||
|
if indir >= math.MaxInt8 { // insane number of indirections
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
rt = p.Elem()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// No luck yet, but if this is a base type (non-pointer), the pointer might satisfy.
|
||||||
|
if typ.Kind() != reflect.Ptr {
|
||||||
|
// Not a pointer, but does the pointer work?
|
||||||
|
if reflect.PtrTo(typ).Implements(iTyp) {
|
||||||
|
return true, -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate that this function is correct ...
|
||||||
|
// culled from OGRE (Object-Oriented Graphics Rendering Engine)
|
||||||
|
// function: halfToFloatI (http://stderr.org/doc/ogre-doc/api/OgreBitwise_8h-source.html)
|
||||||
|
func halfFloatToFloatBits(yy uint16) (d uint32) {
|
||||||
|
y := uint32(yy)
|
||||||
|
s := (y >> 15) & 0x01
|
||||||
|
e := (y >> 10) & 0x1f
|
||||||
|
m := y & 0x03ff
|
||||||
|
|
||||||
|
if e == 0 {
|
||||||
|
if m == 0 { // plu or minus 0
|
||||||
|
return s << 31
|
||||||
|
} else { // Denormalized number -- renormalize it
|
||||||
|
for (m & 0x00000400) == 0 {
|
||||||
|
m <<= 1
|
||||||
|
e -= 1
|
||||||
|
}
|
||||||
|
e += 1
|
||||||
|
const zz uint32 = 0x0400
|
||||||
|
m &= ^zz
|
||||||
|
}
|
||||||
|
} else if e == 31 {
|
||||||
|
if m == 0 { // Inf
|
||||||
|
return (s << 31) | 0x7f800000
|
||||||
|
} else { // NaN
|
||||||
|
return (s << 31) | 0x7f800000 | (m << 13)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e = e + (127 - 15)
|
||||||
|
m = m << 13
|
||||||
|
return (s << 31) | (e << 23) | m
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrowCap will return a new capacity for a slice, given the following:
|
||||||
|
// - oldCap: current capacity
|
||||||
|
// - unit: in-memory size of an element
|
||||||
|
// - num: number of elements to add
|
||||||
|
func growCap(oldCap, unit, num int) (newCap int) {
|
||||||
|
// appendslice logic (if cap < 1024, *2, else *1.25):
|
||||||
|
// leads to many copy calls, especially when copying bytes.
|
||||||
|
// bytes.Buffer model (2*cap + n): much better for bytes.
|
||||||
|
// smarter way is to take the byte-size of the appended element(type) into account
|
||||||
|
|
||||||
|
// maintain 3 thresholds:
|
||||||
|
// t1: if cap <= t1, newcap = 2x
|
||||||
|
// t2: if cap <= t2, newcap = 1.75x
|
||||||
|
// t3: if cap <= t3, newcap = 1.5x
|
||||||
|
// else newcap = 1.25x
|
||||||
|
//
|
||||||
|
// t1, t2, t3 >= 1024 always.
|
||||||
|
// i.e. if unit size >= 16, then always do 2x or 1.25x (ie t1, t2, t3 are all same)
|
||||||
|
//
|
||||||
|
// With this, appending for bytes increase by:
|
||||||
|
// 100% up to 4K
|
||||||
|
// 75% up to 8K
|
||||||
|
// 50% up to 16K
|
||||||
|
// 25% beyond that
|
||||||
|
|
||||||
|
// unit can be 0 e.g. for struct{}{}; handle that appropriately
|
||||||
|
var t1, t2, t3 int // thresholds
|
||||||
|
if unit <= 1 {
|
||||||
|
t1, t2, t3 = 4*1024, 8*1024, 16*1024
|
||||||
|
} else if unit < 16 {
|
||||||
|
t3 = 16 / unit * 1024
|
||||||
|
t1 = t3 * 1 / 4
|
||||||
|
t2 = t3 * 2 / 4
|
||||||
|
} else {
|
||||||
|
t1, t2, t3 = 1024, 1024, 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
var x int // temporary variable
|
||||||
|
|
||||||
|
// x is multiplier here: one of 5, 6, 7 or 8; incr of 25%, 50%, 75% or 100% respectively
|
||||||
|
if oldCap <= t1 { // [0,t1]
|
||||||
|
x = 8
|
||||||
|
} else if oldCap > t3 { // (t3,infinity]
|
||||||
|
x = 5
|
||||||
|
} else if oldCap <= t2 { // (t1,t2]
|
||||||
|
x = 7
|
||||||
|
} else { // (t2,t3]
|
||||||
|
x = 6
|
||||||
|
}
|
||||||
|
newCap = x * oldCap / 4
|
||||||
|
|
||||||
|
if num > 0 {
|
||||||
|
newCap += num
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure newCap is a multiple of 64 (if it is > 64) or 16.
|
||||||
|
if newCap > 64 {
|
||||||
|
if x = newCap % 64; x != 0 {
|
||||||
|
x = newCap / 64
|
||||||
|
newCap = 64 * (x + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if x = newCap % 16; x != 0 {
|
||||||
|
x = newCap / 16
|
||||||
|
newCap = 16 * (x + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandSliceValue(s reflect.Value, num int) reflect.Value {
|
||||||
|
if num <= 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
l0 := s.Len()
|
||||||
|
l1 := l0 + num // new slice length
|
||||||
|
if l1 < l0 {
|
||||||
|
panic("ExpandSlice: slice overflow")
|
||||||
|
}
|
||||||
|
c0 := s.Cap()
|
||||||
|
if l1 <= c0 {
|
||||||
|
return s.Slice(0, l1)
|
||||||
|
}
|
||||||
|
st := s.Type()
|
||||||
|
c1 := growCap(c0, int(st.Elem().Size()), num)
|
||||||
|
s2 := reflect.MakeSlice(st, l1, c1)
|
||||||
|
// println("expandslicevalue: cap-old: ", c0, ", cap-new: ", c1, ", len-new: ", l1)
|
||||||
|
reflect.Copy(s2, s)
|
||||||
|
return s2
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// +build !unsafe
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// stringView returns a view of the []byte as a string.
|
||||||
|
// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
|
||||||
|
// In regular safe mode, it is an allocation and copy.
|
||||||
|
func stringView(v []byte) string {
|
||||||
|
return string(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesView returns a view of the string as a []byte.
|
||||||
|
// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
|
||||||
|
// In regular safe mode, it is an allocation and copy.
|
||||||
|
func bytesView(v string) []byte {
|
||||||
|
return []byte(v)
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
// +build unsafe
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file has unsafe variants of some helper methods.
|
||||||
|
|
||||||
|
type unsafeString struct {
|
||||||
|
Data uintptr
|
||||||
|
Len int
|
||||||
|
}
|
||||||
|
|
||||||
|
type unsafeSlice struct {
|
||||||
|
Data uintptr
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringView returns a view of the []byte as a string.
|
||||||
|
// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
|
||||||
|
// In regular safe mode, it is an allocation and copy.
|
||||||
|
func stringView(v []byte) string {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
bx := (*unsafeSlice)(unsafe.Pointer(&v))
|
||||||
|
sx := unsafeString{bx.Data, bx.Len}
|
||||||
|
return *(*string)(unsafe.Pointer(&sx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesView returns a view of the string as a []byte.
|
||||||
|
// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
|
||||||
|
// In regular safe mode, it is an allocation and copy.
|
||||||
|
func bytesView(v string) []byte {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return zeroByteSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
sx := (*unsafeString)(unsafe.Pointer(&v))
|
||||||
|
bx := unsafeSlice{sx.Data, sx.Len, sx.Len}
|
||||||
|
return *(*[]byte)(unsafe.Pointer(&bx))
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,852 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
MSGPACK
|
||||||
|
|
||||||
|
Msgpack-c implementation powers the c, c++, python, ruby, etc libraries.
|
||||||
|
We need to maintain compatibility with it and how it encodes integer values
|
||||||
|
without caring about the type.
|
||||||
|
|
||||||
|
For compatibility with behaviour of msgpack-c reference implementation:
|
||||||
|
- Go intX (>0) and uintX
|
||||||
|
IS ENCODED AS
|
||||||
|
msgpack +ve fixnum, unsigned
|
||||||
|
- Go intX (<0)
|
||||||
|
IS ENCODED AS
|
||||||
|
msgpack -ve fixnum, signed
|
||||||
|
|
||||||
|
*/
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/rpc"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mpPosFixNumMin byte = 0x00
|
||||||
|
mpPosFixNumMax = 0x7f
|
||||||
|
mpFixMapMin = 0x80
|
||||||
|
mpFixMapMax = 0x8f
|
||||||
|
mpFixArrayMin = 0x90
|
||||||
|
mpFixArrayMax = 0x9f
|
||||||
|
mpFixStrMin = 0xa0
|
||||||
|
mpFixStrMax = 0xbf
|
||||||
|
mpNil = 0xc0
|
||||||
|
_ = 0xc1
|
||||||
|
mpFalse = 0xc2
|
||||||
|
mpTrue = 0xc3
|
||||||
|
mpFloat = 0xca
|
||||||
|
mpDouble = 0xcb
|
||||||
|
mpUint8 = 0xcc
|
||||||
|
mpUint16 = 0xcd
|
||||||
|
mpUint32 = 0xce
|
||||||
|
mpUint64 = 0xcf
|
||||||
|
mpInt8 = 0xd0
|
||||||
|
mpInt16 = 0xd1
|
||||||
|
mpInt32 = 0xd2
|
||||||
|
mpInt64 = 0xd3
|
||||||
|
|
||||||
|
// extensions below
|
||||||
|
mpBin8 = 0xc4
|
||||||
|
mpBin16 = 0xc5
|
||||||
|
mpBin32 = 0xc6
|
||||||
|
mpExt8 = 0xc7
|
||||||
|
mpExt16 = 0xc8
|
||||||
|
mpExt32 = 0xc9
|
||||||
|
mpFixExt1 = 0xd4
|
||||||
|
mpFixExt2 = 0xd5
|
||||||
|
mpFixExt4 = 0xd6
|
||||||
|
mpFixExt8 = 0xd7
|
||||||
|
mpFixExt16 = 0xd8
|
||||||
|
|
||||||
|
mpStr8 = 0xd9 // new
|
||||||
|
mpStr16 = 0xda
|
||||||
|
mpStr32 = 0xdb
|
||||||
|
|
||||||
|
mpArray16 = 0xdc
|
||||||
|
mpArray32 = 0xdd
|
||||||
|
|
||||||
|
mpMap16 = 0xde
|
||||||
|
mpMap32 = 0xdf
|
||||||
|
|
||||||
|
mpNegFixNumMin = 0xe0
|
||||||
|
mpNegFixNumMax = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgpackSpecRpcMultiArgs is a special type which signifies to the MsgpackSpecRpcCodec
|
||||||
|
// that the backend RPC service takes multiple arguments, which have been arranged
|
||||||
|
// in sequence in the slice.
|
||||||
|
//
|
||||||
|
// The Codec then passes it AS-IS to the rpc service (without wrapping it in an
|
||||||
|
// array of 1 element).
|
||||||
|
type MsgpackSpecRpcMultiArgs []interface{}
|
||||||
|
|
||||||
|
// A MsgpackContainer type specifies the different types of msgpackContainers.
|
||||||
|
type msgpackContainerType struct {
|
||||||
|
fixCutoff int
|
||||||
|
bFixMin, b8, b16, b32 byte
|
||||||
|
hasFixMin, has8, has8Always bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
msgpackContainerStr = msgpackContainerType{32, mpFixStrMin, mpStr8, mpStr16, mpStr32, true, true, false}
|
||||||
|
msgpackContainerBin = msgpackContainerType{0, 0, mpBin8, mpBin16, mpBin32, false, true, true}
|
||||||
|
msgpackContainerList = msgpackContainerType{16, mpFixArrayMin, 0, mpArray16, mpArray32, true, false, false}
|
||||||
|
msgpackContainerMap = msgpackContainerType{16, mpFixMapMin, 0, mpMap16, mpMap32, true, false, false}
|
||||||
|
)
|
||||||
|
|
||||||
|
//---------------------------------------------
|
||||||
|
|
||||||
|
type msgpackEncDriver struct {
|
||||||
|
noBuiltInTypes
|
||||||
|
encNoSeparator
|
||||||
|
e *Encoder
|
||||||
|
w encWriter
|
||||||
|
h *MsgpackHandle
|
||||||
|
x [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeNil() {
|
||||||
|
e.w.writen1(mpNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeInt(i int64) {
|
||||||
|
if i >= 0 {
|
||||||
|
e.EncodeUint(uint64(i))
|
||||||
|
} else if i >= -32 {
|
||||||
|
e.w.writen1(byte(i))
|
||||||
|
} else if i >= math.MinInt8 {
|
||||||
|
e.w.writen2(mpInt8, byte(i))
|
||||||
|
} else if i >= math.MinInt16 {
|
||||||
|
e.w.writen1(mpInt16)
|
||||||
|
bigenHelper{e.x[:2], e.w}.writeUint16(uint16(i))
|
||||||
|
} else if i >= math.MinInt32 {
|
||||||
|
e.w.writen1(mpInt32)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(uint32(i))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(mpInt64)
|
||||||
|
bigenHelper{e.x[:8], e.w}.writeUint64(uint64(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeUint(i uint64) {
|
||||||
|
if i <= math.MaxInt8 {
|
||||||
|
e.w.writen1(byte(i))
|
||||||
|
} else if i <= math.MaxUint8 {
|
||||||
|
e.w.writen2(mpUint8, byte(i))
|
||||||
|
} else if i <= math.MaxUint16 {
|
||||||
|
e.w.writen1(mpUint16)
|
||||||
|
bigenHelper{e.x[:2], e.w}.writeUint16(uint16(i))
|
||||||
|
} else if i <= math.MaxUint32 {
|
||||||
|
e.w.writen1(mpUint32)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(uint32(i))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(mpUint64)
|
||||||
|
bigenHelper{e.x[:8], e.w}.writeUint64(uint64(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeBool(b bool) {
|
||||||
|
if b {
|
||||||
|
e.w.writen1(mpTrue)
|
||||||
|
} else {
|
||||||
|
e.w.writen1(mpFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeFloat32(f float32) {
|
||||||
|
e.w.writen1(mpFloat)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(math.Float32bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeFloat64(f float64) {
|
||||||
|
e.w.writen1(mpDouble)
|
||||||
|
bigenHelper{e.x[:8], e.w}.writeUint64(math.Float64bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeExt(v interface{}, xtag uint64, ext Ext, _ *Encoder) {
|
||||||
|
bs := ext.WriteExt(v)
|
||||||
|
if bs == nil {
|
||||||
|
e.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.h.WriteExt {
|
||||||
|
e.encodeExtPreamble(uint8(xtag), len(bs))
|
||||||
|
e.w.writeb(bs)
|
||||||
|
} else {
|
||||||
|
e.EncodeStringBytes(c_RAW, bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
|
||||||
|
e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
|
||||||
|
e.w.writeb(re.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) encodeExtPreamble(xtag byte, l int) {
|
||||||
|
if l == 1 {
|
||||||
|
e.w.writen2(mpFixExt1, xtag)
|
||||||
|
} else if l == 2 {
|
||||||
|
e.w.writen2(mpFixExt2, xtag)
|
||||||
|
} else if l == 4 {
|
||||||
|
e.w.writen2(mpFixExt4, xtag)
|
||||||
|
} else if l == 8 {
|
||||||
|
e.w.writen2(mpFixExt8, xtag)
|
||||||
|
} else if l == 16 {
|
||||||
|
e.w.writen2(mpFixExt16, xtag)
|
||||||
|
} else if l < 256 {
|
||||||
|
e.w.writen2(mpExt8, byte(l))
|
||||||
|
e.w.writen1(xtag)
|
||||||
|
} else if l < 65536 {
|
||||||
|
e.w.writen1(mpExt16)
|
||||||
|
bigenHelper{e.x[:2], e.w}.writeUint16(uint16(l))
|
||||||
|
e.w.writen1(xtag)
|
||||||
|
} else {
|
||||||
|
e.w.writen1(mpExt32)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(uint32(l))
|
||||||
|
e.w.writen1(xtag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeArrayStart(length int) {
|
||||||
|
e.writeContainerLen(msgpackContainerList, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeMapStart(length int) {
|
||||||
|
e.writeContainerLen(msgpackContainerMap, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeString(c charEncoding, s string) {
|
||||||
|
if c == c_RAW && e.h.WriteExt {
|
||||||
|
e.writeContainerLen(msgpackContainerBin, len(s))
|
||||||
|
} else {
|
||||||
|
e.writeContainerLen(msgpackContainerStr, len(s))
|
||||||
|
}
|
||||||
|
if len(s) > 0 {
|
||||||
|
e.w.writestr(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeSymbol(v string) {
|
||||||
|
e.EncodeString(c_UTF8, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) EncodeStringBytes(c charEncoding, bs []byte) {
|
||||||
|
if c == c_RAW && e.h.WriteExt {
|
||||||
|
e.writeContainerLen(msgpackContainerBin, len(bs))
|
||||||
|
} else {
|
||||||
|
e.writeContainerLen(msgpackContainerStr, len(bs))
|
||||||
|
}
|
||||||
|
if len(bs) > 0 {
|
||||||
|
e.w.writeb(bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) writeContainerLen(ct msgpackContainerType, l int) {
|
||||||
|
if ct.hasFixMin && l < ct.fixCutoff {
|
||||||
|
e.w.writen1(ct.bFixMin | byte(l))
|
||||||
|
} else if ct.has8 && l < 256 && (ct.has8Always || e.h.WriteExt) {
|
||||||
|
e.w.writen2(ct.b8, uint8(l))
|
||||||
|
} else if l < 65536 {
|
||||||
|
e.w.writen1(ct.b16)
|
||||||
|
bigenHelper{e.x[:2], e.w}.writeUint16(uint16(l))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(ct.b32)
|
||||||
|
bigenHelper{e.x[:4], e.w}.writeUint32(uint32(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------
|
||||||
|
|
||||||
|
type msgpackDecDriver struct {
|
||||||
|
d *Decoder
|
||||||
|
r decReader // *Decoder decReader decReaderT
|
||||||
|
h *MsgpackHandle
|
||||||
|
b [scratchByteArrayLen]byte
|
||||||
|
bd byte
|
||||||
|
bdRead bool
|
||||||
|
br bool // bytes reader
|
||||||
|
noBuiltInTypes
|
||||||
|
noStreamingCodec
|
||||||
|
decNoSeparator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This returns either a primitive (int, bool, etc) for non-containers,
|
||||||
|
// or a containerType, or a specific type denoting nil or extension.
|
||||||
|
// It is called when a nil interface{} is passed, leaving it up to the DecDriver
|
||||||
|
// to introspect the stream and decide how best to decode.
|
||||||
|
// It deciphers the value by looking at the stream first.
|
||||||
|
func (d *msgpackDecDriver) DecodeNaked() {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
bd := d.bd
|
||||||
|
n := &d.d.n
|
||||||
|
var decodeFurther bool
|
||||||
|
|
||||||
|
switch bd {
|
||||||
|
case mpNil:
|
||||||
|
n.v = valueTypeNil
|
||||||
|
d.bdRead = false
|
||||||
|
case mpFalse:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = false
|
||||||
|
case mpTrue:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = true
|
||||||
|
|
||||||
|
case mpFloat:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
|
||||||
|
case mpDouble:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
|
||||||
|
|
||||||
|
case mpUint8:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(d.r.readn1())
|
||||||
|
case mpUint16:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case mpUint32:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
case mpUint64:
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = uint64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
|
||||||
|
case mpInt8:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int8(d.r.readn1()))
|
||||||
|
case mpInt16:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int16(bigen.Uint16(d.r.readx(2))))
|
||||||
|
case mpInt32:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int32(bigen.Uint32(d.r.readx(4))))
|
||||||
|
case mpInt64:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int64(bigen.Uint64(d.r.readx(8))))
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case bd >= mpPosFixNumMin && bd <= mpPosFixNumMax:
|
||||||
|
// positive fixnum (always signed)
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int8(bd))
|
||||||
|
case bd >= mpNegFixNumMin && bd <= mpNegFixNumMax:
|
||||||
|
// negative fixnum
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(int8(bd))
|
||||||
|
case bd == mpStr8, bd == mpStr16, bd == mpStr32, bd >= mpFixStrMin && bd <= mpFixStrMax:
|
||||||
|
if d.h.RawToString {
|
||||||
|
n.v = valueTypeString
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
} else {
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
}
|
||||||
|
case bd == mpBin8, bd == mpBin16, bd == mpBin32:
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
case bd == mpArray16, bd == mpArray32, bd >= mpFixArrayMin && bd <= mpFixArrayMax:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
decodeFurther = true
|
||||||
|
case bd == mpMap16, bd == mpMap32, bd >= mpFixMapMin && bd <= mpFixMapMax:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
decodeFurther = true
|
||||||
|
case bd >= mpFixExt1 && bd <= mpFixExt16, bd >= mpExt8 && bd <= mpExt32:
|
||||||
|
n.v = valueTypeExt
|
||||||
|
clen := d.readExtLen()
|
||||||
|
n.u = uint64(d.r.readn1())
|
||||||
|
n.l = d.r.readx(clen)
|
||||||
|
default:
|
||||||
|
d.d.errorf("Nil-Deciphered DecodeValue: %s: hex: %x, dec: %d", msgBadDesc, bd, bd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !decodeFurther {
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
if n.v == valueTypeUint && d.h.SignedInteger {
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = int64(n.u)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// int can be decoded from msgpack type: intXXX or uintXXX
|
||||||
|
func (d *msgpackDecDriver) DecodeInt(bitsize uint8) (i int64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
switch d.bd {
|
||||||
|
case mpUint8:
|
||||||
|
i = int64(uint64(d.r.readn1()))
|
||||||
|
case mpUint16:
|
||||||
|
i = int64(uint64(bigen.Uint16(d.r.readx(2))))
|
||||||
|
case mpUint32:
|
||||||
|
i = int64(uint64(bigen.Uint32(d.r.readx(4))))
|
||||||
|
case mpUint64:
|
||||||
|
i = int64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
case mpInt8:
|
||||||
|
i = int64(int8(d.r.readn1()))
|
||||||
|
case mpInt16:
|
||||||
|
i = int64(int16(bigen.Uint16(d.r.readx(2))))
|
||||||
|
case mpInt32:
|
||||||
|
i = int64(int32(bigen.Uint32(d.r.readx(4))))
|
||||||
|
case mpInt64:
|
||||||
|
i = int64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case d.bd >= mpPosFixNumMin && d.bd <= mpPosFixNumMax:
|
||||||
|
i = int64(int8(d.bd))
|
||||||
|
case d.bd >= mpNegFixNumMin && d.bd <= mpNegFixNumMax:
|
||||||
|
i = int64(int8(d.bd))
|
||||||
|
default:
|
||||||
|
d.d.errorf("Unhandled single-byte unsigned integer value: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
|
||||||
|
if bitsize > 0 {
|
||||||
|
if trunc := (i << (64 - bitsize)) >> (64 - bitsize); i != trunc {
|
||||||
|
d.d.errorf("Overflow int value: %v", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// uint can be decoded from msgpack type: intXXX or uintXXX
|
||||||
|
func (d *msgpackDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
switch d.bd {
|
||||||
|
case mpUint8:
|
||||||
|
ui = uint64(d.r.readn1())
|
||||||
|
case mpUint16:
|
||||||
|
ui = uint64(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case mpUint32:
|
||||||
|
ui = uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
case mpUint64:
|
||||||
|
ui = bigen.Uint64(d.r.readx(8))
|
||||||
|
case mpInt8:
|
||||||
|
if i := int64(int8(d.r.readn1())); i >= 0 {
|
||||||
|
ui = uint64(i)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case mpInt16:
|
||||||
|
if i := int64(int16(bigen.Uint16(d.r.readx(2)))); i >= 0 {
|
||||||
|
ui = uint64(i)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case mpInt32:
|
||||||
|
if i := int64(int32(bigen.Uint32(d.r.readx(4)))); i >= 0 {
|
||||||
|
ui = uint64(i)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case mpInt64:
|
||||||
|
if i := int64(bigen.Uint64(d.r.readx(8))); i >= 0 {
|
||||||
|
ui = uint64(i)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case d.bd >= mpPosFixNumMin && d.bd <= mpPosFixNumMax:
|
||||||
|
ui = uint64(d.bd)
|
||||||
|
case d.bd >= mpNegFixNumMin && d.bd <= mpNegFixNumMax:
|
||||||
|
d.d.errorf("Assigning negative signed value: %v, to unsigned type", int(d.bd))
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
d.d.errorf("Unhandled single-byte unsigned integer value: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
|
||||||
|
if bitsize > 0 {
|
||||||
|
if trunc := (ui << (64 - bitsize)) >> (64 - bitsize); ui != trunc {
|
||||||
|
d.d.errorf("Overflow uint value: %v", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// float can either be decoded from msgpack type: float, double or intX
|
||||||
|
func (d *msgpackDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == mpFloat {
|
||||||
|
f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
|
||||||
|
} else if d.bd == mpDouble {
|
||||||
|
f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
|
||||||
|
} else {
|
||||||
|
f = float64(d.DecodeInt(0))
|
||||||
|
}
|
||||||
|
if chkOverflow32 && chkOvf.Float32(f) {
|
||||||
|
d.d.errorf("msgpack: float32 overflow: %v", f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool can be decoded from bool, fixnum 0 or 1.
|
||||||
|
func (d *msgpackDecDriver) DecodeBool() (b bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == mpFalse || d.bd == 0 {
|
||||||
|
// b = false
|
||||||
|
} else if d.bd == mpTrue || d.bd == 1 {
|
||||||
|
b = true
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
var clen int
|
||||||
|
// ignore isstring. Expect that the bytes may be found from msgpackContainerStr or msgpackContainerBin
|
||||||
|
if bd := d.bd; bd == mpBin8 || bd == mpBin16 || bd == mpBin32 {
|
||||||
|
clen = d.readContainerLen(msgpackContainerBin)
|
||||||
|
} else {
|
||||||
|
clen = d.readContainerLen(msgpackContainerStr)
|
||||||
|
}
|
||||||
|
// println("DecodeBytes: clen: ", clen)
|
||||||
|
d.bdRead = false
|
||||||
|
// bytes may be nil, so handle it. if nil, clen=-1.
|
||||||
|
if clen < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if zerocopy {
|
||||||
|
if d.br {
|
||||||
|
return d.r.readx(clen)
|
||||||
|
} else if len(bs) == 0 {
|
||||||
|
bs = d.b[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) DecodeString() (s string) {
|
||||||
|
return string(d.DecodeBytes(d.b[:], true, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) readNextBd() {
|
||||||
|
d.bd = d.r.readn1()
|
||||||
|
d.bdRead = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) uncacheRead() {
|
||||||
|
if d.bdRead {
|
||||||
|
d.r.unreadn1()
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) ContainerType() (vt valueType) {
|
||||||
|
bd := d.bd
|
||||||
|
if bd == mpNil {
|
||||||
|
return valueTypeNil
|
||||||
|
} else if bd == mpBin8 || bd == mpBin16 || bd == mpBin32 ||
|
||||||
|
(!d.h.RawToString &&
|
||||||
|
(bd == mpStr8 || bd == mpStr16 || bd == mpStr32 || (bd >= mpFixStrMin && bd <= mpFixStrMax))) {
|
||||||
|
return valueTypeBytes
|
||||||
|
} else if d.h.RawToString &&
|
||||||
|
(bd == mpStr8 || bd == mpStr16 || bd == mpStr32 || (bd >= mpFixStrMin && bd <= mpFixStrMax)) {
|
||||||
|
return valueTypeString
|
||||||
|
} else if bd == mpArray16 || bd == mpArray32 || (bd >= mpFixArrayMin && bd <= mpFixArrayMax) {
|
||||||
|
return valueTypeArray
|
||||||
|
} else if bd == mpMap16 || bd == mpMap32 || (bd >= mpFixMapMin && bd <= mpFixMapMax) {
|
||||||
|
return valueTypeMap
|
||||||
|
} else {
|
||||||
|
// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
|
||||||
|
}
|
||||||
|
return valueTypeUnset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) TryDecodeAsNil() (v bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == mpNil {
|
||||||
|
d.bdRead = false
|
||||||
|
v = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) readContainerLen(ct msgpackContainerType) (clen int) {
|
||||||
|
bd := d.bd
|
||||||
|
if bd == mpNil {
|
||||||
|
clen = -1 // to represent nil
|
||||||
|
} else if bd == ct.b8 {
|
||||||
|
clen = int(d.r.readn1())
|
||||||
|
} else if bd == ct.b16 {
|
||||||
|
clen = int(bigen.Uint16(d.r.readx(2)))
|
||||||
|
} else if bd == ct.b32 {
|
||||||
|
clen = int(bigen.Uint32(d.r.readx(4)))
|
||||||
|
} else if (ct.bFixMin & bd) == ct.bFixMin {
|
||||||
|
clen = int(ct.bFixMin ^ bd)
|
||||||
|
} else {
|
||||||
|
d.d.errorf("readContainerLen: %s: hex: %x, decimal: %d", msgBadDesc, bd, bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) ReadMapStart() int {
|
||||||
|
return d.readContainerLen(msgpackContainerMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) ReadArrayStart() int {
|
||||||
|
return d.readContainerLen(msgpackContainerList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) readExtLen() (clen int) {
|
||||||
|
switch d.bd {
|
||||||
|
case mpNil:
|
||||||
|
clen = -1 // to represent nil
|
||||||
|
case mpFixExt1:
|
||||||
|
clen = 1
|
||||||
|
case mpFixExt2:
|
||||||
|
clen = 2
|
||||||
|
case mpFixExt4:
|
||||||
|
clen = 4
|
||||||
|
case mpFixExt8:
|
||||||
|
clen = 8
|
||||||
|
case mpFixExt16:
|
||||||
|
clen = 16
|
||||||
|
case mpExt8:
|
||||||
|
clen = int(d.r.readn1())
|
||||||
|
case mpExt16:
|
||||||
|
clen = int(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case mpExt32:
|
||||||
|
clen = int(bigen.Uint32(d.r.readx(4)))
|
||||||
|
default:
|
||||||
|
d.d.errorf("decoding ext bytes: found unexpected byte: %x", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
|
||||||
|
if xtag > 0xff {
|
||||||
|
d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
|
||||||
|
realxtag = uint64(realxtag1)
|
||||||
|
if ext == nil {
|
||||||
|
re := rv.(*RawExt)
|
||||||
|
re.Tag = realxtag
|
||||||
|
re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
|
||||||
|
} else {
|
||||||
|
ext.ReadExt(rv, xbs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
xbd := d.bd
|
||||||
|
if xbd == mpBin8 || xbd == mpBin16 || xbd == mpBin32 {
|
||||||
|
xbs = d.DecodeBytes(nil, false, true)
|
||||||
|
} else if xbd == mpStr8 || xbd == mpStr16 || xbd == mpStr32 ||
|
||||||
|
(xbd >= mpFixStrMin && xbd <= mpFixStrMax) {
|
||||||
|
xbs = d.DecodeBytes(nil, true, true)
|
||||||
|
} else {
|
||||||
|
clen := d.readExtLen()
|
||||||
|
xtag = d.r.readn1()
|
||||||
|
if verifyTag && xtag != tag {
|
||||||
|
d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xbs = d.r.readx(clen)
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
//MsgpackHandle is a Handle for the Msgpack Schema-Free Encoding Format.
|
||||||
|
type MsgpackHandle struct {
|
||||||
|
BasicHandle
|
||||||
|
|
||||||
|
// RawToString controls how raw bytes are decoded into a nil interface{}.
|
||||||
|
RawToString bool
|
||||||
|
|
||||||
|
// WriteExt flag supports encoding configured extensions with extension tags.
|
||||||
|
// It also controls whether other elements of the new spec are encoded (ie Str8).
|
||||||
|
//
|
||||||
|
// With WriteExt=false, configured extensions are serialized as raw bytes
|
||||||
|
// and Str8 is not encoded.
|
||||||
|
//
|
||||||
|
// A stream can still be decoded into a typed value, provided an appropriate value
|
||||||
|
// is provided, but the type cannot be inferred from the stream. If no appropriate
|
||||||
|
// type is provided (e.g. decoding into a nil interface{}), you get back
|
||||||
|
// a []byte or string based on the setting of RawToString.
|
||||||
|
WriteExt bool
|
||||||
|
binaryEncodingType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MsgpackHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
|
||||||
|
return h.SetExt(rt, tag, &setExtWrapper{b: ext})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MsgpackHandle) newEncDriver(e *Encoder) encDriver {
|
||||||
|
return &msgpackEncDriver{e: e, w: e.w, h: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MsgpackHandle) newDecDriver(d *Decoder) decDriver {
|
||||||
|
return &msgpackDecDriver{d: d, h: h, r: d.r, br: d.bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *msgpackEncDriver) reset() {
|
||||||
|
e.w = e.e.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *msgpackDecDriver) reset() {
|
||||||
|
d.r, d.br = d.d.r, d.d.bytes
|
||||||
|
d.bd, d.bdRead = 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
type msgpackSpecRpcCodec struct {
|
||||||
|
rpcCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////// Spec RPC Codec ///////////////////
|
||||||
|
func (c *msgpackSpecRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
|
||||||
|
// WriteRequest can write to both a Go service, and other services that do
|
||||||
|
// not abide by the 1 argument rule of a Go service.
|
||||||
|
// We discriminate based on if the body is a MsgpackSpecRpcMultiArgs
|
||||||
|
var bodyArr []interface{}
|
||||||
|
if m, ok := body.(MsgpackSpecRpcMultiArgs); ok {
|
||||||
|
bodyArr = ([]interface{})(m)
|
||||||
|
} else {
|
||||||
|
bodyArr = []interface{}{body}
|
||||||
|
}
|
||||||
|
r2 := []interface{}{0, uint32(r.Seq), r.ServiceMethod, bodyArr}
|
||||||
|
return c.write(r2, nil, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *msgpackSpecRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
|
||||||
|
var moe interface{}
|
||||||
|
if r.Error != "" {
|
||||||
|
moe = r.Error
|
||||||
|
}
|
||||||
|
if moe != nil && body != nil {
|
||||||
|
body = nil
|
||||||
|
}
|
||||||
|
r2 := []interface{}{1, uint32(r.Seq), moe, body}
|
||||||
|
return c.write(r2, nil, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *msgpackSpecRpcCodec) ReadResponseHeader(r *rpc.Response) error {
|
||||||
|
return c.parseCustomHeader(1, &r.Seq, &r.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *msgpackSpecRpcCodec) ReadRequestHeader(r *rpc.Request) error {
|
||||||
|
return c.parseCustomHeader(0, &r.Seq, &r.ServiceMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *msgpackSpecRpcCodec) ReadRequestBody(body interface{}) error {
|
||||||
|
if body == nil { // read and discard
|
||||||
|
return c.read(nil)
|
||||||
|
}
|
||||||
|
bodyArr := []interface{}{body}
|
||||||
|
return c.read(&bodyArr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *msgpackSpecRpcCodec) parseCustomHeader(expectTypeByte byte, msgid *uint64, methodOrError *string) (err error) {
|
||||||
|
|
||||||
|
if c.isClosed() {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// We read the response header by hand
|
||||||
|
// so that the body can be decoded on its own from the stream at a later time.
|
||||||
|
|
||||||
|
const fia byte = 0x94 //four item array descriptor value
|
||||||
|
// Not sure why the panic of EOF is swallowed above.
|
||||||
|
// if bs1 := c.dec.r.readn1(); bs1 != fia {
|
||||||
|
// err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, bs1)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
var b byte
|
||||||
|
b, err = c.br.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b != fia {
|
||||||
|
err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.read(&b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b != expectTypeByte {
|
||||||
|
err = fmt.Errorf("Unexpected byte descriptor in header. Expecting %v. Received %v", expectTypeByte, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = c.read(msgid); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = c.read(methodOrError); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
// msgpackSpecRpc is the implementation of Rpc that uses custom communication protocol
|
||||||
|
// as defined in the msgpack spec at https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
|
||||||
|
type msgpackSpecRpc struct{}
|
||||||
|
|
||||||
|
// MsgpackSpecRpc implements Rpc using the communication protocol defined in
|
||||||
|
// the msgpack spec at https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md .
|
||||||
|
// Its methods (ServerCodec and ClientCodec) return values that implement RpcCodecBuffered.
|
||||||
|
var MsgpackSpecRpc msgpackSpecRpc
|
||||||
|
|
||||||
|
func (x msgpackSpecRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
|
||||||
|
return &msgpackSpecRpcCodec{newRPCCodec(conn, h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x msgpackSpecRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
|
||||||
|
return &msgpackSpecRpcCodec{newRPCCodec(conn, h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ decDriver = (*msgpackDecDriver)(nil)
|
||||||
|
var _ encDriver = (*msgpackEncDriver)(nil)
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoopHandle returns a no-op handle. It basically does nothing.
|
||||||
|
// It is only useful for benchmarking, as it gives an idea of the
|
||||||
|
// overhead from the codec framework.
|
||||||
|
//
|
||||||
|
// LIBRARY USERS: *** DO NOT USE ***
|
||||||
|
func NoopHandle(slen int) *noopHandle {
|
||||||
|
h := noopHandle{}
|
||||||
|
h.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
h.B = make([][]byte, slen)
|
||||||
|
h.S = make([]string, slen)
|
||||||
|
for i := 0; i < len(h.S); i++ {
|
||||||
|
b := make([]byte, i+1)
|
||||||
|
for j := 0; j < len(b); j++ {
|
||||||
|
b[j] = 'a' + byte(i)
|
||||||
|
}
|
||||||
|
h.B[i] = b
|
||||||
|
h.S[i] = string(b)
|
||||||
|
}
|
||||||
|
return &h
|
||||||
|
}
|
||||||
|
|
||||||
|
// noopHandle does nothing.
|
||||||
|
// It is used to simulate the overhead of the codec framework.
|
||||||
|
type noopHandle struct {
|
||||||
|
BasicHandle
|
||||||
|
binaryEncodingType
|
||||||
|
noopDrv // noopDrv is unexported here, so we can get a copy of it when needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopDrv struct {
|
||||||
|
d *Decoder
|
||||||
|
e *Encoder
|
||||||
|
i int
|
||||||
|
S []string
|
||||||
|
B [][]byte
|
||||||
|
mks []bool // stack. if map (true), else if array (false)
|
||||||
|
mk bool // top of stack. what container are we on? map or array?
|
||||||
|
ct valueType // last response for IsContainerType.
|
||||||
|
cb int // counter for ContainerType
|
||||||
|
rand *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *noopDrv) r(v int) int { return h.rand.Intn(v) }
|
||||||
|
func (h *noopDrv) m(v int) int { h.i++; return h.i % v }
|
||||||
|
|
||||||
|
func (h *noopDrv) newEncDriver(e *Encoder) encDriver { h.e = e; return h }
|
||||||
|
func (h *noopDrv) newDecDriver(d *Decoder) decDriver { h.d = d; return h }
|
||||||
|
|
||||||
|
func (h *noopDrv) reset() {}
|
||||||
|
func (h *noopDrv) uncacheRead() {}
|
||||||
|
|
||||||
|
// --- encDriver
|
||||||
|
|
||||||
|
// stack functions (for map and array)
|
||||||
|
func (h *noopDrv) start(b bool) {
|
||||||
|
// println("start", len(h.mks)+1)
|
||||||
|
h.mks = append(h.mks, b)
|
||||||
|
h.mk = b
|
||||||
|
}
|
||||||
|
func (h *noopDrv) end() {
|
||||||
|
// println("end: ", len(h.mks)-1)
|
||||||
|
h.mks = h.mks[:len(h.mks)-1]
|
||||||
|
if len(h.mks) > 0 {
|
||||||
|
h.mk = h.mks[len(h.mks)-1]
|
||||||
|
} else {
|
||||||
|
h.mk = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *noopDrv) EncodeBuiltin(rt uintptr, v interface{}) {}
|
||||||
|
func (h *noopDrv) EncodeNil() {}
|
||||||
|
func (h *noopDrv) EncodeInt(i int64) {}
|
||||||
|
func (h *noopDrv) EncodeUint(i uint64) {}
|
||||||
|
func (h *noopDrv) EncodeBool(b bool) {}
|
||||||
|
func (h *noopDrv) EncodeFloat32(f float32) {}
|
||||||
|
func (h *noopDrv) EncodeFloat64(f float64) {}
|
||||||
|
func (h *noopDrv) EncodeRawExt(re *RawExt, e *Encoder) {}
|
||||||
|
func (h *noopDrv) EncodeArrayStart(length int) { h.start(true) }
|
||||||
|
func (h *noopDrv) EncodeMapStart(length int) { h.start(false) }
|
||||||
|
func (h *noopDrv) EncodeEnd() { h.end() }
|
||||||
|
|
||||||
|
func (h *noopDrv) EncodeString(c charEncoding, v string) {}
|
||||||
|
func (h *noopDrv) EncodeSymbol(v string) {}
|
||||||
|
func (h *noopDrv) EncodeStringBytes(c charEncoding, v []byte) {}
|
||||||
|
|
||||||
|
func (h *noopDrv) EncodeExt(rv interface{}, xtag uint64, ext Ext, e *Encoder) {}
|
||||||
|
|
||||||
|
// ---- decDriver
|
||||||
|
func (h *noopDrv) initReadNext() {}
|
||||||
|
func (h *noopDrv) CheckBreak() bool { return false }
|
||||||
|
func (h *noopDrv) IsBuiltinType(rt uintptr) bool { return false }
|
||||||
|
func (h *noopDrv) DecodeBuiltin(rt uintptr, v interface{}) {}
|
||||||
|
func (h *noopDrv) DecodeInt(bitsize uint8) (i int64) { return int64(h.m(15)) }
|
||||||
|
func (h *noopDrv) DecodeUint(bitsize uint8) (ui uint64) { return uint64(h.m(35)) }
|
||||||
|
func (h *noopDrv) DecodeFloat(chkOverflow32 bool) (f float64) { return float64(h.m(95)) }
|
||||||
|
func (h *noopDrv) DecodeBool() (b bool) { return h.m(2) == 0 }
|
||||||
|
func (h *noopDrv) DecodeString() (s string) { return h.S[h.m(8)] }
|
||||||
|
|
||||||
|
// func (h *noopDrv) DecodeStringAsBytes(bs []byte) []byte { return h.DecodeBytes(bs) }
|
||||||
|
|
||||||
|
func (h *noopDrv) DecodeBytes(bs []byte, isstring, zerocopy bool) []byte { return h.B[h.m(len(h.B))] }
|
||||||
|
|
||||||
|
func (h *noopDrv) ReadEnd() { h.end() }
|
||||||
|
|
||||||
|
// toggle map/slice
|
||||||
|
func (h *noopDrv) ReadMapStart() int { h.start(true); return h.m(10) }
|
||||||
|
func (h *noopDrv) ReadArrayStart() int { h.start(false); return h.m(10) }
|
||||||
|
|
||||||
|
func (h *noopDrv) ContainerType() (vt valueType) {
|
||||||
|
// return h.m(2) == 0
|
||||||
|
// handle kStruct, which will bomb is it calls this and doesn't get back a map or array.
|
||||||
|
// consequently, if the return value is not map or array, reset it to one of them based on h.m(7) % 2
|
||||||
|
// for kstruct: at least one out of every 2 times, return one of valueTypeMap or Array (else kstruct bombs)
|
||||||
|
// however, every 10th time it is called, we just return something else.
|
||||||
|
var vals = [...]valueType{valueTypeArray, valueTypeMap}
|
||||||
|
// ------------ TAKE ------------
|
||||||
|
// if h.cb%2 == 0 {
|
||||||
|
// if h.ct == valueTypeMap || h.ct == valueTypeArray {
|
||||||
|
// } else {
|
||||||
|
// h.ct = vals[h.m(2)]
|
||||||
|
// }
|
||||||
|
// } else if h.cb%5 == 0 {
|
||||||
|
// h.ct = valueType(h.m(8))
|
||||||
|
// } else {
|
||||||
|
// h.ct = vals[h.m(2)]
|
||||||
|
// }
|
||||||
|
// ------------ TAKE ------------
|
||||||
|
// if h.cb%16 == 0 {
|
||||||
|
// h.ct = valueType(h.cb % 8)
|
||||||
|
// } else {
|
||||||
|
// h.ct = vals[h.cb%2]
|
||||||
|
// }
|
||||||
|
h.ct = vals[h.cb%2]
|
||||||
|
h.cb++
|
||||||
|
return h.ct
|
||||||
|
|
||||||
|
// if h.ct == valueTypeNil || h.ct == valueTypeString || h.ct == valueTypeBytes {
|
||||||
|
// return h.ct
|
||||||
|
// }
|
||||||
|
// return valueTypeUnset
|
||||||
|
// TODO: may need to tweak this so it works.
|
||||||
|
// if h.ct == valueTypeMap && vt == valueTypeArray || h.ct == valueTypeArray && vt == valueTypeMap {
|
||||||
|
// h.cb = !h.cb
|
||||||
|
// h.ct = vt
|
||||||
|
// return h.cb
|
||||||
|
// }
|
||||||
|
// // go in a loop and check it.
|
||||||
|
// h.ct = vt
|
||||||
|
// h.cb = h.m(7) == 0
|
||||||
|
// return h.cb
|
||||||
|
}
|
||||||
|
func (h *noopDrv) TryDecodeAsNil() bool {
|
||||||
|
if h.mk {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return h.m(8) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (h *noopDrv) DecodeExt(rv interface{}, xtag uint64, ext Ext) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *noopDrv) DecodeNaked() {
|
||||||
|
// use h.r (random) not h.m() because h.m() could cause the same value to be given.
|
||||||
|
var sk int
|
||||||
|
if h.mk {
|
||||||
|
// if mapkey, do not support values of nil OR bytes, array, map or rawext
|
||||||
|
sk = h.r(7) + 1
|
||||||
|
} else {
|
||||||
|
sk = h.r(12)
|
||||||
|
}
|
||||||
|
n := &h.d.n
|
||||||
|
switch sk {
|
||||||
|
case 0:
|
||||||
|
n.v = valueTypeNil
|
||||||
|
case 1:
|
||||||
|
n.v, n.b = valueTypeBool, false
|
||||||
|
case 2:
|
||||||
|
n.v, n.b = valueTypeBool, true
|
||||||
|
case 3:
|
||||||
|
n.v, n.i = valueTypeInt, h.DecodeInt(64)
|
||||||
|
case 4:
|
||||||
|
n.v, n.u = valueTypeUint, h.DecodeUint(64)
|
||||||
|
case 5:
|
||||||
|
n.v, n.f = valueTypeFloat, h.DecodeFloat(true)
|
||||||
|
case 6:
|
||||||
|
n.v, n.f = valueTypeFloat, h.DecodeFloat(false)
|
||||||
|
case 7:
|
||||||
|
n.v, n.s = valueTypeString, h.DecodeString()
|
||||||
|
case 8:
|
||||||
|
n.v, n.l = valueTypeBytes, h.B[h.m(len(h.B))]
|
||||||
|
case 9:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
case 10:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
default:
|
||||||
|
n.v = valueTypeExt
|
||||||
|
n.u = h.DecodeUint(64)
|
||||||
|
n.l = h.B[h.m(len(h.B))]
|
||||||
|
}
|
||||||
|
h.ct = n.v
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package codec
|
||||||
|
|
||||||
|
//go:generate bash prebuild.sh
|
|
@ -0,0 +1,199 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# _needgen is a helper function to tell if we need to generate files for msgp, codecgen.
|
||||||
|
_needgen() {
|
||||||
|
local a="$1"
|
||||||
|
zneedgen=0
|
||||||
|
if [[ ! -e "$a" ]]
|
||||||
|
then
|
||||||
|
zneedgen=1
|
||||||
|
echo 1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for i in `ls -1 *.go.tmpl gen.go values_test.go`
|
||||||
|
do
|
||||||
|
if [[ "$a" -ot "$i" ]]
|
||||||
|
then
|
||||||
|
zneedgen=1
|
||||||
|
echo 1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# _build generates fast-path.go and gen-helper.go.
|
||||||
|
#
|
||||||
|
# It is needed because there is some dependency between the generated code
|
||||||
|
# and the other classes. Consequently, we have to totally remove the
|
||||||
|
# generated files and put stubs in place, before calling "go run" again
|
||||||
|
# to recreate them.
|
||||||
|
_build() {
|
||||||
|
if ! [[ "${zforce}" == "1" ||
|
||||||
|
"1" == $( _needgen "fast-path.generated.go" ) ||
|
||||||
|
"1" == $( _needgen "gen-helper.generated.go" ) ||
|
||||||
|
"1" == $( _needgen "gen.generated.go" ) ||
|
||||||
|
1 == 0 ]]
|
||||||
|
then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# echo "Running prebuild"
|
||||||
|
if [ "${zbak}" == "1" ]
|
||||||
|
then
|
||||||
|
# echo "Backing up old generated files"
|
||||||
|
_zts=`date '+%m%d%Y_%H%M%S'`
|
||||||
|
_gg=".generated.go"
|
||||||
|
[ -e "gen-helper${_gg}" ] && mv gen-helper${_gg} gen-helper${_gg}__${_zts}.bak
|
||||||
|
[ -e "fast-path${_gg}" ] && mv fast-path${_gg} fast-path${_gg}__${_zts}.bak
|
||||||
|
# [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
|
||||||
|
# [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
|
||||||
|
else
|
||||||
|
rm -f fast-path.generated.go gen.generated.go gen-helper.generated.go \
|
||||||
|
*safe.generated.go *_generated_test.go *.generated_ffjson_expose.go
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > gen.generated.go <<EOF
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
|
||||||
|
|
||||||
|
const genDecMapTmpl = \`
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >> gen.generated.go < gen-dec-map.go.tmpl
|
||||||
|
|
||||||
|
cat >> gen.generated.go <<EOF
|
||||||
|
\`
|
||||||
|
|
||||||
|
const genDecListTmpl = \`
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >> gen.generated.go < gen-dec-array.go.tmpl
|
||||||
|
|
||||||
|
cat >> gen.generated.go <<EOF
|
||||||
|
\`
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > gen-from-tmpl.codec.generated.go <<EOF
|
||||||
|
package codec
|
||||||
|
import "io"
|
||||||
|
func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) error {
|
||||||
|
return genInternalGoFile(r, w, safe)
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > gen-from-tmpl.generated.go <<EOF
|
||||||
|
//+build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
//import "flag"
|
||||||
|
import "ugorji.net/codec"
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func run(fnameIn, fnameOut string, safe bool) {
|
||||||
|
fin, err := os.Open(fnameIn)
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
defer fin.Close()
|
||||||
|
fout, err := os.Create(fnameOut)
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
defer fout.Close()
|
||||||
|
err = codec.GenInternalGoFile(fin, fout, safe)
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// do not make safe/unsafe variants.
|
||||||
|
// Instead, depend on escape analysis, and place string creation and usage appropriately.
|
||||||
|
// run("unsafe.go.tmpl", "safe.generated.go", true)
|
||||||
|
// run("unsafe.go.tmpl", "unsafe.generated.go", false)
|
||||||
|
run("fast-path.go.tmpl", "fast-path.generated.go", false)
|
||||||
|
run("gen-helper.go.tmpl", "gen-helper.generated.go", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
go run -tags=notfastpath gen-from-tmpl.generated.go && \
|
||||||
|
rm -f gen-from-tmpl.*generated.go
|
||||||
|
}
|
||||||
|
|
||||||
|
_codegenerators() {
|
||||||
|
if [[ $zforce == "1" ||
|
||||||
|
"1" == $( _needgen "values_codecgen${zsfx}" ) ||
|
||||||
|
"1" == $( _needgen "values_msgp${zsfx}" ) ||
|
||||||
|
"1" == $( _needgen "values_ffjson${zsfx}" ) ||
|
||||||
|
1 == 0 ]]
|
||||||
|
then
|
||||||
|
# codecgen creates some temporary files in the directory (main, pkg).
|
||||||
|
# Consequently, we should start msgp and ffjson first, and also put a small time latency before
|
||||||
|
# starting codecgen.
|
||||||
|
# Without this, ffjson chokes on one of the temporary files from codecgen.
|
||||||
|
if [[ $zexternal == "1" ]]
|
||||||
|
then
|
||||||
|
echo "ffjson ... " && \
|
||||||
|
ffjson -w values_ffjson${zsfx} $zfin &
|
||||||
|
zzzIdFF=$!
|
||||||
|
echo "msgp ... " && \
|
||||||
|
msgp -tests=false -o=values_msgp${zsfx} -file=$zfin &
|
||||||
|
zzzIdMsgp=$!
|
||||||
|
|
||||||
|
sleep 1 # give ffjson and msgp some buffer time. see note above.
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "codecgen - !unsafe ... " && \
|
||||||
|
codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 19780 $zfin &
|
||||||
|
zzzIdC=$!
|
||||||
|
echo "codecgen - unsafe ... " && \
|
||||||
|
codecgen -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 19781 $zfin &
|
||||||
|
zzzIdCU=$!
|
||||||
|
wait $zzzIdC $zzzIdCU $zzzIdMsgp $zzzIdFF && \
|
||||||
|
# remove (M|Unm)arshalJSON implementations, so they don't conflict with encoding/json bench \
|
||||||
|
if [[ $zexternal == "1" ]]
|
||||||
|
then
|
||||||
|
sed -i 's+ MarshalJSON(+ _MarshalJSON(+g' values_ffjson${zsfx} && \
|
||||||
|
sed -i 's+ UnmarshalJSON(+ _UnmarshalJSON(+g' values_ffjson${zsfx}
|
||||||
|
fi && \
|
||||||
|
echo "generators done!" && \
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# _init reads the arguments and sets up the flags
|
||||||
|
_init() {
|
||||||
|
OPTIND=1
|
||||||
|
while getopts "fbx" flag
|
||||||
|
do
|
||||||
|
case "x$flag" in
|
||||||
|
'xf') zforce=1;;
|
||||||
|
'xb') zbak=1;;
|
||||||
|
'xx') zexternal=1;;
|
||||||
|
*) echo "prebuild.sh accepts [-fbx] only"; return 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
OPTIND=1
|
||||||
|
}
|
||||||
|
|
||||||
|
# main script.
|
||||||
|
# First ensure that this is being run from the basedir (i.e. dirname of script is .)
|
||||||
|
if [ "." = `dirname $0` ]
|
||||||
|
then
|
||||||
|
zmydir=`pwd`
|
||||||
|
zfin="test_values.generated.go"
|
||||||
|
zsfx="_generated_test.go"
|
||||||
|
# rm -f *_generated_test.go
|
||||||
|
rm -f codecgen-*.go && \
|
||||||
|
_init "$@" && \
|
||||||
|
_build && \
|
||||||
|
cp $zmydir/values_test.go $zmydir/$zfin && \
|
||||||
|
_codegenerators && \
|
||||||
|
echo prebuild done successfully
|
||||||
|
rm -f $zmydir/$zfin
|
||||||
|
else
|
||||||
|
echo "Script must be run from the directory it resides in"
|
||||||
|
fi
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net/rpc"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rpcEncodeTerminator allows a handler specify a []byte terminator to send after each Encode.
|
||||||
|
//
|
||||||
|
// Some codecs like json need to put a space after each encoded value, to serve as a
|
||||||
|
// delimiter for things like numbers (else json codec will continue reading till EOF).
|
||||||
|
type rpcEncodeTerminator interface {
|
||||||
|
rpcEncodeTerminate() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rpc provides a rpc Server or Client Codec for rpc communication.
|
||||||
|
type Rpc interface {
|
||||||
|
ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec
|
||||||
|
ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
// RpcCodecBuffered allows access to the underlying bufio.Reader/Writer
|
||||||
|
// used by the rpc connection. It accommodates use-cases where the connection
|
||||||
|
// should be used by rpc and non-rpc functions, e.g. streaming a file after
|
||||||
|
// sending an rpc response.
|
||||||
|
type RpcCodecBuffered interface {
|
||||||
|
BufferedReader() *bufio.Reader
|
||||||
|
BufferedWriter() *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
|
||||||
|
// rpcCodec defines the struct members and common methods.
|
||||||
|
type rpcCodec struct {
|
||||||
|
rwc io.ReadWriteCloser
|
||||||
|
dec *Decoder
|
||||||
|
enc *Encoder
|
||||||
|
bw *bufio.Writer
|
||||||
|
br *bufio.Reader
|
||||||
|
mu sync.Mutex
|
||||||
|
h Handle
|
||||||
|
|
||||||
|
cls bool
|
||||||
|
clsmu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
|
||||||
|
bw := bufio.NewWriter(conn)
|
||||||
|
br := bufio.NewReader(conn)
|
||||||
|
return rpcCodec{
|
||||||
|
rwc: conn,
|
||||||
|
bw: bw,
|
||||||
|
br: br,
|
||||||
|
enc: NewEncoder(bw, h),
|
||||||
|
dec: NewDecoder(br, h),
|
||||||
|
h: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) BufferedReader() *bufio.Reader {
|
||||||
|
return c.br
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) BufferedWriter() *bufio.Writer {
|
||||||
|
return c.bw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err error) {
|
||||||
|
if c.isClosed() {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
if err = c.enc.Encode(obj1); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t, tOk := c.h.(rpcEncodeTerminator)
|
||||||
|
if tOk {
|
||||||
|
c.bw.Write(t.rpcEncodeTerminate())
|
||||||
|
}
|
||||||
|
if writeObj2 {
|
||||||
|
if err = c.enc.Encode(obj2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tOk {
|
||||||
|
c.bw.Write(t.rpcEncodeTerminate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if doFlush {
|
||||||
|
return c.bw.Flush()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) read(obj interface{}) (err error) {
|
||||||
|
if c.isClosed() {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
//If nil is passed in, we should still attempt to read content to nowhere.
|
||||||
|
if obj == nil {
|
||||||
|
var obj2 interface{}
|
||||||
|
return c.dec.Decode(&obj2)
|
||||||
|
}
|
||||||
|
return c.dec.Decode(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) isClosed() bool {
|
||||||
|
c.clsmu.RLock()
|
||||||
|
x := c.cls
|
||||||
|
c.clsmu.RUnlock()
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) Close() error {
|
||||||
|
if c.isClosed() {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
c.clsmu.Lock()
|
||||||
|
c.cls = true
|
||||||
|
c.clsmu.Unlock()
|
||||||
|
return c.rwc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rpcCodec) ReadResponseBody(body interface{}) error {
|
||||||
|
return c.read(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
|
||||||
|
type goRpcCodec struct {
|
||||||
|
rpcCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
|
||||||
|
// Must protect for concurrent access as per API
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.write(r, body, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.write(r, body, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goRpcCodec) ReadResponseHeader(r *rpc.Response) error {
|
||||||
|
return c.read(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goRpcCodec) ReadRequestHeader(r *rpc.Request) error {
|
||||||
|
return c.read(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goRpcCodec) ReadRequestBody(body interface{}) error {
|
||||||
|
return c.read(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
|
||||||
|
// goRpc is the implementation of Rpc that uses the communication protocol
|
||||||
|
// as defined in net/rpc package.
|
||||||
|
type goRpc struct{}
|
||||||
|
|
||||||
|
// GoRpc implements Rpc using the communication protocol defined in net/rpc package.
|
||||||
|
// Its methods (ServerCodec and ClientCodec) return values that implement RpcCodecBuffered.
|
||||||
|
var GoRpc goRpc
|
||||||
|
|
||||||
|
func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
|
||||||
|
return &goRpcCodec{newRPCCodec(conn, h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x goRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
|
||||||
|
return &goRpcCodec{newRPCCodec(conn, h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RpcCodecBuffered = (*rpcCodec)(nil) // ensure *rpcCodec implements RpcCodecBuffered
|
|
@ -0,0 +1,526 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ uint8 = iota
|
||||||
|
simpleVdNil = 1
|
||||||
|
simpleVdFalse = 2
|
||||||
|
simpleVdTrue = 3
|
||||||
|
simpleVdFloat32 = 4
|
||||||
|
simpleVdFloat64 = 5
|
||||||
|
|
||||||
|
// each lasts for 4 (ie n, n+1, n+2, n+3)
|
||||||
|
simpleVdPosInt = 8
|
||||||
|
simpleVdNegInt = 12
|
||||||
|
|
||||||
|
// containers: each lasts for 4 (ie n, n+1, n+2, ... n+7)
|
||||||
|
simpleVdString = 216
|
||||||
|
simpleVdByteArray = 224
|
||||||
|
simpleVdArray = 232
|
||||||
|
simpleVdMap = 240
|
||||||
|
simpleVdExt = 248
|
||||||
|
)
|
||||||
|
|
||||||
|
type simpleEncDriver struct {
|
||||||
|
noBuiltInTypes
|
||||||
|
encNoSeparator
|
||||||
|
e *Encoder
|
||||||
|
h *SimpleHandle
|
||||||
|
w encWriter
|
||||||
|
b [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeNil() {
|
||||||
|
e.w.writen1(simpleVdNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeBool(b bool) {
|
||||||
|
if b {
|
||||||
|
e.w.writen1(simpleVdTrue)
|
||||||
|
} else {
|
||||||
|
e.w.writen1(simpleVdFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeFloat32(f float32) {
|
||||||
|
e.w.writen1(simpleVdFloat32)
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(math.Float32bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeFloat64(f float64) {
|
||||||
|
e.w.writen1(simpleVdFloat64)
|
||||||
|
bigenHelper{e.b[:8], e.w}.writeUint64(math.Float64bits(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeInt(v int64) {
|
||||||
|
if v < 0 {
|
||||||
|
e.encUint(uint64(-v), simpleVdNegInt)
|
||||||
|
} else {
|
||||||
|
e.encUint(uint64(v), simpleVdPosInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeUint(v uint64) {
|
||||||
|
e.encUint(v, simpleVdPosInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) encUint(v uint64, bd uint8) {
|
||||||
|
if v <= math.MaxUint8 {
|
||||||
|
e.w.writen2(bd, uint8(v))
|
||||||
|
} else if v <= math.MaxUint16 {
|
||||||
|
e.w.writen1(bd + 1)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
|
||||||
|
} else if v <= math.MaxUint32 {
|
||||||
|
e.w.writen1(bd + 2)
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(uint32(v))
|
||||||
|
} else { // if v <= math.MaxUint64 {
|
||||||
|
e.w.writen1(bd + 3)
|
||||||
|
bigenHelper{e.b[:8], e.w}.writeUint64(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) encLen(bd byte, length int) {
|
||||||
|
if length == 0 {
|
||||||
|
e.w.writen1(bd)
|
||||||
|
} else if length <= math.MaxUint8 {
|
||||||
|
e.w.writen1(bd + 1)
|
||||||
|
e.w.writen1(uint8(length))
|
||||||
|
} else if length <= math.MaxUint16 {
|
||||||
|
e.w.writen1(bd + 2)
|
||||||
|
bigenHelper{e.b[:2], e.w}.writeUint16(uint16(length))
|
||||||
|
} else if int64(length) <= math.MaxUint32 {
|
||||||
|
e.w.writen1(bd + 3)
|
||||||
|
bigenHelper{e.b[:4], e.w}.writeUint32(uint32(length))
|
||||||
|
} else {
|
||||||
|
e.w.writen1(bd + 4)
|
||||||
|
bigenHelper{e.b[:8], e.w}.writeUint64(uint64(length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, _ *Encoder) {
|
||||||
|
bs := ext.WriteExt(rv)
|
||||||
|
if bs == nil {
|
||||||
|
e.EncodeNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.encodeExtPreamble(uint8(xtag), len(bs))
|
||||||
|
e.w.writeb(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
|
||||||
|
e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
|
||||||
|
e.w.writeb(re.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) encodeExtPreamble(xtag byte, length int) {
|
||||||
|
e.encLen(simpleVdExt, length)
|
||||||
|
e.w.writen1(xtag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeArrayStart(length int) {
|
||||||
|
e.encLen(simpleVdArray, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeMapStart(length int) {
|
||||||
|
e.encLen(simpleVdMap, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeString(c charEncoding, v string) {
|
||||||
|
e.encLen(simpleVdString, len(v))
|
||||||
|
e.w.writestr(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeSymbol(v string) {
|
||||||
|
e.EncodeString(c_UTF8, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
|
||||||
|
e.encLen(simpleVdByteArray, len(v))
|
||||||
|
e.w.writeb(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
type simpleDecDriver struct {
|
||||||
|
d *Decoder
|
||||||
|
h *SimpleHandle
|
||||||
|
r decReader
|
||||||
|
bdRead bool
|
||||||
|
bd byte
|
||||||
|
br bool // bytes reader
|
||||||
|
noBuiltInTypes
|
||||||
|
noStreamingCodec
|
||||||
|
decNoSeparator
|
||||||
|
b [scratchByteArrayLen]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) readNextBd() {
|
||||||
|
d.bd = d.r.readn1()
|
||||||
|
d.bdRead = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) uncacheRead() {
|
||||||
|
if d.bdRead {
|
||||||
|
d.r.unreadn1()
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) ContainerType() (vt valueType) {
|
||||||
|
if d.bd == simpleVdNil {
|
||||||
|
return valueTypeNil
|
||||||
|
} else if d.bd == simpleVdByteArray || d.bd == simpleVdByteArray+1 ||
|
||||||
|
d.bd == simpleVdByteArray+2 || d.bd == simpleVdByteArray+3 || d.bd == simpleVdByteArray+4 {
|
||||||
|
return valueTypeBytes
|
||||||
|
} else if d.bd == simpleVdString || d.bd == simpleVdString+1 ||
|
||||||
|
d.bd == simpleVdString+2 || d.bd == simpleVdString+3 || d.bd == simpleVdString+4 {
|
||||||
|
return valueTypeString
|
||||||
|
} else if d.bd == simpleVdArray || d.bd == simpleVdArray+1 ||
|
||||||
|
d.bd == simpleVdArray+2 || d.bd == simpleVdArray+3 || d.bd == simpleVdArray+4 {
|
||||||
|
return valueTypeArray
|
||||||
|
} else if d.bd == simpleVdMap || d.bd == simpleVdMap+1 ||
|
||||||
|
d.bd == simpleVdMap+2 || d.bd == simpleVdMap+3 || d.bd == simpleVdMap+4 {
|
||||||
|
return valueTypeMap
|
||||||
|
} else {
|
||||||
|
// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
|
||||||
|
}
|
||||||
|
return valueTypeUnset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) TryDecodeAsNil() bool {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == simpleVdNil {
|
||||||
|
d.bdRead = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) decCheckInteger() (ui uint64, neg bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
switch d.bd {
|
||||||
|
case simpleVdPosInt:
|
||||||
|
ui = uint64(d.r.readn1())
|
||||||
|
case simpleVdPosInt + 1:
|
||||||
|
ui = uint64(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case simpleVdPosInt + 2:
|
||||||
|
ui = uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
case simpleVdPosInt + 3:
|
||||||
|
ui = uint64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
case simpleVdNegInt:
|
||||||
|
ui = uint64(d.r.readn1())
|
||||||
|
neg = true
|
||||||
|
case simpleVdNegInt + 1:
|
||||||
|
ui = uint64(bigen.Uint16(d.r.readx(2)))
|
||||||
|
neg = true
|
||||||
|
case simpleVdNegInt + 2:
|
||||||
|
ui = uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
neg = true
|
||||||
|
case simpleVdNegInt + 3:
|
||||||
|
ui = uint64(bigen.Uint64(d.r.readx(8)))
|
||||||
|
neg = true
|
||||||
|
default:
|
||||||
|
d.d.errorf("decIntAny: Integer only valid from pos/neg integer1..8. Invalid descriptor: %v", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// don't do this check, because callers may only want the unsigned value.
|
||||||
|
// if ui > math.MaxInt64 {
|
||||||
|
// d.d.errorf("decIntAny: Integer out of range for signed int64: %v", ui)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeInt(bitsize uint8) (i int64) {
|
||||||
|
ui, neg := d.decCheckInteger()
|
||||||
|
i, overflow := chkOvf.SignedInt(ui)
|
||||||
|
if overflow {
|
||||||
|
d.d.errorf("simple: overflow converting %v to signed integer", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if neg {
|
||||||
|
i = -i
|
||||||
|
}
|
||||||
|
if chkOvf.Int(i, bitsize) {
|
||||||
|
d.d.errorf("simple: overflow integer: %v", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
|
||||||
|
ui, neg := d.decCheckInteger()
|
||||||
|
if neg {
|
||||||
|
d.d.errorf("Assigning negative signed value to unsigned type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if chkOvf.Uint(ui, bitsize) {
|
||||||
|
d.d.errorf("simple: overflow integer: %v", ui)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == simpleVdFloat32 {
|
||||||
|
f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
|
||||||
|
} else if d.bd == simpleVdFloat64 {
|
||||||
|
f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
|
||||||
|
} else {
|
||||||
|
if d.bd >= simpleVdPosInt && d.bd <= simpleVdNegInt+3 {
|
||||||
|
f = float64(d.DecodeInt(64))
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Float only valid from float32/64: Invalid descriptor: %v", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if chkOverflow32 && chkOvf.Float32(f) {
|
||||||
|
d.d.errorf("msgpack: float32 overflow: %v", f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool can be decoded from bool only (single byte).
|
||||||
|
func (d *simpleDecDriver) DecodeBool() (b bool) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == simpleVdTrue {
|
||||||
|
b = true
|
||||||
|
} else if d.bd == simpleVdFalse {
|
||||||
|
} else {
|
||||||
|
d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) ReadMapStart() (length int) {
|
||||||
|
d.bdRead = false
|
||||||
|
return d.decLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) ReadArrayStart() (length int) {
|
||||||
|
d.bdRead = false
|
||||||
|
return d.decLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) decLen() int {
|
||||||
|
switch d.bd % 8 {
|
||||||
|
case 0:
|
||||||
|
return 0
|
||||||
|
case 1:
|
||||||
|
return int(d.r.readn1())
|
||||||
|
case 2:
|
||||||
|
return int(bigen.Uint16(d.r.readx(2)))
|
||||||
|
case 3:
|
||||||
|
ui := uint64(bigen.Uint32(d.r.readx(4)))
|
||||||
|
if chkOvf.Uint(ui, intBitsize) {
|
||||||
|
d.d.errorf("simple: overflow integer: %v", ui)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(ui)
|
||||||
|
case 4:
|
||||||
|
ui := bigen.Uint64(d.r.readx(8))
|
||||||
|
if chkOvf.Uint(ui, intBitsize) {
|
||||||
|
d.d.errorf("simple: overflow integer: %v", ui)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(ui)
|
||||||
|
}
|
||||||
|
d.d.errorf("decLen: Cannot read length: bd%%8 must be in range 0..4. Got: %d", d.bd%8)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeString() (s string) {
|
||||||
|
return string(d.DecodeBytes(d.b[:], true, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
if d.bd == simpleVdNil {
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clen := d.decLen()
|
||||||
|
d.bdRead = false
|
||||||
|
if zerocopy {
|
||||||
|
if d.br {
|
||||||
|
return d.r.readx(clen)
|
||||||
|
} else if len(bs) == 0 {
|
||||||
|
bs = d.b[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
|
||||||
|
if xtag > 0xff {
|
||||||
|
d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
|
||||||
|
realxtag = uint64(realxtag1)
|
||||||
|
if ext == nil {
|
||||||
|
re := rv.(*RawExt)
|
||||||
|
re.Tag = realxtag
|
||||||
|
re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
|
||||||
|
} else {
|
||||||
|
ext.ReadExt(rv, xbs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
switch d.bd {
|
||||||
|
case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
|
||||||
|
l := d.decLen()
|
||||||
|
xtag = d.r.readn1()
|
||||||
|
if verifyTag && xtag != tag {
|
||||||
|
d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xbs = d.r.readx(l)
|
||||||
|
case simpleVdByteArray, simpleVdByteArray + 1, simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
|
||||||
|
xbs = d.DecodeBytes(nil, false, true)
|
||||||
|
default:
|
||||||
|
d.d.errorf("Invalid d.bd for extensions (Expecting extensions or byte array). Got: 0x%x", d.bd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.bdRead = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) DecodeNaked() {
|
||||||
|
if !d.bdRead {
|
||||||
|
d.readNextBd()
|
||||||
|
}
|
||||||
|
|
||||||
|
n := &d.d.n
|
||||||
|
var decodeFurther bool
|
||||||
|
|
||||||
|
switch d.bd {
|
||||||
|
case simpleVdNil:
|
||||||
|
n.v = valueTypeNil
|
||||||
|
case simpleVdFalse:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = false
|
||||||
|
case simpleVdTrue:
|
||||||
|
n.v = valueTypeBool
|
||||||
|
n.b = true
|
||||||
|
case simpleVdPosInt, simpleVdPosInt + 1, simpleVdPosInt + 2, simpleVdPosInt + 3:
|
||||||
|
if d.h.SignedInteger {
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = d.DecodeInt(64)
|
||||||
|
} else {
|
||||||
|
n.v = valueTypeUint
|
||||||
|
n.u = d.DecodeUint(64)
|
||||||
|
}
|
||||||
|
case simpleVdNegInt, simpleVdNegInt + 1, simpleVdNegInt + 2, simpleVdNegInt + 3:
|
||||||
|
n.v = valueTypeInt
|
||||||
|
n.i = d.DecodeInt(64)
|
||||||
|
case simpleVdFloat32:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = d.DecodeFloat(true)
|
||||||
|
case simpleVdFloat64:
|
||||||
|
n.v = valueTypeFloat
|
||||||
|
n.f = d.DecodeFloat(false)
|
||||||
|
case simpleVdString, simpleVdString + 1, simpleVdString + 2, simpleVdString + 3, simpleVdString + 4:
|
||||||
|
n.v = valueTypeString
|
||||||
|
n.s = d.DecodeString()
|
||||||
|
case simpleVdByteArray, simpleVdByteArray + 1, simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
|
||||||
|
n.v = valueTypeBytes
|
||||||
|
n.l = d.DecodeBytes(nil, false, false)
|
||||||
|
case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
|
||||||
|
n.v = valueTypeExt
|
||||||
|
l := d.decLen()
|
||||||
|
n.u = uint64(d.r.readn1())
|
||||||
|
n.l = d.r.readx(l)
|
||||||
|
case simpleVdArray, simpleVdArray + 1, simpleVdArray + 2, simpleVdArray + 3, simpleVdArray + 4:
|
||||||
|
n.v = valueTypeArray
|
||||||
|
decodeFurther = true
|
||||||
|
case simpleVdMap, simpleVdMap + 1, simpleVdMap + 2, simpleVdMap + 3, simpleVdMap + 4:
|
||||||
|
n.v = valueTypeMap
|
||||||
|
decodeFurther = true
|
||||||
|
default:
|
||||||
|
d.d.errorf("decodeNaked: Unrecognized d.bd: 0x%x", d.bd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !decodeFurther {
|
||||||
|
d.bdRead = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
// SimpleHandle is a Handle for a very simple encoding format.
|
||||||
|
//
|
||||||
|
// simple is a simplistic codec similar to binc, but not as compact.
|
||||||
|
// - Encoding of a value is always preceded by the descriptor byte (bd)
|
||||||
|
// - True, false, nil are encoded fully in 1 byte (the descriptor)
|
||||||
|
// - Integers (intXXX, uintXXX) are encoded in 1, 2, 4 or 8 bytes (plus a descriptor byte).
|
||||||
|
// There are positive (uintXXX and intXXX >= 0) and negative (intXXX < 0) integers.
|
||||||
|
// - Floats are encoded in 4 or 8 bytes (plus a descriptor byte)
|
||||||
|
// - Lenght of containers (strings, bytes, array, map, extensions)
|
||||||
|
// are encoded in 0, 1, 2, 4 or 8 bytes.
|
||||||
|
// Zero-length containers have no length encoded.
|
||||||
|
// For others, the number of bytes is given by pow(2, bd%3)
|
||||||
|
// - maps are encoded as [bd] [length] [[key][value]]...
|
||||||
|
// - arrays are encoded as [bd] [length] [value]...
|
||||||
|
// - extensions are encoded as [bd] [length] [tag] [byte]...
|
||||||
|
// - strings/bytearrays are encoded as [bd] [length] [byte]...
|
||||||
|
//
|
||||||
|
// The full spec will be published soon.
|
||||||
|
type SimpleHandle struct {
|
||||||
|
BasicHandle
|
||||||
|
binaryEncodingType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SimpleHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
|
||||||
|
return h.SetExt(rt, tag, &setExtWrapper{b: ext})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SimpleHandle) newEncDriver(e *Encoder) encDriver {
|
||||||
|
return &simpleEncDriver{e: e, w: e.w, h: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SimpleHandle) newDecDriver(d *Decoder) decDriver {
|
||||||
|
return &simpleDecDriver{d: d, h: h, r: d.r, br: d.bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *simpleEncDriver) reset() {
|
||||||
|
e.w = e.e.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *simpleDecDriver) reset() {
|
||||||
|
d.r, d.br = d.d.r, d.d.bytes
|
||||||
|
d.bd, d.bdRead = 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ decDriver = (*simpleDecDriver)(nil)
|
||||||
|
var _ encDriver = (*simpleEncDriver)(nil)
|
|
@ -0,0 +1,639 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cbor": "AA==",
|
||||||
|
"hex": "00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "AQ==",
|
||||||
|
"hex": "01",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "Cg==",
|
||||||
|
"hex": "0a",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "Fw==",
|
||||||
|
"hex": "17",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 23
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GBg=",
|
||||||
|
"hex": "1818",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GBk=",
|
||||||
|
"hex": "1819",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GGQ=",
|
||||||
|
"hex": "1864",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GQPo",
|
||||||
|
"hex": "1903e8",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GgAPQkA=",
|
||||||
|
"hex": "1a000f4240",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "GwAAAOjUpRAA",
|
||||||
|
"hex": "1b000000e8d4a51000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1000000000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "G///////////",
|
||||||
|
"hex": "1bffffffffffffffff",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 18446744073709551615
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "wkkBAAAAAAAAAAA=",
|
||||||
|
"hex": "c249010000000000000000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 18446744073709551616
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "O///////////",
|
||||||
|
"hex": "3bffffffffffffffff",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -18446744073709551616,
|
||||||
|
"skip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "w0kBAAAAAAAAAAA=",
|
||||||
|
"hex": "c349010000000000000000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -18446744073709551617
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "IA==",
|
||||||
|
"hex": "20",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "KQ==",
|
||||||
|
"hex": "29",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "OGM=",
|
||||||
|
"hex": "3863",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "OQPn",
|
||||||
|
"hex": "3903e7",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+QAA",
|
||||||
|
"hex": "f90000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+YAA",
|
||||||
|
"hex": "f98000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+TwA",
|
||||||
|
"hex": "f93c00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+z/xmZmZmZma",
|
||||||
|
"hex": "fb3ff199999999999a",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+T4A",
|
||||||
|
"hex": "f93e00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+Xv/",
|
||||||
|
"hex": "f97bff",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 65504.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+kfDUAA=",
|
||||||
|
"hex": "fa47c35000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 100000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+n9///8=",
|
||||||
|
"hex": "fa7f7fffff",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 3.4028234663852886e+38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+3435DyIAHWc",
|
||||||
|
"hex": "fb7e37e43c8800759c",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 1.0e+300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+QAB",
|
||||||
|
"hex": "f90001",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 5.960464477539063e-08
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+QQA",
|
||||||
|
"hex": "f90400",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": 6.103515625e-05
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+cQA",
|
||||||
|
"hex": "f9c400",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -4.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+8AQZmZmZmZm",
|
||||||
|
"hex": "fbc010666666666666",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": -4.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+XwA",
|
||||||
|
"hex": "f97c00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+X4A",
|
||||||
|
"hex": "f97e00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "NaN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+fwA",
|
||||||
|
"hex": "f9fc00",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "-Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+n+AAAA=",
|
||||||
|
"hex": "fa7f800000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+n/AAAA=",
|
||||||
|
"hex": "fa7fc00000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "NaN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+v+AAAA=",
|
||||||
|
"hex": "faff800000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "-Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+3/wAAAAAAAA",
|
||||||
|
"hex": "fb7ff0000000000000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+3/4AAAAAAAA",
|
||||||
|
"hex": "fb7ff8000000000000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "NaN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+//wAAAAAAAA",
|
||||||
|
"hex": "fbfff0000000000000",
|
||||||
|
"roundtrip": false,
|
||||||
|
"diagnostic": "-Infinity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "9A==",
|
||||||
|
"hex": "f4",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "9Q==",
|
||||||
|
"hex": "f5",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "9g==",
|
||||||
|
"hex": "f6",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "9w==",
|
||||||
|
"hex": "f7",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "undefined"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "8A==",
|
||||||
|
"hex": "f0",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "simple(16)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+Bg=",
|
||||||
|
"hex": "f818",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "simple(24)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "+P8=",
|
||||||
|
"hex": "f8ff",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "simple(255)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==",
|
||||||
|
"hex": "c074323031332d30332d32315432303a30343a30305a",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "0(\"2013-03-21T20:04:00Z\")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "wRpRS2ew",
|
||||||
|
"hex": "c11a514b67b0",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "1(1363896240)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "wftB1FLZ7CAAAA==",
|
||||||
|
"hex": "c1fb41d452d9ec200000",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "1(1363896240.5)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "10QBAgME",
|
||||||
|
"hex": "d74401020304",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "23(h'01020304')"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "2BhFZElFVEY=",
|
||||||
|
"hex": "d818456449455446",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "24(h'6449455446')"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==",
|
||||||
|
"hex": "d82076687474703a2f2f7777772e6578616d706c652e636f6d",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "32(\"http://www.example.com\")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "QA==",
|
||||||
|
"hex": "40",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "h''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "RAECAwQ=",
|
||||||
|
"hex": "4401020304",
|
||||||
|
"roundtrip": true,
|
||||||
|
"diagnostic": "h'01020304'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "YA==",
|
||||||
|
"hex": "60",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "YWE=",
|
||||||
|
"hex": "6161",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "ZElFVEY=",
|
||||||
|
"hex": "6449455446",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "IETF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "YiJc",
|
||||||
|
"hex": "62225c",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "\"\\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "YsO8",
|
||||||
|
"hex": "62c3bc",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "ü"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "Y+awtA==",
|
||||||
|
"hex": "63e6b0b4",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "水"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "ZPCQhZE=",
|
||||||
|
"hex": "64f0908591",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": "𐅑"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gA==",
|
||||||
|
"hex": "80",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gwECAw==",
|
||||||
|
"hex": "83010203",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gwGCAgOCBAU=",
|
||||||
|
"hex": "8301820203820405",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=",
|
||||||
|
"hex": "98190102030405060708090a0b0c0d0e0f101112131415161718181819",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "oA==",
|
||||||
|
"hex": "a0",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "ogECAwQ=",
|
||||||
|
"hex": "a201020304",
|
||||||
|
"roundtrip": true,
|
||||||
|
"skip": true,
|
||||||
|
"diagnostic": "{1: 2, 3: 4}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "omFhAWFiggID",
|
||||||
|
"hex": "a26161016162820203",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": {
|
||||||
|
"a": 1,
|
||||||
|
"b": [
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gmFhoWFiYWM=",
|
||||||
|
"hex": "826161a161626163",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": [
|
||||||
|
"a",
|
||||||
|
{
|
||||||
|
"b": "c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "pWFhYUFhYmFCYWNhQ2FkYURhZWFF",
|
||||||
|
"hex": "a56161614161626142616361436164614461656145",
|
||||||
|
"roundtrip": true,
|
||||||
|
"decoded": {
|
||||||
|
"a": "A",
|
||||||
|
"b": "B",
|
||||||
|
"c": "C",
|
||||||
|
"d": "D",
|
||||||
|
"e": "E"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "X0IBAkMDBAX/",
|
||||||
|
"hex": "5f42010243030405ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"skip": true,
|
||||||
|
"diagnostic": "(_ h'0102', h'030405')"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "f2VzdHJlYWRtaW5n/w==",
|
||||||
|
"hex": "7f657374726561646d696e67ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": "streaming"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "n/8=",
|
||||||
|
"hex": "9fff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "nwGCAgOfBAX//w==",
|
||||||
|
"hex": "9f018202039f0405ffff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "nwGCAgOCBAX/",
|
||||||
|
"hex": "9f01820203820405ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gwGCAgOfBAX/",
|
||||||
|
"hex": "83018202039f0405ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gwGfAgP/ggQF",
|
||||||
|
"hex": "83019f0203ff820405",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=",
|
||||||
|
"hex": "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "v2FhAWFinwID//8=",
|
||||||
|
"hex": "bf61610161629f0203ffff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": {
|
||||||
|
"a": 1,
|
||||||
|
"b": [
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "gmFhv2FiYWP/",
|
||||||
|
"hex": "826161bf61626163ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": [
|
||||||
|
"a",
|
||||||
|
{
|
||||||
|
"b": "c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cbor": "v2NGdW71Y0FtdCH/",
|
||||||
|
"hex": "bf6346756ef563416d7421ff",
|
||||||
|
"roundtrip": false,
|
||||||
|
"decoded": {
|
||||||
|
"Fun": true,
|
||||||
|
"Amt": -2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,126 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# This will create golden files in a directory passed to it.
|
||||||
|
# A Test calls this internally to create the golden files
|
||||||
|
# So it can process them (so we don't have to checkin the files).
|
||||||
|
|
||||||
|
# Ensure msgpack-python and cbor are installed first, using:
|
||||||
|
# sudo apt-get install python-dev
|
||||||
|
# sudo apt-get install python-pip
|
||||||
|
# pip install --user msgpack-python msgpack-rpc-python cbor
|
||||||
|
|
||||||
|
# Ensure all "string" keys are utf strings (else encoded as bytes)
|
||||||
|
|
||||||
|
import cbor, msgpack, msgpackrpc, sys, os, threading
|
||||||
|
|
||||||
|
def get_test_data_list():
|
||||||
|
# get list with all primitive types, and a combo type
|
||||||
|
l0 = [
|
||||||
|
-8,
|
||||||
|
-1616,
|
||||||
|
-32323232,
|
||||||
|
-6464646464646464,
|
||||||
|
192,
|
||||||
|
1616,
|
||||||
|
32323232,
|
||||||
|
6464646464646464,
|
||||||
|
192,
|
||||||
|
-3232.0,
|
||||||
|
-6464646464.0,
|
||||||
|
3232.0,
|
||||||
|
6464.0,
|
||||||
|
6464646464.0,
|
||||||
|
False,
|
||||||
|
True,
|
||||||
|
u"null",
|
||||||
|
None,
|
||||||
|
u"some&day>some<day",
|
||||||
|
1328176922000002000,
|
||||||
|
u"",
|
||||||
|
-2206187877999998000,
|
||||||
|
u"bytestring",
|
||||||
|
270,
|
||||||
|
u"none",
|
||||||
|
-2013855847999995777,
|
||||||
|
#-6795364578871345152,
|
||||||
|
]
|
||||||
|
l1 = [
|
||||||
|
{ "true": True,
|
||||||
|
"false": False },
|
||||||
|
{ "true": u"True",
|
||||||
|
"false": False,
|
||||||
|
"uint16(1616)": 1616 },
|
||||||
|
{ "list": [1616, 32323232, True, -3232.0, {"TRUE":True, "FALSE":False}, [True, False] ],
|
||||||
|
"int32":32323232, "bool": True,
|
||||||
|
"LONG STRING": u"123456789012345678901234567890123456789012345678901234567890",
|
||||||
|
"SHORT STRING": u"1234567890" },
|
||||||
|
{ True: "true", 138: False, "false": 200 }
|
||||||
|
]
|
||||||
|
|
||||||
|
l = []
|
||||||
|
l.extend(l0)
|
||||||
|
l.append(l0)
|
||||||
|
l.append(1)
|
||||||
|
l.extend(l1)
|
||||||
|
return l
|
||||||
|
|
||||||
|
def build_test_data(destdir):
|
||||||
|
l = get_test_data_list()
|
||||||
|
for i in range(len(l)):
|
||||||
|
# packer = msgpack.Packer()
|
||||||
|
serialized = msgpack.dumps(l[i])
|
||||||
|
f = open(os.path.join(destdir, str(i) + '.msgpack.golden'), 'wb')
|
||||||
|
f.write(serialized)
|
||||||
|
f.close()
|
||||||
|
serialized = cbor.dumps(l[i])
|
||||||
|
f = open(os.path.join(destdir, str(i) + '.cbor.golden'), 'wb')
|
||||||
|
f.write(serialized)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def doRpcServer(port, stopTimeSec):
|
||||||
|
class EchoHandler(object):
|
||||||
|
def Echo123(self, msg1, msg2, msg3):
|
||||||
|
return ("1:%s 2:%s 3:%s" % (msg1, msg2, msg3))
|
||||||
|
def EchoStruct(self, msg):
|
||||||
|
return ("%s" % msg)
|
||||||
|
|
||||||
|
addr = msgpackrpc.Address('localhost', port)
|
||||||
|
server = msgpackrpc.Server(EchoHandler())
|
||||||
|
server.listen(addr)
|
||||||
|
# run thread to stop it after stopTimeSec seconds if > 0
|
||||||
|
if stopTimeSec > 0:
|
||||||
|
def myStopRpcServer():
|
||||||
|
server.stop()
|
||||||
|
t = threading.Timer(stopTimeSec, myStopRpcServer)
|
||||||
|
t.start()
|
||||||
|
server.start()
|
||||||
|
|
||||||
|
def doRpcClientToPythonSvc(port):
|
||||||
|
address = msgpackrpc.Address('localhost', port)
|
||||||
|
client = msgpackrpc.Client(address, unpack_encoding='utf-8')
|
||||||
|
print client.call("Echo123", "A1", "B2", "C3")
|
||||||
|
print client.call("EchoStruct", {"A" :"Aa", "B":"Bb", "C":"Cc"})
|
||||||
|
|
||||||
|
def doRpcClientToGoSvc(port):
|
||||||
|
# print ">>>> port: ", port, " <<<<<"
|
||||||
|
address = msgpackrpc.Address('localhost', port)
|
||||||
|
client = msgpackrpc.Client(address, unpack_encoding='utf-8')
|
||||||
|
print client.call("TestRpcInt.Echo123", ["A1", "B2", "C3"])
|
||||||
|
print client.call("TestRpcInt.EchoStruct", {"A" :"Aa", "B":"Bb", "C":"Cc"})
|
||||||
|
|
||||||
|
def doMain(args):
|
||||||
|
if len(args) == 2 and args[0] == "testdata":
|
||||||
|
build_test_data(args[1])
|
||||||
|
elif len(args) == 3 and args[0] == "rpc-server":
|
||||||
|
doRpcServer(int(args[1]), int(args[2]))
|
||||||
|
elif len(args) == 2 and args[0] == "rpc-client-python-service":
|
||||||
|
doRpcClientToPythonSvc(int(args[1]))
|
||||||
|
elif len(args) == 2 and args[0] == "rpc-client-go-service":
|
||||||
|
doRpcClientToGoSvc(int(args[1]))
|
||||||
|
else:
|
||||||
|
print("Usage: test.py " +
|
||||||
|
"[testdata|rpc-server|rpc-client-python-service|rpc-client-go-service] ...")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
doMain(sys.argv[1:])
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Run all the different permutations of all the tests.
|
||||||
|
# This helps ensure that nothing gets broken.
|
||||||
|
|
||||||
|
_run() {
|
||||||
|
# 1. VARIATIONS: regular (t), canonical (c), IO R/W (i),
|
||||||
|
# binc-nosymbols (n), struct2array (s), intern string (e),
|
||||||
|
# json-indent (d), circular (l)
|
||||||
|
# 2. MODE: reflection (r), external (x), codecgen (g), unsafe (u), notfastpath (f)
|
||||||
|
# 3. OPTIONS: verbose (v), reset (z), must (m),
|
||||||
|
#
|
||||||
|
# Use combinations of mode to get exactly what you want,
|
||||||
|
# and then pass the variations you need.
|
||||||
|
|
||||||
|
ztags=""
|
||||||
|
zargs=""
|
||||||
|
local OPTIND
|
||||||
|
OPTIND=1
|
||||||
|
# "_xurtcinsvgzmefdl" === "_cdefgilmnrtsuvxz"
|
||||||
|
while getopts "_cdefgilmnrtsuvwxz" flag
|
||||||
|
do
|
||||||
|
case "x$flag" in
|
||||||
|
'xr') ;;
|
||||||
|
'xf') ztags="$ztags notfastpath" ;;
|
||||||
|
'xg') ztags="$ztags codecgen" ;;
|
||||||
|
'xx') ztags="$ztags x" ;;
|
||||||
|
'xu') ztags="$ztags unsafe" ;;
|
||||||
|
'xv') zargs="$zargs -tv" ;;
|
||||||
|
'xz') zargs="$zargs -tr" ;;
|
||||||
|
'xm') zargs="$zargs -tm" ;;
|
||||||
|
'xl') zargs="$zargs -tl" ;;
|
||||||
|
'xw') zargs="$zargs -tx=10" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# shift $((OPTIND-1))
|
||||||
|
printf '............. TAGS: %s .............\n' "$ztags"
|
||||||
|
# echo ">>>>>>> TAGS: $ztags"
|
||||||
|
|
||||||
|
OPTIND=1
|
||||||
|
while getopts "_cdefgilmnrtsuvwxz" flag
|
||||||
|
do
|
||||||
|
case "x$flag" in
|
||||||
|
'xt') printf ">>>>>>> REGULAR : "; go test "-tags=$ztags" $zargs ; sleep 2 ;;
|
||||||
|
'xc') printf ">>>>>>> CANONICAL : "; go test "-tags=$ztags" $zargs -tc; sleep 2 ;;
|
||||||
|
'xi') printf ">>>>>>> I/O : "; go test "-tags=$ztags" $zargs -ti; sleep 2 ;;
|
||||||
|
'xn') printf ">>>>>>> NO_SYMBOLS : "; go test "-tags=$ztags" -run=Binc $zargs -tn; sleep 2 ;;
|
||||||
|
'xs') printf ">>>>>>> TO_ARRAY : "; go test "-tags=$ztags" $zargs -ts; sleep 2 ;;
|
||||||
|
'xe') printf ">>>>>>> INTERN : "; go test "-tags=$ztags" $zargs -te; sleep 2 ;;
|
||||||
|
'xd') printf ">>>>>>> INDENT : ";
|
||||||
|
go test "-tags=$ztags" -run=JsonCodecsTable -td=-1 $zargs;
|
||||||
|
go test "-tags=$ztags" -run=JsonCodecsTable -td=8 $zargs;
|
||||||
|
sleep 2 ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
OPTIND=1
|
||||||
|
}
|
||||||
|
|
||||||
|
# echo ">>>>>>> RUNNING VARIATIONS OF TESTS"
|
||||||
|
if [[ "x$@" = "x" || "x$@" = "x-A" ]]; then
|
||||||
|
# All: r, x, g, gu
|
||||||
|
_run "-_tcinsed_ml" # regular
|
||||||
|
_run "-_tcinsed_ml_z" # regular with reset
|
||||||
|
_run "-w_tcinsed_ml" # regular with max init len
|
||||||
|
_run "-_tcinsed_ml_f" # regular with no fastpath (notfastpath)
|
||||||
|
_run "-x_tcinsed_ml" # external
|
||||||
|
_run "-gx_tcinsed_ml" # codecgen: requires external
|
||||||
|
_run "-gxu_tcinsed_ml" # codecgen + unsafe
|
||||||
|
elif [[ "x$@" = "x-Z" ]]; then
|
||||||
|
# Regular
|
||||||
|
_run "-_tcinsed_ml" # regular
|
||||||
|
_run "-_tcinsed_ml_z" # regular with reset
|
||||||
|
elif [[ "x$@" = "x-F" ]]; then
|
||||||
|
# regular with notfastpath
|
||||||
|
_run "-_tcinsed_ml_f" # regular
|
||||||
|
_run "-_tcinsed_ml_zf" # regular with reset
|
||||||
|
elif [[ "x$@" = "x-C" ]]; then
|
||||||
|
# codecgen
|
||||||
|
_run "-gx_tcinsed_ml" # codecgen: requires external
|
||||||
|
_run "-gxu_tcinsed_ml" # codecgen + unsafe
|
||||||
|
_run "-gxuw_tcinsed_ml" # codecgen + unsafe + maxinitlen
|
||||||
|
elif [[ "x$@" = "x-X" ]]; then
|
||||||
|
# external
|
||||||
|
_run "-x_tcinsed_ml" # external
|
||||||
|
elif [[ "x$@" = "x-h" || "x$@" = "x-?" ]]; then
|
||||||
|
cat <<EOF
|
||||||
|
Usage: tests.sh [options...]
|
||||||
|
-A run through all tests (regular, external, codecgen)
|
||||||
|
-Z regular tests only
|
||||||
|
-F regular tests only (without fastpath, so they run quickly)
|
||||||
|
-C codecgen only
|
||||||
|
-X external only
|
||||||
|
-h show help (usage)
|
||||||
|
-? same as -h
|
||||||
|
(no options)
|
||||||
|
same as -A
|
||||||
|
(unrecognized options)
|
||||||
|
just pass on the options from the command line
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# e.g. ./tests.sh "-w_tcinsed_ml"
|
||||||
|
_run "$@"
|
||||||
|
fi
|
|
@ -0,0 +1,233 @@
|
||||||
|
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||||
|
timeExtEncFn = func(rv reflect.Value) (bs []byte, err error) {
|
||||||
|
defer panicToErr(&err)
|
||||||
|
bs = timeExt{}.WriteExt(rv.Interface())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeExtDecFn = func(rv reflect.Value, bs []byte) (err error) {
|
||||||
|
defer panicToErr(&err)
|
||||||
|
timeExt{}.ReadExt(rv.Interface(), bs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeExt struct{}
|
||||||
|
|
||||||
|
func (x timeExt) WriteExt(v interface{}) (bs []byte) {
|
||||||
|
switch v2 := v.(type) {
|
||||||
|
case time.Time:
|
||||||
|
bs = encodeTime(v2)
|
||||||
|
case *time.Time:
|
||||||
|
bs = encodeTime(*v2)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unsupported format for time conversion: expecting time.Time; got %T", v2))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (x timeExt) ReadExt(v interface{}, bs []byte) {
|
||||||
|
tt, err := decodeTime(bs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*(v.(*time.Time)) = tt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x timeExt) ConvertExt(v interface{}) interface{} {
|
||||||
|
return x.WriteExt(v)
|
||||||
|
}
|
||||||
|
func (x timeExt) UpdateExt(v interface{}, src interface{}) {
|
||||||
|
x.ReadExt(v, src.([]byte))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeTime encodes a time.Time as a []byte, including
|
||||||
|
// information on the instant in time and UTC offset.
|
||||||
|
//
|
||||||
|
// Format Description
|
||||||
|
//
|
||||||
|
// A timestamp is composed of 3 components:
|
||||||
|
//
|
||||||
|
// - secs: signed integer representing seconds since unix epoch
|
||||||
|
// - nsces: unsigned integer representing fractional seconds as a
|
||||||
|
// nanosecond offset within secs, in the range 0 <= nsecs < 1e9
|
||||||
|
// - tz: signed integer representing timezone offset in minutes east of UTC,
|
||||||
|
// and a dst (daylight savings time) flag
|
||||||
|
//
|
||||||
|
// When encoding a timestamp, the first byte is the descriptor, which
|
||||||
|
// defines which components are encoded and how many bytes are used to
|
||||||
|
// encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
|
||||||
|
// is not encoded in the byte array explicitly*.
|
||||||
|
//
|
||||||
|
// Descriptor 8 bits are of the form `A B C DDD EE`:
|
||||||
|
// A: Is secs component encoded? 1 = true
|
||||||
|
// B: Is nsecs component encoded? 1 = true
|
||||||
|
// C: Is tz component encoded? 1 = true
|
||||||
|
// DDD: Number of extra bytes for secs (range 0-7).
|
||||||
|
// If A = 1, secs encoded in DDD+1 bytes.
|
||||||
|
// If A = 0, secs is not encoded, and is assumed to be 0.
|
||||||
|
// If A = 1, then we need at least 1 byte to encode secs.
|
||||||
|
// DDD says the number of extra bytes beyond that 1.
|
||||||
|
// E.g. if DDD=0, then secs is represented in 1 byte.
|
||||||
|
// if DDD=2, then secs is represented in 3 bytes.
|
||||||
|
// EE: Number of extra bytes for nsecs (range 0-3).
|
||||||
|
// If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)
|
||||||
|
//
|
||||||
|
// Following the descriptor bytes, subsequent bytes are:
|
||||||
|
//
|
||||||
|
// secs component encoded in `DDD + 1` bytes (if A == 1)
|
||||||
|
// nsecs component encoded in `EE + 1` bytes (if B == 1)
|
||||||
|
// tz component encoded in 2 bytes (if C == 1)
|
||||||
|
//
|
||||||
|
// secs and nsecs components are integers encoded in a BigEndian
|
||||||
|
// 2-complement encoding format.
|
||||||
|
//
|
||||||
|
// tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
|
||||||
|
// Least significant bit 0 are described below:
|
||||||
|
//
|
||||||
|
// Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
|
||||||
|
// Bit 15 = have\_dst: set to 1 if we set the dst flag.
|
||||||
|
// Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
|
||||||
|
// Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
|
||||||
|
//
|
||||||
|
func encodeTime(t time.Time) []byte {
|
||||||
|
//t := rv.Interface().(time.Time)
|
||||||
|
tsecs, tnsecs := t.Unix(), t.Nanosecond()
|
||||||
|
var (
|
||||||
|
bd byte
|
||||||
|
btmp [8]byte
|
||||||
|
bs [16]byte
|
||||||
|
i int = 1
|
||||||
|
)
|
||||||
|
l := t.Location()
|
||||||
|
if l == time.UTC {
|
||||||
|
l = nil
|
||||||
|
}
|
||||||
|
if tsecs != 0 {
|
||||||
|
bd = bd | 0x80
|
||||||
|
bigen.PutUint64(btmp[:], uint64(tsecs))
|
||||||
|
f := pruneSignExt(btmp[:], tsecs >= 0)
|
||||||
|
bd = bd | (byte(7-f) << 2)
|
||||||
|
copy(bs[i:], btmp[f:])
|
||||||
|
i = i + (8 - f)
|
||||||
|
}
|
||||||
|
if tnsecs != 0 {
|
||||||
|
bd = bd | 0x40
|
||||||
|
bigen.PutUint32(btmp[:4], uint32(tnsecs))
|
||||||
|
f := pruneSignExt(btmp[:4], true)
|
||||||
|
bd = bd | byte(3-f)
|
||||||
|
copy(bs[i:], btmp[f:4])
|
||||||
|
i = i + (4 - f)
|
||||||
|
}
|
||||||
|
if l != nil {
|
||||||
|
bd = bd | 0x20
|
||||||
|
// Note that Go Libs do not give access to dst flag.
|
||||||
|
_, zoneOffset := t.Zone()
|
||||||
|
//zoneName, zoneOffset := t.Zone()
|
||||||
|
zoneOffset /= 60
|
||||||
|
z := uint16(zoneOffset)
|
||||||
|
bigen.PutUint16(btmp[:2], z)
|
||||||
|
// clear dst flags
|
||||||
|
bs[i] = btmp[0] & 0x3f
|
||||||
|
bs[i+1] = btmp[1]
|
||||||
|
i = i + 2
|
||||||
|
}
|
||||||
|
bs[0] = bd
|
||||||
|
return bs[0:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeTime decodes a []byte into a time.Time.
|
||||||
|
func decodeTime(bs []byte) (tt time.Time, err error) {
|
||||||
|
bd := bs[0]
|
||||||
|
var (
|
||||||
|
tsec int64
|
||||||
|
tnsec uint32
|
||||||
|
tz uint16
|
||||||
|
i byte = 1
|
||||||
|
i2 byte
|
||||||
|
n byte
|
||||||
|
)
|
||||||
|
if bd&(1<<7) != 0 {
|
||||||
|
var btmp [8]byte
|
||||||
|
n = ((bd >> 2) & 0x7) + 1
|
||||||
|
i2 = i + n
|
||||||
|
copy(btmp[8-n:], bs[i:i2])
|
||||||
|
//if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
|
||||||
|
if bs[i]&(1<<7) != 0 {
|
||||||
|
copy(btmp[0:8-n], bsAll0xff)
|
||||||
|
//for j,k := byte(0), 8-n; j < k; j++ { btmp[j] = 0xff }
|
||||||
|
}
|
||||||
|
i = i2
|
||||||
|
tsec = int64(bigen.Uint64(btmp[:]))
|
||||||
|
}
|
||||||
|
if bd&(1<<6) != 0 {
|
||||||
|
var btmp [4]byte
|
||||||
|
n = (bd & 0x3) + 1
|
||||||
|
i2 = i + n
|
||||||
|
copy(btmp[4-n:], bs[i:i2])
|
||||||
|
i = i2
|
||||||
|
tnsec = bigen.Uint32(btmp[:])
|
||||||
|
}
|
||||||
|
if bd&(1<<5) == 0 {
|
||||||
|
tt = time.Unix(tsec, int64(tnsec)).UTC()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
|
||||||
|
// However, we need name here, so it can be shown when time is printed.
|
||||||
|
// Zone name is in form: UTC-08:00.
|
||||||
|
// Note that Go Libs do not give access to dst flag, so we ignore dst bits
|
||||||
|
|
||||||
|
i2 = i + 2
|
||||||
|
tz = bigen.Uint16(bs[i:i2])
|
||||||
|
i = i2
|
||||||
|
// sign extend sign bit into top 2 MSB (which were dst bits):
|
||||||
|
if tz&(1<<13) == 0 { // positive
|
||||||
|
tz = tz & 0x3fff //clear 2 MSBs: dst bits
|
||||||
|
} else { // negative
|
||||||
|
tz = tz | 0xc000 //set 2 MSBs: dst bits
|
||||||
|
//tzname[3] = '-' (TODO: verify. this works here)
|
||||||
|
}
|
||||||
|
tzint := int16(tz)
|
||||||
|
if tzint == 0 {
|
||||||
|
tt = time.Unix(tsec, int64(tnsec)).UTC()
|
||||||
|
} else {
|
||||||
|
// For Go Time, do not use a descriptive timezone.
|
||||||
|
// It's unnecessary, and makes it harder to do a reflect.DeepEqual.
|
||||||
|
// The Offset already tells what the offset should be, if not on UTC and unknown zone name.
|
||||||
|
// var zoneName = timeLocUTCName(tzint)
|
||||||
|
tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone("", int(tzint)*60))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeLocUTCName(tzint int16) string {
|
||||||
|
if tzint == 0 {
|
||||||
|
return "UTC"
|
||||||
|
}
|
||||||
|
var tzname = []byte("UTC+00:00")
|
||||||
|
//tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
|
||||||
|
//tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
|
||||||
|
var tzhr, tzmin int16
|
||||||
|
if tzint < 0 {
|
||||||
|
tzname[3] = '-' // (TODO: verify. this works here)
|
||||||
|
tzhr, tzmin = -tzint/60, (-tzint)%60
|
||||||
|
} else {
|
||||||
|
tzhr, tzmin = tzint/60, tzint%60
|
||||||
|
}
|
||||||
|
tzname[4] = timeDigits[tzhr/10]
|
||||||
|
tzname[5] = timeDigits[tzhr%10]
|
||||||
|
tzname[7] = timeDigits[tzmin/10]
|
||||||
|
tzname[8] = timeDigits[tzmin%10]
|
||||||
|
return string(tzname)
|
||||||
|
//return time.FixedZone(string(tzname), int(tzint)*60)
|
||||||
|
}
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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}
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,22 +123,28 @@
|
||||||
"revisionTime": "2015-10-06T22:16:25Z"
|
"revisionTime": "2015-10-06T22:16:25Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "RsNwOto8G8aXIiRrlFn4dtU9q/g=",
|
"checksumSHA1": "vPTW4XEbp0oKyrZRqBAcx+QMSr8=",
|
||||||
"path": "github.com/gin-gonic/gin",
|
"path": "github.com/gin-gonic/gin",
|
||||||
"revision": "e2212d40c62a98b388a5eb48ecbdcf88534688ba",
|
"revision": "ad2dacedd654cdc1c1526971fe83c118318975ba",
|
||||||
"revisionTime": "2016-12-04T22:13:08Z"
|
"revisionTime": "2017-03-24T12:43:23Z",
|
||||||
|
"version": "=develop",
|
||||||
|
"versionExact": "develop"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "UsILDoIB2S7ra+w2fMdb85mX3HM=",
|
"checksumSHA1": "wEYVkapMAF7S2iVWlWzLHu598dE=",
|
||||||
"path": "github.com/gin-gonic/gin/binding",
|
"path": "github.com/gin-gonic/gin/binding",
|
||||||
"revision": "e2212d40c62a98b388a5eb48ecbdcf88534688ba",
|
"revision": "ad2dacedd654cdc1c1526971fe83c118318975ba",
|
||||||
"revisionTime": "2016-12-04T22:13:08Z"
|
"revisionTime": "2017-03-24T12:43:23Z",
|
||||||
|
"version": "=develop",
|
||||||
|
"versionExact": "develop"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "PHv9FNb7YavJWtAHcY6ZgXmkmHs=",
|
"checksumSHA1": "L0jAVQ9YZsoU1RpJDcKFdMEnLUs=",
|
||||||
"path": "github.com/gin-gonic/gin/render",
|
"path": "github.com/gin-gonic/gin/render",
|
||||||
"revision": "e2212d40c62a98b388a5eb48ecbdcf88534688ba",
|
"revision": "ad2dacedd654cdc1c1526971fe83c118318975ba",
|
||||||
"revisionTime": "2016-12-04T22:13:08Z"
|
"revisionTime": "2017-03-24T12:43:23Z",
|
||||||
|
"version": "=develop",
|
||||||
|
"versionExact": "develop"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "kBeNcaKk56FguvPSUCEaH6AxpRc=",
|
"checksumSHA1": "kBeNcaKk56FguvPSUCEaH6AxpRc=",
|
||||||
|
@ -398,6 +404,12 @@
|
||||||
"revision": "d4a8a3d30d5729f85edfba1745241f3a621d0359",
|
"revision": "d4a8a3d30d5729f85edfba1745241f3a621d0359",
|
||||||
"revisionTime": "2016-09-03T21:37:29Z"
|
"revisionTime": "2016-09-03T21:37:29Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "MxLnUmfrP+r5HfCZM29+WPKebn8=",
|
||||||
|
"path": "github.com/ugorji/go/codec",
|
||||||
|
"revision": "708a42d246822952f38190a8d8c4e6b16a0e600c",
|
||||||
|
"revisionTime": "2017-03-12T11:21:14Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KeaQhXgb4KlZWJM7aIgcJwCKNOg=",
|
"checksumSHA1": "KeaQhXgb4KlZWJM7aIgcJwCKNOg=",
|
||||||
"path": "golang.org/x/crypto/pkcs12",
|
"path": "golang.org/x/crypto/pkcs12",
|
||||||
|
@ -464,6 +476,12 @@
|
||||||
"revision": "90f0b59102629831cc109845475a8d77043412ec",
|
"revision": "90f0b59102629831cc109845475a8d77043412ec",
|
||||||
"revisionTime": "2016-03-25T10:38:24Z"
|
"revisionTime": "2016-03-25T10:38:24Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "pyAPYrymvmZl0M/Mr4yfjOQjA8I=",
|
||||||
|
"path": "gopkg.in/gin-contrib/sse.v0",
|
||||||
|
"revision": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae",
|
||||||
|
"revisionTime": "2017-01-09T09:34:21Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "39V1idWER42Lmcmg2Uy40wMzOlo=",
|
"checksumSHA1": "39V1idWER42Lmcmg2Uy40wMzOlo=",
|
||||||
"path": "gopkg.in/go-playground/validator.v8",
|
"path": "gopkg.in/go-playground/validator.v8",
|
||||||
|
|
Loading…
Reference in New Issue