mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 23:31:56 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 420e80ea50 | |||
| 337591b2bb | |||
| fa6bcdad04 | |||
| ee87c43eb9 | |||
| bccc58b54d | |||
| 3da1c25471 | |||
| 7d0a271000 | |||
| 102631d98d | |||
| 6c3d2fc339 | |||
| 1d79831651 | |||
| 0458ad41dd | |||
| b9453b84bb | |||
| ab81ff8bfb | |||
| f2339a7244 | |||
| 978e01f77e | |||
| 1275930f99 | |||
| 8e9091cc48 | |||
| 76add5ca77 | |||
| e8407848ec | |||
| 29d809535e | |||
| cc1c6d9abc | |||
| 69b28e358c | |||
| 5ed3d4e29a | |||
| 4f468b8a5d | |||
| e3aad7799e | |||
| cc8aa0efb5 | |||
| 475939a77f | |||
| 4ccef23dbc | |||
| 2b0d704777 | |||
| ee80e55833 | |||
| 2345720b2b | |||
| d714a62bd3 | |||
| 7d4afd4ae0 | |||
| 056693c814 | |||
| 73d948bc32 | |||
| 5e6f606f4e | |||
| f758361730 |
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: "Bug report \U0001F41B"
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us improve cloudflared
|
||||
title: ''
|
||||
labels: awaiting reply, bug
|
||||
assignees: ''
|
||||
title: "\U0001F41B"
|
||||
labels: 'Priority: Normal, Type: Bug'
|
||||
assignees: abelinkinbio
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: "\U0001F4DD Documentation"
|
||||
about: Request new or updated documentation for cloudflared
|
||||
title: "\U0001F4DD"
|
||||
labels: 'Priority: Normal, Type: Documentation'
|
||||
assignees: abelinkinbio, ranbel
|
||||
|
||||
---
|
||||
|
||||
**Available Documentation**
|
||||
A link to the documentation that is available today and the areas which could be improved.
|
||||
|
||||
**Suggested Documentation**
|
||||
A clear and concise description of the documentation, tutorial, or guide that should be added.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the documentation request here.
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: "Feature request \U0001F4A1"
|
||||
name: "\U0001F4A1 Feature request"
|
||||
about: Suggest a feature or enhancement for cloudflared
|
||||
title: ''
|
||||
labels: awaiting reply, feature-request
|
||||
assignees: ''
|
||||
title: "\U0001F4A1"
|
||||
labels: 'Priority: Normal, Type: Feature Request'
|
||||
assignees: abelinkinbio, sssilver
|
||||
|
||||
---
|
||||
|
||||
@@ -12,8 +12,6 @@ jobs:
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Install go-sumtype
|
||||
run: go get github.com/sudarshan-reddy/go-sumtype
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Test
|
||||
|
||||
@@ -6,6 +6,8 @@ ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
TARGET_GOOS=${TARGET_GOOS} \
|
||||
TARGET_GOARCH=${TARGET_GOARCH}
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared"
|
||||
|
||||
WORKDIR /go/src/github.com/cloudflare/cloudflared/
|
||||
|
||||
|
||||
@@ -293,11 +293,6 @@ quic-deps:
|
||||
.PHONY: vet
|
||||
vet:
|
||||
go vet -mod=vendor ./...
|
||||
# go get github.com/sudarshan-reddy/go-sumtype (don't do this in build directory or this will cause vendor issues)
|
||||
# Note: If you have github.com/BurntSushi/go-sumtype then you might have to use the repo above instead
|
||||
# for now because it uses an older version of golang.org/x/tools.
|
||||
which go-sumtype
|
||||
go-sumtype $$(go list -mod=vendor ./...)
|
||||
|
||||
.PHONY: goimports
|
||||
goimports:
|
||||
|
||||
@@ -43,7 +43,7 @@ Once installed, you can authenticate `cloudflared` into your Cloudflare account
|
||||
* Route traffic to that Tunnel:
|
||||
* Via public [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns)
|
||||
* Or via a public hostname guided by a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb)
|
||||
* Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/private-networks)
|
||||
* Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/)
|
||||
|
||||
|
||||
## TryCloudflare
|
||||
|
||||
@@ -1,3 +1,38 @@
|
||||
2022.6.2
|
||||
- 2022-06-13 TUN-6381: Write error data on QUIC stream when we fail to talk to the origin; separate logging for protocol errors vs. origin errors.
|
||||
- 2022-06-17 TUN-6414: Remove go-sumtype from cloudflared build process
|
||||
- 2022-06-01 Add Http2Origin option to force HTTP/2 origin connections
|
||||
- 2022-06-02 fix ingress rules unit test
|
||||
- 2022-06-09 Update remaining OriginRequestConfig functions for Http2Origins
|
||||
- 2022-05-31 Add image source label to docker container.
|
||||
- 2022-05-10 Warp Private Network link updated
|
||||
|
||||
2022.6.1
|
||||
- 2022-06-14 TUN-6395: Fix writing RPM repo data
|
||||
|
||||
2022.6.0
|
||||
- 2022-06-14 Revert "TUN-6010: Add component tests for --edge-ip-version"
|
||||
- 2022-06-14 Revert "TUN-6373: Add edge-ip-version to remotely pushed configuration"
|
||||
- 2022-06-14 Revert "TUN-6384: Correct duplicate connection error to fetch new IP first"
|
||||
- 2022-06-14 Revert "TUN-6007: Implement new edge discovery algorithm"
|
||||
- 2022-06-13 TUN-6385: Don't share err between acceptStream loop and per-stream goroutines
|
||||
- 2022-06-13 TUN-6384: Correct duplicate connection error to fetch new IP first
|
||||
- 2022-06-13 TUN-6373: Add edge-ip-version to remotely pushed configuration
|
||||
- 2022-06-13 TUN-6380: Enforce connect and keep-alive timeouts for TCP connections in both WARP routing and websocket based TCP proxy.
|
||||
- 2022-06-11 Update issue templates
|
||||
- 2022-06-11 Amendment to previous PR
|
||||
- 2022-06-09 TUN-6347: Add TCP stream logs with FlowID
|
||||
- 2022-06-08 TUN-6361: Add cloudflared arm builds to pkging as well
|
||||
- 2022-06-07 TUN-6357: Add connector id to ready check endpoint
|
||||
- 2022-06-07 TUN-6010: Add component tests for --edge-ip-version
|
||||
- 2022-06-06 TUN-6191: Update quic-go to v0.27.1 and with custom patch to allow keep alive period to be configurable
|
||||
- 2022-06-03 TUN-6343: Fix QUIC->HTTP2 fallback
|
||||
- 2022-06-02 TUN-6339: Add config for IPv6 support
|
||||
- 2022-06-02 TUN-6341: Fix default config value for edge-ip-version
|
||||
- 2022-06-01 TUN-6323: Add Xenial and Trusty for Ubuntu pkging
|
||||
- 2022-05-31 TUN-6210: Add cloudflared.repo to make it easy for yum installs
|
||||
- 2022-05-30 TUN-6293: Update yaml v3 to latest hotfix
|
||||
- 2022-05-20 TUN-6007: Implement new edge discovery algorithm
|
||||
2022.5.3
|
||||
- 2022-05-30 TUN-6308: Add debug logs to see if packets are sent/received from edge
|
||||
- 2022-05-30 TUN-6301: Allow to update logger used by UDP session manager
|
||||
|
||||
@@ -153,7 +153,6 @@ stretch: &stretch
|
||||
- gotest-to-teamcity
|
||||
pre-cache: &test_pre_cache
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
- go get github.com/sudarshan-reddy/go-sumtype@v0.0.0-20210827105221-82eca7e5abb1
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/facebookgo/grace/gracenet"
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/google/uuid"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
@@ -348,6 +349,14 @@ func StartServer(
|
||||
log.Err(err).Msg("Couldn't start tunnel")
|
||||
return err
|
||||
}
|
||||
var clientID uuid.UUID
|
||||
if tunnelConfig.NamedTunnel != nil {
|
||||
clientID, err = uuid.FromBytes(tunnelConfig.NamedTunnel.Client.ClientID)
|
||||
if err != nil {
|
||||
// set to nil for classic tunnels
|
||||
clientID = uuid.Nil
|
||||
}
|
||||
}
|
||||
|
||||
orchestrator, err := orchestration.NewOrchestrator(ctx, orchestratorConfig, tunnelConfig.Tags, tunnelConfig.Log)
|
||||
if err != nil {
|
||||
@@ -363,7 +372,7 @@ func StartServer(
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
readinessServer := metrics.NewReadyServer(log)
|
||||
readinessServer := metrics.NewReadyServer(log, clientID)
|
||||
observer.RegisterSink(readinessServer)
|
||||
errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, orchestrator, log)
|
||||
}()
|
||||
@@ -514,6 +523,13 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
Usage: "Cloudflare Edge region to connect to. Omit or set to empty to connect to the global region.",
|
||||
EnvVars: []string{"TUNNEL_REGION"},
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: "edge-ip-version",
|
||||
Usage: "Cloudflare Edge ip address version to connect with. {4, 6, auto}",
|
||||
EnvVars: []string{"TUNNEL_EDGE_IP_VERSION"},
|
||||
Value: "4",
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: tlsconfig.CaCertFlag,
|
||||
Usage: "Certificate Authority authenticating connections with Cloudflare's edge network.",
|
||||
@@ -821,6 +837,13 @@ func configureProxyFlags(shouldHide bool) []cli.Flag {
|
||||
EnvVars: []string{"TUNNEL_NO_CHUNKED_ENCODING"},
|
||||
Hidden: shouldHide,
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: ingress.Http2OriginFlag,
|
||||
Usage: "Enables HTTP/2 origin servers.",
|
||||
EnvVars: []string{"TUNNEL_ORIGIN_ENABLE_HTTP2"},
|
||||
Hidden: shouldHide,
|
||||
Value: false,
|
||||
}),
|
||||
}
|
||||
return append(flags, sshFlags(shouldHide)...)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
@@ -324,6 +325,10 @@ func prepareTunnelConfig(
|
||||
CompressionSetting: h2mux.CompressionSetting(uint64(c.Int("compression-quality"))),
|
||||
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
|
||||
}
|
||||
edgeIPVersion, err := parseConfigIPVersion(c.String("edge-ip-version"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tunnelConfig := &supervisor.TunnelConfig{
|
||||
GracePeriod: gracePeriod,
|
||||
@@ -332,6 +337,7 @@ func prepareTunnelConfig(
|
||||
ClientID: clientID,
|
||||
EdgeAddrs: c.StringSlice("edge"),
|
||||
Region: c.String("region"),
|
||||
EdgeIPVersion: edgeIPVersion,
|
||||
HAConnections: c.Int("ha-connections"),
|
||||
IncidentLookup: supervisor.NewIncidentLookup(),
|
||||
IsAutoupdated: c.Bool("is-autoupdated"),
|
||||
@@ -352,7 +358,7 @@ func prepareTunnelConfig(
|
||||
}
|
||||
orchestratorConfig := &orchestration.Config{
|
||||
Ingress: &ingressRules,
|
||||
WarpRoutingEnabled: warpRoutingEnabled,
|
||||
WarpRouting: ingress.NewWarpRoutingConfig(&cfg.WarpRouting),
|
||||
ConfigurationFlags: parseConfigFlags(c),
|
||||
}
|
||||
return tunnelConfig, orchestratorConfig, nil
|
||||
@@ -404,3 +410,18 @@ func dedup(slice []string) []string {
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// ParseConfigIPVersion returns the IP version from possible expected values from config
|
||||
func parseConfigIPVersion(version string) (v allregions.ConfigIPVersion, err error) {
|
||||
switch version {
|
||||
case "4":
|
||||
v = allregions.IPv4Only
|
||||
case "6":
|
||||
v = allregions.IPv6Only
|
||||
case "auto":
|
||||
v = allregions.Auto
|
||||
default: // unspecified or invalid
|
||||
err = fmt.Errorf("invalid value for edge-ip-version: %s", version)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v2/altsrc"
|
||||
"golang.org/x/net/idna"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cfapi"
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||
|
||||
@@ -227,6 +227,8 @@ type OriginRequestConfig struct {
|
||||
ProxyType *string `yaml:"proxyType" json:"proxyType,omitempty"`
|
||||
// IP rules for the proxy service
|
||||
IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules,omitempty"`
|
||||
// Attempt to connect to origin with HTTP/2
|
||||
Http2Origin *bool `yaml:"http2Origin" json:"http2Origin,omitempty"`
|
||||
}
|
||||
|
||||
type IngressIPRule struct {
|
||||
@@ -244,7 +246,9 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type WarpRoutingConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"`
|
||||
TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"`
|
||||
}
|
||||
|
||||
type configFileSettings struct {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestConfigFileSettings(t *testing.T) {
|
||||
@@ -23,7 +23,9 @@ func TestConfigFileSettings(t *testing.T) {
|
||||
Service: "https://localhost:8001",
|
||||
}
|
||||
warpRouting = WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
Enabled: true,
|
||||
ConnectTimeout: &CustomDuration{Duration: 2 * time.Second},
|
||||
TCPKeepAlive: &CustomDuration{Duration: 10 * time.Second},
|
||||
}
|
||||
)
|
||||
rawYAML := `
|
||||
@@ -48,6 +50,9 @@ ingress:
|
||||
service: https://localhost:8001
|
||||
warp-routing:
|
||||
enabled: true
|
||||
connectTimeout: 2s
|
||||
tcpKeepAlive: 10s
|
||||
|
||||
retries: 5
|
||||
grace-period: 30s
|
||||
percentage: 3.14
|
||||
@@ -111,7 +116,7 @@ counters:
|
||||
|
||||
}
|
||||
|
||||
var rawConfig = []byte(`
|
||||
var rawJsonConfig = []byte(`
|
||||
{
|
||||
"connectTimeout": 10,
|
||||
"tlsTimeout": 30,
|
||||
@@ -139,7 +144,8 @@ var rawConfig = []byte(`
|
||||
"ports": [443, 4443],
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"http2Origin": true
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -148,15 +154,14 @@ func TestMarshalUnmarshalOriginRequest(t *testing.T) {
|
||||
name string
|
||||
marshalFunc func(in interface{}) (out []byte, err error)
|
||||
unMarshalFunc func(in []byte, out interface{}) (err error)
|
||||
baseUnit time.Duration
|
||||
}{
|
||||
{"json", json.Marshal, json.Unmarshal, time.Second},
|
||||
{"yaml", yaml.Marshal, yaml.Unmarshal, time.Nanosecond},
|
||||
{"json", json.Marshal, json.Unmarshal},
|
||||
{"yaml", yaml.Marshal, yaml.Unmarshal},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assertConfig(t, tc.marshalFunc, tc.unMarshalFunc, tc.baseUnit)
|
||||
assertConfig(t, tc.marshalFunc, tc.unMarshalFunc)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -165,18 +170,17 @@ func assertConfig(
|
||||
t *testing.T,
|
||||
marshalFunc func(in interface{}) (out []byte, err error),
|
||||
unMarshalFunc func(in []byte, out interface{}) (err error),
|
||||
baseUnit time.Duration,
|
||||
) {
|
||||
var config OriginRequestConfig
|
||||
var config2 OriginRequestConfig
|
||||
|
||||
assert.NoError(t, unMarshalFunc(rawConfig, &config))
|
||||
assert.NoError(t, json.Unmarshal(rawJsonConfig, &config))
|
||||
|
||||
assert.Equal(t, baseUnit*10, config.ConnectTimeout.Duration)
|
||||
assert.Equal(t, baseUnit*30, config.TLSTimeout.Duration)
|
||||
assert.Equal(t, baseUnit*30, config.TCPKeepAlive.Duration)
|
||||
assert.Equal(t, time.Second*10, config.ConnectTimeout.Duration)
|
||||
assert.Equal(t, time.Second*30, config.TLSTimeout.Duration)
|
||||
assert.Equal(t, time.Second*30, config.TCPKeepAlive.Duration)
|
||||
assert.Equal(t, true, *config.NoHappyEyeballs)
|
||||
assert.Equal(t, baseUnit*60, config.KeepAliveTimeout.Duration)
|
||||
assert.Equal(t, time.Second*60, config.KeepAliveTimeout.Duration)
|
||||
assert.Equal(t, 10, *config.KeepAliveConnections)
|
||||
assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader)
|
||||
assert.Equal(t, "app.tunnel.com", *config.OriginServerName)
|
||||
@@ -188,6 +192,7 @@ func assertConfig(
|
||||
assert.Equal(t, true, *config.NoTLSVerify)
|
||||
assert.Equal(t, uint(9000), *config.ProxyPort)
|
||||
assert.Equal(t, "socks", *config.ProxyType)
|
||||
assert.Equal(t, true, *config.Http2Origin)
|
||||
|
||||
privateV4 := "10.0.0.0/8"
|
||||
privateV6 := "fc00::/7"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/cloudflare/cloudflared/watcher"
|
||||
)
|
||||
|
||||
@@ -132,6 +132,7 @@ type TCPRequest struct {
|
||||
Dest string
|
||||
CFRay string
|
||||
LBProbe bool
|
||||
FlowID string
|
||||
}
|
||||
|
||||
// ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package connection
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
|
||||
const (
|
||||
LogFieldLocation = "location"
|
||||
LogFieldIPAddress = "ip"
|
||||
observerChannelBufferSize = 16
|
||||
)
|
||||
|
||||
@@ -41,11 +43,12 @@ func (o *Observer) RegisterSink(sink EventSink) {
|
||||
o.addSinkChan <- sink
|
||||
}
|
||||
|
||||
func (o *Observer) logServerInfo(connIndex uint8, location, msg string) {
|
||||
func (o *Observer) logServerInfo(connIndex uint8, location string, address net.IP, msg string) {
|
||||
o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location})
|
||||
o.log.Info().
|
||||
Uint8(LogFieldConnIndex, connIndex).
|
||||
Str(LogFieldLocation, location).
|
||||
IPAddr(LogFieldIPAddress, address).
|
||||
Msg(msg)
|
||||
o.metrics.registerServerLocation(uint8ToString(connIndex), location)
|
||||
}
|
||||
|
||||
+37
-19
@@ -31,11 +31,13 @@ const (
|
||||
HTTPMethodKey = "HttpMethod"
|
||||
// HTTPHostKey is used to get or set http Method in QUIC ALPN if the underlying proxy connection type is HTTP.
|
||||
HTTPHostKey = "HttpHost"
|
||||
|
||||
QUICMetadataFlowID = "FlowID"
|
||||
)
|
||||
|
||||
// QUICConnection represents the type that facilitates Proxying via QUIC streams.
|
||||
type QUICConnection struct {
|
||||
session quic.Session
|
||||
session quic.Connection
|
||||
logger *zerolog.Logger
|
||||
orchestrator Orchestrator
|
||||
sessionManager datagramsession.Manager
|
||||
@@ -120,6 +122,11 @@ func (q *QUICConnection) serveControlStream(ctx context.Context, controlStream q
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the session with no errors specified.
|
||||
func (q *QUICConnection) Close() {
|
||||
q.session.CloseWithError(0, "")
|
||||
}
|
||||
|
||||
func (q *QUICConnection) acceptStream(ctx context.Context) error {
|
||||
defer q.Close()
|
||||
for {
|
||||
@@ -131,20 +138,17 @@ func (q *QUICConnection) acceptStream(ctx context.Context) error {
|
||||
}
|
||||
return fmt.Errorf("failed to accept QUIC stream: %w", err)
|
||||
}
|
||||
go func() {
|
||||
stream := quicpogs.NewSafeStreamCloser(quicStream)
|
||||
defer stream.Close()
|
||||
|
||||
if err = q.handleStream(stream); err != nil {
|
||||
q.logger.Err(err).Msg("Failed to handle QUIC stream")
|
||||
}
|
||||
}()
|
||||
go q.runStream(quicStream)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the session with no errors specified.
|
||||
func (q *QUICConnection) Close() {
|
||||
q.session.CloseWithError(0, "")
|
||||
func (q *QUICConnection) runStream(quicStream quic.Stream) {
|
||||
stream := quicpogs.NewSafeStreamCloser(quicStream)
|
||||
defer stream.Close()
|
||||
|
||||
if err := q.handleStream(stream); err != nil {
|
||||
q.logger.Err(err).Msg("Failed to handle QUIC stream")
|
||||
}
|
||||
}
|
||||
|
||||
func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error {
|
||||
@@ -156,7 +160,7 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error {
|
||||
case quicpogs.DataStreamProtocolSignature:
|
||||
reqServerStream, err := quicpogs.NewRequestServerStream(stream, signature)
|
||||
if err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
return q.handleDataStream(reqServerStream)
|
||||
case quicpogs.RPCStreamProtocolSignature:
|
||||
@@ -171,27 +175,41 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error {
|
||||
}
|
||||
|
||||
func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) error {
|
||||
connectRequest, err := stream.ReadConnectRequestData()
|
||||
request, err := stream.ReadConnectRequestData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := q.dispatchRequest(stream, err, request); err != nil {
|
||||
_ = stream.WriteConnectResponseData(err)
|
||||
q.logger.Err(err).Str("type", request.Type.String()).Str("dest", request.Dest).Msg("Request failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *QUICConnection) dispatchRequest(stream *quicpogs.RequestServerStream, err error, request *quicpogs.ConnectRequest) error {
|
||||
originProxy, err := q.orchestrator.GetOriginProxy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch connectRequest.Type {
|
||||
|
||||
switch request.Type {
|
||||
case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket:
|
||||
tracedReq, err := buildHTTPRequest(connectRequest, stream)
|
||||
tracedReq, err := buildHTTPRequest(request, stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := newHTTPResponseAdapter(stream)
|
||||
return originProxy.ProxyHTTP(w, tracedReq, connectRequest.Type == quicpogs.ConnectionTypeWebsocket)
|
||||
return originProxy.ProxyHTTP(w, tracedReq, request.Type == quicpogs.ConnectionTypeWebsocket)
|
||||
|
||||
case quicpogs.ConnectionTypeTCP:
|
||||
rwa := &streamReadWriteAcker{stream}
|
||||
return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest})
|
||||
metadata := request.MetadataMap()
|
||||
return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{
|
||||
Dest: request.Dest,
|
||||
FlowID: metadata[QUICMetadataFlowID],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
var (
|
||||
testTLSServerConfig = quicpogs.GenerateTLSConfig()
|
||||
testQUICConfig = &quic.Config{
|
||||
KeepAlive: true,
|
||||
KeepAlivePeriod: 5 * time.Second,
|
||||
EnableDatagrams: true,
|
||||
}
|
||||
)
|
||||
@@ -502,7 +502,7 @@ func TestServeUDPSession(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Establish QUIC connection with edge
|
||||
edgeQUICSessionChan := make(chan quic.Session)
|
||||
edgeQUICSessionChan := make(chan quic.Connection)
|
||||
go func() {
|
||||
earlyListener, err := quic.Listen(udpListener, testTLSServerConfig, testQUICConfig)
|
||||
require.NoError(t, err)
|
||||
@@ -522,7 +522,7 @@ func TestServeUDPSession(t *testing.T) {
|
||||
cancel()
|
||||
}
|
||||
|
||||
func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Session, closeType closeReason, expectedReason string, t *testing.T) {
|
||||
func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Connection, closeType closeReason, expectedReason string, t *testing.T) {
|
||||
var (
|
||||
payload = []byte(t.Name())
|
||||
)
|
||||
@@ -583,7 +583,7 @@ const (
|
||||
closedByTimeout
|
||||
)
|
||||
|
||||
func runRPCServer(ctx context.Context, session quic.Session, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) {
|
||||
func runRPCServer(ctx context.Context, session quic.Connection, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) {
|
||||
stream, err := session.AcceptStream(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
@@ -114,7 +115,7 @@ func (rsc *registrationServerClient) RegisterConnection(
|
||||
|
||||
observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc()
|
||||
|
||||
observer.logServerInfo(connIndex, conn.Location, fmt.Sprintf("Connection %s registered", conn.UUID))
|
||||
observer.logServerInfo(connIndex, conn.Location, options.OriginLocalIP, fmt.Sprintf("Connection %s registered", conn.UUID))
|
||||
observer.sendConnectedEvent(connIndex, conn.Location)
|
||||
|
||||
return conn, nil
|
||||
@@ -274,7 +275,7 @@ func (h *h2muxConnection) logServerInfo(ctx context.Context, rpcClient *tunnelSe
|
||||
h.observer.log.Err(err).Msg("Failed to retrieve server information")
|
||||
return err
|
||||
}
|
||||
h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, "Connection established")
|
||||
h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, net.IP{}, "Connection established")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,40 @@ var (
|
||||
netLookupIP = net.LookupIP
|
||||
)
|
||||
|
||||
// ConfigIPVersion is the selection of IP versions from config
|
||||
type ConfigIPVersion int8
|
||||
|
||||
const (
|
||||
Auto ConfigIPVersion = 2
|
||||
IPv4Only ConfigIPVersion = 4
|
||||
IPv6Only ConfigIPVersion = 6
|
||||
)
|
||||
|
||||
// IPVersion is the IP version of an EdgeAddr
|
||||
type EdgeIPVersion int8
|
||||
|
||||
const (
|
||||
V4 EdgeIPVersion = 4
|
||||
V6 EdgeIPVersion = 6
|
||||
)
|
||||
|
||||
// String returns the enum's constant name.
|
||||
func (c EdgeIPVersion) String() string {
|
||||
switch c {
|
||||
case V4:
|
||||
return "4"
|
||||
case V6:
|
||||
return "6"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// EdgeAddr is a representation of possible ways to refer an edge location.
|
||||
type EdgeAddr struct {
|
||||
TCP *net.TCPAddr
|
||||
UDP *net.UDPAddr
|
||||
TCP *net.TCPAddr
|
||||
UDP *net.UDPAddr
|
||||
IPVersion EdgeIPVersion
|
||||
}
|
||||
|
||||
// If the call to net.LookupSRV fails, try to fall back to DoT from Cloudflare directly.
|
||||
@@ -120,9 +150,14 @@ func resolveSRV(srv *net.SRV) ([]*EdgeAddr, error) {
|
||||
}
|
||||
addrs := make([]*EdgeAddr, len(ips))
|
||||
for i, ip := range ips {
|
||||
version := V6
|
||||
if ip.To4() != nil {
|
||||
version = V4
|
||||
}
|
||||
addrs[i] = &EdgeAddr{
|
||||
TCP: &net.TCPAddr{IP: ip, Port: int(srv.Port)},
|
||||
UDP: &net.UDPAddr{IP: ip, Port: int(srv.Port)},
|
||||
TCP: &net.TCPAddr{IP: ip, Port: int(srv.Port)},
|
||||
UDP: &net.UDPAddr{IP: ip, Port: int(srv.Port)},
|
||||
IPVersion: version,
|
||||
}
|
||||
}
|
||||
return addrs, nil
|
||||
@@ -143,9 +178,14 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) {
|
||||
log.Error().Str(logFieldAddress, addr).Err(err).Msg("failed to resolve to UDP address")
|
||||
continue
|
||||
}
|
||||
version := V6
|
||||
if udpAddr.IP.To4() != nil {
|
||||
version = V4
|
||||
}
|
||||
resolved = append(resolved, &EdgeAddr{
|
||||
TCP: tcpAddr,
|
||||
UDP: udpAddr,
|
||||
TCP: tcpAddr,
|
||||
UDP: udpAddr,
|
||||
IPVersion: version,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func newMockAddrs(port uint16, numRegions uint8, numAddrsPerRegion uint8) mockAd
|
||||
IP: net.ParseIP(fmt.Sprintf("10.0.%v.%v", r, a)),
|
||||
Port: int(port),
|
||||
}
|
||||
addrs = append(addrs, &EdgeAddr{tcpAddr, udpAddr})
|
||||
addrs = append(addrs, &EdgeAddr{tcpAddr, udpAddr, V4})
|
||||
}
|
||||
addrMap[srv] = addrs
|
||||
numAddrs += len(addrs)
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lucas-clemente/quic-go v0.24.0
|
||||
github.com/lucas-clemente/quic-go v0.27.1
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/miekg/dns v1.1.45
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
@@ -44,8 +44,7 @@ require (
|
||||
gopkg.in/coreos/go-oidc.v2 v2.2.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
zombiezen.com/go/capnproto2 v2.18.0+incompatible
|
||||
)
|
||||
|
||||
@@ -64,7 +63,6 @@ require (
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -76,9 +74,9 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.8 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
@@ -102,11 +100,12 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect
|
||||
google.golang.org/grpc v1.45.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
||||
|
||||
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62
|
||||
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da
|
||||
|
||||
// Avoid 'CVE-2022-21698'
|
||||
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||
|
||||
@@ -111,8 +111,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 h1:PLTB4iA6sOgAItzQY642tYdcGKfG/7i2gu93JQGgUcM=
|
||||
github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
|
||||
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da h1:FmuwbQ8RU/ftTKnfz5diawqvQFH1KDB9wN2Q8S2wqds=
|
||||
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -184,7 +184,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@@ -406,13 +405,12 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
@@ -1110,8 +1108,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+68
-13
@@ -12,10 +12,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConnectTimeout = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second}
|
||||
defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second}
|
||||
defaultHTTPConnectTimeout = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultWarpRoutingConnectTimeout = config.CustomDuration{Duration: 5 * time.Second}
|
||||
defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second}
|
||||
defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second}
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,16 +36,51 @@ const (
|
||||
NoChunkedEncodingFlag = "no-chunked-encoding"
|
||||
ProxyAddressFlag = "proxy-address"
|
||||
ProxyPortFlag = "proxy-port"
|
||||
Http2OriginFlag = "http2-origin"
|
||||
)
|
||||
|
||||
const (
|
||||
socksProxy = "socks"
|
||||
)
|
||||
|
||||
type WarpRoutingConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ConnectTimeout config.CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"`
|
||||
TCPKeepAlive config.CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"`
|
||||
}
|
||||
|
||||
func NewWarpRoutingConfig(raw *config.WarpRoutingConfig) WarpRoutingConfig {
|
||||
cfg := WarpRoutingConfig{
|
||||
Enabled: raw.Enabled,
|
||||
ConnectTimeout: defaultWarpRoutingConnectTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
}
|
||||
if raw.ConnectTimeout != nil {
|
||||
cfg.ConnectTimeout = *raw.ConnectTimeout
|
||||
}
|
||||
if raw.TCPKeepAlive != nil {
|
||||
cfg.TCPKeepAlive = *raw.TCPKeepAlive
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (c *WarpRoutingConfig) RawConfig() config.WarpRoutingConfig {
|
||||
raw := config.WarpRoutingConfig{
|
||||
Enabled: c.Enabled,
|
||||
}
|
||||
if c.ConnectTimeout.Duration != defaultWarpRoutingConnectTimeout.Duration {
|
||||
raw.ConnectTimeout = &c.ConnectTimeout
|
||||
}
|
||||
if c.TCPKeepAlive.Duration != defaultTCPKeepAlive.Duration {
|
||||
raw.TCPKeepAlive = &c.TCPKeepAlive
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
// RemoteConfig models ingress settings that can be managed remotely, for example through the dashboard.
|
||||
type RemoteConfig struct {
|
||||
Ingress Ingress
|
||||
WarpRouting config.WarpRoutingConfig
|
||||
WarpRouting WarpRoutingConfig
|
||||
}
|
||||
|
||||
type RemoteConfigJSON struct {
|
||||
@@ -72,18 +108,18 @@ func (rc *RemoteConfig) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
rc.Ingress = ingress
|
||||
rc.WarpRouting = rawConfig.WarpRouting
|
||||
rc.WarpRouting = NewWarpRoutingConfig(&rawConfig.WarpRouting)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
var connectTimeout config.CustomDuration = defaultConnectTimeout
|
||||
var tlsTimeout config.CustomDuration = defaultTLSTimeout
|
||||
var tcpKeepAlive config.CustomDuration = defaultTCPKeepAlive
|
||||
var connectTimeout = defaultHTTPConnectTimeout
|
||||
var tlsTimeout = defaultTLSTimeout
|
||||
var tcpKeepAlive = defaultTCPKeepAlive
|
||||
var noHappyEyeballs bool
|
||||
var keepAliveConnections int = defaultKeepAliveConnections
|
||||
var keepAliveTimeout config.CustomDuration = defaultKeepAliveTimeout
|
||||
var keepAliveConnections = defaultKeepAliveConnections
|
||||
var keepAliveTimeout = defaultKeepAliveTimeout
|
||||
var httpHostHeader string
|
||||
var originServerName string
|
||||
var caPool string
|
||||
@@ -93,6 +129,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
var proxyAddress = defaultProxyAddress
|
||||
var proxyPort uint
|
||||
var proxyType string
|
||||
var http2Origin bool
|
||||
if flag := ProxyConnectTimeoutFlag; c.IsSet(flag) {
|
||||
connectTimeout = config.CustomDuration{Duration: c.Duration(flag)}
|
||||
}
|
||||
@@ -136,9 +173,13 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
// Note TUN-3758 , we use Int because UInt is not supported with altsrc
|
||||
proxyPort = uint(c.Int(flag))
|
||||
}
|
||||
if flag := Http2OriginFlag; c.IsSet(flag) {
|
||||
http2Origin = c.Bool(flag)
|
||||
}
|
||||
if c.IsSet(Socks5Flag) {
|
||||
proxyType = socksProxy
|
||||
}
|
||||
|
||||
return OriginRequestConfig{
|
||||
ConnectTimeout: connectTimeout,
|
||||
TLSTimeout: tlsTimeout,
|
||||
@@ -155,12 +196,13 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
ProxyAddress: proxyAddress,
|
||||
ProxyPort: proxyPort,
|
||||
ProxyType: proxyType,
|
||||
Http2Origin: http2Origin,
|
||||
}
|
||||
}
|
||||
|
||||
func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||
out := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
@@ -220,6 +262,9 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Http2Origin != nil {
|
||||
out.Http2Origin = *c.Http2Origin
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -263,6 +308,8 @@ type OriginRequestConfig struct {
|
||||
ProxyType string `yaml:"proxyType" json:"proxyType"`
|
||||
// IP rules for the proxy service
|
||||
IPRules []ipaccess.Rule `yaml:"ipRules" json:"ipRules"`
|
||||
// Attempt to connect to origin with HTTP/2
|
||||
Http2Origin bool `yaml:"http2Origin" json:"http2Origin"`
|
||||
}
|
||||
|
||||
func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) {
|
||||
@@ -368,6 +415,12 @@ func (defaults *OriginRequestConfig) setIPRules(overrides config.OriginRequestCo
|
||||
}
|
||||
}
|
||||
|
||||
func (defaults *OriginRequestConfig) setHttp2Origin(overrides config.OriginRequestConfig) {
|
||||
if val := overrides.Http2Origin; val != nil {
|
||||
defaults.Http2Origin = *val
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfig gets config for the requests that cloudflared sends to origins.
|
||||
// Each field has a setter method which sets a value for the field by trying to find:
|
||||
// 1. The user config for this rule
|
||||
@@ -393,6 +446,7 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi
|
||||
cfg.setProxyAddress(overrides)
|
||||
cfg.setProxyType(overrides)
|
||||
cfg.setIPRules(overrides)
|
||||
cfg.setHttp2Origin(overrides)
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -404,7 +458,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig
|
||||
var keepAliveTimeout *config.CustomDuration
|
||||
var proxyAddress *string
|
||||
|
||||
if c.ConnectTimeout != defaultConnectTimeout {
|
||||
if c.ConnectTimeout != defaultHTTPConnectTimeout {
|
||||
connectTimeout = &c.ConnectTimeout
|
||||
}
|
||||
if c.TLSTimeout != defaultTLSTimeout {
|
||||
@@ -440,6 +494,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig
|
||||
ProxyPort: zeroUIntToNil(c.ProxyPort),
|
||||
ProxyType: emptyStringToNil(c.ProxyType),
|
||||
IPRules: convertToRawIPRules(c.IPRules),
|
||||
Http2Origin: defaultBoolToNil(c.Http2Origin),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/ipaccess"
|
||||
@@ -274,7 +274,7 @@ func TestOriginRequestConfigDefaults(t *testing.T) {
|
||||
// Rule 0 didn't override anything, so it inherits the cloudflared defaults
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
@@ -404,7 +404,7 @@ func TestDefaultConfigFromCLI(t *testing.T) {
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
|
||||
expected := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
|
||||
+10
-2
@@ -97,8 +97,16 @@ type WarpRoutingService struct {
|
||||
Proxy StreamBasedOriginProxy
|
||||
}
|
||||
|
||||
func NewWarpRoutingService() *WarpRoutingService {
|
||||
return &WarpRoutingService{Proxy: &rawTCPService{name: ServiceWarpRouting}}
|
||||
func NewWarpRoutingService(config WarpRoutingConfig) *WarpRoutingService {
|
||||
svc := &rawTCPService{
|
||||
name: ServiceWarpRouting,
|
||||
dialer: net.Dialer{
|
||||
Timeout: config.ConnectTimeout.Duration,
|
||||
KeepAlive: config.TCPKeepAlive.Duration,
|
||||
},
|
||||
}
|
||||
|
||||
return &WarpRoutingService{Proxy: svc}
|
||||
}
|
||||
|
||||
// Get a single origin service from the CLI/config.
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/ipaccess"
|
||||
|
||||
+8
-14
@@ -1,26 +1,20 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnsupportedConnectionType = errors.New("internal error: unsupported connection type")
|
||||
)
|
||||
|
||||
// HTTPOriginProxy can be implemented by origin services that want to proxy http requests.
|
||||
type HTTPOriginProxy interface {
|
||||
// RoundTrip is how cloudflared proxies eyeball requests to the actual origin services
|
||||
// RoundTripper is how cloudflared proxies eyeball requests to the actual origin services
|
||||
http.RoundTripper
|
||||
}
|
||||
|
||||
// StreamBasedOriginProxy can be implemented by origin services that want to proxy ws/TCP.
|
||||
type StreamBasedOriginProxy interface {
|
||||
EstablishConnection(dest string) (OriginConnection, error)
|
||||
EstablishConnection(ctx context.Context, dest string) (OriginConnection, error)
|
||||
}
|
||||
|
||||
func (o *unixSocketPath) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -59,8 +53,8 @@ func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
conn, err := net.Dial("tcp", dest)
|
||||
func (o *rawTCPService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) {
|
||||
conn, err := o.dialer.DialContext(ctx, "tcp", dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,13 +65,13 @@ func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, erro
|
||||
return originConn, nil
|
||||
}
|
||||
|
||||
func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
func (o *tcpOverWSService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) {
|
||||
var err error
|
||||
if !o.isBastion {
|
||||
dest = o.dest
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", dest)
|
||||
conn, err := o.dialer.DialContext(ctx, "tcp", dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,6 +83,6 @@ func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, e
|
||||
|
||||
}
|
||||
|
||||
func (o *socksProxyOverWSService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
func (o *socksProxyOverWSService) EstablishConnection(_ctx context.Context, _dest string) (OriginConnection, error) {
|
||||
return o.conn, nil
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestRawTCPServiceEstablishConnection(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Origin not listening for new connection, should return an error
|
||||
_, err = rawTCPService.EstablishConnection(req.URL.String())
|
||||
_, err = rawTCPService.EstablishConnection(context.Background(), req.URL.String())
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) {
|
||||
t.Run(test.testCase, func(t *testing.T) {
|
||||
if test.expectErr {
|
||||
bastionHost, _ := carrier.ResolveBastionDest(test.req)
|
||||
_, err := test.service.EstablishConnection(bastionHost)
|
||||
_, err := test.service.EstablishConnection(context.Background(), bastionHost)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
})
|
||||
@@ -99,7 +99,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) {
|
||||
for _, service := range []*tcpOverWSService{newTCPOverWSService(originURL), newBastionService()} {
|
||||
// Origin not listening for new connection, should return an error
|
||||
bastionHost, _ := carrier.ResolveBastionDest(bastionReq)
|
||||
_, err := service.EstablishConnection(bastionHost)
|
||||
_, err := service.EstablishConnection(context.Background(), bastionHost)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ func (o httpService) MarshalJSON() ([]byte, error) {
|
||||
// rawTCPService dials TCP to the destination specified by the client
|
||||
// It's used by warp routing
|
||||
type rawTCPService struct {
|
||||
name string
|
||||
name string
|
||||
dialer net.Dialer
|
||||
}
|
||||
|
||||
func (o *rawTCPService) String() string {
|
||||
@@ -113,6 +114,7 @@ type tcpOverWSService struct {
|
||||
dest string
|
||||
isBastion bool
|
||||
streamHandler streamHandlerFunc
|
||||
dialer net.Dialer
|
||||
}
|
||||
|
||||
type socksProxyOverWSService struct {
|
||||
@@ -176,6 +178,8 @@ func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg Ori
|
||||
} else {
|
||||
o.streamHandler = DefaultStreamHandler
|
||||
}
|
||||
o.dialer.Timeout = cfg.ConnectTimeout.Duration
|
||||
o.dialer.KeepAlive = cfg.TCPKeepAlive.Duration
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -286,6 +290,7 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol
|
||||
TLSHandshakeTimeout: cfg.TLSTimeout.Duration,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify},
|
||||
ForceAttemptHTTP2: cfg.Http2Origin,
|
||||
}
|
||||
if _, isHelloWorld := service.(*helloWorld); !isHelloWorld && cfg.OriginServerName != "" {
|
||||
httpTransport.TLSClientConfig.ServerName = cfg.OriginServerName
|
||||
|
||||
@@ -182,25 +182,25 @@ func TestMarshalJSON(t *testing.T) {
|
||||
{
|
||||
name: "Nil",
|
||||
path: nil,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Nil regex",
|
||||
path: &Regexp{Regexp: nil},
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
path: &Regexp{Regexp: regexp.MustCompile("")},
|
||||
expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`,
|
||||
expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Basic",
|
||||
path: &Regexp{Regexp: regexp.MustCompile("/echo")},
|
||||
expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`,
|
||||
expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
+12
-7
@@ -5,21 +5,24 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
conn "github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/tunnelstate"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// ReadyServer serves HTTP 200 if the tunnel can serve traffic. Intended for k8s readiness checks.
|
||||
type ReadyServer struct {
|
||||
tracker *tunnelstate.ConnTracker
|
||||
clientID uuid.UUID
|
||||
tracker *tunnelstate.ConnTracker
|
||||
}
|
||||
|
||||
// NewReadyServer initializes a ReadyServer and starts listening for dis/connection events.
|
||||
func NewReadyServer(log *zerolog.Logger) *ReadyServer {
|
||||
func NewReadyServer(log *zerolog.Logger, clientID uuid.UUID) *ReadyServer {
|
||||
return &ReadyServer{
|
||||
tracker: tunnelstate.NewConnTracker(log),
|
||||
clientID: clientID,
|
||||
tracker: tunnelstate.NewConnTracker(log),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +31,9 @@ func (rs *ReadyServer) OnTunnelEvent(c conn.Event) {
|
||||
}
|
||||
|
||||
type body struct {
|
||||
Status int `json:"status"`
|
||||
ReadyConnections uint `json:"readyConnections"`
|
||||
Status int `json:"status"`
|
||||
ReadyConnections uint `json:"readyConnections"`
|
||||
ConnectorID uuid.UUID `json:"connectorId"`
|
||||
}
|
||||
|
||||
// ServeHTTP responds with HTTP 200 if the tunnel is connected to the edge.
|
||||
@@ -39,6 +43,7 @@ func (rs *ReadyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
body := body{
|
||||
Status: statusCode,
|
||||
ReadyConnections: readyConnections,
|
||||
ConnectorID: rs.clientID,
|
||||
}
|
||||
msg, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cloudflare/cloudflared/tunnelstate"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/tunnelstate"
|
||||
)
|
||||
|
||||
func TestReadyServer_makeResponse(t *testing.T) {
|
||||
@@ -66,7 +66,7 @@ func TestReadyServer_makeResponse(t *testing.T) {
|
||||
|
||||
func TestReadinessEventHandling(t *testing.T) {
|
||||
nopLogger := zerolog.Nop()
|
||||
rs := NewReadyServer(&nopLogger)
|
||||
rs := NewReadyServer(&nopLogger, uuid.Nil)
|
||||
|
||||
// start not ok
|
||||
code, ready := rs.makeResponse()
|
||||
|
||||
@@ -19,8 +19,8 @@ type newLocalConfig struct {
|
||||
|
||||
// Config is the original config as read and parsed by cloudflared.
|
||||
type Config struct {
|
||||
Ingress *ingress.Ingress
|
||||
WarpRoutingEnabled bool
|
||||
Ingress *ingress.Ingress
|
||||
WarpRouting ingress.WarpRoutingConfig
|
||||
|
||||
// Extra settings used to configure this instance but that are not eligible for remotely management
|
||||
// ie. (--protocol, --loglevel, ...)
|
||||
@@ -37,7 +37,7 @@ func (rc *newLocalConfig) MarshalJSON() ([]byte, error) {
|
||||
// UI doesn't support top level configs, so we reconcile to individual ingress configs.
|
||||
GlobalOriginRequest: nil,
|
||||
IngressRules: convertToUnvalidatedIngressRules(rc.RemoteConfig.Ingress),
|
||||
WarpRouting: rc.RemoteConfig.WarpRouting,
|
||||
WarpRouting: rc.RemoteConfig.WarpRouting.RawConfig(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,17 @@ package orchestration
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/ingress"
|
||||
)
|
||||
|
||||
// TestNewLocalConfig_MarshalJSON tests that we are able to converte a compiled and validated config back
|
||||
// into an "unvalidated" format which is compatible with Remote Managed configurations.
|
||||
func TestNewLocalConfig_MarshalJSON(t *testing.T) {
|
||||
|
||||
rawConfig := []byte(`
|
||||
{
|
||||
"originRequest": {
|
||||
@@ -57,7 +58,11 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) {
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true,
|
||||
"connectTimeout": 1
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -73,10 +78,18 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) {
|
||||
jsonSerde, err := json.Marshal(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
var config ingress.RemoteConfig
|
||||
err = json.Unmarshal(jsonSerde, &config)
|
||||
var remoteConfig ingress.RemoteConfig
|
||||
err = json.Unmarshal(jsonSerde, &remoteConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, config.WarpRouting.Enabled, false)
|
||||
require.Equal(t, config.Ingress.Rules, expectedConfig.Ingress.Rules)
|
||||
require.Equal(t, remoteConfig.WarpRouting, ingress.WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
ConnectTimeout: config.CustomDuration{
|
||||
Duration: time.Second,
|
||||
},
|
||||
TCPKeepAlive: config.CustomDuration{
|
||||
Duration: 30 * time.Second, // default value is 30 seconds
|
||||
},
|
||||
})
|
||||
require.Equal(t, remoteConfig.Ingress.Rules, expectedConfig.Ingress.Rules)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewOrchestrator(ctx context.Context, config *Config, tags []tunnelpogs.Tag,
|
||||
log: log,
|
||||
shutdownC: ctx.Done(),
|
||||
}
|
||||
if err := o.updateIngress(*config.Ingress, config.WarpRoutingEnabled); err != nil {
|
||||
if err := o.updateIngress(*config.Ingress, config.WarpRouting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go o.waitToCloseLastProxy()
|
||||
@@ -80,7 +80,7 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up
|
||||
}
|
||||
}
|
||||
|
||||
if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting.Enabled); err != nil {
|
||||
if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting); err != nil {
|
||||
o.log.Err(err).
|
||||
Int32("version", version).
|
||||
Str("config", string(config)).
|
||||
@@ -103,7 +103,7 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up
|
||||
}
|
||||
|
||||
// The caller is responsible to make sure there is no concurrent access
|
||||
func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEnabled bool) error {
|
||||
func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting ingress.WarpRoutingConfig) error {
|
||||
select {
|
||||
case <-o.shutdownC:
|
||||
return fmt.Errorf("cloudflared already shutdown")
|
||||
@@ -118,10 +118,10 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEn
|
||||
if err := ingressRules.StartOrigins(o.log, proxyShutdownC); err != nil {
|
||||
return errors.Wrap(err, "failed to start origin")
|
||||
}
|
||||
newProxy := proxy.NewOriginProxy(ingressRules, warpRoutingEnabled, o.tags, o.log)
|
||||
newProxy := proxy.NewOriginProxy(ingressRules, warpRouting, o.tags, o.log)
|
||||
o.proxy.Store(newProxy)
|
||||
o.config.Ingress = &ingressRules
|
||||
o.config.WarpRoutingEnabled = warpRoutingEnabled
|
||||
o.config.WarpRouting = warpRouting
|
||||
|
||||
// If proxyShutdownC is nil, there is no previous running proxy
|
||||
if o.proxyShutdownC != nil {
|
||||
@@ -139,7 +139,7 @@ func (o *Orchestrator) GetConfigJSON() ([]byte, error) {
|
||||
c := &newLocalConfig{
|
||||
RemoteConfig: ingress.RemoteConfig{
|
||||
Ingress: *o.config.Ingress,
|
||||
WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled},
|
||||
WarpRouting: o.config.WarpRouting,
|
||||
},
|
||||
ConfigurationFlags: o.config.ConfigurationFlags,
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func (o *Orchestrator) GetVersionedConfigJSON() ([]byte, error) {
|
||||
OriginRequest ingress.OriginRequestConfig `json:"originRequest"`
|
||||
}{
|
||||
Ingress: o.config.Ingress.Rules,
|
||||
WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled},
|
||||
WarpRouting: o.config.WarpRouting.RawConfig(),
|
||||
OriginRequest: o.config.Ingress.Defaults,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ var (
|
||||
// - receiving an old version is noop
|
||||
func TestUpdateConfiguration(t *testing.T) {
|
||||
initConfig := &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
WarpRoutingEnabled: false,
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, &testLogger)
|
||||
require.NoError(t, err)
|
||||
@@ -87,7 +86,8 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"connectTimeout": 10
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -121,7 +121,8 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
require.Equal(t, config.CustomDuration{Duration: time.Second * 90}, configV2.Ingress.Rules[2].Config.ConnectTimeout)
|
||||
require.Equal(t, false, configV2.Ingress.Rules[2].Config.NoTLSVerify)
|
||||
require.Equal(t, true, configV2.Ingress.Rules[2].Config.NoHappyEyeballs)
|
||||
require.True(t, configV2.WarpRoutingEnabled)
|
||||
require.True(t, configV2.WarpRouting.Enabled)
|
||||
require.Equal(t, configV2.WarpRouting.ConnectTimeout.Duration, 10*time.Second)
|
||||
|
||||
originProxyV2, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
@@ -164,7 +165,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
require.Len(t, configV10.Ingress.Rules, 1)
|
||||
require.True(t, configV10.Ingress.Rules[0].Matches("blogs.tunnel.io", "/2022/02/10"))
|
||||
require.Equal(t, ingress.HelloWorldService, configV10.Ingress.Rules[0].Service.String())
|
||||
require.False(t, configV10.WarpRoutingEnabled)
|
||||
require.False(t, configV10.WarpRouting.Enabled)
|
||||
|
||||
originProxyV10, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
@@ -246,8 +247,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
appliedV2 = make(chan struct{})
|
||||
|
||||
initConfig = &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
WarpRoutingEnabled: false,
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -476,8 +476,7 @@ func TestClosePreviousProxies(t *testing.T) {
|
||||
}
|
||||
`)
|
||||
initConfig = &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
WarpRoutingEnabled: false,
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -534,8 +533,7 @@ func TestPersistentConnection(t *testing.T) {
|
||||
)
|
||||
msg := t.Name()
|
||||
initConfig := &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
WarpRoutingEnabled: false,
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, &testLogger)
|
||||
require.NoError(t, err)
|
||||
@@ -645,8 +643,7 @@ func TestPersistentConnection(t *testing.T) {
|
||||
func TestSerializeLocalConfig(t *testing.T) {
|
||||
c := &newLocalConfig{
|
||||
RemoteConfig: ingress.RemoteConfig{
|
||||
Ingress: ingress.Ingress{},
|
||||
WarpRouting: config.WarpRoutingConfig{},
|
||||
Ingress: ingress.Ingress{},
|
||||
},
|
||||
ConfigurationFlags: map[string]string{"a": "b"},
|
||||
}
|
||||
|
||||
+18
-16
@@ -28,6 +28,7 @@ const (
|
||||
LogFieldCFRay = "cfRay"
|
||||
LogFieldRule = "ingressRule"
|
||||
LogFieldOriginService = "originService"
|
||||
LogFieldFlowID = "flowID"
|
||||
)
|
||||
|
||||
// Proxy represents a means to Proxy between cloudflared and the origin services.
|
||||
@@ -41,7 +42,7 @@ type Proxy struct {
|
||||
// NewOriginProxy returns a new instance of the Proxy struct.
|
||||
func NewOriginProxy(
|
||||
ingressRules ingress.Ingress,
|
||||
warpRoutingEnabled bool,
|
||||
warpRouting ingress.WarpRoutingConfig,
|
||||
tags []tunnelpogs.Tag,
|
||||
log *zerolog.Logger,
|
||||
) *Proxy {
|
||||
@@ -50,8 +51,8 @@ func NewOriginProxy(
|
||||
tags: tags,
|
||||
log: log,
|
||||
}
|
||||
if warpRoutingEnabled {
|
||||
proxy.warpRouting = ingress.NewWarpRoutingService()
|
||||
if warpRouting.Enabled {
|
||||
proxy.warpRouting = ingress.NewWarpRoutingService(warpRouting)
|
||||
log.Info().Msgf("Warp-routing is enabled")
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ func (p *Proxy) ProxyHTTP(
|
||||
logFields,
|
||||
); err != nil {
|
||||
rule, srv := ruleField(p.ingressRules, ruleNum)
|
||||
p.logRequestError(err, cfRay, rule, srv)
|
||||
p.logRequestError(err, cfRay, "", rule, srv)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -107,9 +108,9 @@ func (p *Proxy) ProxyHTTP(
|
||||
}
|
||||
|
||||
rws := connection.NewHTTPResponseReadWriterAcker(w, req)
|
||||
if err := p.proxyStream(req.Context(), rws, dest, originProxy, logFields); err != nil {
|
||||
if err := p.proxyStream(req.Context(), rws, dest, originProxy); err != nil {
|
||||
rule, srv := ruleField(p.ingressRules, ruleNum)
|
||||
p.logRequestError(err, cfRay, rule, srv)
|
||||
p.logRequestError(err, cfRay, "", rule, srv)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -136,17 +137,15 @@ func (p *Proxy) ProxyTCP(
|
||||
serveCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
logFields := logFields{
|
||||
cfRay: req.CFRay,
|
||||
lbProbe: req.LBProbe,
|
||||
rule: ingress.ServiceWarpRouting,
|
||||
}
|
||||
p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started")
|
||||
|
||||
if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy, logFields); err != nil {
|
||||
p.logRequestError(err, req.CFRay, "", ingress.ServiceWarpRouting)
|
||||
if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil {
|
||||
p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting)
|
||||
return err
|
||||
}
|
||||
|
||||
p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream finished successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -250,9 +249,8 @@ func (p *Proxy) proxyStream(
|
||||
rwa connection.ReadWriteAcker,
|
||||
dest string,
|
||||
connectionProxy ingress.StreamBasedOriginProxy,
|
||||
fields logFields,
|
||||
) error {
|
||||
originConn, err := connectionProxy.EstablishConnection(dest)
|
||||
originConn, err := connectionProxy.EstablishConnection(ctx, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -317,6 +315,7 @@ type logFields struct {
|
||||
cfRay string
|
||||
lbProbe bool
|
||||
rule interface{}
|
||||
flowID string
|
||||
}
|
||||
|
||||
func (p *Proxy) logRequest(r *http.Request, fields logFields) {
|
||||
@@ -360,12 +359,15 @@ func (p *Proxy) logOriginResponse(resp *http.Response, fields logFields) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) logRequestError(err error, cfRay string, rule, service string) {
|
||||
func (p *Proxy) logRequestError(err error, cfRay string, flowID string, rule, service string) {
|
||||
requestErrors.Inc()
|
||||
log := p.log.Error().Err(err)
|
||||
if cfRay != "" {
|
||||
log = log.Str(LogFieldCFRay, cfRay)
|
||||
}
|
||||
if flowID != "" {
|
||||
log = log.Str(LogFieldFlowID, flowID)
|
||||
}
|
||||
if rule != "" {
|
||||
log = log.Str(LogFieldRule, rule)
|
||||
}
|
||||
|
||||
+12
-7
@@ -32,7 +32,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
testTags = []tunnelpogs.Tag{tunnelpogs.Tag{Name: "Name", Value: "value"}}
|
||||
testTags = []tunnelpogs.Tag{tunnelpogs.Tag{Name: "Name", Value: "value"}}
|
||||
noWarpRouting = ingress.WarpRoutingConfig{}
|
||||
testWarpRouting = ingress.WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
ConnectTimeout: config.CustomDuration{Duration: time.Second},
|
||||
}
|
||||
)
|
||||
|
||||
type mockHTTPRespWriter struct {
|
||||
@@ -138,7 +143,7 @@ func TestProxySingleOrigin(t *testing.T) {
|
||||
|
||||
require.NoError(t, ingressRule.StartOrigins(&log, ctx.Done()))
|
||||
|
||||
proxy := NewOriginProxy(ingressRule, false, testTags, &log)
|
||||
proxy := NewOriginProxy(ingressRule, noWarpRouting, testTags, &log)
|
||||
t.Run("testProxyHTTP", testProxyHTTP(proxy))
|
||||
t.Run("testProxyWebsocket", testProxyWebsocket(proxy))
|
||||
t.Run("testProxySSE", testProxySSE(proxy))
|
||||
@@ -345,7 +350,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
require.NoError(t, ingress.StartOrigins(&log, ctx.Done()))
|
||||
|
||||
proxy := NewOriginProxy(ingress, false, testTags, &log)
|
||||
proxy := NewOriginProxy(ingress, noWarpRouting, testTags, &log)
|
||||
|
||||
for _, test := range tests {
|
||||
responseWriter := newMockHTTPRespWriter()
|
||||
@@ -393,7 +398,7 @@ func TestProxyError(t *testing.T) {
|
||||
|
||||
log := zerolog.Nop()
|
||||
|
||||
proxy := NewOriginProxy(ing, false, testTags, &log)
|
||||
proxy := NewOriginProxy(ing, noWarpRouting, testTags, &log)
|
||||
|
||||
responseWriter := newMockHTTPRespWriter()
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil)
|
||||
@@ -509,7 +514,7 @@ func TestConnections(t *testing.T) {
|
||||
originService: runEchoTCPService,
|
||||
eyeballResponseWriter: newTCPRespWriter(replayer),
|
||||
eyeballRequestBody: newTCPRequestBody([]byte("test2")),
|
||||
warpRoutingService: ingress.NewWarpRoutingService(),
|
||||
warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting),
|
||||
connectionType: connection.TypeTCP,
|
||||
requestHeaders: map[string][]string{
|
||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
||||
@@ -526,7 +531,7 @@ func TestConnections(t *testing.T) {
|
||||
originService: runEchoWSService,
|
||||
// eyeballResponseWriter gets set after roundtrip dial.
|
||||
eyeballRequestBody: newPipedWSRequestBody([]byte("test3")),
|
||||
warpRoutingService: ingress.NewWarpRoutingService(),
|
||||
warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting),
|
||||
requestHeaders: map[string][]string{
|
||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
||||
},
|
||||
@@ -652,7 +657,7 @@ func TestConnections(t *testing.T) {
|
||||
|
||||
ingressRule := createSingleIngressConfig(t, test.args.ingressServiceScheme+ln.Addr().String())
|
||||
ingressRule.StartOrigins(logger, ctx.Done())
|
||||
proxy := NewOriginProxy(ingressRule, true, testTags, logger)
|
||||
proxy := NewOriginProxy(ingressRule, testWarpRouting, testTags, logger)
|
||||
proxy.warpRouting = test.args.warpRoutingService
|
||||
|
||||
dest := ln.Addr().String()
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@ const (
|
||||
)
|
||||
|
||||
type DatagramMuxer struct {
|
||||
session quic.Session
|
||||
session quic.Connection
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
func NewDatagramMuxer(quicSession quic.Session, logger *zerolog.Logger) (*DatagramMuxer, error) {
|
||||
func NewDatagramMuxer(quicSession quic.Connection, logger *zerolog.Logger) (*DatagramMuxer, error) {
|
||||
return &DatagramMuxer{
|
||||
session: quicSession,
|
||||
logger: logger,
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestMaxDatagramPayload(t *testing.T) {
|
||||
payload := make([]byte, maxDatagramPayloadSize)
|
||||
|
||||
quicConfig := &quic.Config{
|
||||
KeepAlive: true,
|
||||
KeepAlivePeriod: 5 * time.Millisecond,
|
||||
EnableDatagrams: true,
|
||||
MaxDatagramFrameSize: MaxDatagramFrameSize,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
capnp "zombiezen.com/go/capnproto2"
|
||||
"zombiezen.com/go/capnproto2/pogs"
|
||||
|
||||
@@ -16,6 +18,18 @@ const (
|
||||
ConnectionTypeTCP
|
||||
)
|
||||
|
||||
func (c ConnectionType) String() string {
|
||||
switch c {
|
||||
case ConnectionTypeHTTP:
|
||||
return "http"
|
||||
case ConnectionTypeWebsocket:
|
||||
return "ws"
|
||||
case ConnectionTypeTCP:
|
||||
return "tcp"
|
||||
}
|
||||
panic(fmt.Sprintf("invalid ConnectionType: %d", c))
|
||||
}
|
||||
|
||||
// ConnectRequest is the representation of metadata sent at the start of a QUIC application handshake.
|
||||
type ConnectRequest struct {
|
||||
Dest string `capnp:"dest"`
|
||||
|
||||
@@ -37,7 +37,8 @@ const (
|
||||
protocolVersionLength = 2
|
||||
|
||||
HandshakeIdleTimeout = 5 * time.Second
|
||||
MaxIdleTimeout = 15 * time.Second
|
||||
MaxIdleTimeout = 5 * time.Second
|
||||
MaxIdlePingPeriod = 1 * time.Second
|
||||
)
|
||||
|
||||
// RequestServerStream is a stream to serve requests
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
var (
|
||||
testTLSServerConfig = GenerateTLSConfig()
|
||||
testQUICConfig = &quic.Config{
|
||||
KeepAlive: true,
|
||||
KeepAlivePeriod: 5 * time.Second,
|
||||
EnableDatagrams: true,
|
||||
}
|
||||
exchanges = 1000
|
||||
|
||||
+116
-52
@@ -8,23 +8,24 @@
|
||||
them to be in an uploadable state.
|
||||
2. Upload these packages to a storage in a format that apt and yum expect.
|
||||
"""
|
||||
from subprocess import Popen, PIPE
|
||||
import os
|
||||
import argparse
|
||||
import base64
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import gnupg
|
||||
import boto3
|
||||
import gnupg
|
||||
from botocore.client import Config
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
# The front facing R2 URL to access assets from.
|
||||
R2_ASSET_URL = 'https://demo-r2-worker.cloudflare-tunnel.workers.dev/'
|
||||
|
||||
|
||||
class PkgUploader:
|
||||
def __init__(self, account_id, bucket_name, client_id, client_secret):
|
||||
self.account_id = account_id
|
||||
@@ -35,14 +36,14 @@ class PkgUploader:
|
||||
def upload_pkg_to_r2(self, filename, upload_file_path):
|
||||
endpoint_url = f"https://{self.account_id}.r2.cloudflarestorage.com"
|
||||
token_secret_hash = sha256(self.client_secret.encode()).hexdigest()
|
||||
|
||||
|
||||
config = Config(
|
||||
region_name = 'auto',
|
||||
region_name='auto',
|
||||
s3={
|
||||
"addressing_style": "path",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
r2 = boto3.client(
|
||||
"s3",
|
||||
endpoint_url=endpoint_url,
|
||||
@@ -57,7 +58,7 @@ class PkgUploader:
|
||||
except ClientError as e:
|
||||
raise e
|
||||
|
||||
|
||||
|
||||
class PkgCreator:
|
||||
"""
|
||||
The distribution conf is what dictates to reprepro, the debian packaging building
|
||||
@@ -72,15 +73,16 @@ class PkgCreator:
|
||||
description - (String)
|
||||
gpg_key_id - gpg key id of what you want to use to sign the packages.(String)
|
||||
"""
|
||||
def create_distribution_conf(self,
|
||||
file_path,
|
||||
origin,
|
||||
label,
|
||||
releases,
|
||||
archs,
|
||||
components,
|
||||
description,
|
||||
gpg_key_id ):
|
||||
|
||||
def create_distribution_conf(self,
|
||||
file_path,
|
||||
origin,
|
||||
label,
|
||||
releases,
|
||||
archs,
|
||||
components,
|
||||
description,
|
||||
gpg_key_id):
|
||||
with open(file_path, "w+") as distributions_file:
|
||||
for release in releases:
|
||||
distributions_file.write(f"Origin: {origin}\n")
|
||||
@@ -102,6 +104,7 @@ class PkgCreator:
|
||||
db and pool contain information and metadata about builds. We can ignore these.
|
||||
dist: contains all the pkgs and signed releases that are necessary for an apt download.
|
||||
"""
|
||||
|
||||
def create_deb_pkgs(self, release, deb_file):
|
||||
print(f"creating deb pkgs: {release} : {deb_file}")
|
||||
p = Popen(["reprepro", "includedeb", release, deb_file], stdout=PIPE, stderr=PIPE)
|
||||
@@ -120,20 +123,41 @@ class PkgCreator:
|
||||
|
||||
self._sign_repomd()
|
||||
|
||||
"""
|
||||
creates a <binary>.repo file with details like so
|
||||
[cloudflared-stable]
|
||||
name=cloudflared-stable
|
||||
baseurl=https://pkg.cloudflare.com/cloudflared/rpm
|
||||
enabled=1
|
||||
type=rpm
|
||||
gpgcheck=1
|
||||
gpgkey=https://pkg.cloudflare.com/cloudflare-main.gpg
|
||||
"""
|
||||
|
||||
def create_repo_file(self, file_path, binary_name, baseurl, gpgkey_url):
|
||||
with open(os.path.join(file_path, binary_name + '.repo'), "w+") as repo_file:
|
||||
repo_file.write(f"[{binary_name}-stable]")
|
||||
repo_file.write(f"{binary_name}-stable")
|
||||
repo_file.write(f"baseurl={baseurl}/rpm")
|
||||
repo_file.write("enabled=1")
|
||||
repo_file.write("type=rpm")
|
||||
repo_file.write("gpgcheck=1")
|
||||
repo_file.write(f"gpgkey={gpgkey_url}")
|
||||
|
||||
def _sign_rpms(self, file_path):
|
||||
p = Popen(["rpm" , "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE)
|
||||
p = Popen(["rpm", "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE)
|
||||
out, err = p.communicate()
|
||||
if p.returncode != 0:
|
||||
print(f"rpm sign result result => {out}, {err}")
|
||||
raise
|
||||
|
||||
def _sign_repomd(self):
|
||||
p = Popen(["gpg", "--batch", "--detach-sign", "--armor", "./rpm/repodata/repomd.xml"], stdout=PIPE, stderr=PIPE)
|
||||
p = Popen(["gpg", "--batch", "--detach-sign", "--armor", "./rpm/repodata/repomd.xml"], stdout=PIPE, stderr=PIPE)
|
||||
out, err = p.communicate()
|
||||
if p.returncode != 0:
|
||||
print(f"sign repomd result => {out}, {err}")
|
||||
raise
|
||||
|
||||
|
||||
"""
|
||||
sets up and signs the RPM directories in the following format:
|
||||
- rpm
|
||||
@@ -143,9 +167,10 @@ class PkgCreator:
|
||||
|
||||
this assumes the assets are in the format <prefix>-<aarch64/x86_64/386>.rpm
|
||||
"""
|
||||
|
||||
def _setup_rpm_pkg_directories(self, artifacts_path, gpg_key_name, archs=["aarch64", "x86_64", "386"]):
|
||||
for arch in archs:
|
||||
for root, _ , files in os.walk(artifacts_path):
|
||||
for root, _, files in os.walk(artifacts_path):
|
||||
for file in files:
|
||||
if file.endswith(f"{arch}.rpm"):
|
||||
new_dir = f"./rpm/{arch}"
|
||||
@@ -154,11 +179,12 @@ class PkgCreator:
|
||||
new_path = os.path.join(new_dir, file)
|
||||
shutil.copyfile(old_path, new_path)
|
||||
self._sign_rpms(new_path)
|
||||
|
||||
|
||||
"""
|
||||
imports gpg keys into the system so reprepro and createrepo can use it to sign packages.
|
||||
it returns the GPG ID after a successful import
|
||||
"""
|
||||
|
||||
def import_gpg_keys(self, private_key, public_key):
|
||||
gpg = gnupg.GPG()
|
||||
private_key = base64.b64decode(private_key)
|
||||
@@ -172,12 +198,13 @@ class PkgCreator:
|
||||
basically rpm --import <key_file>
|
||||
This enables us to sign rpms.
|
||||
"""
|
||||
|
||||
def import_rpm_key(self, public_key):
|
||||
file_name = "pb.key"
|
||||
with open(file_name, "wb") as f:
|
||||
public_key = base64.b64decode(public_key)
|
||||
f.write(public_key)
|
||||
|
||||
|
||||
p = Popen(["rpm", "--import", file_name], stdout=PIPE, stderr=PIPE)
|
||||
out, err = p.communicate()
|
||||
if p.returncode != 0:
|
||||
@@ -192,18 +219,21 @@ class PkgCreator:
|
||||
and the release will be uploaded to the default path.
|
||||
binary: name of the binary to upload
|
||||
"""
|
||||
|
||||
|
||||
def upload_from_directories(pkg_uploader, directory, release, binary):
|
||||
for root, _ , files in os.walk(directory):
|
||||
for root, _, files in os.walk(directory):
|
||||
for file in files:
|
||||
upload_file_name = os.path.join(binary, root, file)
|
||||
if release:
|
||||
upload_file_name = os.path.join(release, upload_file_name)
|
||||
filename = os.path.join(root,file)
|
||||
try:
|
||||
filename = os.path.join(root, file)
|
||||
try:
|
||||
pkg_uploader.upload_pkg_to_r2(filename, upload_file_name)
|
||||
except ClientError as e:
|
||||
logging.error(e)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
"""
|
||||
1. looks into a built_artifacts folder for cloudflared debs
|
||||
@@ -217,19 +247,22 @@ def upload_from_directories(pkg_uploader, directory, release, binary):
|
||||
|
||||
release_version: is the cloudflared release version. Only provide this if you want a permanent backup.
|
||||
"""
|
||||
def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, release_version):
|
||||
|
||||
|
||||
def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component,
|
||||
release_version):
|
||||
# set configuration for package creation.
|
||||
print(f"initialising configuration for {binary_name} , {archs}")
|
||||
Path("./conf").mkdir(parents=True, exist_ok=True)
|
||||
pkg_creator.create_distribution_conf(
|
||||
"./conf/distributions",
|
||||
binary_name,
|
||||
binary_name,
|
||||
releases,
|
||||
archs,
|
||||
package_component,
|
||||
f"apt repository for {binary_name}",
|
||||
gpg_key_id)
|
||||
"./conf/distributions",
|
||||
binary_name,
|
||||
binary_name,
|
||||
releases,
|
||||
archs,
|
||||
package_component,
|
||||
f"apt repository for {binary_name}",
|
||||
gpg_key_id)
|
||||
|
||||
# create deb pkgs
|
||||
for release in releases:
|
||||
@@ -246,9 +279,20 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary
|
||||
upload_from_directories(pkg_uploader, "dists", release_version, binary_name)
|
||||
upload_from_directories(pkg_uploader, "pool", release_version, binary_name)
|
||||
|
||||
def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name, gpg_key_name):
|
||||
|
||||
def create_rpm_packaging(
|
||||
pkg_creator,
|
||||
pkg_uploader,
|
||||
artifacts_path,
|
||||
release_version,
|
||||
binary_name,
|
||||
gpg_key_name,
|
||||
base_url,
|
||||
gpg_key_url,
|
||||
):
|
||||
print(f"creating rpm pkgs...")
|
||||
pkg_creator.create_rpm_pkgs(artifacts_path, gpg_key_name)
|
||||
pkg_creator.create_repo_file(artifacts_path, binary_name, base_url, gpg_key_url)
|
||||
|
||||
print("uploading latest to r2...")
|
||||
upload_from_directories(pkg_uploader, "rpm", None, binary_name)
|
||||
@@ -264,50 +308,61 @@ def parse_args():
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--bucket", default=os.environ.get("R2_BUCKET"), help="R2 Bucket name"
|
||||
"--bucket", default=os.environ.get("R2_BUCKET"), help="R2 Bucket name"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID"
|
||||
"--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--secret", default=os.environ.get("R2_CLIENT_SECRET"), help="R2 Client Secret"
|
||||
"--secret", default=os.environ.get("R2_CLIENT_SECRET"), help="R2 Client Secret"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--account", default=os.environ.get("R2_ACCOUNT_ID"), help="R2 Account Tag"
|
||||
"--account", default=os.environ.get("R2_ACCOUNT_ID"), help="R2 Account Tag"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\
|
||||
"--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\
|
||||
prefixed with. Leave empty if you don't want tagged release versions backed up to R2."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--binary", default=os.environ.get("BINARY_NAME"), help="The name of the binary the packages are for"
|
||||
"--binary", default=os.environ.get("BINARY_NAME"), help="The name of the binary the packages are for"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--gpg-private-key", default=os.environ.get("LINUX_SIGNING_PRIVATE_KEY"), help="GPG private key to sign the\
|
||||
"--gpg-private-key", default=os.environ.get("LINUX_SIGNING_PRIVATE_KEY"), help="GPG private key to sign the\
|
||||
packages"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--gpg-public-key", default=os.environ.get("LINUX_SIGNING_PUBLIC_KEY"), help="GPG public key used for\
|
||||
"--gpg-public-key", default=os.environ.get("LINUX_SIGNING_PUBLIC_KEY"), help="GPG public key used for\
|
||||
signing packages"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic"],
|
||||
help="list of debian based releases that need to be packaged for"
|
||||
"--gpg-public-key-url", default=os.environ.get("GPG_PUBLIC_KEY_URL"), help="GPG public key url that\
|
||||
downloaders can use to verify signing"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--archs", default=["amd64", "386", "arm64"], help="list of architectures we want to package for. Note that\
|
||||
"--pkg-upload-url", default=os.environ.get("PKG_URL"), help="URL to be used by downloaders"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic",
|
||||
"xenial", "trusty"],
|
||||
help="list of debian based releases that need to be packaged for"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--archs", default=["amd64", "386", "arm64", "arm"], help="list of architectures we want to package for. Note that\
|
||||
it is the caller's responsiblity to ensure that these debs are already present in a directory. This script\
|
||||
will not build binaries or create their debs."
|
||||
)
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
args = parse_args()
|
||||
@@ -322,6 +377,15 @@ if __name__ == "__main__":
|
||||
pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret)
|
||||
print(f"signing with gpg_key: {gpg_key_id}")
|
||||
create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs,
|
||||
"main", args.release_tag)
|
||||
|
||||
create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary, gpg_key_name)
|
||||
"main", args.release_tag)
|
||||
|
||||
create_rpm_packaging(
|
||||
pkg_creator,
|
||||
pkg_uploader,
|
||||
"./built_artifacts",
|
||||
args.release_tag,
|
||||
args.binary,
|
||||
gpg_key_name,
|
||||
args.gpg_public_key_url,
|
||||
args.pkg_upload_url,
|
||||
)
|
||||
|
||||
@@ -63,7 +63,6 @@ var errEarlyShutdown = errors.New("shutdown started")
|
||||
|
||||
type tunnelError struct {
|
||||
index int
|
||||
addr *allregions.EdgeAddr
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -235,7 +234,7 @@ func (s *Supervisor) startFirstTunnel(
|
||||
)
|
||||
const firstConnIndex = 0
|
||||
defer func() {
|
||||
s.tunnelErrors <- tunnelError{index: firstConnIndex, addr: addr, err: err}
|
||||
s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err}
|
||||
}()
|
||||
|
||||
addr, err = s.edgeIPs.GetAddr(firstConnIndex)
|
||||
@@ -306,7 +305,7 @@ func (s *Supervisor) startTunnel(
|
||||
err error
|
||||
)
|
||||
defer func() {
|
||||
s.tunnelErrors <- tunnelError{index: index, addr: addr, err: err}
|
||||
s.tunnelErrors <- tunnelError{index: index, err: err}
|
||||
}()
|
||||
|
||||
addr, err = s.edgeIPs.GetDifferentAddr(index)
|
||||
|
||||
@@ -43,6 +43,7 @@ type TunnelConfig struct {
|
||||
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
|
||||
EdgeAddrs []string
|
||||
Region string
|
||||
EdgeIPVersion allregions.ConfigIPVersion
|
||||
HAConnections int
|
||||
IncidentLookup IncidentLookup
|
||||
IsAutoupdated bool
|
||||
@@ -549,9 +550,9 @@ func ServeQUIC(
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
||||
KeepAlivePeriod: quicpogs.MaxIdlePingPeriod,
|
||||
MaxIncomingStreams: connection.MaxConcurrentStreams,
|
||||
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
|
||||
KeepAlive: true,
|
||||
EnableDatagrams: true,
|
||||
MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize,
|
||||
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
vendor
|
||||
*.out
|
||||
*.log
|
||||
*.test
|
||||
.vscode
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
|
||||
script:
|
||||
- go get github.com/golang/dep/cmd/dep github.com/stretchr/testify
|
||||
- dep ensure -v -vendor-only
|
||||
- go test ./gojay/codegen/test/... -race
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1a37f9f2ae10d161d9688fb6008ffa14e1631e5068cc3e9698008b9e8d40d575"
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
pruneopts = ""
|
||||
revision = "457ea5c15ccf3b87db582c450e80101989da35f7"
|
||||
version = "v0.40.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a"
|
||||
name = "github.com/go-errors/errors"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "a6af135bd4e28680facf08a3d206b454abc877a4"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
pruneopts = ""
|
||||
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:cae59d7b8243c671c9f544965522ba35c0fec48ee80adb9f1400cd2f33abbbec"
|
||||
name = "github.com/mailru/easyjson"
|
||||
packages = [
|
||||
".",
|
||||
"buffer",
|
||||
"jlexer",
|
||||
"jwriter",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "1ea4449da9834f4d333f1cc461c374aea217d249"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
|
||||
version = "v0.8.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8d4bbd8ab012efc77ab6b97286f2aff262bcdeac9803bb57d75cf7d0a5e6a877"
|
||||
name = "github.com/viant/assertly"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "04f45e0aeb6f3455884877b047a97bcc95dc9493"
|
||||
version = "v0.4.8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5913451bc2d274673c0716efe226a137625740cd9380641f4d8300ff4f2d82a0"
|
||||
name = "github.com/viant/toolbox"
|
||||
packages = [
|
||||
".",
|
||||
"cred",
|
||||
"data",
|
||||
"storage",
|
||||
"url",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "1be8e4d172138324f40d55ea61a2aeab0c5ce864"
|
||||
version = "v0.24.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:9d150270ca2c3356f2224a0878daa1652e4d0b25b345f18b4f6e156cc4b8ec5e"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"blowfish",
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"internal/chacha20",
|
||||
"internal/subtle",
|
||||
"poly1305",
|
||||
"ssh",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f99c8df09eb5bff426315721bfa5f16a99cad32c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:5a56f211e7c12a65c5585c629457a2fb91d8719844ee8fab92727ea8adb5721c"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"context/ctxhttp",
|
||||
"websocket",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "461777fb6f67e8cb9d70cda16573678d085a74cf"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:01bdbbc604dcd5afb6f66a717f69ad45e9643c72d5bc11678d44ffa5c50f9e42"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [
|
||||
".",
|
||||
"google",
|
||||
"internal",
|
||||
"jws",
|
||||
"jwt",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8ddb956f67d4c176abbbc42b7514aaeaf9ea30daa24e27d2cf30ad82f9334a2c"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["cpu"]
|
||||
pruneopts = ""
|
||||
revision = "1e42afee0f762ed3d76e6dd942e4181855fd1849"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a"
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [
|
||||
".",
|
||||
"internal",
|
||||
"internal/app_identity",
|
||||
"internal/base",
|
||||
"internal/datastore",
|
||||
"internal/log",
|
||||
"internal/modules",
|
||||
"internal/remote_api",
|
||||
"internal/urlfetch",
|
||||
"urlfetch",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880"
|
||||
version = "v1.6.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
|
||||
version = "v2.2.2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/go-errors/errors",
|
||||
"github.com/mailru/easyjson",
|
||||
"github.com/mailru/easyjson/jlexer",
|
||||
"github.com/mailru/easyjson/jwriter",
|
||||
"github.com/viant/assertly",
|
||||
"github.com/viant/toolbox",
|
||||
"github.com/viant/toolbox/url",
|
||||
"golang.org/x/net/websocket",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
ignored = ["github.com/francoispqt/benchmarks*","github.com/stretchr/testify*","github.com/stretchr/testify","github.com/json-iterator/go","github.com/buger/jsonparser"]
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 gojay
|
||||
|
||||
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.
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race -run=^Test -v
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=coverage.out -covermode=atomic
|
||||
|
||||
.PHONY: coverhtml
|
||||
coverhtml:
|
||||
go tool cover -html=coverage.out
|
||||
-855
@@ -1,855 +0,0 @@
|
||||
[](https://travis-ci.org/francoispqt/gojay)
|
||||
[](https://codecov.io/gh/francoispqt/gojay)
|
||||
[](https://goreportcard.com/report/github.com/francoispqt/gojay)
|
||||
[](https://godoc.org/github.com/francoispqt/gojay)
|
||||

|
||||
[](https://sourcegraph.com/github.com/francoispqt/gojay)
|
||||

|
||||
|
||||
# GoJay
|
||||
|
||||
<img src="https://github.com/francoispqt/gojay/raw/master/gojay.png" width="200px">
|
||||
|
||||
GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).
|
||||
|
||||
It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.
|
||||
|
||||
Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API.
|
||||
|
||||
There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster.
|
||||
|
||||
# Why another JSON parser?
|
||||
|
||||
I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end.
|
||||
|
||||
Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer.
|
||||
|
||||
This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API.
|
||||
|
||||
# Get started
|
||||
|
||||
```bash
|
||||
go get github.com/francoispqt/gojay
|
||||
```
|
||||
|
||||
* [Encoder](#encoding)
|
||||
* [Decoder](#decoding)
|
||||
* [Stream API](#stream-api)
|
||||
* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay)
|
||||
|
||||
## Decoding
|
||||
|
||||
Decoding is done through two different API similar to standard `encoding/json`:
|
||||
* [Unmarshal](#unmarshal-api)
|
||||
* [Decode](#decode-api)
|
||||
|
||||
|
||||
Example of basic stucture decoding with Unmarshal:
|
||||
```go
|
||||
import "github.com/francoispqt/gojay"
|
||||
|
||||
type user struct {
|
||||
id int
|
||||
name string
|
||||
email string
|
||||
}
|
||||
// implement gojay.UnmarshalerJSONObject
|
||||
func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
|
||||
switch key {
|
||||
case "id":
|
||||
return dec.Int(&u.id)
|
||||
case "name":
|
||||
return dec.String(&u.name)
|
||||
case "email":
|
||||
return dec.String(&u.email)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *user) NKeys() int {
|
||||
return 3
|
||||
}
|
||||
|
||||
func main() {
|
||||
u := &user{}
|
||||
d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`)
|
||||
err := gojay.UnmarshalJSONObject(d, u)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
with Decode:
|
||||
```go
|
||||
func main() {
|
||||
u := &user{}
|
||||
dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`)))
|
||||
err := dec.DecodeObject(d, u)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Unmarshal API
|
||||
|
||||
Unmarshal API decodes a `[]byte` to a given pointer with a single function.
|
||||
|
||||
Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not.
|
||||
|
||||
If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`.
|
||||
|
||||
Unmarshal API comes with three functions:
|
||||
* Unmarshal
|
||||
```go
|
||||
func Unmarshal(data []byte, v interface{}) error
|
||||
```
|
||||
|
||||
* UnmarshalJSONObject
|
||||
```go
|
||||
func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error
|
||||
```
|
||||
|
||||
* UnmarshalJSONArray
|
||||
```go
|
||||
func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error
|
||||
```
|
||||
|
||||
|
||||
### Decode API
|
||||
|
||||
Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods.
|
||||
|
||||
__Getting a *gojay.Decoder or Borrowing__
|
||||
|
||||
You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`.
|
||||
|
||||
After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using.
|
||||
|
||||
Example getting a fresh an releasing:
|
||||
```go
|
||||
str := ""
|
||||
dec := gojay.NewDecoder(strings.NewReader(`"test"`))
|
||||
defer dec.Release()
|
||||
if err := dec.Decode(&str); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
Example borrowing a decoder and releasing:
|
||||
```go
|
||||
str := ""
|
||||
dec := gojay.BorrowDecoder(strings.NewReader(`"test"`))
|
||||
defer dec.Release()
|
||||
if err := dec.Decode(&str); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
`*gojay.Decoder` has multiple methods to decode to specific types:
|
||||
* Decode
|
||||
```go
|
||||
func (dec *gojay.Decoder) Decode(v interface{}) error
|
||||
```
|
||||
* DecodeObject
|
||||
```go
|
||||
func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error
|
||||
```
|
||||
* DecodeArray
|
||||
```go
|
||||
func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error
|
||||
```
|
||||
* DecodeInt
|
||||
```go
|
||||
func (dec *gojay.Decoder) DecodeInt(v *int) error
|
||||
```
|
||||
* DecodeBool
|
||||
```go
|
||||
func (dec *gojay.Decoder) DecodeBool(v *bool) error
|
||||
```
|
||||
* DecodeString
|
||||
```go
|
||||
func (dec *gojay.Decoder) DecodeString(v *string) error
|
||||
```
|
||||
|
||||
All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods.
|
||||
|
||||
Example:
|
||||
```go
|
||||
reader := strings.NewReader(`"John Doe"`)
|
||||
dec := NewDecoder(reader)
|
||||
|
||||
var str string
|
||||
err := dec.DecodeString(&str)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(str) // John Doe
|
||||
```
|
||||
|
||||
### Structs and Maps
|
||||
#### UnmarshalerJSONObject Interface
|
||||
|
||||
To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface:
|
||||
```go
|
||||
type UnmarshalerJSONObject interface {
|
||||
UnmarshalJSONObject(*gojay.Decoder, string) error
|
||||
NKeys() int
|
||||
}
|
||||
```
|
||||
`UnmarshalJSONObject` method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalJSONObject method will never be called.
|
||||
|
||||
`NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed.
|
||||
|
||||
Example of implementation for a struct:
|
||||
```go
|
||||
type user struct {
|
||||
id int
|
||||
name string
|
||||
email string
|
||||
}
|
||||
// implement UnmarshalerJSONObject
|
||||
func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
|
||||
switch key {
|
||||
case "id":
|
||||
return dec.Int(&u.id)
|
||||
case "name":
|
||||
return dec.String(&u.name)
|
||||
case "email":
|
||||
return dec.String(&u.email)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *user) NKeys() int {
|
||||
return 3
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation for a `map[string]string`:
|
||||
```go
|
||||
// define our custom map type implementing UnmarshalerJSONObject
|
||||
type message map[string]string
|
||||
|
||||
// Implementing Unmarshaler
|
||||
func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
|
||||
str := ""
|
||||
err := dec.String(&str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m[k] = str
|
||||
return nil
|
||||
}
|
||||
|
||||
// we return 0, it tells the Decoder to decode all keys
|
||||
func (m message) NKeys() int {
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
### Arrays, Slices and Channels
|
||||
|
||||
To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface:
|
||||
```go
|
||||
type UnmarshalerJSONArray interface {
|
||||
UnmarshalJSONArray(*gojay.Decoder) error
|
||||
}
|
||||
```
|
||||
UnmarshalJSONArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called.
|
||||
|
||||
Example of implementation with a slice:
|
||||
```go
|
||||
type testSlice []string
|
||||
// implement UnmarshalerJSONArray
|
||||
func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
|
||||
str := ""
|
||||
if err := dec.String(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
*t = append(*t, str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`))
|
||||
var slice testSlice
|
||||
err := dec.DecodeArray(&slice)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(slice) // [Tom Jim]
|
||||
dec.Release()
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation with a channel:
|
||||
```go
|
||||
type testChannel chan string
|
||||
// implement UnmarshalerJSONArray
|
||||
func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error {
|
||||
str := ""
|
||||
if err := dec.String(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
c <- str
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`))
|
||||
c := make(testChannel, 2)
|
||||
err := dec.DecodeArray(c)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
fmt.Println(<-c)
|
||||
}
|
||||
close(c)
|
||||
dec.Release()
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation with an array:
|
||||
```go
|
||||
type testArray [3]string
|
||||
// implement UnmarshalerJSONArray
|
||||
func (a *testArray) UnmarshalJSONArray(dec *Decoder) error {
|
||||
var str string
|
||||
if err := dec.String(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
a[dec.Index()] = str
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`))
|
||||
var a testArray
|
||||
err := dec.DecodeArray(&a)
|
||||
fmt.Println(a) // [Tom Jim Bob]
|
||||
dec.Release()
|
||||
}
|
||||
```
|
||||
|
||||
### Other types
|
||||
To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface.
|
||||
|
||||
Example of encoding strings:
|
||||
```go
|
||||
func main() {
|
||||
json := []byte(`"Jay"`)
|
||||
var v string
|
||||
err := gojay.Unmarshal(json, &v)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(v) // Jay
|
||||
}
|
||||
```
|
||||
|
||||
### Decode values methods
|
||||
When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types.
|
||||
|
||||
Non exhaustive list of methods available (to see all methods, check the godoc):
|
||||
```go
|
||||
dec.Int
|
||||
dec.Int8
|
||||
dec.Int16
|
||||
dec.Int32
|
||||
dec.Int64
|
||||
dec.Uint8
|
||||
dec.Uint16
|
||||
dec.Uint32
|
||||
dec.Uint64
|
||||
dec.String
|
||||
dec.Time
|
||||
dec.Bool
|
||||
dec.SQLNullString
|
||||
dec.SQLNullInt64
|
||||
```
|
||||
|
||||
|
||||
## Encoding
|
||||
|
||||
Encoding is done through two different API similar to standard `encoding/json`:
|
||||
* [Marshal](#marshal-api)
|
||||
* [Encode](#encode-api)
|
||||
|
||||
Example of basic structure encoding with Marshal:
|
||||
```go
|
||||
import "github.com/francoispqt/gojay"
|
||||
|
||||
type user struct {
|
||||
id int
|
||||
name string
|
||||
email string
|
||||
}
|
||||
|
||||
// implement MarshalerJSONObject
|
||||
func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.IntKey("id", u.id)
|
||||
enc.StringKey("name", u.name)
|
||||
enc.StringKey("email", u.email)
|
||||
}
|
||||
func (u *user) IsNil() bool {
|
||||
return u == nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
u := &user{1, "gojay", "gojay@email.com"}
|
||||
b, err := gojay.MarshalJSONObject(u)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"}
|
||||
}
|
||||
```
|
||||
|
||||
with Encode:
|
||||
```go
|
||||
func main() {
|
||||
u := &user{1, "gojay", "gojay@email.com"}
|
||||
b := strings.Builder{}
|
||||
enc := gojay.NewEncoder(&b)
|
||||
if err := enc.Encode(u); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"}
|
||||
}
|
||||
```
|
||||
|
||||
### Marshal API
|
||||
|
||||
Marshal API encodes a value to a JSON `[]byte` with a single function.
|
||||
|
||||
Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not.
|
||||
|
||||
If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`.
|
||||
|
||||
Marshal API comes with three functions:
|
||||
* Marshal
|
||||
```go
|
||||
func Marshal(v interface{}) ([]byte, error)
|
||||
```
|
||||
|
||||
* MarshalJSONObject
|
||||
```go
|
||||
func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error)
|
||||
```
|
||||
|
||||
* MarshalJSONArray
|
||||
```go
|
||||
func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error)
|
||||
```
|
||||
|
||||
### Encode API
|
||||
|
||||
Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods.
|
||||
|
||||
__Getting a *gojay.Encoder or Borrowing__
|
||||
|
||||
You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`.
|
||||
|
||||
After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using.
|
||||
|
||||
Example getting a fresh encoder an releasing:
|
||||
```go
|
||||
str := "test"
|
||||
b := strings.Builder{}
|
||||
enc := gojay.NewEncoder(&b)
|
||||
defer enc.Release()
|
||||
if err := enc.Encode(str); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
Example borrowing an encoder and releasing:
|
||||
```go
|
||||
str := "test"
|
||||
b := strings.Builder{}
|
||||
enc := gojay.BorrowEncoder(b)
|
||||
defer enc.Release()
|
||||
if err := enc.Encode(str); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
`*gojay.Encoder` has multiple methods to encoder specific types to JSON:
|
||||
* Encode
|
||||
```go
|
||||
func (enc *gojay.Encoder) Encode(v interface{}) error
|
||||
```
|
||||
* EncodeObject
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error
|
||||
```
|
||||
* EncodeArray
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error
|
||||
```
|
||||
* EncodeInt
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeInt(n int) error
|
||||
```
|
||||
* EncodeInt64
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeInt64(n int64) error
|
||||
```
|
||||
* EncodeFloat
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeFloat(n float64) error
|
||||
```
|
||||
* EncodeBool
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeBool(v bool) error
|
||||
```
|
||||
* EncodeString
|
||||
```go
|
||||
func (enc *gojay.Encoder) EncodeString(s string) error
|
||||
```
|
||||
|
||||
### Structs and Maps
|
||||
|
||||
To encode a structure, the structure must implement the MarshalerJSONObject interface:
|
||||
```go
|
||||
type MarshalerJSONObject interface {
|
||||
MarshalJSONObject(enc *gojay.Encoder)
|
||||
IsNil() bool
|
||||
}
|
||||
```
|
||||
`MarshalJSONObject` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods.
|
||||
|
||||
IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection.
|
||||
|
||||
Example of implementation for a struct:
|
||||
```go
|
||||
type user struct {
|
||||
id int
|
||||
name string
|
||||
email string
|
||||
}
|
||||
|
||||
// implement MarshalerJSONObject
|
||||
func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.IntKey("id", u.id)
|
||||
enc.StringKey("name", u.name)
|
||||
enc.StringKey("email", u.email)
|
||||
}
|
||||
func (u *user) IsNil() bool {
|
||||
return u == nil
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation for a `map[string]string`:
|
||||
```go
|
||||
// define our custom map type implementing MarshalerJSONObject
|
||||
type message map[string]string
|
||||
|
||||
// Implementing Marshaler
|
||||
func (m message) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
for k, v := range m {
|
||||
enc.StringKey(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (m message) IsNil() bool {
|
||||
return m == nil
|
||||
}
|
||||
```
|
||||
|
||||
### Arrays and Slices
|
||||
To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface:
|
||||
```go
|
||||
type MarshalerJSONArray interface {
|
||||
MarshalJSONArray(enc *gojay.Encoder)
|
||||
IsNil() bool
|
||||
}
|
||||
```
|
||||
`MarshalJSONArray` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods.
|
||||
|
||||
`IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature.
|
||||
|
||||
Example of implementation:
|
||||
```go
|
||||
type users []*user
|
||||
// implement MarshalerJSONArray
|
||||
func (u *users) MarshalJSONArray(enc *gojay.Encoder) {
|
||||
for _, e := range u {
|
||||
enc.Object(e)
|
||||
}
|
||||
}
|
||||
func (u *users) IsNil() bool {
|
||||
return len(u) == 0
|
||||
}
|
||||
```
|
||||
|
||||
### Other types
|
||||
To encode other types (string, int, float, booleans), you don't need to implement any interface.
|
||||
|
||||
Example of encoding strings:
|
||||
```go
|
||||
func main() {
|
||||
name := "Jay"
|
||||
b, err := gojay.Marshal(name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(b)) // "Jay"
|
||||
}
|
||||
```
|
||||
|
||||
# Stream API
|
||||
|
||||
### Stream Decoding
|
||||
GoJay ships with a powerful stream decoder.
|
||||
|
||||
It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming.
|
||||
|
||||
When using the Stream API, the Decoder implements context.Context to provide graceful cancellation.
|
||||
|
||||
To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation.
|
||||
|
||||
```go
|
||||
type UnmarshalerStream interface {
|
||||
UnmarshalStream(*StreamDecoder) error
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation of stream reading from a WebSocket connection:
|
||||
```go
|
||||
// implement UnmarshalerStream
|
||||
type ChannelStream chan *user
|
||||
|
||||
func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error {
|
||||
u := &user{}
|
||||
if err := dec.Object(u); err != nil {
|
||||
return err
|
||||
}
|
||||
c <- u
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// get our websocket connection
|
||||
origin := "http://localhost/"
|
||||
url := "ws://localhost:12345/ws"
|
||||
ws, err := websocket.Dial(url, "", origin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// create our channel which will receive our objects
|
||||
streamChan := ChannelStream(make(chan *user))
|
||||
// borrow a decoder
|
||||
dec := gojay.Stream.BorrowDecoder(ws)
|
||||
// start decoding, it will block until a JSON message is decoded from the WebSocket
|
||||
// or until Done channel is closed
|
||||
go dec.DecodeStream(streamChan)
|
||||
for {
|
||||
select {
|
||||
case v := <-streamChan:
|
||||
// Got something from my websocket!
|
||||
log.Println(v)
|
||||
case <-dec.Done():
|
||||
log.Println("finished reading from WebSocket")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Stream Encoding
|
||||
GoJay ships with a powerful stream encoder part of the Stream API.
|
||||
|
||||
It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine.
|
||||
|
||||
When using the Stream API, the Encoder implements context.Context to provide graceful cancellation.
|
||||
|
||||
To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation.
|
||||
|
||||
```go
|
||||
type MarshalerStream interface {
|
||||
MarshalStream(enc *gojay.StreamEncoder)
|
||||
}
|
||||
```
|
||||
|
||||
Example of implementation of stream writing to a WebSocket:
|
||||
```go
|
||||
// Our structure which will be pushed to our stream
|
||||
type user struct {
|
||||
id int
|
||||
name string
|
||||
email string
|
||||
}
|
||||
|
||||
func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.IntKey("id", u.id)
|
||||
enc.StringKey("name", u.name)
|
||||
enc.StringKey("email", u.email)
|
||||
}
|
||||
func (u *user) IsNil() bool {
|
||||
return u == nil
|
||||
}
|
||||
|
||||
// Our MarshalerStream implementation
|
||||
type StreamChan chan *user
|
||||
|
||||
func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) {
|
||||
select {
|
||||
case <-enc.Done():
|
||||
return
|
||||
case o := <-s:
|
||||
enc.Object(o)
|
||||
}
|
||||
}
|
||||
|
||||
// Our main function
|
||||
func main() {
|
||||
// get our websocket connection
|
||||
origin := "http://localhost/"
|
||||
url := "ws://localhost:12345/ws"
|
||||
ws, err := websocket.Dial(url, "", origin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// we borrow an encoder set stdout as the writer,
|
||||
// set the number of consumer to 10
|
||||
// and tell the encoder to separate each encoded element
|
||||
// added to the channel by a new line character
|
||||
enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited()
|
||||
// instantiate our MarshalerStream
|
||||
s := StreamChan(make(chan *user))
|
||||
// start the stream encoder
|
||||
// will block its goroutine until enc.Cancel(error) is called
|
||||
// or until something is written to the channel
|
||||
go enc.EncodeStream(s)
|
||||
// write to our MarshalerStream
|
||||
for i := 0; i < 1000; i++ {
|
||||
s <- &user{i, "username", "user@email.com"}
|
||||
}
|
||||
// Wait
|
||||
<-enc.Done()
|
||||
}
|
||||
```
|
||||
|
||||
# Unsafe API
|
||||
|
||||
Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON.
|
||||
|
||||
If you are not sure if your JSON is valid, don't use the Unsafe API.
|
||||
|
||||
Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :)
|
||||
|
||||
Access the `Unsafe` API this way:
|
||||
```go
|
||||
gojay.Unsafe.Unmarshal(b, v)
|
||||
```
|
||||
|
||||
|
||||
# Benchmarks
|
||||
|
||||
Benchmarks encode and decode three different data based on size (small, medium, large).
|
||||
|
||||
To run benchmark for decoder:
|
||||
```bash
|
||||
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench
|
||||
```
|
||||
|
||||
To run benchmark for encoder:
|
||||
```bash
|
||||
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench
|
||||
```
|
||||
|
||||
# Benchmark Results
|
||||
## Decode
|
||||
|
||||
<img src="https://images2.imgbox.com/78/01/49OExcPh_o.png" width="500px">
|
||||
|
||||
### Small Payload
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|-----------------|-----------|--------------|-----------|
|
||||
| Std Library | 2547 | 496 | 4 |
|
||||
| JsonIter | 2046 | 312 | 12 |
|
||||
| JsonParser | 1408 | 0 | 0 |
|
||||
| EasyJson | 929 | 240 | 2 |
|
||||
| **GoJay** | **807** | **256** | **2** |
|
||||
| **GoJay-unsafe**| **712** | **112** | **1** |
|
||||
|
||||
### Medium Payload
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|-----------------|-----------|----------|-----------|
|
||||
| Std Library | 30148 | 2152 | 496 |
|
||||
| JsonIter | 16309 | 2976 | 80 |
|
||||
| JsonParser | 7793 | 0 | 0 |
|
||||
| EasyJson | 7957 | 232 | 6 |
|
||||
| **GoJay** | **4984** | **2448** | **8** |
|
||||
| **GoJay-unsafe**| **4809** | **144** | **7** |
|
||||
|
||||
### Large Payload
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|-----------------|-----------|-------------|-----------|
|
||||
| JsonIter | 210078 | 41712 | 1136 |
|
||||
| EasyJson | 106626 | 160 | 2 |
|
||||
| JsonParser | 66813 | 0 | 0 |
|
||||
| **GoJay** | **52153** | **31241** | **77** |
|
||||
| **GoJay-unsafe**| **48277** | **2561** | **76** |
|
||||
|
||||
## Encode
|
||||
|
||||
<img src="https://images2.imgbox.com/e9/cc/pnM8c7Gf_o.png" width="500px">
|
||||
|
||||
### Small Struct
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|----------------|----------|--------------|-----------|
|
||||
| Std Library | 1280 | 464 | 3 |
|
||||
| EasyJson | 871 | 944 | 6 |
|
||||
| JsonIter | 866 | 272 | 3 |
|
||||
| **GoJay** | **543** | **112** | **1** |
|
||||
| **GoJay-func** | **347** | **0** | **0** |
|
||||
|
||||
### Medium Struct
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|-------------|----------|--------------|-----------|
|
||||
| Std Library | 5006 | 1496 | 25 |
|
||||
| JsonIter | 2232 | 1544 | 20 |
|
||||
| EasyJson | 1997 | 1544 | 19 |
|
||||
| **GoJay** | **1522** | **312** | **14** |
|
||||
|
||||
### Large Struct
|
||||
[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go)
|
||||
|
||||
[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)
|
||||
|
||||
| | ns/op | bytes/op | allocs/op |
|
||||
|-------------|-----------|--------------|-----------|
|
||||
| Std Library | 66441 | 20576 | 332 |
|
||||
| JsonIter | 35247 | 20255 | 328 |
|
||||
| EasyJson | 32053 | 15474 | 327 |
|
||||
| **GoJay** | **27847** | **9802** | **318** |
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome :)
|
||||
|
||||
If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja)
|
||||
|
||||
-386
@@ -1,386 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||
//
|
||||
// v must implement UnmarshalerJSONArray.
|
||||
//
|
||||
// If a JSON value is not appropriate for a given target type, or if a JSON number
|
||||
// overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can.
|
||||
func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
|
||||
dec := borrowDecoder(nil, 0)
|
||||
defer dec.Release()
|
||||
dec.data = make([]byte, len(data))
|
||||
copy(dec.data, data)
|
||||
dec.length = len(data)
|
||||
_, err := dec.decodeArray(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.err != nil {
|
||||
return dec.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSONObject parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||
//
|
||||
// v must implement UnmarshalerJSONObject.
|
||||
//
|
||||
// If a JSON value is not appropriate for a given target type, or if a JSON number
|
||||
// overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can.
|
||||
func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error {
|
||||
dec := borrowDecoder(nil, 0)
|
||||
defer dec.Release()
|
||||
dec.data = make([]byte, len(data))
|
||||
copy(dec.data, data)
|
||||
dec.length = len(data)
|
||||
_, err := dec.decodeObject(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.err != nil {
|
||||
return dec.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||
// If v is nil, not an implementation of UnmarshalerJSONObject or UnmarshalerJSONArray or not one of the following types:
|
||||
// *string, **string, *int, **int, *int8, **int8, *int16, **int16, *int32, **int32, *int64, **int64, *uint8, **uint8, *uint16, **uint16,
|
||||
// *uint32, **uint32, *uint64, **uint64, *float64, **float64, *float32, **float32, *bool, **bool
|
||||
// Unmarshal returns an InvalidUnmarshalError.
|
||||
//
|
||||
//
|
||||
// If a JSON value is not appropriate for a given target type, or if a JSON number
|
||||
// overflows the target type, Unmarshal skips that field and completes the unmarshaling as best it can.
|
||||
// If no more serious errors are encountered, Unmarshal returns an UnmarshalTypeError describing the earliest such error.
|
||||
// In any case, it's not guaranteed that all the remaining fields following the problematic one will be unmarshaled into the target object.
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
var err error
|
||||
var dec *Decoder
|
||||
switch vt := v.(type) {
|
||||
case *string:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeString(vt)
|
||||
case **string:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeStringNull(vt)
|
||||
case *int:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt(vt)
|
||||
case **int:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeIntNull(vt)
|
||||
case *int8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt8(vt)
|
||||
case **int8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt8Null(vt)
|
||||
case *int16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt16(vt)
|
||||
case **int16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt16Null(vt)
|
||||
case *int32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt32(vt)
|
||||
case **int32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt32Null(vt)
|
||||
case *int64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt64(vt)
|
||||
case **int64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt64Null(vt)
|
||||
case *uint8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint8(vt)
|
||||
case **uint8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint8Null(vt)
|
||||
case *uint16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint16(vt)
|
||||
case **uint16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint16Null(vt)
|
||||
case *uint32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint32(vt)
|
||||
case **uint32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint32Null(vt)
|
||||
case *uint64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint64(vt)
|
||||
case **uint64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint64Null(vt)
|
||||
case *float64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat64(vt)
|
||||
case **float64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat64Null(vt)
|
||||
case *float32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat32(vt)
|
||||
case **float32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat32Null(vt)
|
||||
case *bool:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeBool(vt)
|
||||
case **bool:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeBoolNull(vt)
|
||||
case UnmarshalerJSONObject:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = make([]byte, len(data))
|
||||
copy(dec.data, data)
|
||||
_, err = dec.decodeObject(vt)
|
||||
case UnmarshalerJSONArray:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = make([]byte, len(data))
|
||||
copy(dec.data, data)
|
||||
_, err = dec.decodeArray(vt)
|
||||
case *interface{}:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = make([]byte, len(data))
|
||||
copy(dec.data, data)
|
||||
err = dec.decodeInterface(vt)
|
||||
default:
|
||||
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
|
||||
}
|
||||
defer dec.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dec.err
|
||||
}
|
||||
|
||||
// UnmarshalerJSONObject is the interface to implement to decode a JSON Object.
|
||||
type UnmarshalerJSONObject interface {
|
||||
UnmarshalJSONObject(*Decoder, string) error
|
||||
NKeys() int
|
||||
}
|
||||
|
||||
// UnmarshalerJSONArray is the interface to implement to decode a JSON Array.
|
||||
type UnmarshalerJSONArray interface {
|
||||
UnmarshalJSONArray(*Decoder) error
|
||||
}
|
||||
|
||||
// A Decoder reads and decodes JSON values from an input stream.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
data []byte
|
||||
err error
|
||||
isPooled byte
|
||||
called byte
|
||||
child byte
|
||||
cursor int
|
||||
length int
|
||||
keysDone int
|
||||
arrayIndex int
|
||||
}
|
||||
|
||||
// Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
// The differences between Decode and Unmarshal are:
|
||||
// - Decode reads from an io.Reader in the Decoder, whereas Unmarshal reads from a []byte
|
||||
// - Decode leaves to the user the option of borrowing and releasing a Decoder, whereas Unmarshal internally always borrows a Decoder and releases it when the unmarshaling is completed
|
||||
func (dec *Decoder) Decode(v interface{}) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
var err error
|
||||
switch vt := v.(type) {
|
||||
case *string:
|
||||
err = dec.decodeString(vt)
|
||||
case **string:
|
||||
err = dec.decodeStringNull(vt)
|
||||
case *int:
|
||||
err = dec.decodeInt(vt)
|
||||
case **int:
|
||||
err = dec.decodeIntNull(vt)
|
||||
case *int8:
|
||||
err = dec.decodeInt8(vt)
|
||||
case **int8:
|
||||
err = dec.decodeInt8Null(vt)
|
||||
case *int16:
|
||||
err = dec.decodeInt16(vt)
|
||||
case **int16:
|
||||
err = dec.decodeInt16Null(vt)
|
||||
case *int32:
|
||||
err = dec.decodeInt32(vt)
|
||||
case **int32:
|
||||
err = dec.decodeInt32Null(vt)
|
||||
case *int64:
|
||||
err = dec.decodeInt64(vt)
|
||||
case **int64:
|
||||
err = dec.decodeInt64Null(vt)
|
||||
case *uint8:
|
||||
err = dec.decodeUint8(vt)
|
||||
case **uint8:
|
||||
err = dec.decodeUint8Null(vt)
|
||||
case *uint16:
|
||||
err = dec.decodeUint16(vt)
|
||||
case **uint16:
|
||||
err = dec.decodeUint16Null(vt)
|
||||
case *uint32:
|
||||
err = dec.decodeUint32(vt)
|
||||
case **uint32:
|
||||
err = dec.decodeUint32Null(vt)
|
||||
case *uint64:
|
||||
err = dec.decodeUint64(vt)
|
||||
case **uint64:
|
||||
err = dec.decodeUint64Null(vt)
|
||||
case *float64:
|
||||
err = dec.decodeFloat64(vt)
|
||||
case **float64:
|
||||
err = dec.decodeFloat64Null(vt)
|
||||
case *float32:
|
||||
err = dec.decodeFloat32(vt)
|
||||
case **float32:
|
||||
err = dec.decodeFloat32Null(vt)
|
||||
case *bool:
|
||||
err = dec.decodeBool(vt)
|
||||
case **bool:
|
||||
err = dec.decodeBoolNull(vt)
|
||||
case UnmarshalerJSONObject:
|
||||
_, err = dec.decodeObject(vt)
|
||||
case UnmarshalerJSONArray:
|
||||
_, err = dec.decodeArray(vt)
|
||||
case *EmbeddedJSON:
|
||||
err = dec.decodeEmbeddedJSON(vt)
|
||||
case *interface{}:
|
||||
err = dec.decodeInterface(vt)
|
||||
default:
|
||||
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dec.err
|
||||
}
|
||||
|
||||
// Non exported
|
||||
|
||||
func isDigit(b byte) bool {
|
||||
switch b {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) read() bool {
|
||||
if dec.r != nil {
|
||||
// if we reach the end, double the buffer to ensure there's always more space
|
||||
if len(dec.data) == dec.length {
|
||||
nLen := dec.length * 2
|
||||
if nLen == 0 {
|
||||
nLen = 512
|
||||
}
|
||||
Buf := make([]byte, nLen, nLen)
|
||||
copy(Buf, dec.data)
|
||||
dec.data = Buf
|
||||
}
|
||||
var n int
|
||||
var err error
|
||||
for n == 0 {
|
||||
n, err = dec.r.Read(dec.data[dec.length:])
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
dec.err = err
|
||||
return false
|
||||
}
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
dec.length = dec.length + n
|
||||
return true
|
||||
}
|
||||
}
|
||||
dec.length = dec.length + n
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dec *Decoder) nextChar() byte {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
}
|
||||
d := dec.data[dec.cursor]
|
||||
return d
|
||||
}
|
||||
return 0
|
||||
}
|
||||
-247
@@ -1,247 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "reflect"
|
||||
|
||||
// DecodeArray reads the next JSON-encoded value from the decoder's input (io.Reader)
|
||||
// and stores it in the value pointed to by v.
|
||||
//
|
||||
// v must implement UnmarshalerJSONArray.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
_, err := dec.decodeArray(v)
|
||||
return err
|
||||
}
|
||||
func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
|
||||
// remember last array index in case of nested arrays
|
||||
lastArrayIndex := dec.arrayIndex
|
||||
dec.arrayIndex = 0
|
||||
defer func() {
|
||||
dec.arrayIndex = lastArrayIndex
|
||||
}()
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '[':
|
||||
dec.cursor = dec.cursor + 1
|
||||
// array is open, char is not space start readings
|
||||
for dec.nextChar() != 0 {
|
||||
// closing array
|
||||
if dec.data[dec.cursor] == ']' {
|
||||
dec.cursor = dec.cursor + 1
|
||||
return dec.cursor, nil
|
||||
}
|
||||
// calling unmarshall function for each element of the slice
|
||||
err := arr.UnmarshalJSONArray(dec)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec.arrayIndex++
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
case 'n':
|
||||
// is null
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
// can't unmarshall to struct
|
||||
// we skip array and set Error
|
||||
dec.err = dec.makeInvalidUnmarshalErr(arr)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
default:
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
|
||||
// remember last array index in case of nested arrays
|
||||
lastArrayIndex := dec.arrayIndex
|
||||
dec.arrayIndex = 0
|
||||
defer func() {
|
||||
dec.arrayIndex = lastArrayIndex
|
||||
}()
|
||||
vv := reflect.ValueOf(v)
|
||||
vvt := vv.Type()
|
||||
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
|
||||
dec.err = ErrUnmarshalPtrExpected
|
||||
return 0, dec.err
|
||||
}
|
||||
// not an array not an error, but do not know what to do
|
||||
// do not check syntax
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '[':
|
||||
dec.cursor = dec.cursor + 1
|
||||
// create our new type
|
||||
elt := vv.Elem()
|
||||
n := reflect.New(elt.Type().Elem())
|
||||
var arr UnmarshalerJSONArray
|
||||
var ok bool
|
||||
if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok {
|
||||
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
|
||||
return 0, dec.err
|
||||
}
|
||||
// array is open, char is not space start readings
|
||||
for dec.nextChar() != 0 {
|
||||
// closing array
|
||||
if dec.data[dec.cursor] == ']' {
|
||||
elt.Set(n)
|
||||
dec.cursor = dec.cursor + 1
|
||||
return dec.cursor, nil
|
||||
}
|
||||
// calling unmarshall function for each element of the slice
|
||||
err := arr.UnmarshalJSONArray(dec)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec.arrayIndex++
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
case 'n':
|
||||
// is null
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
// can't unmarshall to struct
|
||||
// we skip array and set Error
|
||||
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
default:
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipArray() (int, error) {
|
||||
var arraysOpen = 1
|
||||
var arraysClosed = 0
|
||||
// var stringOpen byte = 0
|
||||
for j := dec.cursor; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case ']':
|
||||
arraysClosed++
|
||||
// everything is closed return
|
||||
if arraysOpen == arraysClosed {
|
||||
// add char to object data
|
||||
return j + 1, nil
|
||||
}
|
||||
case '[':
|
||||
arraysOpen++
|
||||
case '"':
|
||||
j++
|
||||
var isInEscapeSeq bool
|
||||
var isFirstQuote = true
|
||||
for ; j < dec.length || dec.read(); j++ {
|
||||
if dec.data[j] != '"' {
|
||||
continue
|
||||
}
|
||||
if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) {
|
||||
break
|
||||
} else {
|
||||
isInEscapeSeq = false
|
||||
}
|
||||
if isFirstQuote {
|
||||
isFirstQuote = false
|
||||
}
|
||||
// loop backward and count how many anti slash found
|
||||
// to see if string is effectively escaped
|
||||
ct := 0
|
||||
for i := j - 1; i > 0; i-- {
|
||||
if dec.data[i] != '\\' {
|
||||
break
|
||||
}
|
||||
ct++
|
||||
}
|
||||
// is pair number of slashes, quote is not escaped
|
||||
if ct&1 == 0 {
|
||||
break
|
||||
}
|
||||
isInEscapeSeq = true
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
// DecodeArrayFunc is a func type implementing UnmarshalerJSONArray.
|
||||
// Use it to cast a `func(*Decoder) error` to Unmarshal an array on the fly.
|
||||
|
||||
type DecodeArrayFunc func(*Decoder) error
|
||||
|
||||
// UnmarshalJSONArray implements UnmarshalerJSONArray.
|
||||
func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error {
|
||||
return f(dec)
|
||||
}
|
||||
|
||||
// IsNil implements UnmarshalerJSONArray.
|
||||
func (f DecodeArrayFunc) IsNil() bool {
|
||||
return f == nil
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddArray decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
|
||||
func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error {
|
||||
return dec.Array(v)
|
||||
}
|
||||
|
||||
// AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
|
||||
func (dec *Decoder) AddArrayNull(v interface{}) error {
|
||||
return dec.ArrayNull(v)
|
||||
}
|
||||
|
||||
// Array decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
|
||||
func (dec *Decoder) Array(v UnmarshalerJSONArray) error {
|
||||
newCursor, err := dec.decodeArray(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.cursor = newCursor
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// ArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
|
||||
// v should be a pointer to an UnmarshalerJSONArray,
|
||||
// if `null` value is encountered in JSON, it will leave the value v untouched,
|
||||
// else it will create a new instance of the UnmarshalerJSONArray behind v.
|
||||
func (dec *Decoder) ArrayNull(v interface{}) error {
|
||||
newCursor, err := dec.decodeArrayNull(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.cursor = newCursor
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Index returns the index of an array being decoded.
|
||||
func (dec *Decoder) Index() int {
|
||||
return dec.arrayIndex
|
||||
}
|
||||
-241
@@ -1,241 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// DecodeBool reads the next JSON-encoded value from the decoder's input (io.Reader)
|
||||
// and stores it in the boolean pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeBool(v *bool) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeBool(v)
|
||||
}
|
||||
func (dec *Decoder) decodeBool(v *bool) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case 't':
|
||||
dec.cursor++
|
||||
err := dec.assertTrue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = true
|
||||
return nil
|
||||
case 'f':
|
||||
dec.cursor++
|
||||
err := dec.assertFalse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = false
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = false
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (dec *Decoder) decodeBoolNull(v **bool) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case 't':
|
||||
dec.cursor++
|
||||
err := dec.assertTrue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(bool)
|
||||
}
|
||||
**v = true
|
||||
return nil
|
||||
case 'f':
|
||||
dec.cursor++
|
||||
err := dec.assertFalse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(bool)
|
||||
}
|
||||
**v = false
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) assertTrue() error {
|
||||
i := 0
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch i {
|
||||
case 0:
|
||||
if dec.data[dec.cursor] != 'r' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 1:
|
||||
if dec.data[dec.cursor] != 'u' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 2:
|
||||
if dec.data[dec.cursor] != 'e' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 3:
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\b', '\t', '\n', ',', ']', '}':
|
||||
// dec.cursor--
|
||||
return nil
|
||||
default:
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i == 3 {
|
||||
return nil
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) assertNull() error {
|
||||
i := 0
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch i {
|
||||
case 0:
|
||||
if dec.data[dec.cursor] != 'u' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 1:
|
||||
if dec.data[dec.cursor] != 'l' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 2:
|
||||
if dec.data[dec.cursor] != 'l' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 3:
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\t', '\n', ',', ']', '}':
|
||||
// dec.cursor--
|
||||
return nil
|
||||
default:
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i == 3 {
|
||||
return nil
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) assertFalse() error {
|
||||
i := 0
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch i {
|
||||
case 0:
|
||||
if dec.data[dec.cursor] != 'a' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 1:
|
||||
if dec.data[dec.cursor] != 'l' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 2:
|
||||
if dec.data[dec.cursor] != 's' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 3:
|
||||
if dec.data[dec.cursor] != 'e' {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
case 4:
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\t', '\n', ',', ']', '}':
|
||||
// dec.cursor--
|
||||
return nil
|
||||
default:
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i == 4 {
|
||||
return nil
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddBool decodes the JSON value within an object or an array to a *bool.
|
||||
// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
|
||||
// If next key is null, bool will be false.
|
||||
func (dec *Decoder) AddBool(v *bool) error {
|
||||
return dec.Bool(v)
|
||||
}
|
||||
|
||||
// AddBoolNull decodes the JSON value within an object or an array to a *bool.
|
||||
// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
|
||||
// If next key is null, bool will be false.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddBoolNull(v **bool) error {
|
||||
return dec.BoolNull(v)
|
||||
}
|
||||
|
||||
// Bool decodes the JSON value within an object or an array to a *bool.
|
||||
// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
|
||||
// If next key is null, bool will be false.
|
||||
func (dec *Decoder) Bool(v *bool) error {
|
||||
err := dec.decodeBool(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// BoolNull decodes the JSON value within an object or an array to a *bool.
|
||||
// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
|
||||
// If next key is null, bool will be false.
|
||||
func (dec *Decoder) BoolNull(v **bool) error {
|
||||
err := dec.decodeBoolNull(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// EmbeddedJSON is a raw encoded JSON value.
|
||||
// It can be used to delay JSON decoding or precompute a JSON encoding.
|
||||
type EmbeddedJSON []byte
|
||||
|
||||
func (dec *Decoder) decodeEmbeddedJSON(ej *EmbeddedJSON) error {
|
||||
var err error
|
||||
if ej == nil {
|
||||
return InvalidUnmarshalError("Invalid nil pointer given")
|
||||
}
|
||||
var beginOfEmbeddedJSON int
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
// is null
|
||||
case 'n':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case 't':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor++
|
||||
err := dec.assertTrue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// is false
|
||||
case 'f':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor++
|
||||
err := dec.assertFalse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// is an object
|
||||
case '{':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor = dec.cursor + 1
|
||||
dec.cursor, err = dec.skipObject()
|
||||
// is string
|
||||
case '"':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor = dec.cursor + 1
|
||||
err = dec.skipString() // why no new dec.cursor in result?
|
||||
// is array
|
||||
case '[':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor = dec.cursor + 1
|
||||
dec.cursor, err = dec.skipArray()
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
|
||||
beginOfEmbeddedJSON = dec.cursor
|
||||
dec.cursor, err = dec.skipNumber()
|
||||
}
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
if dec.cursor-1 >= beginOfEmbeddedJSON {
|
||||
*ej = append(*ej, dec.data[beginOfEmbeddedJSON:dec.cursor]...)
|
||||
}
|
||||
dec.called |= 1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AddEmbeddedJSON adds an EmbeddedsJSON to the value pointed by v.
|
||||
// It can be used to delay JSON decoding or precompute a JSON encoding.
|
||||
func (dec *Decoder) AddEmbeddedJSON(v *EmbeddedJSON) error {
|
||||
return dec.EmbeddedJSON(v)
|
||||
}
|
||||
|
||||
// EmbeddedJSON adds an EmbeddedsJSON to the value pointed by v.
|
||||
// It can be used to delay JSON decoding or precompute a JSON encoding.
|
||||
func (dec *Decoder) EmbeddedJSON(v *EmbeddedJSON) error {
|
||||
err := dec.decodeEmbeddedJSON(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// TODO @afiune for now we are using the standard json unmarshaling but in
|
||||
// the future it would be great to implement one here inside this repo
|
||||
import "encoding/json"
|
||||
|
||||
// DecodeInterface reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by i.
|
||||
//
|
||||
// i must be an interface poiter
|
||||
func (dec *Decoder) DecodeInterface(i *interface{}) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
err := dec.decodeInterface(i)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeInterface(i *interface{}) error {
|
||||
start, end, err := dec.getObject()
|
||||
if err != nil {
|
||||
dec.cursor = start
|
||||
return err
|
||||
}
|
||||
|
||||
// if start & end are equal the object is a null, don't unmarshal
|
||||
if start == end {
|
||||
return nil
|
||||
}
|
||||
|
||||
object := dec.data[start:end]
|
||||
if err = json.Unmarshal(object, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec.cursor = end
|
||||
return nil
|
||||
}
|
||||
|
||||
// @afiune Maybe return the type as well?
|
||||
func (dec *Decoder) getObject() (start int, end int, err error) {
|
||||
// start cursor
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
// is null
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err = dec.assertNull()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Set start & end to the same cursor to indicate the object
|
||||
// is a null and should not be unmarshal
|
||||
start = dec.cursor
|
||||
end = dec.cursor
|
||||
return
|
||||
case 't':
|
||||
start = dec.cursor
|
||||
dec.cursor++
|
||||
err = dec.assertTrue()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
end = dec.cursor
|
||||
dec.cursor++
|
||||
return
|
||||
// is false
|
||||
case 'f':
|
||||
start = dec.cursor
|
||||
dec.cursor++
|
||||
err = dec.assertFalse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
end = dec.cursor
|
||||
dec.cursor++
|
||||
return
|
||||
// is an object
|
||||
case '{':
|
||||
start = dec.cursor
|
||||
dec.cursor++
|
||||
end, err = dec.skipObject()
|
||||
dec.cursor = end
|
||||
return
|
||||
// is string
|
||||
case '"':
|
||||
start = dec.cursor
|
||||
dec.cursor++
|
||||
start, end, err = dec.getString()
|
||||
start--
|
||||
dec.cursor = end
|
||||
return
|
||||
// is array
|
||||
case '[':
|
||||
start = dec.cursor
|
||||
dec.cursor++
|
||||
end, err = dec.skipArray()
|
||||
dec.cursor = end
|
||||
return
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
|
||||
start = dec.cursor
|
||||
end, err = dec.skipNumber()
|
||||
dec.cursor = end
|
||||
return
|
||||
default:
|
||||
err = dec.raiseInvalidJSONErr(dec.cursor)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = dec.raiseInvalidJSONErr(dec.cursor)
|
||||
return
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddInterface decodes the JSON value within an object or an array to a interface{}.
|
||||
func (dec *Decoder) AddInterface(v *interface{}) error {
|
||||
return dec.Interface(v)
|
||||
}
|
||||
|
||||
// Interface decodes the JSON value within an object or an array to an interface{}.
|
||||
func (dec *Decoder) Interface(value *interface{}) error {
|
||||
err := dec.decodeInterface(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var digits []int8
|
||||
|
||||
const maxInt64toMultiply = math.MaxInt64 / 10
|
||||
const maxInt32toMultiply = math.MaxInt32 / 10
|
||||
const maxInt16toMultiply = math.MaxInt16 / 10
|
||||
const maxInt8toMultiply = math.MaxInt8 / 10
|
||||
const maxUint8toMultiply = math.MaxUint8 / 10
|
||||
const maxUint16toMultiply = math.MaxUint16 / 10
|
||||
const maxUint32toMultiply = math.MaxUint32 / 10
|
||||
const maxUint64toMultiply = math.MaxUint64 / 10
|
||||
const maxUint32Length = 10
|
||||
const maxUint64Length = 20
|
||||
const maxUint16Length = 5
|
||||
const maxUint8Length = 3
|
||||
const maxInt32Length = 10
|
||||
const maxInt64Length = 19
|
||||
const maxInt16Length = 5
|
||||
const maxInt8Length = 3
|
||||
const invalidNumber = int8(-1)
|
||||
|
||||
var pow10uint64 = [21]uint64{
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000,
|
||||
10000000000,
|
||||
100000000000,
|
||||
1000000000000,
|
||||
10000000000000,
|
||||
100000000000000,
|
||||
1000000000000000,
|
||||
10000000000000000,
|
||||
100000000000000000,
|
||||
1000000000000000000,
|
||||
10000000000000000000,
|
||||
}
|
||||
|
||||
var skipNumberEndCursorIncrement [256]int
|
||||
|
||||
func init() {
|
||||
digits = make([]int8, 256)
|
||||
for i := 0; i < len(digits); i++ {
|
||||
digits[i] = invalidNumber
|
||||
}
|
||||
for i := int8('0'); i <= int8('9'); i++ {
|
||||
digits[i] = i - int8('0')
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i++ {
|
||||
switch i {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-':
|
||||
skipNumberEndCursorIncrement[i] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipNumber() (int, error) {
|
||||
end := dec.cursor + 1
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
end += skipNumberEndCursorIncrement[dec.data[j]]
|
||||
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-', ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
case ',', '}', ']':
|
||||
return end, nil
|
||||
default:
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return end, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) getExponent() (int64, error) {
|
||||
start := dec.cursor
|
||||
end := dec.cursor
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] { // is positive
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = dec.cursor + 1
|
||||
case '-':
|
||||
dec.cursor++
|
||||
exp, err := dec.getExponent()
|
||||
return -exp, err
|
||||
case '+':
|
||||
dec.cursor++
|
||||
return dec.getExponent()
|
||||
default:
|
||||
// if nothing return 0
|
||||
// could raise error
|
||||
if start == end {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoi64(start, end-1), nil
|
||||
}
|
||||
}
|
||||
if start == end {
|
||||
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoi64(start, end-1), nil
|
||||
}
|
||||
-516
@@ -1,516 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// DecodeFloat64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float64 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeFloat64(v *float64) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeFloat64(v)
|
||||
}
|
||||
func (dec *Decoder) decodeFloat64(v *float64) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getFloat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.cursor = dec.cursor + 1
|
||||
val, err := dec.getFloatNegative()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = -val
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeFloat64Null(v **float64) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getFloat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(float64)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.cursor = dec.cursor + 1
|
||||
val, err := dec.getFloatNegative()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(float64)
|
||||
}
|
||||
**v = -val
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getFloatNegative() (float64, error) {
|
||||
// look for following numbers
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return dec.getFloat()
|
||||
default:
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getFloat() (float64, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case '.':
|
||||
// we get part before decimal as integer
|
||||
beforeDecimal := dec.atoi64(start, end)
|
||||
// then we get part after decimal as integer
|
||||
start = j + 1
|
||||
// get number after the decimal point
|
||||
for i := j + 1; i < dec.length || dec.read(); i++ {
|
||||
c := dec.data[i]
|
||||
if isDigit(c) {
|
||||
end = i
|
||||
// multiply the before decimal point portion by 10 using bitwise
|
||||
// make sure it doesn't overflow
|
||||
if end-start < 18 {
|
||||
beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
|
||||
}
|
||||
continue
|
||||
} else if (c == 'e' || c == 'E') && j < i-1 {
|
||||
// we have an exponent, convert first the value we got before the exponent
|
||||
var afterDecimal int64
|
||||
expI := end - start + 2
|
||||
// if exp is too long, it means number is too long, just truncate the number
|
||||
if expI >= len(pow10uint64) || expI < 0 {
|
||||
expI = len(pow10uint64) - 2
|
||||
afterDecimal = dec.atoi64(start, start+expI-2)
|
||||
} else {
|
||||
// then we add both integers
|
||||
// then we divide the number by the power found
|
||||
afterDecimal = dec.atoi64(start, end)
|
||||
}
|
||||
dec.cursor = i + 1
|
||||
pow := pow10uint64[expI]
|
||||
floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
|
||||
exp, err := dec.getExponent()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // absolute exponent
|
||||
if pExp >= int64(len(pow10uint64)) || pExp < 0 {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
// if exponent is negative
|
||||
if exp < 0 {
|
||||
return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil
|
||||
}
|
||||
return float64(floatVal) * float64(pow10uint64[pExp]), nil
|
||||
}
|
||||
dec.cursor = i
|
||||
break
|
||||
}
|
||||
if end >= dec.length || end < start {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
var afterDecimal int64
|
||||
expI := end - start + 2
|
||||
// if exp is too long, it means number is too long, just truncate the number
|
||||
if expI >= len(pow10uint64) || expI < 0 {
|
||||
expI = 19
|
||||
afterDecimal = dec.atoi64(start, start+expI-2)
|
||||
} else {
|
||||
afterDecimal = dec.atoi64(start, end)
|
||||
}
|
||||
|
||||
pow := pow10uint64[expI]
|
||||
// then we add both integers
|
||||
// then we divide the number by the power found
|
||||
return float64(beforeDecimal+afterDecimal) / float64(pow), nil
|
||||
case 'e', 'E':
|
||||
dec.cursor = j + 1
|
||||
// we get part before decimal as integer
|
||||
beforeDecimal := uint64(dec.atoi64(start, end))
|
||||
// get exponent
|
||||
exp, err := dec.getExponent()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
|
||||
if pExp >= int64(len(pow10uint64)) || pExp < 0 {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
// if exponent is negative
|
||||
if exp < 0 {
|
||||
return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil
|
||||
}
|
||||
return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil
|
||||
case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal
|
||||
dec.cursor = j
|
||||
return float64(dec.atoi64(start, end)), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return float64(dec.atoi64(start, end)), nil
|
||||
}
|
||||
|
||||
// DecodeFloat32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float32 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeFloat32(v *float32) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeFloat32(v)
|
||||
}
|
||||
func (dec *Decoder) decodeFloat32(v *float32) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.cursor = dec.cursor + 1
|
||||
val, err := dec.getFloat32Negative()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = -val
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeFloat32Null(v **float32) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(float32)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.cursor = dec.cursor + 1
|
||||
val, err := dec.getFloat32Negative()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(float32)
|
||||
}
|
||||
**v = -val
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getFloat32Negative() (float32, error) {
|
||||
// look for following numbers
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return dec.getFloat32()
|
||||
default:
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getFloat32() (float32, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case '.':
|
||||
// we get part before decimal as integer
|
||||
beforeDecimal := dec.atoi64(start, end)
|
||||
// then we get part after decimal as integer
|
||||
start = j + 1
|
||||
// get number after the decimal point
|
||||
// multiple the before decimal point portion by 10 using bitwise
|
||||
for i := j + 1; i < dec.length || dec.read(); i++ {
|
||||
c := dec.data[i]
|
||||
if isDigit(c) {
|
||||
end = i
|
||||
// multiply the before decimal point portion by 10 using bitwise
|
||||
// make sure it desn't overflow
|
||||
if end-start < 9 {
|
||||
beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
|
||||
}
|
||||
continue
|
||||
} else if (c == 'e' || c == 'E') && j < i-1 {
|
||||
// we get the number before decimal
|
||||
var afterDecimal int64
|
||||
expI := end - start + 2
|
||||
// if exp is too long, it means number is too long, just truncate the number
|
||||
if expI >= 12 || expI < 0 {
|
||||
expI = 10
|
||||
afterDecimal = dec.atoi64(start, start+expI-2)
|
||||
} else {
|
||||
afterDecimal = dec.atoi64(start, end)
|
||||
}
|
||||
dec.cursor = i + 1
|
||||
pow := pow10uint64[expI]
|
||||
// then we add both integers
|
||||
// then we divide the number by the power found
|
||||
floatVal := float32(beforeDecimal+afterDecimal) / float32(pow)
|
||||
exp, err := dec.getExponent()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
|
||||
if pExp >= int64(len(pow10uint64)) || pExp < 0 {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
// if exponent is negative
|
||||
if exp < 0 {
|
||||
return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil
|
||||
}
|
||||
return float32(floatVal) * float32(pow10uint64[pExp]), nil
|
||||
}
|
||||
dec.cursor = i
|
||||
break
|
||||
}
|
||||
if end >= dec.length || end < start {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
// then we add both integers
|
||||
// then we divide the number by the power found
|
||||
var afterDecimal int64
|
||||
expI := end - start + 2
|
||||
// if exp is too long, it means number is too long, just truncate the number
|
||||
if expI >= 12 || expI < 0 {
|
||||
expI = 10
|
||||
afterDecimal = dec.atoi64(start, start+expI-2)
|
||||
} else {
|
||||
// then we add both integers
|
||||
// then we divide the number by the power found
|
||||
afterDecimal = dec.atoi64(start, end)
|
||||
}
|
||||
pow := pow10uint64[expI]
|
||||
return float32(beforeDecimal+afterDecimal) / float32(pow), nil
|
||||
case 'e', 'E':
|
||||
dec.cursor = j + 1
|
||||
// we get part before decimal as integer
|
||||
beforeDecimal := dec.atoi64(start, end)
|
||||
// get exponent
|
||||
exp, err := dec.getExponent()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1
|
||||
if pExp >= int64(len(pow10uint64)) || pExp < 0 {
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
// if exponent is negative
|
||||
if exp < 0 {
|
||||
return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil
|
||||
}
|
||||
return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil
|
||||
case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal
|
||||
dec.cursor = j
|
||||
return float32(dec.atoi64(start, end)), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return float32(dec.atoi64(start, end)), nil
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddFloat decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddFloat(v *float64) error {
|
||||
return dec.Float64(v)
|
||||
}
|
||||
|
||||
// AddFloatNull decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddFloatNull(v **float64) error {
|
||||
return dec.Float64Null(v)
|
||||
}
|
||||
|
||||
// AddFloat64 decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddFloat64(v *float64) error {
|
||||
return dec.Float64(v)
|
||||
}
|
||||
|
||||
// AddFloat64Null decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddFloat64Null(v **float64) error {
|
||||
return dec.Float64Null(v)
|
||||
}
|
||||
|
||||
// AddFloat32 decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddFloat32(v *float32) error {
|
||||
return dec.Float32(v)
|
||||
}
|
||||
|
||||
// AddFloat32Null decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddFloat32Null(v **float32) error {
|
||||
return dec.Float32Null(v)
|
||||
}
|
||||
|
||||
// Float decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Float(v *float64) error {
|
||||
return dec.Float64(v)
|
||||
}
|
||||
|
||||
// FloatNull decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) FloatNull(v **float64) error {
|
||||
return dec.Float64Null(v)
|
||||
}
|
||||
|
||||
// Float64 decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Float64(v *float64) error {
|
||||
err := dec.decodeFloat64(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64Null decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Float64Null(v **float64) error {
|
||||
err := dec.decodeFloat64Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float32 decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Float32(v *float32) error {
|
||||
err := dec.decodeFloat32(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float32Null decodes the JSON value within an object or an array to a *float64.
|
||||
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Float32Null(v **float32) error {
|
||||
err := dec.decodeFloat32Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-1338
File diff suppressed because it is too large
Load Diff
-715
@@ -1,715 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// DecodeUint8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint8 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeUint8(v *uint8) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeUint8(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeUint8(v *uint8) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-': // if negative, we just set it to 0 and set error
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeUint8Null(v **uint8) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint8)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-': // if negative, we just set it to 0 and set error
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint8)
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getUint8() (uint8, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
case '.', ',', '}', ']':
|
||||
dec.cursor = j
|
||||
return dec.atoui8(start, end), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoui8(start, end), nil
|
||||
}
|
||||
|
||||
// DecodeUint16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint16 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeUint16(v *uint16) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeUint16(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeUint16(v *uint16) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeUint16Null(v **uint16) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint16)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint16)
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getUint16() (uint16, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
case '.', ',', '}', ']':
|
||||
dec.cursor = j
|
||||
return dec.atoui16(start, end), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoui16(start, end), nil
|
||||
}
|
||||
|
||||
// DecodeUint32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint32 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeUint32(v *uint32) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeUint32(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeUint32(v *uint32) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeUint32Null(v **uint32) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint32)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint32)
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getUint32() (uint32, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
case '.', ',', '}', ']':
|
||||
dec.cursor = j
|
||||
return dec.atoui32(start, end), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoui32(start, end), nil
|
||||
}
|
||||
|
||||
// DecodeUint64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint64 pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeUint64(v *uint64) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeUint64(v)
|
||||
}
|
||||
func (dec *Decoder) decodeUint64(v *uint64) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
func (dec *Decoder) decodeUint64Null(v **uint64) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch c := dec.data[dec.cursor]; c {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
val, err := dec.getUint64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint64)
|
||||
}
|
||||
**v = val
|
||||
return nil
|
||||
case '-':
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(uint64)
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) getUint64() (uint64, error) {
|
||||
var end = dec.cursor
|
||||
var start = dec.cursor
|
||||
// look for following numbers
|
||||
for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
end = j
|
||||
continue
|
||||
case ' ', '\n', '\t', '\r', '.', ',', '}', ']':
|
||||
dec.cursor = j
|
||||
return dec.atoui64(start, end), nil
|
||||
}
|
||||
// invalid json we expect numbers, dot (single one), comma, or spaces
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.atoui64(start, end), nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) atoui64(start, end int) uint64 {
|
||||
var ll = end + 1 - start
|
||||
var val = uint64(digits[dec.data[start]])
|
||||
end = end + 1
|
||||
if ll < maxUint64Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint64(digits[dec.data[i]])
|
||||
val = (val << 3) + (val << 1) + uintv
|
||||
}
|
||||
} else if ll == maxUint64Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint64(digits[dec.data[i]])
|
||||
if val > maxUint64toMultiply {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val = (val << 3) + (val << 1)
|
||||
if math.MaxUint64-val < uintv {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val += uintv
|
||||
}
|
||||
} else {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (dec *Decoder) atoui32(start, end int) uint32 {
|
||||
var ll = end + 1 - start
|
||||
var val uint32
|
||||
val = uint32(digits[dec.data[start]])
|
||||
end = end + 1
|
||||
if ll < maxUint32Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint32(digits[dec.data[i]])
|
||||
val = (val << 3) + (val << 1) + uintv
|
||||
}
|
||||
} else if ll == maxUint32Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint32(digits[dec.data[i]])
|
||||
if val > maxUint32toMultiply {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val = (val << 3) + (val << 1)
|
||||
if math.MaxUint32-val < uintv {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val += uintv
|
||||
}
|
||||
} else if ll > maxUint32Length {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
val = 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (dec *Decoder) atoui16(start, end int) uint16 {
|
||||
var ll = end + 1 - start
|
||||
var val uint16
|
||||
val = uint16(digits[dec.data[start]])
|
||||
end = end + 1
|
||||
if ll < maxUint16Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint16(digits[dec.data[i]])
|
||||
val = (val << 3) + (val << 1) + uintv
|
||||
}
|
||||
} else if ll == maxUint16Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint16(digits[dec.data[i]])
|
||||
if val > maxUint16toMultiply {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val = (val << 3) + (val << 1)
|
||||
if math.MaxUint16-val < uintv {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val += uintv
|
||||
}
|
||||
} else if ll > maxUint16Length {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
val = 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (dec *Decoder) atoui8(start, end int) uint8 {
|
||||
var ll = end + 1 - start
|
||||
var val uint8
|
||||
val = uint8(digits[dec.data[start]])
|
||||
end = end + 1
|
||||
if ll < maxUint8Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint8(digits[dec.data[i]])
|
||||
val = (val << 3) + (val << 1) + uintv
|
||||
}
|
||||
} else if ll == maxUint8Length {
|
||||
for i := start + 1; i < end; i++ {
|
||||
uintv := uint8(digits[dec.data[i]])
|
||||
if val > maxUint8toMultiply {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val = (val << 3) + (val << 1)
|
||||
if math.MaxUint8-val < uintv {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
return 0
|
||||
}
|
||||
val += uintv
|
||||
}
|
||||
} else if ll > maxUint8Length {
|
||||
dec.err = dec.makeInvalidUnmarshalErr(val)
|
||||
val = 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddUint8 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddUint8(v *uint8) error {
|
||||
return dec.Uint8(v)
|
||||
}
|
||||
|
||||
// AddUint8Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddUint8Null(v **uint8) error {
|
||||
return dec.Uint8Null(v)
|
||||
}
|
||||
|
||||
// AddUint16 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddUint16(v *uint16) error {
|
||||
return dec.Uint16(v)
|
||||
}
|
||||
|
||||
// AddUint16Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddUint16Null(v **uint16) error {
|
||||
return dec.Uint16Null(v)
|
||||
}
|
||||
|
||||
// AddUint32 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddUint32(v *uint32) error {
|
||||
return dec.Uint32(v)
|
||||
}
|
||||
|
||||
// AddUint32Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddUint32Null(v **uint32) error {
|
||||
return dec.Uint32Null(v)
|
||||
}
|
||||
|
||||
// AddUint64 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) AddUint64(v *uint64) error {
|
||||
return dec.Uint64(v)
|
||||
}
|
||||
|
||||
// AddUint64Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddUint64Null(v **uint64) error {
|
||||
return dec.Uint64Null(v)
|
||||
}
|
||||
|
||||
// Uint8 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint8(v *uint8) error {
|
||||
err := dec.decodeUint8(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint8Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint8Null(v **uint8) error {
|
||||
err := dec.decodeUint8Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint16 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint16(v *uint16) error {
|
||||
err := dec.decodeUint16(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint16Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint16Null(v **uint16) error {
|
||||
err := dec.decodeUint16Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint32(v *uint32) error {
|
||||
err := dec.decodeUint32(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint32Null(v **uint32) error {
|
||||
err := dec.decodeUint32Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64 decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint64(v *uint64) error {
|
||||
err := dec.decodeUint64(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64Null decodes the JSON value within an object or an array to an *int.
|
||||
// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
|
||||
func (dec *Decoder) Uint64Null(v **uint64) error {
|
||||
err := dec.decodeUint64Null(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-407
@@ -1,407 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DecodeObject reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v.
|
||||
//
|
||||
// v must implement UnmarshalerJSONObject.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeObject(j UnmarshalerJSONObject) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
_, err := dec.decodeObject(j)
|
||||
return err
|
||||
}
|
||||
func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) {
|
||||
keys := j.NKeys()
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
case '{':
|
||||
dec.cursor = dec.cursor + 1
|
||||
// if keys is zero we will parse all keys
|
||||
// we run two loops for micro optimization
|
||||
if keys == 0 {
|
||||
for dec.cursor < dec.length || dec.read() {
|
||||
k, done, err := dec.nextKey()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if done {
|
||||
return dec.cursor, nil
|
||||
}
|
||||
err = j.UnmarshalJSONObject(dec, k)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
return 0, err
|
||||
} else if dec.called&1 == 0 {
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
dec.keysDone++
|
||||
}
|
||||
dec.called &= 0
|
||||
}
|
||||
} else {
|
||||
for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
|
||||
k, done, err := dec.nextKey()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if done {
|
||||
return dec.cursor, nil
|
||||
}
|
||||
err = j.UnmarshalJSONObject(dec, k)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
return 0, err
|
||||
} else if dec.called&1 == 0 {
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
dec.keysDone++
|
||||
}
|
||||
dec.called &= 0
|
||||
}
|
||||
}
|
||||
// will get to that point when keysDone is not lower than keys anymore
|
||||
// in that case, we make sure cursor goes to the end of object, but we skip
|
||||
// unmarshalling
|
||||
if dec.child&1 != 0 {
|
||||
end, err := dec.skipObject()
|
||||
dec.cursor = end
|
||||
return dec.cursor, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
default:
|
||||
// can't unmarshal to struct
|
||||
dec.err = dec.makeInvalidUnmarshalErr(j)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) {
|
||||
// make sure the value is a pointer
|
||||
vv := reflect.ValueOf(v)
|
||||
vvt := vv.Type()
|
||||
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
|
||||
dec.err = ErrUnmarshalPtrExpected
|
||||
return 0, dec.err
|
||||
}
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
case '{':
|
||||
elt := vv.Elem()
|
||||
n := reflect.New(elt.Type().Elem())
|
||||
elt.Set(n)
|
||||
var j UnmarshalerJSONObject
|
||||
var ok bool
|
||||
if j, ok = n.Interface().(UnmarshalerJSONObject); !ok {
|
||||
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
|
||||
return 0, dec.err
|
||||
}
|
||||
keys := j.NKeys()
|
||||
dec.cursor = dec.cursor + 1
|
||||
// if keys is zero we will parse all keys
|
||||
// we run two loops for micro optimization
|
||||
if keys == 0 {
|
||||
for dec.cursor < dec.length || dec.read() {
|
||||
k, done, err := dec.nextKey()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if done {
|
||||
return dec.cursor, nil
|
||||
}
|
||||
err = j.UnmarshalJSONObject(dec, k)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
return 0, err
|
||||
} else if dec.called&1 == 0 {
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
dec.keysDone++
|
||||
}
|
||||
dec.called &= 0
|
||||
}
|
||||
} else {
|
||||
for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
|
||||
k, done, err := dec.nextKey()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if done {
|
||||
return dec.cursor, nil
|
||||
}
|
||||
err = j.UnmarshalJSONObject(dec, k)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
return 0, err
|
||||
} else if dec.called&1 == 0 {
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
dec.keysDone++
|
||||
}
|
||||
dec.called &= 0
|
||||
}
|
||||
}
|
||||
// will get to that point when keysDone is not lower than keys anymore
|
||||
// in that case, we make sure cursor goes to the end of object, but we skip
|
||||
// unmarshalling
|
||||
if dec.child&1 != 0 {
|
||||
end, err := dec.skipObject()
|
||||
dec.cursor = end
|
||||
return dec.cursor, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
default:
|
||||
// can't unmarshal to struct
|
||||
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dec.cursor, nil
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipObject() (int, error) {
|
||||
var objectsOpen = 1
|
||||
var objectsClosed = 0
|
||||
for j := dec.cursor; j < dec.length || dec.read(); j++ {
|
||||
switch dec.data[j] {
|
||||
case '}':
|
||||
objectsClosed++
|
||||
// everything is closed return
|
||||
if objectsOpen == objectsClosed {
|
||||
// add char to object data
|
||||
return j + 1, nil
|
||||
}
|
||||
case '{':
|
||||
objectsOpen++
|
||||
case '"':
|
||||
j++
|
||||
var isInEscapeSeq bool
|
||||
var isFirstQuote = true
|
||||
for ; j < dec.length || dec.read(); j++ {
|
||||
if dec.data[j] != '"' {
|
||||
continue
|
||||
}
|
||||
if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) {
|
||||
break
|
||||
} else {
|
||||
isInEscapeSeq = false
|
||||
}
|
||||
if isFirstQuote {
|
||||
isFirstQuote = false
|
||||
}
|
||||
// loop backward and count how many anti slash found
|
||||
// to see if string is effectively escaped
|
||||
ct := 0
|
||||
for i := j - 1; i > 0; i-- {
|
||||
if dec.data[i] != '\\' {
|
||||
break
|
||||
}
|
||||
ct++
|
||||
}
|
||||
// is pair number of slashes, quote is not escaped
|
||||
if ct&1 == 0 {
|
||||
break
|
||||
}
|
||||
isInEscapeSeq = true
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) nextKey() (string, bool, error) {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
case '"':
|
||||
dec.cursor = dec.cursor + 1
|
||||
start, end, err := dec.getString()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
var found byte
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
if dec.data[dec.cursor] == ':' {
|
||||
found |= 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if found&1 != 0 {
|
||||
dec.cursor++
|
||||
d := dec.data[start : end-1]
|
||||
return *(*string)(unsafe.Pointer(&d)), false, nil
|
||||
}
|
||||
return "", false, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
case '}':
|
||||
dec.cursor = dec.cursor + 1
|
||||
return "", true, nil
|
||||
default:
|
||||
// can't unmarshall to struct
|
||||
return "", false, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
}
|
||||
return "", false, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipData() error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
// is null
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 't':
|
||||
dec.cursor++
|
||||
err := dec.assertTrue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// is false
|
||||
case 'f':
|
||||
dec.cursor++
|
||||
err := dec.assertFalse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// is an object
|
||||
case '{':
|
||||
dec.cursor = dec.cursor + 1
|
||||
end, err := dec.skipObject()
|
||||
dec.cursor = end
|
||||
return err
|
||||
// is string
|
||||
case '"':
|
||||
dec.cursor = dec.cursor + 1
|
||||
err := dec.skipString()
|
||||
return err
|
||||
// is array
|
||||
case '[':
|
||||
dec.cursor = dec.cursor + 1
|
||||
end, err := dec.skipArray()
|
||||
dec.cursor = end
|
||||
return err
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
|
||||
end, err := dec.skipNumber()
|
||||
dec.cursor = end
|
||||
return err
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
// DecodeObjectFunc is a func type implementing UnmarshalerJSONObject.
|
||||
// Use it to cast a `func(*Decoder, k string) error` to Unmarshal an object on the fly.
|
||||
type DecodeObjectFunc func(*Decoder, string) error
|
||||
|
||||
// UnmarshalJSONObject implements UnmarshalerJSONObject.
|
||||
func (f DecodeObjectFunc) UnmarshalJSONObject(dec *Decoder, k string) error {
|
||||
return f(dec, k)
|
||||
}
|
||||
|
||||
// NKeys implements UnmarshalerJSONObject.
|
||||
func (f DecodeObjectFunc) NKeys() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddObject decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
|
||||
func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error {
|
||||
return dec.Object(v)
|
||||
}
|
||||
|
||||
// AddObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
|
||||
func (dec *Decoder) AddObjectNull(v interface{}) error {
|
||||
return dec.ObjectNull(v)
|
||||
}
|
||||
|
||||
// Object decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
|
||||
func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
|
||||
initialKeysDone := dec.keysDone
|
||||
initialChild := dec.child
|
||||
dec.keysDone = 0
|
||||
dec.called = 0
|
||||
dec.child |= 1
|
||||
newCursor, err := dec.decodeObject(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.cursor = newCursor
|
||||
dec.keysDone = initialKeysDone
|
||||
dec.child = initialChild
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// ObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
|
||||
// v should be a pointer to an UnmarshalerJSONObject,
|
||||
// if `null` value is encountered in JSON, it will leave the value v untouched,
|
||||
// else it will create a new instance of the UnmarshalerJSONObject behind v.
|
||||
func (dec *Decoder) ObjectNull(v interface{}) error {
|
||||
initialKeysDone := dec.keysDone
|
||||
initialChild := dec.child
|
||||
dec.keysDone = 0
|
||||
dec.called = 0
|
||||
dec.child |= 1
|
||||
newCursor, err := dec.decodeObjectNull(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.cursor = newCursor
|
||||
dec.keysDone = initialKeysDone
|
||||
dec.child = initialChild
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var decPool = sync.Pool{
|
||||
New: newDecoderPool,
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 32; i++ {
|
||||
decPool.Put(NewDecoder(nil))
|
||||
}
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder.
|
||||
// It takes an io.Reader implementation as data input.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
called: 0,
|
||||
cursor: 0,
|
||||
keysDone: 0,
|
||||
err: nil,
|
||||
r: r,
|
||||
data: make([]byte, 512),
|
||||
length: 0,
|
||||
isPooled: 0,
|
||||
}
|
||||
}
|
||||
func newDecoderPool() interface{} {
|
||||
return NewDecoder(nil)
|
||||
}
|
||||
|
||||
// BorrowDecoder borrows a Decoder from the pool.
|
||||
// It takes an io.Reader implementation as data input.
|
||||
//
|
||||
// In order to benefit from the pool, a borrowed decoder must be released after usage.
|
||||
func BorrowDecoder(r io.Reader) *Decoder {
|
||||
return borrowDecoder(r, 512)
|
||||
}
|
||||
func borrowDecoder(r io.Reader, bufSize int) *Decoder {
|
||||
dec := decPool.Get().(*Decoder)
|
||||
dec.called = 0
|
||||
dec.keysDone = 0
|
||||
dec.cursor = 0
|
||||
dec.err = nil
|
||||
dec.r = r
|
||||
dec.length = 0
|
||||
dec.isPooled = 0
|
||||
if bufSize > 0 {
|
||||
dec.data = make([]byte, bufSize)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Release sends back a Decoder to the pool.
|
||||
// If a decoder is used after calling Release
|
||||
// a panic will be raised with an InvalidUsagePooledDecoderError error.
|
||||
func (dec *Decoder) Release() {
|
||||
dec.isPooled = 1
|
||||
decPool.Put(dec)
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// AddSliceString unmarshals the next JSON array of strings to the given *[]string s
|
||||
func (dec *Decoder) AddSliceString(s *[]string) error {
|
||||
return dec.SliceString(s)
|
||||
}
|
||||
|
||||
// SliceString unmarshals the next JSON array of strings to the given *[]string s
|
||||
func (dec *Decoder) SliceString(s *[]string) error {
|
||||
err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
|
||||
var str string
|
||||
if err := dec.String(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, str)
|
||||
return nil
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSliceInt unmarshals the next JSON array of integers to the given *[]int s
|
||||
func (dec *Decoder) AddSliceInt(s *[]int) error {
|
||||
return dec.SliceInt(s)
|
||||
}
|
||||
|
||||
// SliceInt unmarshals the next JSON array of integers to the given *[]int s
|
||||
func (dec *Decoder) SliceInt(s *[]int) error {
|
||||
err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
|
||||
var i int
|
||||
if err := dec.Int(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, i)
|
||||
return nil
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
|
||||
func (dec *Decoder) AddSliceFloat64(s *[]float64) error {
|
||||
return dec.SliceFloat64(s)
|
||||
}
|
||||
|
||||
// SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
|
||||
func (dec *Decoder) SliceFloat64(s *[]float64) error {
|
||||
err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
|
||||
var i float64
|
||||
if err := dec.Float64(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, i)
|
||||
return nil
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddBool unmarshals the next JSON array of boolegers to the given *[]bool s
|
||||
func (dec *Decoder) AddSliceBool(s *[]bool) error {
|
||||
return dec.SliceBool(s)
|
||||
}
|
||||
|
||||
// SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s
|
||||
func (dec *Decoder) SliceBool(s *[]bool) error {
|
||||
err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
|
||||
var b bool
|
||||
if err := dec.Bool(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = append(*s, b)
|
||||
return nil
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// DecodeSQLNullString decodes a sql.NullString
|
||||
func (dec *Decoder) DecodeSQLNullString(v *sql.NullString) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeSQLNullString(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeSQLNullString(v *sql.NullString) error {
|
||||
var str string
|
||||
if err := dec.decodeString(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
v.String = str
|
||||
v.Valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeSQLNullInt64 decodes a sql.NullInt64
|
||||
func (dec *Decoder) DecodeSQLNullInt64(v *sql.NullInt64) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeSQLNullInt64(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeSQLNullInt64(v *sql.NullInt64) error {
|
||||
var i int64
|
||||
if err := dec.decodeInt64(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Int64 = i
|
||||
v.Valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeSQLNullFloat64 decodes a sql.NullString with the given format
|
||||
func (dec *Decoder) DecodeSQLNullFloat64(v *sql.NullFloat64) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeSQLNullFloat64(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeSQLNullFloat64(v *sql.NullFloat64) error {
|
||||
var i float64
|
||||
if err := dec.decodeFloat64(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Float64 = i
|
||||
v.Valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeSQLNullBool decodes a sql.NullString with the given format
|
||||
func (dec *Decoder) DecodeSQLNullBool(v *sql.NullBool) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeSQLNullBool(v)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeSQLNullBool(v *sql.NullBool) error {
|
||||
var b bool
|
||||
if err := dec.decodeBool(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Bool = b
|
||||
v.Valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddSQLNullString decodes the JSON value within an object or an array to qn *sql.NullString
|
||||
func (dec *Decoder) AddSQLNullString(v *sql.NullString) error {
|
||||
return dec.SQLNullString(v)
|
||||
}
|
||||
|
||||
// SQLNullString decodes the JSON value within an object or an array to an *sql.NullString
|
||||
func (dec *Decoder) SQLNullString(v *sql.NullString) error {
|
||||
var b *string
|
||||
if err := dec.StringNull(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
if b == nil {
|
||||
v.Valid = false
|
||||
} else {
|
||||
v.String = *b
|
||||
v.Valid = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullInt64 decodes the JSON value within an object or an array to qn *sql.NullInt64
|
||||
func (dec *Decoder) AddSQLNullInt64(v *sql.NullInt64) error {
|
||||
return dec.SQLNullInt64(v)
|
||||
}
|
||||
|
||||
// SQLNullInt64 decodes the JSON value within an object or an array to an *sql.NullInt64
|
||||
func (dec *Decoder) SQLNullInt64(v *sql.NullInt64) error {
|
||||
var b *int64
|
||||
if err := dec.Int64Null(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
if b == nil {
|
||||
v.Valid = false
|
||||
} else {
|
||||
v.Int64 = *b
|
||||
v.Valid = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64 decodes the JSON value within an object or an array to qn *sql.NullFloat64
|
||||
func (dec *Decoder) AddSQLNullFloat64(v *sql.NullFloat64) error {
|
||||
return dec.SQLNullFloat64(v)
|
||||
}
|
||||
|
||||
// SQLNullFloat64 decodes the JSON value within an object or an array to an *sql.NullFloat64
|
||||
func (dec *Decoder) SQLNullFloat64(v *sql.NullFloat64) error {
|
||||
var b *float64
|
||||
if err := dec.Float64Null(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
if b == nil {
|
||||
v.Valid = false
|
||||
} else {
|
||||
v.Float64 = *b
|
||||
v.Valid = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool
|
||||
func (dec *Decoder) AddSQLNullBool(v *sql.NullBool) error {
|
||||
return dec.SQLNullBool(v)
|
||||
}
|
||||
|
||||
// SQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool
|
||||
func (dec *Decoder) SQLNullBool(v *sql.NullBool) error {
|
||||
var b *bool
|
||||
if err := dec.BoolNull(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
if b == nil {
|
||||
v.Valid = false
|
||||
} else {
|
||||
v.Bool = *b
|
||||
v.Valid = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UnmarshalerStream is the interface to implement for a slice, an array or a slice
|
||||
// to decode a line delimited JSON to.
|
||||
type UnmarshalerStream interface {
|
||||
UnmarshalStream(*StreamDecoder) error
|
||||
}
|
||||
|
||||
// Stream is a struct holding the Stream api
|
||||
var Stream = stream{}
|
||||
|
||||
type stream struct{}
|
||||
|
||||
// A StreamDecoder reads and decodes JSON values from an input stream.
|
||||
//
|
||||
// It implements conext.Context and provide a channel to notify interruption.
|
||||
type StreamDecoder struct {
|
||||
mux sync.RWMutex
|
||||
*Decoder
|
||||
done chan struct{}
|
||||
deadline *time.Time
|
||||
}
|
||||
|
||||
// DecodeStream reads the next line delimited JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by c.
|
||||
//
|
||||
// c must implement UnmarshalerStream. Ideally c is a channel. See example for implementation.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
if dec.r == nil {
|
||||
dec.err = NoReaderError("No reader given to decode stream")
|
||||
close(dec.done)
|
||||
return dec.err
|
||||
}
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
continue
|
||||
default:
|
||||
// char is not space start reading
|
||||
for dec.nextChar() != 0 {
|
||||
// calling unmarshal stream
|
||||
err := c.UnmarshalStream(dec)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
close(dec.done)
|
||||
return err
|
||||
}
|
||||
// garbage collects buffer
|
||||
// we don't want the buffer to grow extensively
|
||||
dec.data = dec.data[dec.cursor:]
|
||||
dec.length = dec.length - dec.cursor
|
||||
dec.cursor = 0
|
||||
}
|
||||
// close the done channel to signal the end of the job
|
||||
close(dec.done)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
close(dec.done)
|
||||
dec.mux.Lock()
|
||||
err := dec.raiseInvalidJSONErr(dec.cursor)
|
||||
dec.mux.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// context.Context implementation
|
||||
|
||||
// Done returns a channel that's closed when work is done.
|
||||
// It implements context.Context
|
||||
func (dec *StreamDecoder) Done() <-chan struct{} {
|
||||
return dec.done
|
||||
}
|
||||
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
func (dec *StreamDecoder) Deadline() (time.Time, bool) {
|
||||
if dec.deadline != nil {
|
||||
return *dec.deadline, true
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// SetDeadline sets the deadline
|
||||
func (dec *StreamDecoder) SetDeadline(t time.Time) {
|
||||
dec.deadline = &t
|
||||
}
|
||||
|
||||
// Err returns nil if Done is not yet closed.
|
||||
// If Done is closed, Err returns a non-nil error explaining why.
|
||||
// It implements context.Context
|
||||
func (dec *StreamDecoder) Err() error {
|
||||
select {
|
||||
case <-dec.done:
|
||||
dec.mux.RLock()
|
||||
defer dec.mux.RUnlock()
|
||||
return dec.err
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Value implements context.Context
|
||||
func (dec *StreamDecoder) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var streamDecPool = sync.Pool{
|
||||
New: newStreamDecoderPool,
|
||||
}
|
||||
|
||||
// NewDecoder returns a new StreamDecoder.
|
||||
// It takes an io.Reader implementation as data input.
|
||||
// It initiates the done channel returned by Done().
|
||||
func (s stream) NewDecoder(r io.Reader) *StreamDecoder {
|
||||
dec := NewDecoder(r)
|
||||
streamDec := &StreamDecoder{
|
||||
Decoder: dec,
|
||||
done: make(chan struct{}, 1),
|
||||
mux: sync.RWMutex{},
|
||||
}
|
||||
return streamDec
|
||||
}
|
||||
func newStreamDecoderPool() interface{} {
|
||||
return Stream.NewDecoder(nil)
|
||||
}
|
||||
|
||||
// BorrowDecoder borrows a StreamDecoder from the pool.
|
||||
// It takes an io.Reader implementation as data input.
|
||||
// It initiates the done channel returned by Done().
|
||||
//
|
||||
// If no StreamEncoder is available in the pool, it returns a fresh one
|
||||
func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder {
|
||||
return s.borrowDecoder(r, 512)
|
||||
}
|
||||
|
||||
func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder {
|
||||
streamDec := streamDecPool.Get().(*StreamDecoder)
|
||||
streamDec.called = 0
|
||||
streamDec.keysDone = 0
|
||||
streamDec.cursor = 0
|
||||
streamDec.err = nil
|
||||
streamDec.r = r
|
||||
streamDec.length = 0
|
||||
streamDec.isPooled = 0
|
||||
streamDec.done = make(chan struct{}, 1)
|
||||
if bufSize > 0 {
|
||||
streamDec.data = make([]byte, bufSize)
|
||||
}
|
||||
return streamDec
|
||||
}
|
||||
|
||||
// Release sends back a Decoder to the pool.
|
||||
// If a decoder is used after calling Release
|
||||
// a panic will be raised with an InvalidUsagePooledDecoderError error.
|
||||
func (dec *StreamDecoder) Release() {
|
||||
dec.isPooled = 1
|
||||
streamDecPool.Put(dec)
|
||||
}
|
||||
-260
@@ -1,260 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DecodeString reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the string pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) DecodeString(v *string) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeString(v)
|
||||
}
|
||||
func (dec *Decoder) decodeString(v *string) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
// is string
|
||||
continue
|
||||
case '"':
|
||||
dec.cursor++
|
||||
start, end, err := dec.getString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we do minus one to remove the last quote
|
||||
d := dec.data[start : end-1]
|
||||
*v = *(*string)(unsafe.Pointer(&d))
|
||||
dec.cursor = end
|
||||
return nil
|
||||
// is nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeStringNull(v **string) error {
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
switch dec.data[dec.cursor] {
|
||||
case ' ', '\n', '\t', '\r', ',':
|
||||
// is string
|
||||
continue
|
||||
case '"':
|
||||
dec.cursor++
|
||||
start, end, err := dec.getString()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *v == nil {
|
||||
*v = new(string)
|
||||
}
|
||||
// we do minus one to remove the last quote
|
||||
d := dec.data[start : end-1]
|
||||
**v = *(*string)(unsafe.Pointer(&d))
|
||||
dec.cursor = end
|
||||
return nil
|
||||
// is nil
|
||||
case 'n':
|
||||
dec.cursor++
|
||||
err := dec.assertNull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
dec.err = dec.makeInvalidUnmarshalErr(v)
|
||||
err := dec.skipData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) parseEscapedString() error {
|
||||
if dec.cursor >= dec.length && !dec.read() {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
switch dec.data[dec.cursor] {
|
||||
case '"':
|
||||
dec.data[dec.cursor] = '"'
|
||||
case '\\':
|
||||
dec.data[dec.cursor] = '\\'
|
||||
case '/':
|
||||
dec.data[dec.cursor] = '/'
|
||||
case 'b':
|
||||
dec.data[dec.cursor] = '\b'
|
||||
case 'f':
|
||||
dec.data[dec.cursor] = '\f'
|
||||
case 'n':
|
||||
dec.data[dec.cursor] = '\n'
|
||||
case 'r':
|
||||
dec.data[dec.cursor] = '\r'
|
||||
case 't':
|
||||
dec.data[dec.cursor] = '\t'
|
||||
case 'u':
|
||||
start := dec.cursor
|
||||
dec.cursor++
|
||||
str, err := dec.parseUnicode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
diff := dec.cursor - start
|
||||
dec.data = append(append(dec.data[:start-1], str...), dec.data[dec.cursor:]...)
|
||||
dec.length = len(dec.data)
|
||||
dec.cursor += len(str) - diff - 1
|
||||
|
||||
return nil
|
||||
default:
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
dec.data = append(dec.data[:dec.cursor-1], dec.data[dec.cursor:]...)
|
||||
dec.length--
|
||||
|
||||
// Since we've lost a character, our dec.cursor offset is now
|
||||
// 1 past the escaped character which is precisely where we
|
||||
// want it.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) getString() (int, int, error) {
|
||||
// extract key
|
||||
var keyStart = dec.cursor
|
||||
// var str *Builder
|
||||
for dec.cursor < dec.length || dec.read() {
|
||||
switch dec.data[dec.cursor] {
|
||||
// string found
|
||||
case '"':
|
||||
dec.cursor = dec.cursor + 1
|
||||
return keyStart, dec.cursor, nil
|
||||
// slash found
|
||||
case '\\':
|
||||
dec.cursor = dec.cursor + 1
|
||||
err := dec.parseEscapedString()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
default:
|
||||
dec.cursor = dec.cursor + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
return 0, 0, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipEscapedString() error {
|
||||
start := dec.cursor
|
||||
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
|
||||
if dec.data[dec.cursor] != '\\' {
|
||||
d := dec.data[dec.cursor]
|
||||
dec.cursor = dec.cursor + 1
|
||||
nSlash := dec.cursor - start
|
||||
switch d {
|
||||
case '"':
|
||||
// nSlash must be odd
|
||||
if nSlash&1 != 1 {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return nil
|
||||
case 'u': // is unicode, we skip the following characters and place the cursor one one byte backward to avoid it breaking when returning to skipString
|
||||
if err := dec.skipString(); err != nil {
|
||||
return err
|
||||
}
|
||||
dec.cursor--
|
||||
return nil
|
||||
case 'n', 'r', 't', '/', 'f', 'b':
|
||||
return nil
|
||||
default:
|
||||
// nSlash must be even
|
||||
if nSlash&1 == 1 {
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
|
||||
func (dec *Decoder) skipString() error {
|
||||
for dec.cursor < dec.length || dec.read() {
|
||||
switch dec.data[dec.cursor] {
|
||||
// found the closing quote
|
||||
// let's return
|
||||
case '"':
|
||||
dec.cursor = dec.cursor + 1
|
||||
return nil
|
||||
// solidus found start parsing an escaped string
|
||||
case '\\':
|
||||
dec.cursor = dec.cursor + 1
|
||||
err := dec.skipEscapedString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
dec.cursor = dec.cursor + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
return dec.raiseInvalidJSONErr(len(dec.data) - 1)
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddString decodes the JSON value within an object or an array to a *string.
|
||||
// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
|
||||
func (dec *Decoder) AddString(v *string) error {
|
||||
return dec.String(v)
|
||||
}
|
||||
|
||||
// AddStringNull decodes the JSON value within an object or an array to a *string.
|
||||
// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) AddStringNull(v **string) error {
|
||||
return dec.StringNull(v)
|
||||
}
|
||||
|
||||
// String decodes the JSON value within an object or an array to a *string.
|
||||
// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
|
||||
func (dec *Decoder) String(v *string) error {
|
||||
err := dec.decodeString(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringNull decodes the JSON value within an object or an array to a **string.
|
||||
// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
|
||||
// If a `null` is encountered, gojay does not change the value of the pointer.
|
||||
func (dec *Decoder) StringNull(v **string) error {
|
||||
err := dec.decodeStringNull(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func (dec *Decoder) getUnicode() (rune, error) {
|
||||
i := 0
|
||||
r := rune(0)
|
||||
for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ {
|
||||
c := dec.data[dec.cursor]
|
||||
if c >= '0' && c <= '9' {
|
||||
r = r*16 + rune(c-'0')
|
||||
} else if c >= 'a' && c <= 'f' {
|
||||
r = r*16 + rune(c-'a'+10)
|
||||
} else if c >= 'A' && c <= 'F' {
|
||||
r = r*16 + rune(c-'A'+10)
|
||||
} else {
|
||||
return 0, InvalidJSONError("Invalid unicode code point")
|
||||
}
|
||||
i++
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) {
|
||||
switch c {
|
||||
case 't':
|
||||
str = append(str, '\t')
|
||||
case 'n':
|
||||
str = append(str, '\n')
|
||||
case 'r':
|
||||
str = append(str, '\r')
|
||||
case 'b':
|
||||
str = append(str, '\b')
|
||||
case 'f':
|
||||
str = append(str, '\f')
|
||||
case '\\':
|
||||
str = append(str, '\\')
|
||||
default:
|
||||
return nil, InvalidJSONError("Invalid JSON")
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) parseUnicode() ([]byte, error) {
|
||||
// get unicode after u
|
||||
r, err := dec.getUnicode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// no error start making new string
|
||||
str := make([]byte, 16, 16)
|
||||
i := 0
|
||||
// check if code can be a surrogate utf16
|
||||
if utf16.IsSurrogate(r) {
|
||||
if dec.cursor >= dec.length && !dec.read() {
|
||||
return nil, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
c := dec.data[dec.cursor]
|
||||
if c != '\\' {
|
||||
i += utf8.EncodeRune(str, r)
|
||||
return str[:i], nil
|
||||
}
|
||||
dec.cursor++
|
||||
if dec.cursor >= dec.length && !dec.read() {
|
||||
return nil, dec.raiseInvalidJSONErr(dec.cursor)
|
||||
}
|
||||
c = dec.data[dec.cursor]
|
||||
if c != 'u' {
|
||||
i += utf8.EncodeRune(str, r)
|
||||
str, err = dec.appendEscapeChar(str[:i], c)
|
||||
if err != nil {
|
||||
dec.err = err
|
||||
return nil, err
|
||||
}
|
||||
i++
|
||||
dec.cursor++
|
||||
return str[:i], nil
|
||||
}
|
||||
dec.cursor++
|
||||
r2, err := dec.getUnicode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
combined := utf16.DecodeRune(r, r2)
|
||||
if combined == '\uFFFD' {
|
||||
i += utf8.EncodeRune(str, r)
|
||||
i += utf8.EncodeRune(str, r2)
|
||||
} else {
|
||||
i += utf8.EncodeRune(str, combined)
|
||||
}
|
||||
return str[:i], nil
|
||||
}
|
||||
i += utf8.EncodeRune(str, r)
|
||||
return str[:i], nil
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DecodeTime decodes time with the given format
|
||||
func (dec *Decoder) DecodeTime(v *time.Time, format string) error {
|
||||
if dec.isPooled == 1 {
|
||||
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
|
||||
}
|
||||
return dec.decodeTime(v, format)
|
||||
}
|
||||
|
||||
func (dec *Decoder) decodeTime(v *time.Time, format string) error {
|
||||
if format == time.RFC3339 {
|
||||
var ej = make(EmbeddedJSON, 0, 20)
|
||||
if err := dec.decodeEmbeddedJSON(&ej); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.UnmarshalJSON(ej); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var str string
|
||||
if err := dec.decodeString(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
tt, err := time.Parse(format, str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = tt
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add Values functions
|
||||
|
||||
// AddTime decodes the JSON value within an object or an array to a *time.Time with the given format
|
||||
func (dec *Decoder) AddTime(v *time.Time, format string) error {
|
||||
return dec.Time(v, format)
|
||||
}
|
||||
|
||||
// Time decodes the JSON value within an object or an array to a *time.Time with the given format
|
||||
func (dec *Decoder) Time(v *time.Time, format string) error {
|
||||
err := dec.decodeTime(v, format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.called |= 1
|
||||
return nil
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Unsafe is the structure holding the unsafe version of the API.
|
||||
// The difference between unsafe api and regular api is that the regular API
|
||||
// copies the buffer passed to Unmarshal functions to a new internal buffer.
|
||||
// Making it safer because internally GoJay uses unsafe.Pointer to transform slice of bytes into a string.
|
||||
var Unsafe = decUnsafe{}
|
||||
|
||||
type decUnsafe struct{}
|
||||
|
||||
func (u decUnsafe) UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
|
||||
dec := borrowDecoder(nil, 0)
|
||||
defer dec.Release()
|
||||
dec.data = data
|
||||
dec.length = len(data)
|
||||
_, err := dec.decodeArray(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (u decUnsafe) UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error {
|
||||
dec := borrowDecoder(nil, 0)
|
||||
defer dec.Release()
|
||||
dec.data = data
|
||||
dec.length = len(data)
|
||||
_, err := dec.decodeObject(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (u decUnsafe) Unmarshal(data []byte, v interface{}) error {
|
||||
var err error
|
||||
var dec *Decoder
|
||||
switch vt := v.(type) {
|
||||
case *string:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeString(vt)
|
||||
case *int:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt(vt)
|
||||
case *int8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt8(vt)
|
||||
case *int16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt16(vt)
|
||||
case *int32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt32(vt)
|
||||
case *int64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeInt64(vt)
|
||||
case *uint8:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint8(vt)
|
||||
case *uint16:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint16(vt)
|
||||
case *uint32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint32(vt)
|
||||
case *uint64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeUint64(vt)
|
||||
case *float64:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat64(vt)
|
||||
case *float32:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeFloat32(vt)
|
||||
case *bool:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
err = dec.decodeBool(vt)
|
||||
case UnmarshalerJSONObject:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
_, err = dec.decodeObject(vt)
|
||||
case UnmarshalerJSONArray:
|
||||
dec = borrowDecoder(nil, 0)
|
||||
dec.length = len(data)
|
||||
dec.data = data
|
||||
_, err = dec.decodeArray(vt)
|
||||
default:
|
||||
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
|
||||
}
|
||||
defer dec.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dec.err
|
||||
}
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var nullBytes = []byte("null")
|
||||
|
||||
// MarshalJSONArray returns the JSON encoding of v, an implementation of MarshalerJSONArray.
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// type TestSlice []*TestStruct
|
||||
//
|
||||
// func (t TestSlice) MarshalJSONArray(enc *Encoder) {
|
||||
// for _, e := range t {
|
||||
// enc.AddObject(e)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// test := &TestSlice{
|
||||
// &TestStruct{123456},
|
||||
// &TestStruct{7890},
|
||||
// }
|
||||
// b, _ := Marshal(test)
|
||||
// fmt.Println(b) // [{"id":123456},{"id":7890}]
|
||||
// }
|
||||
func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) {
|
||||
enc := BorrowEncoder(nil)
|
||||
enc.grow(512)
|
||||
enc.writeByte('[')
|
||||
v.(MarshalerJSONArray).MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
|
||||
defer func() {
|
||||
enc.buf = make([]byte, 0, 512)
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// MarshalJSONObject returns the JSON encoding of v, an implementation of MarshalerJSONObject.
|
||||
//
|
||||
// Example:
|
||||
// type Object struct {
|
||||
// id int
|
||||
// }
|
||||
// func (s *Object) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
// enc.IntKey("id", s.id)
|
||||
// }
|
||||
// func (s *Object) IsNil() bool {
|
||||
// return s == nil
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// test := &Object{
|
||||
// id: 123456,
|
||||
// }
|
||||
// b, _ := gojay.Marshal(test)
|
||||
// fmt.Println(b) // {"id":123456}
|
||||
// }
|
||||
func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) {
|
||||
enc := BorrowEncoder(nil)
|
||||
enc.grow(512)
|
||||
|
||||
defer func() {
|
||||
enc.buf = make([]byte, 0, 512)
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
return enc.encodeObject(v)
|
||||
}
|
||||
|
||||
// Marshal returns the JSON encoding of v.
|
||||
//
|
||||
// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types:
|
||||
// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool
|
||||
// Marshal returns an InvalidMarshalError.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return marshal(v, false)
|
||||
}
|
||||
|
||||
// MarshalAny returns the JSON encoding of v.
|
||||
//
|
||||
// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types:
|
||||
// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool
|
||||
// MarshalAny falls back to "json/encoding" package to marshal the value.
|
||||
func MarshalAny(v interface{}) ([]byte, error) {
|
||||
return marshal(v, true)
|
||||
}
|
||||
|
||||
func marshal(v interface{}, any bool) ([]byte, error) {
|
||||
var (
|
||||
enc = BorrowEncoder(nil)
|
||||
|
||||
buf []byte
|
||||
err error
|
||||
)
|
||||
|
||||
defer func() {
|
||||
enc.buf = make([]byte, 0, 512)
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
buf, err = func() ([]byte, error) {
|
||||
switch vt := v.(type) {
|
||||
case MarshalerJSONObject:
|
||||
return enc.encodeObject(vt)
|
||||
case MarshalerJSONArray:
|
||||
return enc.encodeArray(vt)
|
||||
case string:
|
||||
return enc.encodeString(vt)
|
||||
case bool:
|
||||
return enc.encodeBool(vt)
|
||||
case int:
|
||||
return enc.encodeInt(vt)
|
||||
case int64:
|
||||
return enc.encodeInt64(vt)
|
||||
case int32:
|
||||
return enc.encodeInt(int(vt))
|
||||
case int16:
|
||||
return enc.encodeInt(int(vt))
|
||||
case int8:
|
||||
return enc.encodeInt(int(vt))
|
||||
case uint64:
|
||||
return enc.encodeInt(int(vt))
|
||||
case uint32:
|
||||
return enc.encodeInt(int(vt))
|
||||
case uint16:
|
||||
return enc.encodeInt(int(vt))
|
||||
case uint8:
|
||||
return enc.encodeInt(int(vt))
|
||||
case float64:
|
||||
return enc.encodeFloat(vt)
|
||||
case float32:
|
||||
return enc.encodeFloat32(vt)
|
||||
case *EmbeddedJSON:
|
||||
return enc.encodeEmbeddedJSON(vt)
|
||||
default:
|
||||
if any {
|
||||
return json.Marshal(vt)
|
||||
}
|
||||
|
||||
return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
|
||||
}
|
||||
}()
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// MarshalerJSONObject is the interface to implement for struct to be encoded
|
||||
type MarshalerJSONObject interface {
|
||||
MarshalJSONObject(enc *Encoder)
|
||||
IsNil() bool
|
||||
}
|
||||
|
||||
// MarshalerJSONArray is the interface to implement
|
||||
// for a slice or an array to be encoded
|
||||
type MarshalerJSONArray interface {
|
||||
MarshalJSONArray(enc *Encoder)
|
||||
IsNil() bool
|
||||
}
|
||||
|
||||
// An Encoder writes JSON values to an output stream.
|
||||
type Encoder struct {
|
||||
buf []byte
|
||||
isPooled byte
|
||||
w io.Writer
|
||||
err error
|
||||
hasKeys bool
|
||||
keys []string
|
||||
}
|
||||
|
||||
// AppendBytes allows a modular usage by appending bytes manually to the current state of the buffer.
|
||||
func (enc *Encoder) AppendBytes(b []byte) {
|
||||
enc.writeBytes(b)
|
||||
}
|
||||
|
||||
// AppendByte allows a modular usage by appending a single byte manually to the current state of the buffer.
|
||||
func (enc *Encoder) AppendByte(b byte) {
|
||||
enc.writeByte(b)
|
||||
}
|
||||
|
||||
// Buf returns the Encoder's buffer.
|
||||
func (enc *Encoder) Buf() []byte {
|
||||
return enc.buf
|
||||
}
|
||||
|
||||
// Write writes to the io.Writer and resets the buffer.
|
||||
func (enc *Encoder) Write() (int, error) {
|
||||
i, err := enc.w.Write(enc.buf)
|
||||
enc.buf = enc.buf[:0]
|
||||
return i, err
|
||||
}
|
||||
|
||||
func (enc *Encoder) getPreviousRune() byte {
|
||||
last := len(enc.buf) - 1
|
||||
return enc.buf[last]
|
||||
}
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// EncodeArray encodes an implementation of MarshalerJSONArray to JSON
|
||||
func (enc *Encoder) EncodeArray(v MarshalerJSONArray) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeArray(v)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (enc *Encoder) encodeArray(v MarshalerJSONArray) ([]byte, error) {
|
||||
enc.grow(200)
|
||||
enc.writeByte('[')
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
return enc.buf, enc.err
|
||||
}
|
||||
|
||||
// AddArray adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) AddArray(v MarshalerJSONArray) {
|
||||
enc.Array(v)
|
||||
}
|
||||
|
||||
// AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerAddArrayOmitEmpty
|
||||
func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) {
|
||||
enc.ArrayOmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement Marshaler, if v is empty, `null` will be encoded`
|
||||
func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) {
|
||||
enc.ArrayNullEmpty(v)
|
||||
}
|
||||
|
||||
// AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) {
|
||||
enc.ArrayKey(key, v)
|
||||
}
|
||||
|
||||
// AddArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil.
|
||||
// Must be called inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
|
||||
enc.ArrayKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil.
|
||||
// Must be called inside an object as it will encode a key. `null` will be encoded`
|
||||
func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
|
||||
enc.ArrayKeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) Array(v MarshalerJSONArray) {
|
||||
if v.IsNil() {
|
||||
enc.grow(3)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('[')
|
||||
enc.writeByte(']')
|
||||
return
|
||||
}
|
||||
enc.grow(100)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('[')
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// ArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) {
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
enc.grow(4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('[')
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) {
|
||||
enc.grow(4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v.IsNil() {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeByte('[')
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
|
||||
// value must implement Marshaler
|
||||
func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.IsNil() {
|
||||
enc.grow(2 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyArr)
|
||||
enc.writeByte(']')
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyArr)
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil.
|
||||
// Must be called inside an object as it will encode a key.
|
||||
func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyArr)
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil.
|
||||
// Must be called inside an object as it will encode a key.
|
||||
func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v.IsNil() {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyArr)
|
||||
v.MarshalJSONArray(enc)
|
||||
enc.writeByte(']')
|
||||
}
|
||||
|
||||
// EncodeArrayFunc is a custom func type implementing MarshaleArray.
|
||||
// Use it to cast a func(*Encoder) to Marshal an object.
|
||||
//
|
||||
// enc := gojay.NewEncoder(io.Writer)
|
||||
// enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) {
|
||||
// enc.AddStringKey("hello", "world")
|
||||
// }))
|
||||
type EncodeArrayFunc func(*Encoder)
|
||||
|
||||
// MarshalJSONArray implements MarshalerJSONArray.
|
||||
func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) {
|
||||
f(enc)
|
||||
}
|
||||
|
||||
// IsNil implements MarshalerJSONArray.
|
||||
func (f EncodeArrayFunc) IsNil() bool {
|
||||
return f == nil
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "strconv"
|
||||
|
||||
// EncodeBool encodes a bool to JSON
|
||||
func (enc *Encoder) EncodeBool(v bool) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeBool(v)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeBool encodes a bool to JSON
|
||||
func (enc *Encoder) encodeBool(v bool) ([]byte, error) {
|
||||
enc.grow(5)
|
||||
if v {
|
||||
enc.writeString("true")
|
||||
} else {
|
||||
enc.writeString("false")
|
||||
}
|
||||
return enc.buf, enc.err
|
||||
}
|
||||
|
||||
// AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddBool(v bool) {
|
||||
enc.Bool(v)
|
||||
}
|
||||
|
||||
// AddBoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddBoolOmitEmpty(v bool) {
|
||||
enc.BoolOmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddBoolNullEmpty(v bool) {
|
||||
enc.BoolNullEmpty(v)
|
||||
}
|
||||
|
||||
// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddBoolKey(key string, v bool) {
|
||||
enc.BoolKey(key, v)
|
||||
}
|
||||
|
||||
// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) {
|
||||
enc.BoolKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) {
|
||||
enc.BoolKeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Bool(v bool) {
|
||||
enc.grow(5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v {
|
||||
enc.writeString("true")
|
||||
} else {
|
||||
enc.writeString("false")
|
||||
}
|
||||
}
|
||||
|
||||
// BoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) BoolOmitEmpty(v bool) {
|
||||
if v == false {
|
||||
return
|
||||
}
|
||||
enc.grow(5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeString("true")
|
||||
}
|
||||
|
||||
// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) BoolNullEmpty(v bool) {
|
||||
enc.grow(5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == false {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeString("true")
|
||||
}
|
||||
|
||||
// BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) BoolKey(key string, value bool) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendBool(enc.buf, value)
|
||||
}
|
||||
|
||||
// BoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == false {
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendBool(enc.buf, v)
|
||||
}
|
||||
|
||||
// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == false {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendBool(enc.buf, v)
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package gojay
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
// grow grows b's capacity, if necessary, to guarantee space for
|
||||
// another n bytes. After grow(n), at least n bytes can be written to b
|
||||
// without another allocation. If n is negative, grow panics.
|
||||
func (enc *Encoder) grow(n int) {
|
||||
if cap(enc.buf)-len(enc.buf) < n {
|
||||
Buf := make([]byte, len(enc.buf), 2*cap(enc.buf)+n)
|
||||
copy(Buf, enc.buf)
|
||||
enc.buf = Buf
|
||||
}
|
||||
}
|
||||
|
||||
// Write appends the contents of p to b's Buffer.
|
||||
// Write always returns len(p), nil.
|
||||
func (enc *Encoder) writeBytes(p []byte) {
|
||||
enc.buf = append(enc.buf, p...)
|
||||
}
|
||||
|
||||
func (enc *Encoder) writeTwoBytes(b1 byte, b2 byte) {
|
||||
enc.buf = append(enc.buf, b1, b2)
|
||||
}
|
||||
|
||||
// WriteByte appends the byte c to b's Buffer.
|
||||
// The returned error is always nil.
|
||||
func (enc *Encoder) writeByte(c byte) {
|
||||
enc.buf = append(enc.buf, c)
|
||||
}
|
||||
|
||||
// WriteString appends the contents of s to b's Buffer.
|
||||
// It returns the length of s and a nil error.
|
||||
func (enc *Encoder) writeString(s string) {
|
||||
enc.buf = append(enc.buf, s...)
|
||||
}
|
||||
|
||||
func (enc *Encoder) writeStringEscape(s string) {
|
||||
l := len(s)
|
||||
for i := 0; i < l; i++ {
|
||||
c := s[i]
|
||||
if c >= 0x20 && c != '\\' && c != '"' {
|
||||
enc.writeByte(c)
|
||||
continue
|
||||
}
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
enc.writeTwoBytes('\\', c)
|
||||
case '\n':
|
||||
enc.writeTwoBytes('\\', 'n')
|
||||
case '\f':
|
||||
enc.writeTwoBytes('\\', 'f')
|
||||
case '\b':
|
||||
enc.writeTwoBytes('\\', 'b')
|
||||
case '\r':
|
||||
enc.writeTwoBytes('\\', 'r')
|
||||
case '\t':
|
||||
enc.writeTwoBytes('\\', 't')
|
||||
default:
|
||||
enc.writeString(`\u00`)
|
||||
enc.writeTwoBytes(hex[c>>4], hex[c&0xF])
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// EncodeEmbeddedJSON encodes an embedded JSON.
|
||||
// is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write()
|
||||
func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
enc.buf = *v
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) {
|
||||
enc.writeBytes(*v)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AddEmbeddedJSON adds an EmbeddedJSON to be encoded.
|
||||
//
|
||||
// It basically blindly writes the bytes to the final buffer. Therefore,
|
||||
// it expects the JSON to be of proper format.
|
||||
func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) {
|
||||
enc.grow(len(*v) + 4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeBytes(*v)
|
||||
}
|
||||
|
||||
// AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty.
|
||||
//
|
||||
// It basically blindly writes the bytes to the final buffer. Therefore,
|
||||
// it expects the JSON to be of proper format.
|
||||
func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) {
|
||||
if v == nil || len(*v) == 0 {
|
||||
return
|
||||
}
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeBytes(*v)
|
||||
}
|
||||
|
||||
// AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded.
|
||||
//
|
||||
// It basically blindly writes the bytes to the final buffer. Therefore,
|
||||
// it expects the JSON to be of proper format.
|
||||
func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(len(key) + len(*v) + 5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.writeBytes(*v)
|
||||
}
|
||||
|
||||
// AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty.
|
||||
//
|
||||
// It basically blindly writes the bytes to the final buffer. Therefore,
|
||||
// it expects the JSON to be of proper format.
|
||||
func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == nil || len(*v) == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(len(key) + len(*v) + 5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.writeBytes(*v)
|
||||
}
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Encode encodes a value to JSON.
|
||||
//
|
||||
// If Encode cannot find a way to encode the type to JSON
|
||||
// it will return an InvalidMarshalError.
|
||||
func (enc *Encoder) Encode(v interface{}) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
switch vt := v.(type) {
|
||||
case string:
|
||||
return enc.EncodeString(vt)
|
||||
case bool:
|
||||
return enc.EncodeBool(vt)
|
||||
case MarshalerJSONArray:
|
||||
return enc.EncodeArray(vt)
|
||||
case MarshalerJSONObject:
|
||||
return enc.EncodeObject(vt)
|
||||
case int:
|
||||
return enc.EncodeInt(vt)
|
||||
case int64:
|
||||
return enc.EncodeInt64(vt)
|
||||
case int32:
|
||||
return enc.EncodeInt(int(vt))
|
||||
case int8:
|
||||
return enc.EncodeInt(int(vt))
|
||||
case uint64:
|
||||
return enc.EncodeUint64(vt)
|
||||
case uint32:
|
||||
return enc.EncodeInt(int(vt))
|
||||
case uint16:
|
||||
return enc.EncodeInt(int(vt))
|
||||
case uint8:
|
||||
return enc.EncodeInt(int(vt))
|
||||
case float64:
|
||||
return enc.EncodeFloat(vt)
|
||||
case float32:
|
||||
return enc.EncodeFloat32(vt)
|
||||
case *EmbeddedJSON:
|
||||
return enc.EncodeEmbeddedJSON(vt)
|
||||
default:
|
||||
return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
|
||||
}
|
||||
}
|
||||
|
||||
// AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInterface(value interface{}) {
|
||||
switch vt := value.(type) {
|
||||
case string:
|
||||
enc.AddString(vt)
|
||||
case bool:
|
||||
enc.AddBool(vt)
|
||||
case MarshalerJSONArray:
|
||||
enc.AddArray(vt)
|
||||
case MarshalerJSONObject:
|
||||
enc.AddObject(vt)
|
||||
case int:
|
||||
enc.AddInt(vt)
|
||||
case int64:
|
||||
enc.AddInt(int(vt))
|
||||
case int32:
|
||||
enc.AddInt(int(vt))
|
||||
case int8:
|
||||
enc.AddInt(int(vt))
|
||||
case uint64:
|
||||
enc.AddUint64(vt)
|
||||
case uint32:
|
||||
enc.AddInt(int(vt))
|
||||
case uint16:
|
||||
enc.AddInt(int(vt))
|
||||
case uint8:
|
||||
enc.AddInt(int(vt))
|
||||
case float64:
|
||||
enc.AddFloat(vt)
|
||||
case float32:
|
||||
enc.AddFloat32(vt)
|
||||
default:
|
||||
if vt != nil {
|
||||
enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInterfaceKey(key string, value interface{}) {
|
||||
switch vt := value.(type) {
|
||||
case string:
|
||||
enc.AddStringKey(key, vt)
|
||||
case bool:
|
||||
enc.AddBoolKey(key, vt)
|
||||
case MarshalerJSONArray:
|
||||
enc.AddArrayKey(key, vt)
|
||||
case MarshalerJSONObject:
|
||||
enc.AddObjectKey(key, vt)
|
||||
case int:
|
||||
enc.AddIntKey(key, vt)
|
||||
case int64:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint64:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case float64:
|
||||
enc.AddFloatKey(key, vt)
|
||||
case float32:
|
||||
enc.AddFloat32Key(key, vt)
|
||||
default:
|
||||
if vt != nil {
|
||||
enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// AddInterfaceKeyOmitEmpty adds an interface{} to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInterfaceKeyOmitEmpty(key string, v interface{}) {
|
||||
switch vt := v.(type) {
|
||||
case string:
|
||||
enc.AddStringKeyOmitEmpty(key, vt)
|
||||
case bool:
|
||||
enc.AddBoolKeyOmitEmpty(key, vt)
|
||||
case MarshalerJSONArray:
|
||||
enc.AddArrayKeyOmitEmpty(key, vt)
|
||||
case MarshalerJSONObject:
|
||||
enc.AddObjectKeyOmitEmpty(key, vt)
|
||||
case int:
|
||||
enc.AddIntKeyOmitEmpty(key, vt)
|
||||
case int64:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case int32:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case int16:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case int8:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case uint64:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case uint32:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case uint16:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case uint8:
|
||||
enc.AddIntKeyOmitEmpty(key, int(vt))
|
||||
case float64:
|
||||
enc.AddFloatKeyOmitEmpty(key, vt)
|
||||
case float32:
|
||||
enc.AddFloat32KeyOmitEmpty(key, vt)
|
||||
default:
|
||||
if vt != nil {
|
||||
enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// AddNull adds a `null` to be encoded. Must be used while encoding an array.`
|
||||
func (enc *Encoder) AddNull() {
|
||||
enc.Null()
|
||||
}
|
||||
|
||||
// Null adds a `null` to be encoded. Must be used while encoding an array.`
|
||||
func (enc *Encoder) Null() {
|
||||
enc.grow(5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeBytes(nullBytes)
|
||||
}
|
||||
|
||||
// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.`
|
||||
func (enc *Encoder) AddNullKey(key string) {
|
||||
enc.NullKey(key)
|
||||
}
|
||||
|
||||
// NullKey adds a `null` to be encoded. Must be used while encoding an array.`
|
||||
func (enc *Encoder) NullKey(key string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.writeBytes(nullBytes)
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
package gojay
|
||||
-368
@@ -1,368 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "strconv"
|
||||
|
||||
// EncodeFloat encodes a float64 to JSON
|
||||
func (enc *Encoder) EncodeFloat(n float64) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeFloat(n)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeFloat encodes a float64 to JSON
|
||||
func (enc *Encoder) encodeFloat(n float64) ([]byte, error) {
|
||||
enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// EncodeFloat32 encodes a float32 to JSON
|
||||
func (enc *Encoder) EncodeFloat32(n float32) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeFloat32(n)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) {
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddFloat(v float64) {
|
||||
enc.Float64(v)
|
||||
}
|
||||
|
||||
// AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddFloatOmitEmpty(v float64) {
|
||||
enc.Float64OmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddFloatNullEmpty(v float64) {
|
||||
enc.Float64NullEmpty(v)
|
||||
}
|
||||
|
||||
// Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Float(v float64) {
|
||||
enc.Float64(v)
|
||||
}
|
||||
|
||||
// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) FloatOmitEmpty(v float64) {
|
||||
enc.Float64OmitEmpty(v)
|
||||
}
|
||||
|
||||
// FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) FloatNullEmpty(v float64) {
|
||||
enc.Float64NullEmpty(v)
|
||||
}
|
||||
|
||||
// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloatKey(key string, v float64) {
|
||||
enc.Float64Key(key, v)
|
||||
}
|
||||
|
||||
// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) {
|
||||
enc.Float64KeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) {
|
||||
enc.Float64KeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) FloatKey(key string, v float64) {
|
||||
enc.Float64Key(key, v)
|
||||
}
|
||||
|
||||
// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) {
|
||||
enc.Float64KeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) {
|
||||
enc.Float64KeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddFloat64(v float64) {
|
||||
enc.Float(v)
|
||||
}
|
||||
|
||||
// AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddFloat64OmitEmpty(v float64) {
|
||||
enc.FloatOmitEmpty(v)
|
||||
}
|
||||
|
||||
// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Float64(v float64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Float64OmitEmpty(v float64) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Float64NullEmpty(v float64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloat64Key(key string, v float64) {
|
||||
enc.FloatKey(key, v)
|
||||
}
|
||||
|
||||
// AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) {
|
||||
enc.FloatKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Float64Key(key string, value float64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.grow(10)
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddFloat32(v float32) {
|
||||
enc.Float32(v)
|
||||
}
|
||||
|
||||
// AddFloat32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddFloat32OmitEmpty(v float32) {
|
||||
enc.Float32OmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddFloat32NullEmpty(v float32) {
|
||||
enc.Float32NullEmpty(v)
|
||||
}
|
||||
|
||||
// Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Float32(v float32) {
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// Float32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Float32OmitEmpty(v float32) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// Float32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Float32NullEmpty(v float32) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloat32Key(key string, v float32) {
|
||||
enc.Float32Key(key, v)
|
||||
}
|
||||
|
||||
// AddFloat32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) {
|
||||
enc.Float32KeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) {
|
||||
enc.Float32KeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Float32Key(key string, v float32) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeByte('"')
|
||||
enc.writeByte(':')
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// Float32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
|
||||
}
|
||||
-500
@@ -1,500 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "strconv"
|
||||
|
||||
// EncodeInt encodes an int to JSON
|
||||
func (enc *Encoder) EncodeInt(n int) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeInt(n)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeInt encodes an int to JSON
|
||||
func (enc *Encoder) encodeInt(n int) ([]byte, error) {
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(n), 10)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// EncodeInt64 encodes an int64 to JSON
|
||||
func (enc *Encoder) EncodeInt64(n int64) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeInt64(n)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeInt64 encodes an int to JSON
|
||||
func (enc *Encoder) encodeInt64(n int64) ([]byte, error) {
|
||||
enc.buf = strconv.AppendInt(enc.buf, n, 10)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInt(v int) {
|
||||
enc.Int(v)
|
||||
}
|
||||
|
||||
// AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddIntOmitEmpty(v int) {
|
||||
enc.IntOmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddIntNullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddIntNullEmpty(v int) {
|
||||
enc.IntNullEmpty(v)
|
||||
}
|
||||
|
||||
// Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Int(v int) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// IntOmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) IntOmitEmpty(v int) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// IntNullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) IntNullEmpty(v int) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddIntKey(key string, v int) {
|
||||
enc.IntKey(key, v)
|
||||
}
|
||||
|
||||
// AddIntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) {
|
||||
enc.IntKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) {
|
||||
enc.IntKeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// IntKey adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) IntKey(key string, v int) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// IntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) IntKeyOmitEmpty(key string, v int) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) IntKeyNullEmpty(key string, v int) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
|
||||
}
|
||||
|
||||
// AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInt64(v int64) {
|
||||
enc.Int64(v)
|
||||
}
|
||||
|
||||
// AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt64OmitEmpty(v int64) {
|
||||
enc.Int64OmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt64NullEmpty(v int64) {
|
||||
enc.Int64NullEmpty(v)
|
||||
}
|
||||
|
||||
// Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Int64(v int64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Int64OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int64OmitEmpty(v int64) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Int64NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int64NullEmpty(v int64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInt64Key(key string, v int64) {
|
||||
enc.Int64Key(key, v)
|
||||
}
|
||||
|
||||
// AddInt64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) {
|
||||
enc.Int64KeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) {
|
||||
enc.Int64KeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Int64Key(key string, v int64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Int64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendInt(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInt32(v int32) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt32OmitEmpty(v int32) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt32NullEmpty(v int32) {
|
||||
enc.Int64NullEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Int32(v int32) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// Int32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int32OmitEmpty(v int32) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int32NullEmpty(v int32) {
|
||||
enc.Int64NullEmpty(int64(v))
|
||||
}
|
||||
|
||||
// AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInt32Key(key string, v int32) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Int32Key(key string, v int32) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) {
|
||||
enc.Int64KeyNullEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInt16(v int16) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt16OmitEmpty(v int16) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Int16(v int16) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// Int16OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int16OmitEmpty(v int16) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int16NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int16NullEmpty(v int16) {
|
||||
enc.Int64NullEmpty(int64(v))
|
||||
}
|
||||
|
||||
// AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInt16Key(key string, v int16) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) {
|
||||
enc.Int64KeyNullEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Int16Key(key string, v int16) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) {
|
||||
enc.Int64KeyNullEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddInt8(v int8) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt8OmitEmpty(v int8) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddInt8NullEmpty(v int8) {
|
||||
enc.Int64NullEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Int8(v int8) {
|
||||
enc.Int64(int64(v))
|
||||
}
|
||||
|
||||
// Int8OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int8OmitEmpty(v int8) {
|
||||
enc.Int64OmitEmpty(int64(v))
|
||||
}
|
||||
|
||||
// Int8NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Int8NullEmpty(v int8) {
|
||||
enc.Int64NullEmpty(int64(v))
|
||||
}
|
||||
|
||||
// AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddInt8Key(key string, v int8) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) {
|
||||
enc.Int64KeyNullEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Int8Key(key string, v int8) {
|
||||
enc.Int64Key(key, int64(v))
|
||||
}
|
||||
|
||||
// Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) {
|
||||
enc.Int64KeyOmitEmpty(key, int64(v))
|
||||
}
|
||||
|
||||
// Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) {
|
||||
enc.Int64KeyNullEmpty(key, int64(v))
|
||||
}
|
||||
-362
@@ -1,362 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "strconv"
|
||||
|
||||
// EncodeUint64 encodes an int64 to JSON
|
||||
func (enc *Encoder) EncodeUint64(n uint64) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeUint64(n)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeUint64 encodes an int to JSON
|
||||
func (enc *Encoder) encodeUint64(n uint64) ([]byte, error) {
|
||||
enc.buf = strconv.AppendUint(enc.buf, n, 10)
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AddUint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddUint64(v uint64) {
|
||||
enc.Uint64(v)
|
||||
}
|
||||
|
||||
// AddUint64OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint64OmitEmpty(v uint64) {
|
||||
enc.Uint64OmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint64NullEmpty(v uint64) {
|
||||
enc.Uint64NullEmpty(v)
|
||||
}
|
||||
|
||||
// Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Uint64(v uint64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Uint64OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint64OmitEmpty(v uint64) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Uint64NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint64NullEmpty(v uint64) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddUint64Key(key string, v uint64) {
|
||||
enc.Uint64Key(key, v)
|
||||
}
|
||||
|
||||
// AddUint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) {
|
||||
enc.Uint64KeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) {
|
||||
enc.Uint64KeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Uint64Key(key string, v uint64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Uint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == 0 {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.buf = strconv.AppendUint(enc.buf, v, 10)
|
||||
}
|
||||
|
||||
// AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddUint32(v uint32) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint32OmitEmpty(v uint32) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint32NullEmpty(v uint32) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Uint32(v uint32) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint32OmitEmpty(v uint32) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint32NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint32NullEmpty(v uint32) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddUint32Key(key string, v uint32) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Uint32Key(key string, v uint32) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddUint16(v uint16) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint16OmitEmpty(v uint16) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint16NullEmpty(v uint16) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Uint16(v uint16) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint16OmitEmpty(v uint16) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint16NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint16NullEmpty(v uint16) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddUint16Key(key string, v uint16) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Uint16Key(key string, v uint16) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddUint8(v uint8) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint8OmitEmpty(v uint8) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) AddUint8NullEmpty(v uint8) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Uint8(v uint8) {
|
||||
enc.Uint64(uint64(v))
|
||||
}
|
||||
|
||||
// Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint8OmitEmpty(v uint8) {
|
||||
enc.Uint64OmitEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// Uint8NullEmpty adds an int to be encoded and skips it if its value is 0,
|
||||
// must be used inside a slice or array encoding (does not encode a key).
|
||||
func (enc *Encoder) Uint8NullEmpty(v uint8) {
|
||||
enc.Uint64NullEmpty(uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddUint8Key(key string, v uint8) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) Uint8Key(key string, v uint8) {
|
||||
enc.Uint64Key(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) {
|
||||
enc.Uint64KeyOmitEmpty(key, uint64(v))
|
||||
}
|
||||
|
||||
// Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
|
||||
// Must be used inside an object as it will encode a key.
|
||||
func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) {
|
||||
enc.Uint64KeyNullEmpty(key, uint64(v))
|
||||
}
|
||||
-400
@@ -1,400 +0,0 @@
|
||||
package gojay
|
||||
|
||||
var objKeyStr = []byte(`":"`)
|
||||
var objKeyObj = []byte(`":{`)
|
||||
var objKeyArr = []byte(`":[`)
|
||||
var objKey = []byte(`":`)
|
||||
|
||||
// EncodeObject encodes an object to JSON
|
||||
func (enc *Encoder) EncodeObject(v MarshalerJSONObject) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, err := enc.encodeObject(v)
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
_, err = enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeObjectKeys encodes an object to JSON
|
||||
func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
enc.hasKeys = true
|
||||
enc.keys = keys
|
||||
_, err := enc.encodeObject(v)
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
_, err = enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) {
|
||||
enc.grow(512)
|
||||
enc.writeByte('{')
|
||||
if !v.IsNil() {
|
||||
v.MarshalJSONObject(enc)
|
||||
}
|
||||
if enc.hasKeys {
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
}
|
||||
enc.writeByte('}')
|
||||
return enc.buf, enc.err
|
||||
}
|
||||
|
||||
// AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObject(v MarshalerJSONObject) {
|
||||
enc.Object(v)
|
||||
}
|
||||
|
||||
// AddObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) {
|
||||
enc.ObjectOmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) {
|
||||
enc.ObjectNullEmpty(v)
|
||||
}
|
||||
|
||||
// AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) {
|
||||
enc.ObjectKey(key, v)
|
||||
}
|
||||
|
||||
// AddObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) {
|
||||
enc.ObjectKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
|
||||
enc.ObjectKeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) Object(v MarshalerJSONObject) {
|
||||
if v.IsNil() {
|
||||
enc.grow(2)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('{')
|
||||
enc.writeByte('}')
|
||||
return
|
||||
}
|
||||
enc.grow(4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('{')
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject. It will only encode the keys in keys.
|
||||
func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) {
|
||||
if v.IsNil() {
|
||||
enc.grow(2)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' && r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('{')
|
||||
enc.writeByte('}')
|
||||
return
|
||||
}
|
||||
enc.grow(4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('{')
|
||||
|
||||
var origKeys = enc.keys
|
||||
var origHasKeys = enc.hasKeys
|
||||
enc.hasKeys = true
|
||||
enc.keys = keys
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) {
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
enc.grow(2)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('{')
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) {
|
||||
enc.grow(2)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
if v.IsNil() {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeByte('{')
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.IsNil() {
|
||||
enc.grow(2 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyObj)
|
||||
enc.writeByte('}')
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyObj)
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key.
|
||||
// Value must implement MarshalerJSONObject. It will only encode the keys in keys.
|
||||
func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if value.IsNil() {
|
||||
enc.grow(2 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyObj)
|
||||
enc.writeByte('}')
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyObj)
|
||||
var origKeys = enc.keys
|
||||
var origHasKeys = enc.hasKeys
|
||||
enc.hasKeys = true
|
||||
enc.keys = keys
|
||||
value.MarshalJSONObject(enc)
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyObj)
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
// value must implement MarshalerJSONObject
|
||||
func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(5 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v.IsNil() {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeByte('{')
|
||||
|
||||
var origHasKeys = enc.hasKeys
|
||||
var origKeys = enc.keys
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
|
||||
v.MarshalJSONObject(enc)
|
||||
|
||||
enc.hasKeys = origHasKeys
|
||||
enc.keys = origKeys
|
||||
|
||||
enc.writeByte('}')
|
||||
}
|
||||
|
||||
// EncodeObjectFunc is a custom func type implementing MarshaleObject.
|
||||
// Use it to cast a func(*Encoder) to Marshal an object.
|
||||
//
|
||||
// enc := gojay.NewEncoder(io.Writer)
|
||||
// enc.EncodeObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) {
|
||||
// enc.AddStringKey("hello", "world")
|
||||
// }))
|
||||
type EncodeObjectFunc func(*Encoder)
|
||||
|
||||
// MarshalJSONObject implements MarshalerJSONObject.
|
||||
func (f EncodeObjectFunc) MarshalJSONObject(enc *Encoder) {
|
||||
f(enc)
|
||||
}
|
||||
|
||||
// IsNil implements MarshalerJSONObject.
|
||||
func (f EncodeObjectFunc) IsNil() bool {
|
||||
return f == nil
|
||||
}
|
||||
|
||||
func (enc *Encoder) keyExists(k string) bool {
|
||||
if enc.keys == nil {
|
||||
return false
|
||||
}
|
||||
for _, key := range enc.keys {
|
||||
if key == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var encPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return NewEncoder(nil)
|
||||
},
|
||||
}
|
||||
|
||||
var streamEncPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return Stream.NewEncoder(nil)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 32; i++ {
|
||||
encPool.Put(NewEncoder(nil))
|
||||
}
|
||||
for i := 0; i < 32; i++ {
|
||||
streamEncPool.Put(Stream.NewEncoder(nil))
|
||||
}
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder or borrows one from the pool
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w: w}
|
||||
}
|
||||
|
||||
// BorrowEncoder borrows an Encoder from the pool.
|
||||
func BorrowEncoder(w io.Writer) *Encoder {
|
||||
enc := encPool.Get().(*Encoder)
|
||||
enc.w = w
|
||||
enc.buf = enc.buf[:0]
|
||||
enc.isPooled = 0
|
||||
enc.err = nil
|
||||
enc.hasKeys = false
|
||||
enc.keys = nil
|
||||
return enc
|
||||
}
|
||||
|
||||
// Release sends back a Encoder to the pool.
|
||||
func (enc *Encoder) Release() {
|
||||
enc.isPooled = 1
|
||||
encPool.Put(enc)
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// AddSliceString marshals the given []string s
|
||||
func (enc *Encoder) AddSliceString(s []string) {
|
||||
enc.SliceString(s)
|
||||
}
|
||||
|
||||
// SliceString marshals the given []string s
|
||||
func (enc *Encoder) SliceString(s []string) {
|
||||
enc.Array(EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, str := range s {
|
||||
enc.String(str)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceStringKey marshals the given []string s
|
||||
func (enc *Encoder) AddSliceStringKey(k string, s []string) {
|
||||
enc.SliceStringKey(k, s)
|
||||
}
|
||||
|
||||
// SliceStringKey marshals the given []string s
|
||||
func (enc *Encoder) SliceStringKey(k string, s []string) {
|
||||
enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, str := range s {
|
||||
enc.String(str)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceInt marshals the given []int s
|
||||
func (enc *Encoder) AddSliceInt(s []int) {
|
||||
enc.SliceInt(s)
|
||||
}
|
||||
|
||||
// SliceInt marshals the given []int s
|
||||
func (enc *Encoder) SliceInt(s []int) {
|
||||
enc.Array(EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Int(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceIntKey marshals the given []int s
|
||||
func (enc *Encoder) AddSliceIntKey(k string, s []int) {
|
||||
enc.SliceIntKey(k, s)
|
||||
}
|
||||
|
||||
// SliceIntKey marshals the given []int s
|
||||
func (enc *Encoder) SliceIntKey(k string, s []int) {
|
||||
enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Int(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceFloat64 marshals the given []float64 s
|
||||
func (enc *Encoder) AddSliceFloat64(s []float64) {
|
||||
enc.SliceFloat64(s)
|
||||
}
|
||||
|
||||
// SliceFloat64 marshals the given []float64 s
|
||||
func (enc *Encoder) SliceFloat64(s []float64) {
|
||||
enc.Array(EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Float64(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceFloat64Key marshals the given []float64 s
|
||||
func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) {
|
||||
enc.SliceFloat64Key(k, s)
|
||||
}
|
||||
|
||||
// SliceFloat64Key marshals the given []float64 s
|
||||
func (enc *Encoder) SliceFloat64Key(k string, s []float64) {
|
||||
enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Float64(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceBool marshals the given []bool s
|
||||
func (enc *Encoder) AddSliceBool(s []bool) {
|
||||
enc.SliceBool(s)
|
||||
}
|
||||
|
||||
// SliceBool marshals the given []bool s
|
||||
func (enc *Encoder) SliceBool(s []bool) {
|
||||
enc.Array(EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Bool(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// AddSliceBoolKey marshals the given []bool s
|
||||
func (enc *Encoder) AddSliceBoolKey(k string, s []bool) {
|
||||
enc.SliceBoolKey(k, s)
|
||||
}
|
||||
|
||||
// SliceBoolKey marshals the given []bool s
|
||||
func (enc *Encoder) SliceBoolKey(k string, s []bool) {
|
||||
enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
|
||||
for _, i := range s {
|
||||
enc.Bool(i)
|
||||
}
|
||||
}))
|
||||
}
|
||||
-377
@@ -1,377 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// EncodeSQLNullString encodes a string to
|
||||
func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeString(v.String)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullString(v *sql.NullString) {
|
||||
enc.String(v.String)
|
||||
}
|
||||
|
||||
// AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) {
|
||||
if v != nil && v.Valid && v.String != "" {
|
||||
enc.StringOmitEmpty(v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) {
|
||||
if v != nil && v.Valid {
|
||||
enc.StringNullEmpty(v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) {
|
||||
enc.StringKey(key, v.String)
|
||||
}
|
||||
|
||||
// AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) {
|
||||
if v != nil && v.Valid && v.String != "" {
|
||||
enc.StringKeyOmitEmpty(key, v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullString(v *sql.NullString) {
|
||||
enc.String(v.String)
|
||||
}
|
||||
|
||||
// SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) {
|
||||
if v != nil && v.Valid && v.String != "" {
|
||||
enc.String(v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) {
|
||||
if v != nil && v.Valid {
|
||||
enc.StringNullEmpty(v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) {
|
||||
enc.StringKey(key, v.String)
|
||||
}
|
||||
|
||||
// SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) {
|
||||
if v != nil && v.Valid && v.String != "" {
|
||||
enc.StringKeyOmitEmpty(key, v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) {
|
||||
if v != nil && v.Valid {
|
||||
enc.StringKeyNullEmpty(key, v.String)
|
||||
}
|
||||
}
|
||||
|
||||
// NullInt64
|
||||
|
||||
// EncodeSQLNullInt64 encodes a string to
|
||||
func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeInt64(v.Int64)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) {
|
||||
enc.Int64(v.Int64)
|
||||
}
|
||||
|
||||
// AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) {
|
||||
if v != nil && v.Valid && v.Int64 != 0 {
|
||||
enc.Int64OmitEmpty(v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Int64NullEmpty(v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) {
|
||||
enc.Int64Key(key, v.Int64)
|
||||
}
|
||||
|
||||
// AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) {
|
||||
if v != nil && v.Valid && v.Int64 != 0 {
|
||||
enc.Int64KeyOmitEmpty(key, v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Int64KeyNullEmpty(key, v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) {
|
||||
enc.Int64(v.Int64)
|
||||
}
|
||||
|
||||
// SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) {
|
||||
if v != nil && v.Valid && v.Int64 != 0 {
|
||||
enc.Int64(v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Int64NullEmpty(v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) {
|
||||
enc.Int64Key(key, v.Int64)
|
||||
}
|
||||
|
||||
// SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) {
|
||||
if v != nil && v.Valid && v.Int64 != 0 {
|
||||
enc.Int64KeyOmitEmpty(key, v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Int64KeyNullEmpty(key, v.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// NullFloat64
|
||||
|
||||
// EncodeSQLNullFloat64 encodes a string to
|
||||
func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeFloat(v.Float64)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) {
|
||||
enc.Float64(v.Float64)
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid && v.Float64 != 0 {
|
||||
enc.Float64OmitEmpty(v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Float64NullEmpty(v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) {
|
||||
enc.Float64Key(key, v.Float64)
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid && v.Float64 != 0 {
|
||||
enc.Float64KeyOmitEmpty(key, v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Float64KeyNullEmpty(key, v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) {
|
||||
enc.Float64(v.Float64)
|
||||
}
|
||||
|
||||
// SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid && v.Float64 != 0 {
|
||||
enc.Float64(v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Float64NullEmpty(v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) {
|
||||
enc.Float64Key(key, v.Float64)
|
||||
}
|
||||
|
||||
// SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid && v.Float64 != 0 {
|
||||
enc.Float64KeyOmitEmpty(key, v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) {
|
||||
if v != nil && v.Valid {
|
||||
enc.Float64KeyNullEmpty(key, v.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
// NullBool
|
||||
|
||||
// EncodeSQLNullBool encodes a string to
|
||||
func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeBool(v.Bool)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) {
|
||||
enc.Bool(v.Bool)
|
||||
}
|
||||
|
||||
// AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) {
|
||||
if v != nil && v.Valid && v.Bool != false {
|
||||
enc.BoolOmitEmpty(v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) {
|
||||
enc.BoolKey(key, v.Bool)
|
||||
}
|
||||
|
||||
// AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) {
|
||||
if v != nil && v.Valid && v.Bool != false {
|
||||
enc.BoolKeyOmitEmpty(key, v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) {
|
||||
if v != nil && v.Valid {
|
||||
enc.BoolKeyNullEmpty(key, v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBool(v *sql.NullBool) {
|
||||
enc.Bool(v.Bool)
|
||||
}
|
||||
|
||||
// SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) {
|
||||
if v != nil && v.Valid && v.Bool != false {
|
||||
enc.Bool(v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) {
|
||||
if v != nil && v.Valid {
|
||||
enc.BoolNullEmpty(v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) {
|
||||
enc.BoolKey(key, v.Bool)
|
||||
}
|
||||
|
||||
// SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) {
|
||||
if v != nil && v.Valid && v.Bool != false {
|
||||
enc.BoolKeyOmitEmpty(key, v.Bool)
|
||||
}
|
||||
}
|
||||
|
||||
// SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) {
|
||||
if v != nil && v.Valid {
|
||||
enc.BoolKeyNullEmpty(key, v.Bool)
|
||||
}
|
||||
}
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MarshalerStream is the interface to implement
|
||||
// to continuously encode of stream of data.
|
||||
type MarshalerStream interface {
|
||||
MarshalStream(enc *StreamEncoder)
|
||||
}
|
||||
|
||||
// A StreamEncoder reads and encodes values to JSON from an input stream.
|
||||
//
|
||||
// It implements conext.Context and provide a channel to notify interruption.
|
||||
type StreamEncoder struct {
|
||||
mux *sync.RWMutex
|
||||
*Encoder
|
||||
nConsumer int
|
||||
delimiter byte
|
||||
deadline *time.Time
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// EncodeStream spins up a defined number of non blocking consumers of the MarshalerStream m.
|
||||
//
|
||||
// m must implement MarshalerStream. Ideally m is a channel. See example for implementation.
|
||||
//
|
||||
// See the documentation for Marshal for details about the conversion of Go value to JSON.
|
||||
func (s *StreamEncoder) EncodeStream(m MarshalerStream) {
|
||||
// if a single consumer, just use this encoder
|
||||
if s.nConsumer == 1 {
|
||||
go consume(s, s, m)
|
||||
return
|
||||
}
|
||||
// else use this Encoder only for first consumer
|
||||
// and use new encoders for other consumers
|
||||
// this is to avoid concurrent writing to same buffer
|
||||
// resulting in a weird JSON
|
||||
go consume(s, s, m)
|
||||
for i := 1; i < s.nConsumer; i++ {
|
||||
s.mux.RLock()
|
||||
select {
|
||||
case <-s.done:
|
||||
default:
|
||||
ss := Stream.borrowEncoder(s.w)
|
||||
ss.mux.Lock()
|
||||
ss.done = s.done
|
||||
ss.buf = make([]byte, 0, 512)
|
||||
ss.delimiter = s.delimiter
|
||||
go consume(s, ss, m)
|
||||
ss.mux.Unlock()
|
||||
}
|
||||
s.mux.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LineDelimited sets the delimiter to a new line character.
|
||||
//
|
||||
// It will add a new line after each JSON marshaled by the MarshalerStream
|
||||
func (s *StreamEncoder) LineDelimited() *StreamEncoder {
|
||||
s.delimiter = '\n'
|
||||
return s
|
||||
}
|
||||
|
||||
// CommaDelimited sets the delimiter to a comma.
|
||||
//
|
||||
// It will add a new line after each JSON marshaled by the MarshalerStream
|
||||
func (s *StreamEncoder) CommaDelimited() *StreamEncoder {
|
||||
s.delimiter = ','
|
||||
return s
|
||||
}
|
||||
|
||||
// NConsumer sets the number of non blocking go routine to consume the stream.
|
||||
func (s *StreamEncoder) NConsumer(n int) *StreamEncoder {
|
||||
s.nConsumer = n
|
||||
return s
|
||||
}
|
||||
|
||||
// Release sends back a Decoder to the pool.
|
||||
// If a decoder is used after calling Release
|
||||
// a panic will be raised with an InvalidUsagePooledDecoderError error.
|
||||
func (s *StreamEncoder) Release() {
|
||||
s.isPooled = 1
|
||||
streamEncPool.Put(s)
|
||||
}
|
||||
|
||||
// Done returns a channel that's closed when work is done.
|
||||
// It implements context.Context
|
||||
func (s *StreamEncoder) Done() <-chan struct{} {
|
||||
return s.done
|
||||
}
|
||||
|
||||
// Err returns nil if Done is not yet closed.
|
||||
// If Done is closed, Err returns a non-nil error explaining why.
|
||||
// It implements context.Context
|
||||
func (s *StreamEncoder) Err() error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
func (s *StreamEncoder) Deadline() (time.Time, bool) {
|
||||
if s.deadline != nil {
|
||||
return *s.deadline, true
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// SetDeadline sets the deadline
|
||||
func (s *StreamEncoder) SetDeadline(t time.Time) {
|
||||
s.deadline = &t
|
||||
}
|
||||
|
||||
// Value implements context.Context
|
||||
func (s *StreamEncoder) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancel cancels the consumers of the stream, interrupting the stream encoding.
|
||||
//
|
||||
// After calling cancel, Done() will return a closed channel.
|
||||
func (s *StreamEncoder) Cancel(err error) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
select {
|
||||
case <-s.done:
|
||||
default:
|
||||
s.err = err
|
||||
close(s.done)
|
||||
}
|
||||
}
|
||||
|
||||
// AddObject adds an object to be encoded.
|
||||
// value must implement MarshalerJSONObject.
|
||||
func (s *StreamEncoder) AddObject(v MarshalerJSONObject) {
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
s.Encoder.writeByte('{')
|
||||
v.MarshalJSONObject(s.Encoder)
|
||||
s.Encoder.writeByte('}')
|
||||
s.Encoder.writeByte(s.delimiter)
|
||||
}
|
||||
|
||||
// AddString adds a string to be encoded.
|
||||
func (s *StreamEncoder) AddString(v string) {
|
||||
s.Encoder.writeByte('"')
|
||||
s.Encoder.writeString(v)
|
||||
s.Encoder.writeByte('"')
|
||||
s.Encoder.writeByte(s.delimiter)
|
||||
}
|
||||
|
||||
// AddArray adds an implementation of MarshalerJSONArray to be encoded.
|
||||
func (s *StreamEncoder) AddArray(v MarshalerJSONArray) {
|
||||
s.Encoder.writeByte('[')
|
||||
v.MarshalJSONArray(s.Encoder)
|
||||
s.Encoder.writeByte(']')
|
||||
s.Encoder.writeByte(s.delimiter)
|
||||
}
|
||||
|
||||
// AddInt adds an int to be encoded.
|
||||
func (s *StreamEncoder) AddInt(value int) {
|
||||
s.buf = strconv.AppendInt(s.buf, int64(value), 10)
|
||||
s.Encoder.writeByte(s.delimiter)
|
||||
}
|
||||
|
||||
// AddFloat64 adds a float64 to be encoded.
|
||||
func (s *StreamEncoder) AddFloat64(value float64) {
|
||||
s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64)
|
||||
s.Encoder.writeByte(s.delimiter)
|
||||
}
|
||||
|
||||
// AddFloat adds a float64 to be encoded.
|
||||
func (s *StreamEncoder) AddFloat(value float64) {
|
||||
s.AddFloat64(value)
|
||||
}
|
||||
|
||||
// Non exposed
|
||||
|
||||
func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) {
|
||||
defer s.Release()
|
||||
for {
|
||||
select {
|
||||
case <-init.Done():
|
||||
return
|
||||
default:
|
||||
m.MarshalStream(s)
|
||||
if s.Encoder.err != nil {
|
||||
init.Cancel(s.Encoder.err)
|
||||
return
|
||||
}
|
||||
i, err := s.Encoder.Write()
|
||||
if err != nil || i == 0 {
|
||||
init.Cancel(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewEncoder returns a new StreamEncoder.
|
||||
// It takes an io.Writer implementation to output data.
|
||||
// It initiates the done channel returned by Done().
|
||||
func (s stream) NewEncoder(w io.Writer) *StreamEncoder {
|
||||
enc := BorrowEncoder(w)
|
||||
return &StreamEncoder{Encoder: enc, nConsumer: 1, done: make(chan struct{}, 1), mux: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// BorrowEncoder borrows a StreamEncoder from the pool.
|
||||
// It takes an io.Writer implementation to output data.
|
||||
// It initiates the done channel returned by Done().
|
||||
//
|
||||
// If no StreamEncoder is available in the pool, it returns a fresh one
|
||||
func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder {
|
||||
streamEnc := streamEncPool.Get().(*StreamEncoder)
|
||||
streamEnc.w = w
|
||||
streamEnc.Encoder.err = nil
|
||||
streamEnc.done = make(chan struct{}, 1)
|
||||
streamEnc.Encoder.buf = streamEnc.buf[:0]
|
||||
streamEnc.nConsumer = 1
|
||||
streamEnc.isPooled = 0
|
||||
return streamEnc
|
||||
}
|
||||
|
||||
func (s stream) borrowEncoder(w io.Writer) *StreamEncoder {
|
||||
streamEnc := streamEncPool.Get().(*StreamEncoder)
|
||||
streamEnc.isPooled = 0
|
||||
streamEnc.w = w
|
||||
streamEnc.Encoder.err = nil
|
||||
return streamEnc
|
||||
}
|
||||
-186
@@ -1,186 +0,0 @@
|
||||
package gojay
|
||||
|
||||
// EncodeString encodes a string to
|
||||
func (enc *Encoder) EncodeString(s string) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeString(s)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
enc.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeString encodes a string to
|
||||
func (enc *Encoder) encodeString(v string) ([]byte, error) {
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AppendString appends a string to the buffer
|
||||
func (enc *Encoder) AppendString(v string) {
|
||||
enc.grow(len(v) + 2)
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddString(v string) {
|
||||
enc.String(v)
|
||||
}
|
||||
|
||||
// AddStringOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddStringOmitEmpty(v string) {
|
||||
enc.StringOmitEmpty(v)
|
||||
}
|
||||
|
||||
// AddStringNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddStringNullEmpty(v string) {
|
||||
enc.StringNullEmpty(v)
|
||||
}
|
||||
|
||||
// AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddStringKey(key, v string) {
|
||||
enc.StringKey(key, v)
|
||||
}
|
||||
|
||||
// AddStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) {
|
||||
enc.StringKeyOmitEmpty(key, v)
|
||||
}
|
||||
|
||||
// AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddStringKeyNullEmpty(key, v string) {
|
||||
enc.StringKeyNullEmpty(key, v)
|
||||
}
|
||||
|
||||
// String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) String(v string) {
|
||||
enc.grow(len(v) + 4)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// StringOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) StringOmitEmpty(v string) {
|
||||
if v == "" {
|
||||
return
|
||||
}
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// StringNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) StringNullEmpty(v string) {
|
||||
r := enc.getPreviousRune()
|
||||
if v == "" {
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
enc.writeBytes(nullBytes)
|
||||
} else {
|
||||
enc.writeBytes(nullBytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
if r != '[' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// StringKey adds a string to be encoded, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) StringKey(key, v string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(len(key) + len(v) + 5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyStr)
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// StringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) StringKeyOmitEmpty(key, v string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v == "" {
|
||||
return
|
||||
}
|
||||
enc.grow(len(key) + len(v) + 5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyStr)
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
|
||||
// Must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) StringKeyNullEmpty(key, v string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(len(key) + len(v) + 5)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKey)
|
||||
if v == "" {
|
||||
enc.writeBytes(nullBytes)
|
||||
return
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.writeStringEscape(v)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// EncodeTime encodes a *time.Time to JSON with the given format
|
||||
func (enc *Encoder) EncodeTime(t *time.Time, format string) error {
|
||||
if enc.isPooled == 1 {
|
||||
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
|
||||
}
|
||||
_, _ = enc.encodeTime(t, format)
|
||||
_, err := enc.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeInt encodes an int to JSON
|
||||
func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) {
|
||||
enc.writeByte('"')
|
||||
enc.buf = t.AppendFormat(enc.buf, format)
|
||||
enc.writeByte('"')
|
||||
return enc.buf, nil
|
||||
}
|
||||
|
||||
// AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) {
|
||||
enc.TimeKey(key, t, format)
|
||||
}
|
||||
|
||||
// TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
|
||||
func (enc *Encoder) TimeKey(key string, t *time.Time, format string) {
|
||||
if enc.hasKeys {
|
||||
if !enc.keyExists(key) {
|
||||
return
|
||||
}
|
||||
}
|
||||
enc.grow(10 + len(key))
|
||||
r := enc.getPreviousRune()
|
||||
if r != '{' {
|
||||
enc.writeTwoBytes(',', '"')
|
||||
} else {
|
||||
enc.writeByte('"')
|
||||
}
|
||||
enc.writeStringEscape(key)
|
||||
enc.writeBytes(objKeyStr)
|
||||
enc.buf = t.AppendFormat(enc.buf, format)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
|
||||
// AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) AddTime(t *time.Time, format string) {
|
||||
enc.Time(t, format)
|
||||
}
|
||||
|
||||
// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
|
||||
func (enc *Encoder) Time(t *time.Time, format string) {
|
||||
enc.grow(10)
|
||||
r := enc.getPreviousRune()
|
||||
if r != '[' {
|
||||
enc.writeByte(',')
|
||||
}
|
||||
enc.writeByte('"')
|
||||
enc.buf = t.AppendFormat(enc.buf, format)
|
||||
enc.writeByte('"')
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
package gojay
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d"
|
||||
|
||||
// InvalidJSONError is a type representing an error returned when
|
||||
// Decoding encounters invalid JSON.
|
||||
type InvalidJSONError string
|
||||
|
||||
func (err InvalidJSONError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
func (dec *Decoder) raiseInvalidJSONErr(pos int) error {
|
||||
var c byte
|
||||
if len(dec.data) > pos {
|
||||
c = dec.data[pos]
|
||||
}
|
||||
dec.err = InvalidJSONError(
|
||||
fmt.Sprintf(
|
||||
invalidJSONCharErrorMsg,
|
||||
c,
|
||||
pos,
|
||||
),
|
||||
)
|
||||
return dec.err
|
||||
}
|
||||
|
||||
const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'"
|
||||
|
||||
// InvalidUnmarshalError is a type representing an error returned when
|
||||
// Decoding cannot unmarshal JSON to the receiver type for various reasons.
|
||||
type InvalidUnmarshalError string
|
||||
|
||||
func (err InvalidUnmarshalError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error {
|
||||
return InvalidUnmarshalError(
|
||||
fmt.Sprintf(
|
||||
invalidUnmarshalErrorMsg,
|
||||
v,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const invalidMarshalErrorMsg = "Invalid type %T provided to Marshal"
|
||||
|
||||
// InvalidMarshalError is a type representing an error returned when
|
||||
// Encoding did not find the proper way to encode
|
||||
type InvalidMarshalError string
|
||||
|
||||
func (err InvalidMarshalError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// NoReaderError is a type representing an error returned when
|
||||
// decoding requires a reader and none was given
|
||||
type NoReaderError string
|
||||
|
||||
func (err NoReaderError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// InvalidUsagePooledDecoderError is a type representing an error returned
|
||||
// when decoding is called on a still pooled Decoder
|
||||
type InvalidUsagePooledDecoderError string
|
||||
|
||||
func (err InvalidUsagePooledDecoderError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// InvalidUsagePooledEncoderError is a type representing an error returned
|
||||
// when decoding is called on a still pooled Encoder
|
||||
type InvalidUsagePooledEncoderError string
|
||||
|
||||
func (err InvalidUsagePooledEncoderError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// ErrUnmarshalPtrExpected is the error returned when unmarshal expects a pointer value,
|
||||
// When using `dec.ObjectNull` or `dec.ArrayNull` for example.
|
||||
var ErrUnmarshalPtrExpected = errors.New("Cannot unmarshal to given value, a pointer is expected")
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// Package gojay implements encoding and decoding of JSON as defined in RFC 7159.
|
||||
// The mapping between JSON and Go values is described
|
||||
// in the documentation for the Marshal and Unmarshal functions.
|
||||
//
|
||||
// It aims at performance and usability by relying on simple interfaces
|
||||
// to decode and encode structures, slices, arrays and even channels.
|
||||
//
|
||||
// On top of the simple interfaces to implement, gojay provides lots of helpers to decode and encode
|
||||
// multiple of different types natively such as bit.Int, sql.NullString or time.Time
|
||||
package gojay
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
-1
@@ -28,7 +28,6 @@ linters:
|
||||
- ineffassign
|
||||
- misspell
|
||||
- prealloc
|
||||
- scopelint
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- structcheck
|
||||
|
||||
+3
-5
@@ -3,9 +3,6 @@
|
||||
<img src="docs/quic.png" width=303 height=124>
|
||||
|
||||
[](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
|
||||
[](https://travis-ci.org/lucas-clemente/quic-go)
|
||||
[](https://circleci.com/gh/lucas-clemente/quic-go)
|
||||
[](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
|
||||
[](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||
|
||||
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
||||
@@ -13,7 +10,7 @@ In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https:
|
||||
|
||||
## Guides
|
||||
|
||||
*We currently support Go 1.16.x and Go 1.17.x.*
|
||||
*We currently support Go 1.16.x, Go 1.17.x, and Go 1.18.x.*
|
||||
|
||||
Running tests:
|
||||
|
||||
@@ -51,10 +48,11 @@ http.Client{
|
||||
| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support |  |
|
||||
| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS |  |
|
||||
| [go-ipfs](https://github.com/ipfs/go-ipfs) | IPFS implementation in go |  |
|
||||
| [nextdns](https://github.com/nextdns/nextdns) | NextDNS CLI client (DoH Proxy) |  |
|
||||
| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization |  |
|
||||
| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy |  |
|
||||
| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions |  |
|
||||
| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins |  |
|
||||
| [OONI Probe](https://github.com/ooni/probe-cli) | The Open Observatory of Network Interference (OONI) aims to empower decentralized efforts in documenting Internet censorship around the world. |  |
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
+33
-33
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type client struct {
|
||||
conn sendConn
|
||||
sconn sendConn
|
||||
// If the client is created with DialAddr, we create a packet conn.
|
||||
// If it is started with Dial, we take a packet conn as a parameter.
|
||||
createdPacketConn bool
|
||||
@@ -35,7 +35,7 @@ type client struct {
|
||||
|
||||
handshakeChan chan struct{}
|
||||
|
||||
session quicSession
|
||||
conn quicConn
|
||||
|
||||
tracer logging.ConnectionTracer
|
||||
tracingID uint64
|
||||
@@ -49,26 +49,26 @@ var (
|
||||
)
|
||||
|
||||
// DialAddr establishes a new QUIC connection to a server.
|
||||
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
|
||||
// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
|
||||
// The hostname for SNI is taken from the given address.
|
||||
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
||||
func DialAddr(
|
||||
addr string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (Session, error) {
|
||||
) (Connection, error) {
|
||||
return DialAddrContext(context.Background(), addr, tlsConf, config)
|
||||
}
|
||||
|
||||
// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
|
||||
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
|
||||
// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
|
||||
// The hostname for SNI is taken from the given address.
|
||||
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
||||
func DialAddrEarly(
|
||||
addr string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (EarlySession, error) {
|
||||
) (EarlyConnection, error) {
|
||||
return DialAddrEarlyContext(context.Background(), addr, tlsConf, config)
|
||||
}
|
||||
|
||||
@@ -79,13 +79,13 @@ func DialAddrEarlyContext(
|
||||
addr string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (EarlySession, error) {
|
||||
sess, err := dialAddrContext(ctx, addr, tlsConf, config, true)
|
||||
) (EarlyConnection, error) {
|
||||
conn, err := dialAddrContext(ctx, addr, tlsConf, config, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session")
|
||||
return sess, nil
|
||||
utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early connection")
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// DialAddrContext establishes a new QUIC connection to a server using the provided context.
|
||||
@@ -95,7 +95,7 @@ func DialAddrContext(
|
||||
addr string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (Session, error) {
|
||||
) (Connection, error) {
|
||||
return dialAddrContext(ctx, addr, tlsConf, config, false)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func dialAddrContext(
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
use0RTT bool,
|
||||
) (quicSession, error) {
|
||||
) (quicConn, error) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -131,7 +131,7 @@ func Dial(
|
||||
host string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (Session, error) {
|
||||
) (Connection, error) {
|
||||
return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func DialEarly(
|
||||
host string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (EarlySession, error) {
|
||||
) (EarlyConnection, error) {
|
||||
return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config)
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func DialEarlyContext(
|
||||
host string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (EarlySession, error) {
|
||||
) (EarlyConnection, error) {
|
||||
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false)
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func DialContext(
|
||||
host string,
|
||||
tlsConf *tls.Config,
|
||||
config *Config,
|
||||
) (Session, error) {
|
||||
) (Connection, error) {
|
||||
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func dialContext(
|
||||
config *Config,
|
||||
use0RTT bool,
|
||||
createdPacketConn bool,
|
||||
) (quicSession, error) {
|
||||
) (quicConn, error) {
|
||||
if tlsConf == nil {
|
||||
return nil, errors.New("quic: tls.Config not set")
|
||||
}
|
||||
@@ -203,21 +203,21 @@ func dialContext(
|
||||
}
|
||||
c.packetHandlers = packetHandlers
|
||||
|
||||
c.tracingID = nextSessionTracingID()
|
||||
c.tracingID = nextConnTracingID()
|
||||
if c.config.Tracer != nil {
|
||||
c.tracer = c.config.Tracer.TracerForConnection(
|
||||
context.WithValue(ctx, SessionTracingKey, c.tracingID),
|
||||
context.WithValue(ctx, ConnectionTracingKey, c.tracingID),
|
||||
protocol.PerspectiveClient,
|
||||
c.destConnID,
|
||||
)
|
||||
}
|
||||
if c.tracer != nil {
|
||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||
c.tracer.StartedConnection(c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||
}
|
||||
if err := c.dial(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.session, nil
|
||||
return c.conn, nil
|
||||
}
|
||||
|
||||
func newClient(
|
||||
@@ -265,7 +265,7 @@ func newClient(
|
||||
c := &client{
|
||||
srcConnID: srcConnID,
|
||||
destConnID: destConnID,
|
||||
conn: newSendPconn(pconn, remoteAddr),
|
||||
sconn: newSendPconn(pconn, remoteAddr),
|
||||
createdPacketConn: createdPacketConn,
|
||||
use0RTT: use0RTT,
|
||||
tlsConf: tlsConf,
|
||||
@@ -278,10 +278,10 @@ func newClient(
|
||||
}
|
||||
|
||||
func (c *client) dial(ctx context.Context) error {
|
||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||
|
||||
c.session = newClientSession(
|
||||
c.conn,
|
||||
c.conn = newClientConnection(
|
||||
c.sconn,
|
||||
c.packetHandlers,
|
||||
c.destConnID,
|
||||
c.srcConnID,
|
||||
@@ -295,11 +295,11 @@ func (c *client) dial(ctx context.Context) error {
|
||||
c.logger,
|
||||
c.version,
|
||||
)
|
||||
c.packetHandlers.Add(c.srcConnID, c.session)
|
||||
c.packetHandlers.Add(c.srcConnID, c.conn)
|
||||
|
||||
errorChan := make(chan error, 1)
|
||||
go func() {
|
||||
err := c.session.run() // returns as soon as the session is closed
|
||||
err := c.conn.run() // returns as soon as the connection is closed
|
||||
|
||||
if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn {
|
||||
c.packetHandlers.Destroy()
|
||||
@@ -308,15 +308,15 @@ func (c *client) dial(ctx context.Context) error {
|
||||
}()
|
||||
|
||||
// only set when we're using 0-RTT
|
||||
// Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever.
|
||||
var earlySessionChan <-chan struct{}
|
||||
// Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever.
|
||||
var earlyConnChan <-chan struct{}
|
||||
if c.use0RTT {
|
||||
earlySessionChan = c.session.earlySessionReady()
|
||||
earlyConnChan = c.conn.earlyConnReady()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.session.shutdown()
|
||||
c.conn.shutdown()
|
||||
return ctx.Err()
|
||||
case err := <-errorChan:
|
||||
var recreateErr *errCloseForRecreating
|
||||
@@ -327,10 +327,10 @@ func (c *client) dial(ctx context.Context) error {
|
||||
return c.dial(ctx)
|
||||
}
|
||||
return err
|
||||
case <-earlySessionChan:
|
||||
case <-earlyConnChan:
|
||||
// ready to send 0-RTT data
|
||||
return nil
|
||||
case <-c.session.HandshakeComplete().Done():
|
||||
case <-c.conn.HandshakeComplete().Done():
|
||||
// handshake successfully completed
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+24
-24
@@ -7,15 +7,15 @@ import (
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
// A closedLocalSession is a session that we closed locally.
|
||||
// When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
|
||||
// A closedLocalConn is a connection that we closed locally.
|
||||
// When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
|
||||
// with an exponential backoff.
|
||||
type closedLocalSession struct {
|
||||
type closedLocalConn struct {
|
||||
conn sendConn
|
||||
connClosePacket []byte
|
||||
|
||||
closeOnce sync.Once
|
||||
closeChan chan struct{} // is closed when the session is closed or destroyed
|
||||
closeChan chan struct{} // is closed when the connection is closed or destroyed
|
||||
|
||||
receivedPackets chan *receivedPacket
|
||||
counter uint64 // number of packets received
|
||||
@@ -25,16 +25,16 @@ type closedLocalSession struct {
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
var _ packetHandler = &closedLocalSession{}
|
||||
var _ packetHandler = &closedLocalConn{}
|
||||
|
||||
// newClosedLocalSession creates a new closedLocalSession and runs it.
|
||||
func newClosedLocalSession(
|
||||
// newClosedLocalConn creates a new closedLocalConn and runs it.
|
||||
func newClosedLocalConn(
|
||||
conn sendConn,
|
||||
connClosePacket []byte,
|
||||
perspective protocol.Perspective,
|
||||
logger utils.Logger,
|
||||
) packetHandler {
|
||||
s := &closedLocalSession{
|
||||
s := &closedLocalConn{
|
||||
conn: conn,
|
||||
connClosePacket: connClosePacket,
|
||||
perspective: perspective,
|
||||
@@ -46,7 +46,7 @@ func newClosedLocalSession(
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) run() {
|
||||
func (s *closedLocalConn) run() {
|
||||
for {
|
||||
select {
|
||||
case p := <-s.receivedPackets:
|
||||
@@ -57,14 +57,14 @@ func (s *closedLocalSession) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) handlePacket(p *receivedPacket) {
|
||||
func (s *closedLocalConn) handlePacket(p *receivedPacket) {
|
||||
select {
|
||||
case s.receivedPackets <- p:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) {
|
||||
func (s *closedLocalConn) handlePacketImpl(_ *receivedPacket) {
|
||||
s.counter++
|
||||
// exponential backoff
|
||||
// only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving
|
||||
@@ -79,34 +79,34 @@ func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) shutdown() {
|
||||
func (s *closedLocalConn) shutdown() {
|
||||
s.destroy(nil)
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) destroy(error) {
|
||||
func (s *closedLocalConn) destroy(error) {
|
||||
s.closeOnce.Do(func() {
|
||||
close(s.closeChan)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *closedLocalSession) getPerspective() protocol.Perspective {
|
||||
func (s *closedLocalConn) getPerspective() protocol.Perspective {
|
||||
return s.perspective
|
||||
}
|
||||
|
||||
// A closedRemoteSession is a session that was closed remotely.
|
||||
// For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE.
|
||||
// A closedRemoteConn is a connection that was closed remotely.
|
||||
// For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE.
|
||||
// We can just ignore those packets.
|
||||
type closedRemoteSession struct {
|
||||
type closedRemoteConn struct {
|
||||
perspective protocol.Perspective
|
||||
}
|
||||
|
||||
var _ packetHandler = &closedRemoteSession{}
|
||||
var _ packetHandler = &closedRemoteConn{}
|
||||
|
||||
func newClosedRemoteSession(pers protocol.Perspective) packetHandler {
|
||||
return &closedRemoteSession{perspective: pers}
|
||||
func newClosedRemoteConn(pers protocol.Perspective) packetHandler {
|
||||
return &closedRemoteConn{perspective: pers}
|
||||
}
|
||||
|
||||
func (s *closedRemoteSession) handlePacket(*receivedPacket) {}
|
||||
func (s *closedRemoteSession) shutdown() {}
|
||||
func (s *closedRemoteSession) destroy(error) {}
|
||||
func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective }
|
||||
func (s *closedRemoteConn) handlePacket(*receivedPacket) {}
|
||||
func (s *closedRemoteConn) shutdown() {}
|
||||
func (s *closedRemoteConn) destroy(error) {}
|
||||
func (s *closedRemoteConn) getPerspective() protocol.Perspective { return s.perspective }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user