From 3a82defb408da2ff433bee235a4a9eb71f2769f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=B6=85?= <7546302+red_q@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 10:48:32 +0800 Subject: [PATCH] =?UTF-8?q?json=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attack_event/fw.go | 29 +- conn/http_req.go | 15 +- go.mod | 2 + go.sum | 14 + .../Andrew-M-C/go.jsonvalue/CHANGELOG.md | 121 + .../go.jsonvalue/CHANGELOG_zh-cn.md | 126 + .../Andrew-M-C/go.jsonvalue/LICENSE | 101 + .../Andrew-M-C/go.jsonvalue/README.md | 87 + .../Andrew-M-C/go.jsonvalue/any_ltgo17.go | 3 + .../Andrew-M-C/go.jsonvalue/codecov.yml | 8 + .../Andrew-M-C/go.jsonvalue/compare.go | 136 + .../Andrew-M-C/go.jsonvalue/conv.go | 201 ++ .../Andrew-M-C/go.jsonvalue/errors.go | 99 + .../github.com/Andrew-M-C/go.jsonvalue/get.go | 552 ++++ .../Andrew-M-C/go.jsonvalue/import_export.go | 598 +++++ .../go.jsonvalue/insert_append_delete.go | 632 +++++ .../go.jsonvalue/insert_append_delete_must.go | 245 ++ .../go.jsonvalue/internal/buffer/buffer.go | 18 + .../internal/buffer/buffer_impl.go | 32 + .../internal/buffer/buffer_internal.go | 1 + .../go.jsonvalue/internal/unsafe/unsafe.go | 18 + .../Andrew-M-C/go.jsonvalue/iteration.go | 150 ++ .../Andrew-M-C/go.jsonvalue/jsonvalue.go | 598 +++++ .../go.jsonvalue/jsonvalue_internal.go | 112 + .../Andrew-M-C/go.jsonvalue/marshal.go | 325 +++ .../go.jsonvalue/marshaler_unmarshaler.go | 44 + .../github.com/Andrew-M-C/go.jsonvalue/new.go | 273 ++ .../Andrew-M-C/go.jsonvalue/option.go | 634 +++++ .../github.com/Andrew-M-C/go.jsonvalue/set.go | 412 +++ .../Andrew-M-C/go.jsonvalue/set_must.go | 162 ++ .../Andrew-M-C/go.jsonvalue/sort.go | 326 +++ .../Andrew-M-C/go.jsonvalue/unmarshal.go | 900 +++++++ .../github.com/shopspring/decimal/.gitignore | 9 + .../shopspring/decimal/CHANGELOG.md | 76 + vendor/github.com/shopspring/decimal/LICENSE | 45 + .../github.com/shopspring/decimal/README.md | 139 + vendor/github.com/shopspring/decimal/const.go | 63 + .../shopspring/decimal/decimal-go.go | 415 +++ .../github.com/shopspring/decimal/decimal.go | 2339 +++++++++++++++++ .../github.com/shopspring/decimal/rounding.go | 160 ++ vendor/modules.txt | 8 + 41 files changed, 10209 insertions(+), 19 deletions(-) create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG.md create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG_zh-cn.md create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/LICENSE create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/README.md create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/any_ltgo17.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/codecov.yml create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/compare.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/conv.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/errors.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/get.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/import_export.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete_must.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_impl.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_internal.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/internal/unsafe/unsafe.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/iteration.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue_internal.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/marshal.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/marshaler_unmarshaler.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/new.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/option.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/set.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/set_must.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/sort.go create mode 100644 vendor/github.com/Andrew-M-C/go.jsonvalue/unmarshal.go create mode 100644 vendor/github.com/shopspring/decimal/.gitignore create mode 100644 vendor/github.com/shopspring/decimal/CHANGELOG.md create mode 100644 vendor/github.com/shopspring/decimal/LICENSE create mode 100644 vendor/github.com/shopspring/decimal/README.md create mode 100644 vendor/github.com/shopspring/decimal/const.go create mode 100644 vendor/github.com/shopspring/decimal/decimal-go.go create mode 100644 vendor/github.com/shopspring/decimal/decimal.go create mode 100644 vendor/github.com/shopspring/decimal/rounding.go diff --git a/attack_event/fw.go b/attack_event/fw.go index b3082d6..b66117d 100644 --- a/attack_event/fw.go +++ b/attack_event/fw.go @@ -5,7 +5,6 @@ import ( "dt_automate/conn" "dt_automate/tool" "encoding/json" - "fmt" "log" "net/url" "strconv" @@ -56,18 +55,15 @@ func Fw_event(cookieStr string) { "referer": "https://11.2.68.146/wnm/frame/index.php", "cookie": cookieStr, } - datas := conn.DT_POST("https://11.2.68.146/wnm/get.j", header, bytes.NewBufferString(values.Encode()))["NTOP"].(map[string]interface{})["LogPaging"] - var a map[string]interface{} - for v, k := range datas.([]interface{}) { - b := k.(map[string]interface{})["OutputJSON"].(string) - if err := json.Unmarshal([]byte(b), &a); err != nil { - log.Fatalf("Failed to unmarshal JSON: %v", err) - } - fmt.Println(v, a) - fmt.Printf("序号:%d,攻击时间:[%s],安全域:%s-%s##攻击源IP:%s#目的源IP:%s:%s,攻击类型:%s,应用协议:%s,请求域名:%s\n", v, a["Time"], a["SrcZoneName"], a["DestZoneName"], a["SrcIPAddr"], a["DestIPAddr"], strconv.FormatFloat(a["DestPort"].(float64), 'f', 0, 64), a["ThreatName"], a["Application"], a["HttpHost"]) + body := conn.DT_POST("https://11.2.68.146/wnm/get.j", header, bytes.NewBufferString(values.Encode())) + var bodys Person + json.Unmarshal(body, &bodys) + + for v, k := range bodys.LogPaging { + // fmt.Printf("序号:%d,攻击时间:[%s],安全域:%s-%s##攻击源IP:%s#目的源IP:%s:%s,攻击类型:%s,应用协议:%s,请求域名:%s\n", v, a["Time"], a["SrcZoneName"], a["DestZoneName"], a["SrcIPAddr"], a["DestIPAddr"], strconv.FormatFloat(a["DestPort"].(float64), 'f', 0, 64), a["ThreatName"], a["Application"], a["HttpHost"]) sheet.Cell("A" + strconv.Itoa(v+1)).SetString(strconv.Itoa(v)) // 第一列 (A1) - sheet.Cell("B" + strconv.Itoa(v+1)).SetString(a["Time"].(string)) - sheet.Cell("C" + strconv.Itoa(v+1)).SetString(a["SrcZoneName"].(string)) + sheet.Cell("B" + strconv.Itoa(v+1)).SetString(k.Time) + sheet.Cell("C" + strconv.Itoa(v+1)).SetString(k.SrcZoneName) sheet.Cell("D" + strconv.Itoa(v+1)).SetString(a["DestZoneName"].(string)) sheet.Cell("E" + strconv.Itoa(v+1)).SetString(a["SrcIPAddr"].(string)) sheet.Cell("F" + strconv.Itoa(v+1)).SetString(a["DestIPAddr"].(string)) @@ -84,3 +80,12 @@ func Fw_event(cookieStr string) { log.Fatalf("保存文件时出错: %s", err) } } + +type Person struct { + NTOP string `json:"ntop"` + LogPaging []LogPaging `json:"logpaging"` +} +type LogPaging struct { + Time string `json:"time"` + SrcZoneName string `json"srczonename"` +} diff --git a/conn/http_req.go b/conn/http_req.go index debc4b0..be4085a 100644 --- a/conn/http_req.go +++ b/conn/http_req.go @@ -12,7 +12,7 @@ import ( // headers := map[string]string{ // "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", // } -func DT_POST(urls string, headers map[string]string, bytess io.Reader) map[string]interface{} { +func DT_POST(urls string, headers map[string]string, bytess io.Reader) []byte { url, err := url.Parse(urls) if err != nil { log.Println(err) @@ -52,12 +52,13 @@ func DT_POST(urls string, headers map[string]string, bytess io.Reader) map[strin if err != nil { log.Println(err) } - log.Println(string(body)) - var gcresp map[string]interface{} - if err := ScanJson(resp, gcresp); err != nil { - log.Println(err) - } - return gcresp + // log.Println(string(body)) + // var gcresp map[string]interface{} + // if err := ScanJson(resp, gcresp); err != nil { + // log.Println(err) + // } + + return body } // req 请求体 url 请求地址 headers请求头 diff --git a/go.mod b/go.mod index f21b662..293984f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module dt_automate go 1.23.6 require ( + github.com/Andrew-M-C/go.jsonvalue v1.4.1 github.com/Esword618/unioffice v1.4.1 github.com/go-sql-driver/mysql v1.9.0 github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c @@ -19,6 +20,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/jezek/xgb v1.1.1 // indirect github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/stretchr/testify v1.9.0 // indirect golang.org/x/sys v0.28.0 // indirect ) diff --git a/go.sum b/go.sum index c758ab3..2b3b314 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Andrew-M-C/go.jsonvalue v1.4.1 h1:MGtqaKy5Z7Xi/hnWaeqWDMwno79uNRt0XoCGHa1pm74= +github.com/Andrew-M-C/go.jsonvalue v1.4.1/go.mod h1:EsYbZ97LlOhGUs+7qTwZI9KaJrPe6nK8sEZKEqr70Ww= github.com/Esword618/unioffice v1.4.1 h1:qdcC+FpAPfLU3/WbUoICt1WgDR0tDC9ZPmBx4g3LRXM= github.com/Esword618/unioffice v1.4.1/go.mod h1:8BQw+FmXV1Xfu7UeEtwL8wdk8gfM1Ib0RHqklrRr764= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -18,8 +20,12 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c h1:1IlzDla/ZATV/FsRn1ETf7ir91PHS2mrd4VMunEtd9k= github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc= @@ -30,6 +36,12 @@ github.com/playwright-community/playwright-go v0.5001.0 h1:EY3oB+rU9cUp6CLHguWE8 github.com/playwright-community/playwright-go v0.5001.0/go.mod h1:kBNWs/w2aJ2ZUp1wEOOFLXgOqvppFngM5OS+qyhl+ZM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -42,6 +54,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -73,6 +86,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG.md b/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG.md new file mode 100644 index 0000000..0c27bf1 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG.md @@ -0,0 +1,121 @@ +Change Log + +[中文](./CHANGELOG_zh-cn.md) + +- [v1.4.1](#v141) +- [v1.4.0](#v140) +- [v1.3.8](#v138) +- [v1.3.7](#v137) +- [v1.3.6](#v136) +- [v1.3.5](#v135) +- [v1.3.4](#v134) +- [v1.3.3](#v133) +- [v1.3.2](#v132) +- [v1.3.1](#v131) +- [v1.3.0](#v130) +- [v1.2.1](#v121) +- [v1.2.0](#v120) +- [v1.1.1](#v111) + +## v1.4.1 + +- Add `v.At(...).Set(...)` pattern, allowing putting keys ahead (comparing to `v.Set(...).At(...)`). + +## v1.4.0 + +- Allow passing parameter with a single slice or array for Get, Set, Append, Insert, Delete methods. +- Add GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual methods for number comparation. + +## v1.3.8 + +- Fix Issue [#30](https://github.com/Andrew-M-C/go.jsonvalue/issues/30) + +## v1.3.7 + +- Fix Issue [#29](https://github.com/Andrew-M-C/go.jsonvalue/issues/29) + +## v1.3.6 + +- Fix a floating sign [bug](https://github.com/akbarfa49/go.jsonvalue/commit/278817) +- Fix Issue [#27](https://github.com/Andrew-M-C/go.jsonvalue/issues/27) + +## v1.3.5 + +- Fix Issue [#26](https://github.com/Andrew-M-C/go.jsonvalue/issues/26) + +## v1.3.4 + +Release jsonvalue v1.3.4. Comparing to v1.3.3: + +- `*jsonvalue.V` type now implements following interfaces thus it can used directly in `encoding/json`: + - `json.Marshaler`、`json.Unmarshaler` + - `encoding.BinaryMarshaler`、`encoding.BinaryUnmarshaler` +- Supports importing data types those implement `json.Marshaler` or `encoding.TextMarshaler` interfaces, just like `encoding/json` does. +- Add `MustAdd`, `MustAppend`, `MustInsert`, `MustSet`, `MustDelete` methods. These methods will not return sub-values or errors. + - This will prevent golangci-lint warning of "return value is not used" +- Bug fix: When invoking `Append(xxx).InTheBeginning()`, the sub-value was actually and faulty append to the end. +- Pre-allocate some buffer and space when marshaling and unmarshaling. It will speed-up a little bit and save 40% alloc count by average. + +## v1.3.3 + +Release jsonvalue v1.3.3. Comparing to v1.3.2: + +- Release Engligh [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/master/docs/en/README.md). +- Fix #19 and #22. + +## v1.3.2 + +Release jsonvalue 1.3.2. Comparing to v1.3.1, [#17](https://github.com/Andrew-M-C/go.jsonvalue/issues/17) is fixed. + +## v1.3.1 + +Release jsonvalue 1.3.1. Comparing to v1.3.0: + +- It is available to get key sequences of an unmarshaled object JSON +- Supporting marshal object by key sequence of when they are set. +- All invisible ASCII characters will be escaped in `\u00XX` format. + +## v1.3.0 + +Release v1.3.0. Comparing to v1.2.x: + +- No longer escape % symbol by default. +- Add `Import` and `Export` function to support conversion between Go standard types and jsonvalue. +- Support generics-like operations in `Set, Append, Insert, Add, New` functions, which allows programmers to set any legal types without specifying types explicitly. +- Add `OptIndent` to support indent in marshaling. + +## v1.2.1 + +Release v1.2.1. Comparing to v1.2.0: + +- Support not escaping slash symbol / in marshaling. Please refer to `OptEscapeSlash` function. +- Support not escaping several HTML symbols in marshaling (issue #13). Please refer to OptEscapeHTML function. +- Support not escaping unicodes greater than `\u00FF` in marshaling. Please refer to OptUTF8 function. +- Support children-auto-generating in `Append(...).InTheBeginning()` and `Append(...).InTheEnd()`. But keep in mind that Insert operations remains not doing that. +- You can override default marshaling options from now on. Please refer to `SetDefaultMarshalOptions` function. + +Other changes: + +- Instead of Travis-CI, I will use Github Actions in order to generate go test reports from now on. + +## v1.2.0 + +Release v1.2.0, comparing to v1.1.1: + +- Fix the bug that float-pointed numbers with scientific notation format are not supported. ([issue #8](https://github.com/Andrew-M-C/go.jsonvalue/issues/8)) +- Add detailed wiki written in simplified Chinese. If you need English one, please leave me an issue. +- Deprecate `Opt{}` for additional options, and replace with `OptXxx()` functions. +- A first non-forward-compatible feature is released: Parameter formats for `NewFloat64` and `NewFloat32` is changed. + - If you want to specify format of float-pointed numbers, please use `NewFloat64f` and `NewFloat32f` instead. +- Add `ForRangeArr` and `ForRangeObj` functions, deprecating `IterArray` and `IterObject`. +- Support `SetEscapeHTML`. ([issue11](https://github.com/Andrew-M-C/go.jsonvalue/issues/11)) +- Support converting number to legal JSON data when they are `+/-NaN` and `+/-Inf`. + +## v1.1.1 + +Release v1.1.1, comparing to v1.1.0: + +- All functions will NOT return nil `*jsonvalue.V` object instead of an instance with `NotExist` type when error occurs. + - Therefore, programmers can use the returned instance for shorter operations. +- Supports `MustGet()` function, without error returning. +- Supports `String()` for `ValueType` type diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG_zh-cn.md b/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG_zh-cn.md new file mode 100644 index 0000000..7aa9e2b --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/CHANGELOG_zh-cn.md @@ -0,0 +1,126 @@ +变更日志 + +[English](./CHANGELOG.md) + +- [v1.4.1](#v141) +- [v1.4.0](#v140) +- [v1.3.8](#v138) +- [v1.3.7](#v137) +- [v1.3.6](#v136) +- [v1.3.5](#v135) +- [v1.3.4](#v134) +- [v1.3.3](#v133) +- [v1.3.2](#v132) +- [v1.3.1](#v131) +- [v1.3.0](#v130) +- [v1.2.1](#v121) +- [v1.2.0](#v120) +- [v1.1.1](#v111) + +## v1.4.1 + +- 新增 `v.At(...).Set(...)` 模式, 相比 `v.Set(...).At(...)` 模式不同的是将 key 放在前面 + +## v1.4.0 + +- 在 Get, Set, Append, Insert, Delete 等方法中, 允许传递一个切片参数, 用来标识 key 链 +- 添加 GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual 方法用于数字比较 + +## v1.3.8 + +- 修复 Issue [#30](https://github.com/Andrew-M-C/go.jsonvalue/issues/30) + +## v1.3.7 + +- 修复 Issue [#29](https://github.com/Andrew-M-C/go.jsonvalue/issues/29) + +## v1.3.6 + +- 修复一个浮点数符号的 [bug](https://github.com/akbarfa49/go.jsonvalue/commit/278817) +- 修复 Issue [#27](https://github.com/Andrew-M-C/go.jsonvalue/issues/27) + +## v1.3.5 + +- 修复 Issue [#26](https://github.com/Andrew-M-C/go.jsonvalue/issues/26) + +## v1.3.4 + +发布 jsonvalue v1.3.4,相比 v1.3.3: + +- 添加 `*jsonvalue.V` 对以下接口的支持, 使之可以适配 `encoding/json`: + - `json.Marshaler`、`json.Unmarshaler` + - `encoding.BinaryMarshaler`、`encoding.BinaryUnmarshaler` +- `Import` 和 `New` 函数支持导入实现了 `json.Marshaler` 和 `encoding.TextMarshaler` 的数据类型, 就像 `encoding/json` 一样 ([#24](https://github.com/Andrew-M-C/go.jsonvalue/issues/24)) +- 添加 `MustAdd`, `MustAppend`, `MustInsert`, `MustSet`, `MustDelete` 等方法, 这些方法不返回子值和 error 信息 + - 可以避免 golangci-lint 的警告提示 +- Bug 修复: 当 `Append(xxx).InTheBeginning()` 时, 实际上追加到了末尾 +- 在 Unmarshal 时, 通过预估对象 / 数组大小的方式预创建空间, 略微提升一点点速度, 以及节省平均约 40% 的 alloc 数 + +## v1.3.3 + +发布 jsonvalue v1.3.3,相比 v1.3.2: + +- 发布英文 [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/master/docs/en/README.md)。 +- 修复了 #19 和 #22。 + +## v1.3.2 + +发布 jsonvalue 1.3.2,相比 1.3.1,主要是修复了 [#17](https://github.com/Andrew-M-C/go.jsonvalue/issues/17)。 + +## v1.3.1 + +发布 jsonvalue 1.3.1,相比 1.3.0,改动如下: + +- 支持获取 object 类型的键(key)的顺序 +- 按照 key 被设置的顺序进行 object 的序列化 +- 当序列化时,如果遇到 ASCII 的非可打印字符(包括控制字符),均进行 `\u00XX` 转义 + +具体说明情参见 [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/feature/v1.3.0/docs/zh-cn/12_new_feature.md) + +## v1.3.0 + +发布 v1.3.0,相比 v1.2.x,改动如下: + +- 不再默认转义 % 字符 +- 支持与原生 json 的转换,也即支持从 `struct` 和其他类型中导入为 jsonvalue 类型,也支持将 jsonvalue 导出到 Go 标准类型中 +- 支持类泛型操作,最直接的影响就是 `Set, Append, Insert, Add, New` 等函数可以传入任意类型 +- 新增 `OptIndent` 函数以支持可视化锁进 + +具体说明情参见 [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/feature/v1.3.0/docs/zh-cn/12_new_feature.md) + +## v1.2.1 + +发布 v1.2.1,相比 v1.2.1,改动如下: + +- Marshal 时,允许不转义斜杠符号 '/',参见 `OptEscapeSlash` 函数。 +- Marshal 时,允许不转义几个 HTML 关键符号 (issue #13)。参见 OptEscapeHTML 函数 +- Marshal 时,对于大于 `\u00FF` 的字符,允许保留 UTF-8 编码,而不进行转义。参见 OptUTF8 函数 +- `Append(...).InTheBeginning()` 和 `Append(...).InTheEnd()` 操作时,支持自动创建不存在的层级。但 insert 操作依然不会自动创建,这一点请留意 +- 支持设置默认序列化选项,请参见 SetDefaultMarshalOptions 函数 + +其他变更: + +- CI 弃用 Travis-CI,改用 Github Actions + +## v1.2.0 + +发布 v1.2.0,相比 v1.1.1,改动如下: + +- 修复不支持科学计数法浮点数的 bug([issue #8](https://github.com/Andrew-M-C/go.jsonvalue/issues/8)) +- 添加详细的中文 [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/master/docs/zh-cn/README.md)。 +- Marshal 时的额外参数配置,原使用的是 `Opt` 结构体,现推荐改为使用一个或多个 `OptXxx()` 函数 +- 发布第一个不向前兼容的变更: 修改 `NewFloat64` 和 `NewFloat32` 的入参 + - 原函数的 `prec` 变量及相关功能改由 `NewFloat64f` 和 `NewFloat32f` 承担。 +- 添加 `ForRangeArr` 和 `ForRangeObj` 以取代 `IterArray` 和 `IterObject` 函数,参见 [wiki](https://github.com/Andrew-M-C/go.jsonvalue/blob/master/docs/zh-cn/05_iteration.md#%E6%A6%82%E8%BF%B0) +- 支持 `SetEscapeHTML`([issue11](https://github.com/Andrew-M-C/go.jsonvalue/issues/11)) +- 当浮点值中出现 `+/-NaN` 和 `+/-Inf` 时,支持进行特殊转换以符合 JSON 标准 + +## v1.1.1 + +发布 v1.1.1,相比 v1.1.0,变更如下: + +- 无论是否错误,所有返回 `*jsonvalue.V` 的函数都不会返回 nil。如果错误发生,那么至少会返回一个有效的 `*V` 实例,但是 `Type` 等于 `NotExist` + - 这样一来,程序员可以在代码中放心地直接使用返回的 *V 对象,简化代码,而忽略不必要的错误检查。 +- 支持 `MustGet` 函数,只返回 `*V` 而不返回 error 变量,便于一些级联的代码。 +- `ValueType` 类型支持 `String()` 函数。 + diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/LICENSE b/vendor/github.com/Andrew-M-C/go.jsonvalue/LICENSE new file mode 100644 index 0000000..f2f1710 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/LICENSE @@ -0,0 +1,101 @@ +MIT License + +Copyright (c) 2019-2024 Andrew Min Chang + +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. + +Other dependencies and licenses: + +github.com/shopspring/decimal +-------------------------------------------------------------------- + +The MIT License (MIT) + +Copyright (c) 2015 Spring, Inc. + +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. + +- Based on https://github.com/oguzbilgic/fpd, which has the following license: +""" +The MIT License (MIT) + +Copyright (c) 2013 Oguz Bilgic + +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. +""" + +github.com/smartystreets/goconvey +-------------------------------------------------------------------- + +MIT License + +Copyright (c) 2022 Smarty + +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. + +NOTE: Various optional and subordinate components carry their own licensing +requirements and restrictions. Use of those components is subject to the terms +and conditions outlined the respective license of each component. + diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/README.md b/vendor/github.com/Andrew-M-C/go.jsonvalue/README.md new file mode 100644 index 0000000..aadd536 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/README.md @@ -0,0 +1,87 @@ +# Jsonvalue - A Fast and Convenient Alternation of Go map[string]interface{} + +[![Workflow](https://github.com/Andrew-M-C/go.jsonvalue/actions/workflows/go_test_general.yml/badge.svg)](https://github.com/Andrew-M-C/go.jsonvalue/actions/workflows/go_test_general.yml) +[![codecov](https://codecov.io/gh/Andrew-M-C/go.jsonvalue/branch/dev/github_workflow/graph/badge.svg?token=REDI4YDLPR&date=221104)](https://codecov.io/gh/Andrew-M-C/go.jsonvalue) +[![Go report](https://goreportcard.com/badge/github.com/Andrew-M-C/go.jsonvalue?date=221104)](https://goreportcard.com/report/github.com/Andrew-M-C/go.jsonvalue) +[![CodeBeat](https://codebeat.co/badges/ecf87760-2987-48a7-a6dd-4d9fcad57256)](https://codebeat.co/projects/github-com-andrew-m-c-go-jsonvalue-master) + +[![GoDoc](https://godoc.org/github.com/Andrew-M-C/go.jsonvalue?status.svg&date=221104)](https://pkg.go.dev/github.com/Andrew-M-C/go.jsonvalue@v1.4.0) +[![Latest](https://img.shields.io/badge/latest-v1.4.0-blue.svg?date=221104)](https://github.com/Andrew-M-C/go.jsonvalue/tree/v1.4.0) +[![License](https://img.shields.io/badge/license-MIT-blue.svg?date=221104)](https://opensource.org/license/MIT) + +- [Wiki](./docs/en/README.md) +- [中文版](./docs/zh-cn/README.md) + +Package **jsonvalue** is for handling unstructured JSON data or customizing JSON marshaling. It is far more faster and convenient than using `interface{}` with `encoding/json`. + +Please refer to [pkg site](https://pkg.go.dev/github.com/Andrew-M-C/go.jsonvalue) or [wiki](./docs/en/README.md) for detailed usage and examples. + +Especially, please check for jsonvalue's [programming scenarios](./docs/en/10_scenarios.md). + +## Import + +Use following statements to import jsonvalue: + +```go +import ( + jsonvalue "github.com/Andrew-M-C/go.jsonvalue" +) +``` + +## Quick Start + +Sometimes we want to create a complex JSON object like: + +```json +{ + "someObject": { + "someObject": { + "someObject": { + "message": "Hello, JSON!" + } + } + } +} +``` + +With `jsonvalue`, It is quite simple to implement this: + +```go + v := jsonvalue.NewObject() + v.MustSet("Hello, JSON").At("someObject", "someObject", "someObject", "message") + fmt.Println(v.MustMarshalString()) + // Output: + // {"someObject":{"someObject":{"someObject":{"message":"Hello, JSON!"}}} +``` + +Similarly, it is quite easy to create sub-arrays like: + +```json +[ + { + "someArray": [ + "Hello, JSON!" + ] + } +] +``` + +```go + v := jsonvalue.NewArray() + v.MustSet("Hello, JSON").At(0, "someObject", 0) + fmt.Println(v.MustMarshalString()) + // Output: + // [{"someObject":["Hello, JSON"]}] +``` + +In opposite, to parse and read the first JSON above, you can use jsonvalue like this: + +```go + const raw = `{"someObject": {"someObject": {"someObject": {"message": "Hello, JSON!"}}}}` + s := jsonvalue.MustUnmarshalString(s).GetString("someObject", "someObject", "someObject", "message") + fmt.Println(s) + // Output: + // Hello, JSON! +``` + +However, it is quite complex and annoying in automatically creating array. I strongly suggest using `SetArray()` to create the array first, then use `Append()` or `Insert()` to set array elements. Please refer go [godoc](https://godoc.org/github.com/Andrew-M-C/go.jsonvalue) or [Wiki](./docs/en/README.md). diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/any_ltgo17.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/any_ltgo17.go new file mode 100644 index 0000000..a3ad8b3 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/any_ltgo17.go @@ -0,0 +1,3 @@ +package jsonvalue + +type any = interface{} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/codecov.yml b/vendor/github.com/Andrew-M-C/go.jsonvalue/codecov.yml new file mode 100644 index 0000000..4d8ce6f --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/codecov.yml @@ -0,0 +1,8 @@ +codecov: + bot: laplacezhang@126.com + +coverage: + round: down + range: "95..97" + precision: 2 + diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/compare.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/compare.go new file mode 100644 index 0000000..9f7f8b2 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/compare.go @@ -0,0 +1,136 @@ +package jsonvalue + +import ( + "bytes" + + "github.com/shopspring/decimal" +) + +// GreaterThan returns whether v is greater than another value. +// +// Both values should be number type, otherwise false will be returned. +func (v *V) GreaterThan(another *V) bool { + res, _ := greaterThan(v, another) + return res +} + +// GreaterThanOrEqual returns whether v is greater than or equal to another value. +// +// Both values should be number type, otherwise false will be returned. +func (v *V) GreaterThanOrEqual(another *V) bool { + res, _ := greaterOrEqualThan(v, another) + return res +} + +// LessThan returns whether v is less than another value. +// +// Both values should be number type, otherwise false will be returned. +func (v *V) LessThan(another *V) bool { + res, ok := greaterOrEqualThan(v, another) + if !ok { + return false + } + return !res +} + +// LessThanOrEqual returns whether v is less than or equal to another value. +// +// Both values should be number type, otherwise false will be returned. +func (v *V) LessThanOrEqual(another *V) bool { + res, ok := greaterThan(v, another) + if !ok { + return false + } + return !res +} + +func greaterThan(left, right *V) (result, ok bool) { + if left.valueType != Number || right.valueType != Number { + return false, false + } + a1, _ := decimal.NewFromString(string(left.srcByte)) + a2, _ := decimal.NewFromString(string(right.srcByte)) + return a1.GreaterThan(a2), true +} + +func greaterOrEqualThan(left, right *V) (result, ok bool) { + if left.valueType != Number || right.valueType != Number { + return false, false + } + if bytes.Equal(left.srcByte, right.srcByte) { + return true, true + } + a1, _ := decimal.NewFromString(string(left.srcByte)) + a2, _ := decimal.NewFromString(string(right.srcByte)) + return a1.GreaterThanOrEqual(a2), true +} + +// Equal shows whether the content of two JSON values equal to each other. +// +// Equal 判断两个 JSON 的内容是否相等 +func (v *V) Equal(another *V) bool { + if v == nil || another == nil { + return false + } + if v.valueType != another.valueType { + return false + } + + switch v.valueType { + default: // including NotExist, Unknown + return false + case String: + return v.valueStr == another.valueStr + case Number: + return numberEqual(v, another) + case Object: + return objectEqual(v, another) + case Array: + return arrayEqual(v, another) + case Boolean: + return v.valueBool == another.valueBool + case Null: + return true + } +} + +func numberEqual(left, right *V) bool { + if bytes.Equal(left.srcByte, right.srcByte) { + return true + } + + d1, _ := decimal.NewFromString(string(left.srcByte)) + d2, _ := decimal.NewFromString(string(right.srcByte)) + return d1.Equal(d2) +} + +func objectEqual(left, right *V) bool { + if len(left.children.object) != len(right.children.object) { + return false + } + + for k, leftChild := range left.children.object { + rightChild, exist := right.children.object[k] + if !exist { + return false + } + if !leftChild.v.Equal(rightChild.v) { + return false + } + } + return true +} + +func arrayEqual(left, right *V) bool { + if len(left.children.arr) != len(right.children.arr) { + return false + } + + for i, leftChild := range left.children.arr { + rightChild := right.children.arr[i] + if !leftChild.Equal(rightChild) { + return false + } + } + return true +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/conv.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/conv.go new file mode 100644 index 0000000..0c9f90c --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/conv.go @@ -0,0 +1,201 @@ +package jsonvalue + +import ( + "fmt" + "reflect" + + "github.com/Andrew-M-C/go.jsonvalue/internal/buffer" +) + +func formatBool(b bool) string { + if b { + return "true" + } + return "false" +} + +// reference: +// - [UTF-16](https://zh.wikipedia.org/zh-cn/UTF-16) +// - [JavaScript has a Unicode problem](https://mathiasbynens.be/notes/javascript-unicode) +// - [Meaning of escaped unicode characters in JSON](https://stackoverflow.com/questions/21995410/meaning-of-escaped-unicode-characters-in-json) +func escapeGreaterUnicodeToBuffByUTF16(r rune, buf buffer.Buffer) { + if r <= '\uffff' { + _, _ = buf.WriteString(fmt.Sprintf("\\u%04X", r)) + return + } + // if r > 0x10FFFF { + // // invalid unicode + // buf.WriteRune(r) + // return + // } + + r = r - 0x10000 + lo := r & 0x003FF + hi := (r & 0xFFC00) >> 10 + _, _ = buf.WriteString(fmt.Sprintf("\\u%04X", hi+0xD800)) + _, _ = buf.WriteString(fmt.Sprintf("\\u%04X", lo+0xDC00)) +} + +func escapeGreaterUnicodeToBuffByUTF8(r rune, buf buffer.Buffer) { + // Comments below are copied from encoding/json: + // + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if r == '\u2028' || r == '\u2029' { + escapeGreaterUnicodeToBuffByUTF16(r, buf) + } else { + _, _ = buf.WriteRune(r) + } +} + +func escapeNothing(b byte, buf buffer.Buffer) { + _ = buf.WriteByte(b) +} + +func escAsciiControlChar(b byte, buf buffer.Buffer) { + upper := b >> 4 + lower := b & 0x0F + + writeChar := func(c byte) { + if c < 0xA { + _ = buf.WriteByte('0' + c) + } else { + _ = buf.WriteByte('A' + (c - 0xA)) + } + } + + _, _ = buf.Write([]byte{'\\', 'u', '0', '0'}) + writeChar(upper) + writeChar(lower) +} + +func escDoubleQuote(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', '"'}) +} + +func escSlash(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', '/'}) +} + +func escBackslash(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', '\\'}) +} + +func escBackspace(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'b'}) +} + +func escVertTab(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'f'}) +} + +func escTab(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 't'}) +} + +func escNewLine(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'n'}) +} + +func escReturn(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'r'}) +} + +func escLeftAngle(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'u', '0', '0', '3', 'C'}) +} + +func escRightAngle(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'u', '0', '0', '3', 'E'}) +} + +func escAnd(_ byte, buf buffer.Buffer) { + _, _ = buf.Write([]byte{'\\', 'u', '0', '0', '2', '6'}) +} + +// func escPercent(_ byte, buf buffer.Buffer) { +// buf.Write([]byte{'\\', 'u', '0', '0', '2', '5'}) +// } + +func escapeStringToBuff(s string, buf buffer.Buffer, opt *Opt) { + for _, r := range s { + if r <= 0x7F { + b := byte(r) + opt.asciiCharEscapingFunc[b](b, buf) + } else { + opt.unicodeEscapingFunc(r, buf) + } + } +} + +func anyToInt(v any) (u int, err error) { + if v == nil { + return 0, fmt.Errorf("%w: parameter is nil", ErrParameterError) + } + value := reflect.ValueOf(v) + switch value.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return int(value.Int()), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return int(value.Uint()), nil + default: + return 0, fmt.Errorf("%v is not a number", reflect.TypeOf(v)) + } +} + +func anyToString(v any) (s string, err error) { + if v == nil { + return "", fmt.Errorf("%w: parameter is nil", ErrParameterError) + } + rv := reflect.ValueOf(v) + if rv.Type().Kind() == reflect.String { + return rv.String(), nil + } + + return "", fmt.Errorf("%w: %s is not a string", ErrParameterError, reflect.TypeOf(v).String()) +} + +func isSliceAndExtractDividedParams(p any) (ok bool, firstParam any, otherParams []any) { + v := reflect.ValueOf(p) + switch v.Kind() { + default: + return false, nil, nil + case reflect.Slice, reflect.Array: + // yes, go on + } + + paramCount := v.Len() + if paramCount == 0 { + return true, nil, nil + } + + firstParam = v.Index(0).Interface() + + for i := 1; i < v.Len(); i++ { + element := v.Index(i) + otherParams = append(otherParams, element.Interface()) + } + return true, firstParam, otherParams +} + +func isSliceAndExtractJointParams(p any) (bool, []any) { + v := reflect.ValueOf(p) + switch v.Kind() { + default: + return false, nil + case reflect.Slice, reflect.Array: + // yes, go on + } + + res := make([]any, 0, v.Len()) + for i := 0; i < v.Len(); i++ { + element := v.Index(i) + res = append(res, element.Interface()) + } + return true, res +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/errors.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/errors.go new file mode 100644 index 0000000..6162f96 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/errors.go @@ -0,0 +1,99 @@ +package jsonvalue + +// Error is equivalent to string and used to create some error constants in this package. +// Error constants: http://godoc.org/github.com/Andrew-M-C/go.jsonvalue/#pkg-constants +type Error string + +func (e Error) Error() string { + return string(e) +} + +const ( + // ErrNilParameter identifies input parameter is nil + // + // ErrNilParameter 表示参数为空 + ErrNilParameter = Error("nil parameter") + + // ErrValueUninitialized identifies that a V object is not initialized + // + // ErrValueUninitialized 表示当前的 jsonvalue 实例未初始化 + ErrValueUninitialized = Error("jsonvalue instance is not initialized") + + // ErrRawBytesUnrecognized identifies all unexpected raw bytes + // + // ErrRawBytesUnrecognized 表示无法识别的序列文本 + ErrRawBytesUnrecognized = Error("unrecognized raw text") + + // ErrNotValidNumberValue shows that a value starts with number or '-' is not eventually a number value + // + // ErrNotValidNumberValue 表示当前值不是一个合法的数值值 + ErrNotValidNumberValue = Error("not a valid number value") + + // ErrNotValidBoolValue shows that a value starts with 't' or 'f' is not eventually a bool value + // + // ErrNotValidBoolValue 表示当前值不是一个合法的布尔值 + ErrNotValidBoolValue = Error("not a valid bool value") + + // ErrNotValidNullValue shows that a value starts with 'n' is not eventually a bool value + // + // ErrNotValidNullValue 表示当前不是一个 null 值类型的 JSON + ErrNotValidNullValue = Error("not a valid null value") + + // ErrOutOfRange identifies that given position for a JSON array is out of range + // + // ErrOutOfRange 表示请求数组成员超出数组范围 + ErrOutOfRange = Error("out of range") + + // ErrNotFound shows that given target is not found in Delete() + // + // ErrNotFound 表示目标无法找到 + ErrNotFound = Error("target not found") + + // ErrTypeNotMatch shows that value type is not same as GetXxx() + // + // ErrTypeNotMatch 表示指定的对象不匹配 + ErrTypeNotMatch = Error("not match given type") + + // ErrParseNumberFromString shows the error when parsing number from string + // + // ErrParseNumberFromString 表示从 string 类型的 value 中读取数字失败 + ErrParseNumberFromString = Error("failed to parse number from string") + + // ErrNotArrayValue shows that operation target value is not an array + // + // ErrNotArrayValue 表示当前不是一个数组类型 JSON + ErrNotArrayValue = Error("not an array typed value") + + // ErrNotObjectValue shows that operation target value is not an valid object + // + // ErrNotObjectValue 表示当前不是一个合法的对象类型 JSON + ErrNotObjectValue = Error("not an object typed value") + + // ErrIllegalString shows that it is not a legal JSON string typed value + // + // ErrIllegalString 表示字符串不合法 + ErrIllegalString = Error("illegal string") + + // ErrUnsupportedFloat shows that float value is not supported, like +Inf, -Inf and NaN. + // + // ErrUnsupportedFloat 表示 float64 是一个不支持的数值,如 +Inf, -Inf 和 NaN + ErrUnsupportedFloat = Error("unsupported float value") + + // ErrUnsupportedFloatInOpt shows that float value in option is not supported, like +Inf, -Inf and NaN. + // + // ErrUnsupportedFloat 表示配置中的 float64 是一个不支持的数值,如 +Inf, -Inf 和 NaN + ErrUnsupportedFloatInOpt = Error("unsupported float value in option") + + // ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven indicates that if you + // use a slice pr array as first param in Get(...), Set(...).At(...), etc, no + // further params are allowed + // + // ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven 表示如果你在使用 Get(...)、 + // Set(...).At(...) 等类似方法时, 首参数传入一个切片或数组, 那么不允许再传入更多的参数了 + ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven = Error("if first param is a slice or array, no further param are allowed") + + // ErrParameterError indicates misc parameter error. + // + // ErrParameterError 表示各种参数错误 + ErrParameterError = Error("parameter error") +) diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/get.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/get.go new file mode 100644 index 0000000..2ebf605 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/get.go @@ -0,0 +1,552 @@ +package jsonvalue + +import ( + "bytes" + "fmt" + "strings" +) + +// ================ GET ================ + +// Len returns length of an object or array type JSON value. +// +// Len 返回当前对象类型或数组类型的 JSON 的成员长度。如果不是这两种类型,那么会返回 0。 +func (v *V) Len() int { + switch v.valueType { + case Array: + return len(v.children.arr) + case Object: + return len(v.children.object) + default: + return 0 + } +} + +// Get returns JSON value in specified position. Param formats are like At(). +// +// Get 返回按照参数指定的位置的 JSON 成员值。参数格式与 At() 函数相同 +func (v *V) Get(firstParam any, otherParams ...any) (*V, error) { + return get(v, false, firstParam, otherParams...) +} + +// MustGet is same as Get(), but does not return error. If error occurs, a JSON value with +// NotExist type will be returned. +// +// MustGet 与 Get() 函数相同,不过不返回错误。如果发生错误了,那么会返回一个 ValueType() 返回值为 NotExist +// 的 JSON 值对象。 +func (v *V) MustGet(firstParam any, otherParams ...any) *V { + res, _ := get(v, false, firstParam, otherParams...) + return res +} + +func get(v *V, caseless bool, firstParam any, otherParams ...any) (*V, error) { + if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok { + if len(otherParams) > 0 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return get(v, caseless, p1, p2...) + } + child, err := getInCurrentValue(v, caseless, firstParam) + if err != nil { + return &V{}, err + } + + if len(otherParams) == 0 { + return child, nil + } + return get(child, caseless, otherParams[0], otherParams[1:]...) +} + +func initCaselessStorage(v *V) { + if v.children.lowerCaseKeys != nil { + return + } + v.children.lowerCaseKeys = make(map[string]map[string]struct{}, len(v.children.object)) + for k := range v.children.object { + addCaselessKey(v, k) + } +} + +func getFromObjectChildren(v *V, caseless bool, key string) (child *V, exist bool) { + childProperty, exist := v.children.object[key] + if exist { + return childProperty.v, true + } + + if !caseless { + return &V{}, false + } + + initCaselessStorage(v) + + lowerCaseKey := strings.ToLower(key) + keys, exist := v.children.lowerCaseKeys[lowerCaseKey] + if !exist { + return &V{}, false + } + + for actualKey := range keys { + childProperty, exist = v.children.object[actualKey] + if exist { + return childProperty.v, true + } + } + + return &V{}, false +} + +func getInCurrentValue(v *V, caseless bool, param any) (*V, error) { + switch v.valueType { + case Array: + return getInCurrentArray(v, param) + case Object: + return getInCurrentObject(v, caseless, param) + default: + return &V{}, fmt.Errorf("%v type does not supports Get()", v.valueType) + } +} + +func getInCurrentArray(v *V, param any) (*V, error) { + // integer expected + pos, err := anyToInt(param) + if err != nil { + return &V{}, err + } + child, ok := childAtIndex(v, pos) + if !ok { + return &V{}, ErrOutOfRange + } + return child, nil +} + +func getInCurrentObject(v *V, caseless bool, param any) (*V, error) { + // string expected + key, err := anyToString(param) + if err != nil { + return &V{}, err + } + child, exist := getFromObjectChildren(v, caseless, key) + if !exist { + return &V{}, ErrNotFound + } + return child, nil +} + +// GetBytes is similar with v, err := Get(...); v.Bytes(). But if error occurs or Base64 decode error, returns error. +// +// GetBytes 类似于 v, err := Get(...); v.Bytes(),但如果查询中发生错误,或者 base64 解码错误,则返回错误。 +func (v *V) GetBytes(firstParam any, otherParams ...any) ([]byte, error) { + return getBytes(v, false, firstParam, otherParams...) +} + +func getBytes(v *V, caseless bool, firstParam any, otherParams ...any) ([]byte, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return []byte{}, err + } + if ret.valueType != String { + return []byte{}, ErrTypeNotMatch + } + b, err := internal.b64.DecodeString(ret.valueStr) + if err != nil { + return []byte{}, err + } + return b, nil +} + +// GetString is equivalent to v, err := Get(...); v.String(). If error occurs, returns "". +// +// GetString 等效于 v, err := Get(...); v.String()。如果发生错误,则返回 ""。 +func (v *V) GetString(firstParam any, otherParams ...any) (string, error) { + return getString(v, false, firstParam, otherParams...) +} + +func getString(v *V, caseless bool, firstParam any, otherParams ...any) (string, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return "", err + } + if ret.valueType != String { + return "", ErrTypeNotMatch + } + return ret.String(), nil +} + +// GetInt is equivalent to v, err := Get(...); v.Int(). If error occurs, returns 0. +// +// GetInt 等效于 v, err := Get(...); v.Int()。如果发生错误,则返回 0。 +func (v *V) GetInt(firstParam any, otherParams ...any) (int, error) { + return getInt(v, false, firstParam, otherParams...) +} + +func getInt(v *V, caseless bool, firstParam any, otherParams ...any) (int, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Int(), err +} + +// GetUint is equivalent to v, err := Get(...); v.Uint(). If error occurs, returns 0. +// +// GetUint 等效于 v, err := Get(...); v.Uint()。如果发生错误,则返回 0。 +func (v *V) GetUint(firstParam any, otherParams ...any) (uint, error) { + return getUint(v, false, firstParam, otherParams...) +} + +func getUint(v *V, caseless bool, firstParam any, otherParams ...any) (uint, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Uint(), err +} + +// GetInt64 is equivalent to v, err := Get(...); v.Int64(). If error occurs, returns 0. +// +// GetInt64 等效于 v, err := Get(...); v.Int64()。如果发生错误,则返回 0。 +func (v *V) GetInt64(firstParam any, otherParams ...any) (int64, error) { + return getInt64(v, false, firstParam, otherParams...) +} + +func getInt64(v *V, caseless bool, firstParam any, otherParams ...any) (int64, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Int64(), err +} + +// GetUint64 is equivalent to v, err := Get(...); v.Unt64(). If error occurs, returns 0. +// +// GetUint64 等效于 v, err := Get(...); v.Unt64()。如果发生错误,则返回 0。 +func (v *V) GetUint64(firstParam any, otherParams ...any) (uint64, error) { + return getUint64(v, false, firstParam, otherParams...) +} + +func getUint64(v *V, caseless bool, firstParam any, otherParams ...any) (uint64, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Uint64(), err +} + +// GetInt32 is equivalent to v, err := Get(...); v.Int32(). If error occurs, returns 0. +// +// GetInt32 等效于 v, err := Get(...); v.Int32()。如果发生错误,则返回 0。 +func (v *V) GetInt32(firstParam any, otherParams ...any) (int32, error) { + return getInt32(v, false, firstParam, otherParams...) +} + +func getInt32(v *V, caseless bool, firstParam any, otherParams ...any) (int32, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Int32(), err +} + +// GetUint32 is equivalent to v, err := Get(...); v.Uint32(). If error occurs, returns 0. +// +// GetUint32 等效于 v, err := Get(...); v.Uint32()。如果发生错误,则返回 0。 +func (v *V) GetUint32(firstParam any, otherParams ...any) (uint32, error) { + return getUint32(v, false, firstParam, otherParams...) +} + +func getUint32(v *V, caseless bool, firstParam any, otherParams ...any) (uint32, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Uint32(), err +} + +// GetFloat64 is equivalent to v, err := Get(...); v.Float64(). If error occurs, returns 0.0. +// +// GetFloat64 等效于 v, err := Get(...); v.Float64()。如果发生错误,则返回 0.0。 +func (v *V) GetFloat64(firstParam any, otherParams ...any) (float64, error) { + return getFloat64(v, false, firstParam, otherParams...) +} + +func getFloat64(v *V, caseless bool, firstParam any, otherParams ...any) (float64, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Float64(), err +} + +// GetFloat32 is equivalent to v, err := Get(...); v.Float32(). If error occurs, returns 0.0. +// +// GetFloat32 等效于 v, err := Get(...); v.Float32()。如果发生错误,则返回 0.0。 +func (v *V) GetFloat32(firstParam any, otherParams ...any) (float32, error) { + return getFloat32(v, false, firstParam, otherParams...) +} + +func getFloat32(v *V, caseless bool, firstParam any, otherParams ...any) (float32, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return 0, err + } + ret, err = getNumberAndErrorFromValue(ret) + return ret.Float32(), err +} + +// GetBool is equivalent to v, err := Get(...); v.Bool(). If error occurs, returns false. +// +// GetBool 等效于 v, err := Get(...); v.Bool()。如果发生错误,则返回 false。 +func (v *V) GetBool(firstParam any, otherParams ...any) (bool, error) { + return getBool(v, false, firstParam, otherParams...) +} + +func getBool(v *V, caseless bool, firstParam any, otherParams ...any) (bool, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return false, err + } + ret, err = getBoolAndErrorFromValue(ret) + return ret.Bool(), err +} + +// GetNull is equivalent to v, err := Get(...); raise err if error occurs or v.IsNull() == false. +// +// GetNull 等效于 v, err := Get(...);,如果发生错误或者 v.IsNull() == false 则返回错误。 +func (v *V) GetNull(firstParam any, otherParams ...any) error { + return getNull(v, false, firstParam, otherParams...) +} + +func getNull(v *V, caseless bool, firstParam any, otherParams ...any) error { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return err + } + if ret.valueType != Null { + return ErrTypeNotMatch + } + return nil +} + +// GetObject is equivalent to v, err := Get(...); raise err if error occurs or v.IsObject() == false. +// +// GetObject 等效于 v, err := Get(...);,如果发生错误或者 v.IsObject() == false 则返回错误。 +func (v *V) GetObject(firstParam any, otherParams ...any) (*V, error) { + return getObject(v, false, firstParam, otherParams...) +} + +func getObject(v *V, caseless bool, firstParam any, otherParams ...any) (*V, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return &V{}, err + } + if ret.valueType != Object { + return &V{}, ErrTypeNotMatch + } + return ret, nil +} + +// GetArray is equivalent to v, err := Get(...); raise err if or v.IsArray() == false. +// +// GetArray 等效于 v, err := Get(...);,如果发生错误或者 v.IsArray() == false 则返回错误。 +func (v *V) GetArray(firstParam any, otherParams ...any) (*V, error) { + return getArray(v, false, firstParam, otherParams...) +} + +func getArray(v *V, caseless bool, firstParam any, otherParams ...any) (*V, error) { + ret, err := get(v, caseless, firstParam, otherParams...) + if err != nil { + return &V{}, err + } + if ret.valueType != Array { + return &V{}, ErrTypeNotMatch + } + return ret, nil +} + +// ================ CASELESS ================ + +// Caseless is returned by Caseless(). operations of Caseless type are same as (*V).Get(), but are via caseless key. +// +// Caseless 类型通过 Caseless() 函数返回。通过 Caseless 接口操作的所有操作均与 (*v).Get() +// 相同,但是对 key 进行读取的时候,不区分大小写。 +type Caseless interface { + Get(firstParam any, otherParams ...any) (*V, error) + MustGet(firstParam any, otherParams ...any) *V + GetBytes(firstParam any, otherParams ...any) ([]byte, error) + GetString(firstParam any, otherParams ...any) (string, error) + GetInt(firstParam any, otherParams ...any) (int, error) + GetUint(firstParam any, otherParams ...any) (uint, error) + GetInt64(firstParam any, otherParams ...any) (int64, error) + GetUint64(firstParam any, otherParams ...any) (uint64, error) + GetInt32(firstParam any, otherParams ...any) (int32, error) + GetUint32(firstParam any, otherParams ...any) (uint32, error) + GetFloat64(firstParam any, otherParams ...any) (float64, error) + GetFloat32(firstParam any, otherParams ...any) (float32, error) + GetBool(firstParam any, otherParams ...any) (bool, error) + GetNull(firstParam any, otherParams ...any) error + GetObject(firstParam any, otherParams ...any) (*V, error) + GetArray(firstParam any, otherParams ...any) (*V, error) + + Delete(firstParam any, otherParams ...any) error + MustDelete(firstParam any, otherParams ...any) +} + +var _ Caseless = (*V)(nil) + +// Caseless returns Caseless interface to support caseless getting. +// +// IMPORTANT: This function is not gouroutine-safe. Write-mutex (instead of read-mutex) +// should be attached in cross-goroutine scenarios. +// +// Caseless 返回 Caseless 接口,从而实现不区分大小写的 Get 操作。 +// +// 注意: 该函数不是协程安全的,如果在多协程场景下,调用该函数,需要加上写锁,而不能用读锁。 +func (v *V) Caseless() Caseless { + switch v.valueType { + default: + return v + + case Array, Object: + return &caselessOp{ + v: v, + } + } +} + +type caselessOp struct { + v *V +} + +func (g *caselessOp) Get(firstParam any, otherParams ...any) (*V, error) { + return get(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) MustGet(firstParam any, otherParams ...any) *V { + res, _ := get(g.v, true, firstParam, otherParams...) + return res +} + +func (g *caselessOp) GetBytes(firstParam any, otherParams ...any) ([]byte, error) { + return getBytes(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetString(firstParam any, otherParams ...any) (string, error) { + return getString(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetInt(firstParam any, otherParams ...any) (int, error) { + return getInt(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetUint(firstParam any, otherParams ...any) (uint, error) { + return getUint(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetInt64(firstParam any, otherParams ...any) (int64, error) { + return getInt64(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetUint64(firstParam any, otherParams ...any) (uint64, error) { + return getUint64(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetInt32(firstParam any, otherParams ...any) (int32, error) { + return getInt32(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetUint32(firstParam any, otherParams ...any) (uint32, error) { + return getUint32(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetFloat64(firstParam any, otherParams ...any) (float64, error) { + return getFloat64(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetFloat32(firstParam any, otherParams ...any) (float32, error) { + return getFloat32(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetBool(firstParam any, otherParams ...any) (bool, error) { + return getBool(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetNull(firstParam any, otherParams ...any) error { + return getNull(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetObject(firstParam any, otherParams ...any) (*V, error) { + return getObject(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) GetArray(firstParam any, otherParams ...any) (*V, error) { + return getArray(g.v, true, firstParam, otherParams...) +} + +func (g *caselessOp) Delete(firstParam any, otherParams ...any) error { + return g.v.delete(true, firstParam, otherParams...) +} + +func (g *caselessOp) MustDelete(firstParam any, otherParams ...any) { + _ = g.v.delete(true, firstParam, otherParams...) +} + +// ==== internal value access functions ==== + +func getNumberFromNotNumberValue(v *V) *V { + if !v.IsString() { + return NewInt(0) + } + if v.valueStr == "" { + return NewInt64(0) + } + ret, _ := newFromNumber(globalPool{}, bytes.TrimSpace([]byte(v.valueStr))) + err := parseNumber(ret, globalPool{}) + if err != nil { + return NewInt64(0) + } + return ret +} + +func getNumberAndErrorFromValue(v *V) (*V, error) { + switch v.valueType { + default: + return NewInt(0), ErrTypeNotMatch + + case Number: + return v, nil + + case String: + ret, _ := newFromNumber(globalPool{}, bytes.TrimSpace([]byte(v.valueStr))) + err := parseNumber(ret, globalPool{}) + if err != nil { + return NewInt(0), fmt.Errorf("%w: %v", ErrParseNumberFromString, err) + } + return ret, ErrTypeNotMatch + } +} + +func getBoolAndErrorFromValue(v *V) (*V, error) { + switch v.valueType { + default: + return NewBool(false), ErrTypeNotMatch + + case Number: + return NewBool(v.Float64() != 0), ErrTypeNotMatch + + case String: + if v.valueStr == "true" { + return NewBool(true), ErrTypeNotMatch + } + return NewBool(false), ErrTypeNotMatch + + case Boolean: + return v, nil + } +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/import_export.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/import_export.go new file mode 100644 index 0000000..724754e --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/import_export.go @@ -0,0 +1,598 @@ +package jsonvalue + +import ( + "encoding" + "encoding/json" + "fmt" + "reflect" + "strconv" + "strings" +) + +// Export convert jsonvalue to another type of parameter. The target parameter type should match the type of *V. +// +// Export 将 *V 转到符合原生 encoding/json 的一个 struct 中。 +func (v *V) Export(dst any) error { + b, err := v.Marshal() + if err != nil { + return err + } + + return json.Unmarshal(b, dst) +} + +// Import convert json value from a marshal-able parameter to *V. This a experimental function. +// +// Import 将符合 encoding/json 的 struct 转为 *V 类型。不经过 encoding/json,并且支持 Option. +func Import(src any, opts ...Option) (*V, error) { + opt := combineOptions(opts) + ext := ext{} + ext.ignoreOmitempty = opt.ignoreJsonOmitempty + v, fu, err := validateValAndReturnParser(reflect.ValueOf(src), ext) + if err != nil { + return &V{}, err + } + res, err := fu(v, ext) + if err != nil { + return &V{}, err + } + return res, nil +} + +// parserFunc 处理对应 reflect.Value 的函数 +type parserFunc func(v reflect.Value, ex ext) (*V, error) + +type ext struct { + // standard encoding/json tag + omitempty bool + toString bool + + // revert of isExported + private bool + + // extended jsonvalue options + ignoreOmitempty bool +} + +func (e ext) shouldOmitEmpty() bool { + if e.ignoreOmitempty { + return false + } + return e.omitempty || e.private +} + +// validateValAndReturnParser 检查入参合法性并返回相应的处理函数 +func validateValAndReturnParser(v reflect.Value, ex ext) (out reflect.Value, fu parserFunc, err error) { + out = v + + // json.Marshaler and encoding.TextMarshaler + if o, f := checkAndParseMarshaler(v); f != nil { + // jsonvalue itself + if _, ok := v.Interface().(deepCopier); ok { + return out, parseJSONValueDeepCopier, nil + } + return o, f, nil + } + + switch v.Kind() { + default: + // fallthrough + // case reflect.Invalid, reflect.Complex64, reflect.Complex128: + // fallthrough + // case reflect.Chan, reflect.Func, reflect.UnsafePointer: + err = fmt.Errorf("jsonvalue: unsupported type: %v", v.Type()) + + case reflect.Invalid: + fu = parseInvalidValue + + case reflect.Bool: + fu = parseBoolValue + + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + fu = parseIntValue + + case reflect.Uintptr, reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: + fu = parseUintValue + + case reflect.Float32: + fu = parseFloat32Value + + case reflect.Float64: + fu = parseFloat64Value + + case reflect.Array: + fu = parseArrayValue + + case reflect.Interface: + return validateValAndReturnParser(v.Elem(), ex) + + case reflect.Map: + switch v.Type().Key().Kind() { + default: + err = fmt.Errorf("unsupported key type for a map: %v", v.Type().Key()) + case reflect.String: + fu = parseStringMapValue + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + fu = parseIntMapValue + case reflect.Uintptr, reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: + fu = parseUintMapValue + } + + case reflect.Ptr: + fu = parsePtrValue + + case reflect.Slice: + if v.Type() == reflect.TypeOf([]byte{}) { + fu = parseBytesValue + } else { + fu = parseSliceValue + } + + case reflect.String: + fu = parseStringValue + + case reflect.Struct: + fu = parseStructValue + } + + return +} + +// Hit marshaler if fu is not nil. +func checkAndParseMarshaler(v reflect.Value) (out reflect.Value, fu parserFunc) { + out = v + if !v.IsValid() { + return + } + + // check type itself + if v.Type().Implements(internal.types.JSONMarshaler) { + return v, parseJSONMarshaler + } + if v.Type().Implements(internal.types.TextMarshaler) { + return v, parseTextMarshaler + } + + // check its origin type + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return + } + elem := v.Elem() + if elem.Type().Implements(internal.types.JSONMarshaler) { + return elem, parseJSONMarshaler + } + if elem.Type().Implements(internal.types.TextMarshaler) { + return elem, parseTextMarshaler + } + return + } + + // check its pointer type + // referenceType := reflect.PointerTo(v.Type()) // go 1.17 + + referenceType := reflect.PtrTo(v.Type()) + if referenceType.Implements(internal.types.JSONMarshaler) { + return getPointerOfValue(v), parseJSONMarshaler + } + if referenceType.Implements(internal.types.TextMarshaler) { + return getPointerOfValue(v), parseTextMarshaler + } + + return +} + +// reference: +// - [Using reflect to get a pointer to a struct passed as an interface{}](https://groups.google.com/g/golang-nuts/c/KB3_Yj3Ny4c) +// - [reflect.Set slice-of-structs value to a struct, without type assertion (because it's unknown)](https://stackoverflow.com/questions/40474682) +func getPointerOfValue(v reflect.Value) reflect.Value { + vp := reflect.New(reflect.TypeOf(v.Interface())) + vp.Elem().Set(v) + return vp +} + +func parseJSONValueDeepCopier(v reflect.Value, ex ext) (*V, error) { + if v.IsNil() { + return nil, nil // empty + } + j, _ := v.Interface().(deepCopier) + return j.deepCopy(), nil +} + +func parseJSONMarshaler(v reflect.Value, ex ext) (*V, error) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return nil, nil // empty + } + marshaler, _ := v.Interface().(json.Marshaler) + if marshaler == nil { + return nil, nil // empty + } + b, err := marshaler.MarshalJSON() + if err != nil { + return &V{}, fmt.Errorf("JSONMarshaler returns error: %w", err) + } + + res, err := Unmarshal(b) + if err != nil { + return nil, fmt.Errorf("illegal JSON data generated from type '%v', error: %w", v.Type(), err) + } + + if ex.shouldOmitEmpty() { + switch res.ValueType() { + default: + return nil, nil + case String: + if res.String() == "" { + return nil, nil + } + case Number: + if res.Float64() == 0 { + return nil, nil + } + case Array, Object: + if res.Len() == 0 { + return nil, nil + } + case Boolean: + if !res.Bool() { + return nil, nil + } + case Null: + return nil, nil + } + } + + return res, nil +} + +func parseTextMarshaler(v reflect.Value, ex ext) (*V, error) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return nil, nil // empty + } + marshaler, _ := v.Interface().(encoding.TextMarshaler) + if marshaler == nil { + return NewNull(), nil // empty + } + b, err := marshaler.MarshalText() + if err != nil { + return &V{}, err + } + + if len(b) == 0 && ex.shouldOmitEmpty() { + return nil, nil + } + return NewString(string(b)), nil +} + +func parseInvalidValue(_ reflect.Value, ex ext) (*V, error) { + if ex.shouldOmitEmpty() { + return nil, nil + } + return NewNull(), nil +} + +func parseBoolValue(v reflect.Value, ex ext) (*V, error) { + b := v.Bool() + if !b && ex.shouldOmitEmpty() { + return nil, nil + } + if ex.toString { + return NewString(fmt.Sprint(b)), nil + } + return NewBool(b), nil +} + +func parseIntValue(v reflect.Value, ex ext) (*V, error) { + i := v.Int() + if i == 0 && ex.shouldOmitEmpty() { + return nil, nil + } + if ex.toString { + return NewString(strconv.FormatInt(i, 10)), nil + } + return NewInt64(i), nil +} + +func parseUintValue(v reflect.Value, ex ext) (*V, error) { + u := v.Uint() + if u == 0 && ex.shouldOmitEmpty() { + return nil, nil + } + if ex.toString { + return NewString(strconv.FormatUint(u, 10)), nil + } + return NewUint64(u), nil +} + +func parseFloat64Value(v reflect.Value, ex ext) (*V, error) { + f := v.Float() + if f == 0.0 && ex.shouldOmitEmpty() { + return nil, nil + } + if ex.toString { + f64 := NewFloat64(f) + return NewString(f64.MustMarshalString()), nil + } + return NewFloat64(f), nil +} + +func parseFloat32Value(v reflect.Value, ex ext) (*V, error) { + f := v.Float() + if f == 0.0 && ex.shouldOmitEmpty() { + return nil, nil + } + if ex.toString { + f32 := NewFloat32(float32(f)) + return NewString(f32.MustMarshalString()), nil + } + return NewFloat32(float32(f)), nil +} + +func parseArrayValue(v reflect.Value, ex ext) (*V, error) { + ex.omitempty = false + res := NewArray() + le := v.Len() + + for i := 0; i < le; i++ { + vv := v.Index(i) + vv, fu, err := validateValAndReturnParser(vv, ex) + if err != nil { + return nil, err + } + child, err := fu(vv, ex) + if err != nil { + return nil, err + } + res.MustAppend(child).InTheEnd() + } + + return res, nil +} + +func parseMapValue(v reflect.Value, ex ext, keyFunc func(key reflect.Value) string) (*V, error) { + if v.IsNil() { + return parseNullValue(v, ex) + } + + keys := v.MapKeys() + if len(keys) == 0 { + if ex.shouldOmitEmpty() { + return nil, nil + } + return NewObject(), nil + } + + res := NewObject() + + for _, kk := range keys { + vv := v.MapIndex(kk) + vv, fu, err := validateValAndReturnParser(vv, ex) + if err != nil { + return res, err + } + child, err := fu(vv, ex) + if err != nil { + return res, err + } + res.MustSet(child).At(keyFunc(kk)) + } + + return res, nil +} + +func parseStringMapValue(v reflect.Value, ex ext) (*V, error) { + return parseMapValue(v, ex, func(k reflect.Value) string { + return k.String() + }) +} + +func parseIntMapValue(v reflect.Value, ex ext) (*V, error) { + return parseMapValue(v, ex, func(k reflect.Value) string { + return strconv.FormatInt(k.Int(), 10) + }) +} + +func parseUintMapValue(v reflect.Value, ex ext) (*V, error) { + return parseMapValue(v, ex, func(k reflect.Value) string { + return strconv.FormatUint(k.Uint(), 10) + }) +} + +func parsePtrValue(v reflect.Value, ex ext) (*V, error) { + if v.IsNil() { + return parseNullValue(v, ex) + } + + v, fu, err := validateValAndReturnParser(v.Elem(), ex) + if err != nil { + return nil, err + } + + return fu(v, ex) +} + +func parseSliceValue(v reflect.Value, ex ext) (*V, error) { + if v.IsNil() || v.Len() == 0 { + if ex.shouldOmitEmpty() { + return nil, nil + } + return NewArray(), nil + } + + return parseArrayValue(v, ex) +} + +func parseBytesValue(v reflect.Value, ex ext) (*V, error) { + b := v.Interface().([]byte) + if len(b) == 0 && ex.shouldOmitEmpty() { + return nil, nil + } + + return NewBytes(b), nil +} + +func parseStringValue(v reflect.Value, ex ext) (*V, error) { + str := v.String() + if str == "" && ex.shouldOmitEmpty() { + return nil, nil + } + + return NewString(str), nil +} + +func parseStructValue(v reflect.Value, ex ext) (*V, error) { + t := v.Type() + numField := t.NumField() + + res := NewObject() + + for i := 0; i < numField; i++ { + vv := v.Field(i) + tt := t.Field(i) + + keys, children, err := parseStructFieldValue(vv, tt, ex) + if err != nil { + return nil, err + } + + for i, k := range keys { + v := children[i] + res.MustSet(v).At(k) + } + } + + return res, nil +} + +func parseNullValue(_ reflect.Value, ex ext) (*V, error) { + if ex.shouldOmitEmpty() { + return nil, nil + } + return NewNull(), nil +} + +func parseStructFieldValue( + fv reflect.Value, ft reflect.StructField, parentEx ext, +) (keys []string, children []*V, err error) { + + if ft.Anonymous { + return parseStructAnonymousFieldValue(fv, ft, parentEx) + } + + if !fv.CanInterface() { + return + } + + fieldName, ex := readFieldTag(ft, "json", parentEx) + if fieldName == "-" { + return + } + + fv, fu, err := validateValAndReturnParser(fv, ex) + if err != nil { + err = fmt.Errorf("parsing field '%s' error: %w", fieldName, err) + return + } + + child, err := fu(fv, ex) + if err != nil { + err = fmt.Errorf("parsing field '%s' error: %w", fieldName, err) + return + } + if child != nil { + return []string{fieldName}, []*V{child}, nil + } + + return nil, nil, nil +} + +func parseStructAnonymousFieldValue( + fv reflect.Value, ft reflect.StructField, parentEx ext, +) (keys []string, children []*V, err error) { + + fieldName, ex := readAnonymousFieldTag(ft, "json", parentEx) + if fieldName == "-" { + return nil, nil, nil + } + if fv.Kind() == reflect.Ptr && fv.IsNil() { + if ex.shouldOmitEmpty() { + return nil, nil, nil + } + return []string{fieldName}, []*V{NewNull()}, nil + } + + fv, fu, err := validateValAndReturnParser(fv, ex) + if err != nil { + err = fmt.Errorf("parsing anonymous field error: %w", err) + return + } + + child, err := fu(fv, ex) + if err != nil { + err = fmt.Errorf("parsing anonymous field error: %w", err) + return + } + if child == nil { + return nil, nil, nil + } + + switch child.ValueType() { + default: // invalid + return nil, nil, nil + + case String, Number, Boolean, Null, Array: + if ex.private { + return nil, nil, nil + } + return []string{fieldName}, []*V{child}, nil + + case Object: + child.RangeObjectsBySetSequence(func(k string, c *V) bool { + keys = append(keys, k) + children = append(children, c) + return true + }) + return + } +} + +func readFieldTag(ft reflect.StructField, name string, parentEx ext) (field string, ex ext) { + tg := ft.Tag.Get(name) + + if tg == "" { + return ft.Name, ext{ + ignoreOmitempty: parentEx.ignoreOmitempty, + } + } + + parts := strings.Split(tg, ",") + for i, s := range parts { + parts[i] = strings.TrimSpace(s) + if i > 0 { + if s == "omitempty" { + ex.omitempty = true + } else if s == "string" { + ex.toString = true + } + } + } + + field = parts[0] + if field == "" { + field = ft.Name + } + ex.ignoreOmitempty = parentEx.ignoreOmitempty + return +} + +func readAnonymousFieldTag(ft reflect.StructField, name string, parentEx ext) (field string, ex ext) { + field, ex = readFieldTag(ft, name, parentEx) + + firstChar := ft.Name[0] + if firstChar >= 'A' && firstChar <= 'Z' { + ex.private = false + } else { + ex.private = true + } + + return +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete.go new file mode 100644 index 0000000..535a4a2 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete.go @@ -0,0 +1,632 @@ +package jsonvalue + +import ( + "errors" + "fmt" + "strings" +) + +// ================ INSERT ================ + +// MARK: INSERT + +// Inserter type is for After() and Before() methods. +// +// # Should be generated ONLY BY V.Insert() ! +// +// Insert 类型适用于 After() 和 Before() 方法。请注意:该类型仅应由 V.Insert 函数生成! +type Inserter interface { + // After completes the following operation of Insert(). It inserts value AFTER + // specified position. + // + // The last parameter identifies the position where a new JSON is inserted after, + // it should ba an integer, no matter signed or unsigned. If the position is + // zero or positive integer, it tells the index of an array. If the position + // is negative, it tells the backward index of an array. + // + // For example, 0 represents the first, and -2 represents the second last. + // + // After 结束并完成 Insert() 函数的后续插入操作,表示插入到指定位置的前面。 + // + // 在 Before 函数的最后一个参数指定了被插入的 JSON 数组的位置,这个参数应当是一个整型 + // (有无符号类型均可)。 + // 如果这个值等于0或者正整数,那么它指定的是在 JSON 数组中的位置(从0开始)。如果这个值是负数, + // 那么它指定的是 JSON 数组中从最后一个位置开始算起的位置。 + // + // 举例说明:0 表示第一个位置,而 -2 表示倒数第二个位置。 + After(firstParam any, otherParams ...any) (*V, error) + + // Before completes the following operation of Insert(). It inserts value BEFORE + // specified position. + // + // The last parameter identifies the position where a new JSON is inserted after, + // it should ba an integer, no matter signed or unsigned. + // If the position is zero or positive integer, it tells the index of an array. + // If the position is negative, it tells the backward index of an array. + // + // For example, 0 represents the first, and -2 represents the second last. + // + // Before 结束并完成 Insert() 函数的后续插入操作,表示插入到指定位置的后面。 + // + // 在 Before 函数的最后一个参数指定了被插入的 JSON 数组的位置,这个参数应当是一个整型 + //(有无符号类型均可)。 + // 如果这个值等于0或者正整数,那么它指定的是在 JSON 数组中的位置(从0开始)。如果这个值是负数, + // 那么它指定的是 JSON 数组中从最后一个位置开始算起的位置。 + // + // 举例说明:0 表示第一个位置,而 -2 表示倒数第二个位置。 + Before(firstParam any, otherParams ...any) (*V, error) +} + +type insert struct { + v *V + c *V // child + + err error +} + +// Insert starts inserting a child JSON value +// +// Insert 开启一个 JSON 数组成员的插入操作. +func (v *V) Insert(child any) Inserter { + var ch *V + var err error + + if child == nil { + ch = NewNull() + } else if childV, ok := child.(*V); ok { + ch = childV + } else { + ch, err = Import(child) + } + + return &insert{ + v: v, + c: ch, + err: err, + } +} + +// InsertString is equivalent to Insert(jsonvalue.NewString(s)) +// +// InsertString 等效于 Insert(jsonvalue.NewString(s)) +func (v *V) InsertString(s string) Inserter { + return v.Insert(NewString(s)) +} + +// InsertBool is equivalent to Insert(jsonvalue.NewBool(b)) +// +// InsertBool 等效于 Insert(jsonvalue.NewBool(b)) +func (v *V) InsertBool(b bool) Inserter { + return v.Insert(NewBool(b)) +} + +// InsertInt is equivalent to Insert(jsonvalue.NewInt(b)) +// +// InsertInt 等效于 Insert(jsonvalue.NewInt(b)) +func (v *V) InsertInt(i int) Inserter { + return v.Insert(NewInt(i)) +} + +// InsertInt64 is equivalent to Insert(jsonvalue.NewInt64(b)) +// +// InsertInt64 等效于 Insert(jsonvalue.NewInt64(b)) +func (v *V) InsertInt64(i int64) Inserter { + return v.Insert(NewInt64(i)) +} + +// InsertInt32 is equivalent to Insert(jsonvalue.NewInt32(b)) +// +// InsertInt32 等效于 Insert(jsonvalue.NewInt32(b)) +func (v *V) InsertInt32(i int32) Inserter { + return v.Insert(NewInt32(i)) +} + +// InsertUint is equivalent to Insert(jsonvalue.NewUint(b)) +// +// InsertUint 等效于 Insert(jsonvalue.NewUint(b)) +func (v *V) InsertUint(u uint) Inserter { + return v.Insert(NewUint(u)) +} + +// InsertUint64 is equivalent to Insert(jsonvalue.NewUint64(b)) +// +// InsertUint64 等效于 Insert(jsonvalue.NewUint64(b)) +func (v *V) InsertUint64(u uint64) Inserter { + return v.Insert(NewUint64(u)) +} + +// InsertUint32 is equivalent to Insert(jsonvalue.NewUint32(b)) +// +// InsertUint32 等效于 Insert(jsonvalue.NewUint32(b)) +func (v *V) InsertUint32(u uint32) Inserter { + return v.Insert(NewUint32(u)) +} + +// InsertFloat64 is equivalent to Insert(jsonvalue.NewFloat64(b)) +// +// InsertFloat64 等效于 Insert(jsonvalue.NewFloat64(b)) +func (v *V) InsertFloat64(f float64) Inserter { + return v.Insert(NewFloat64(f)) +} + +// InsertFloat32 is equivalent to Insert(jsonvalue.NewFloat32(b)) +// +// InsertFloat32 等效于 Insert(jsonvalue.NewFloat32(b)) +func (v *V) InsertFloat32(f float32) Inserter { + return v.Insert(NewFloat32(f)) +} + +// InsertNull is equivalent to Insert(jsonvalue.NewNull()) +// +// InsertNull 等效于 Insert(jsonvalue.NewNull()) +func (v *V) InsertNull() Inserter { + return v.Insert(NewNull()) +} + +// InsertObject is equivalent to Insert(jsonvalue.NewObject()) +// +// InsertObject 等效于 Insert(jsonvalue.NewObject()) +func (v *V) InsertObject() Inserter { + return v.Insert(NewObject()) +} + +// InsertArray is equivalent to Insert(jsonvalue.NewArray()) +// +// InsertArray 等效于 Insert(jsonvalue.NewArray()) +func (v *V) InsertArray() Inserter { + return v.Insert(NewArray()) +} + +func (ins *insert) Before(firstParam any, otherParams ...any) (*V, error) { + if ins.err != nil { + return &V{}, ins.err + } + if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok { + if len(otherParams) > 0 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return ins.Before(p1, p2...) + } + v := ins.v + c := ins.c + if v.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + + // this is the last iteration + paramCount := len(otherParams) + if paramCount == 0 { + if v.valueType != Array { + return &V{}, ErrNotArrayValue + } + + pos, err := anyToInt(firstParam) + if err != nil { + return &V{}, err + } + + pos = posAtIndexForInsertBefore(v, pos) + if pos < 0 { + return &V{}, ErrOutOfRange + } + insertToArr(v, pos, c) + return c, nil + } + + // this is not the last iteration + child, err := v.GetArray(firstParam, otherParams[:paramCount-1]...) + if err != nil { + return &V{}, err + } + + childIns := &insert{ + v: child, + c: c, + } + return childIns.Before(otherParams[paramCount-1]) +} + +func (ins *insert) After(firstParam any, otherParams ...any) (*V, error) { + if ins.err != nil { + return &V{}, ins.err + } + if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok { + if len(otherParams) > 0 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return ins.After(p1, p2...) + } + v := ins.v + c := ins.c + if nil == v || v.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + + // this is the last iteration + paramCount := len(otherParams) + if paramCount == 0 { + if v.valueType != Array { + return &V{}, ErrNotArrayValue + } + + pos, err := anyToInt(firstParam) + if err != nil { + return &V{}, err + } + + pos, appendToEnd := posAtIndexForInsertAfter(v, pos) + if pos < 0 { + return &V{}, ErrOutOfRange + } + if appendToEnd { + appendToArr(v, c) + } else { + insertToArr(v, pos, c) + } + return c, nil + } + + // this is not the last iteration + child, err := v.GetArray(firstParam, otherParams[:paramCount-1]...) + if err != nil { + return &V{}, err + } + + childIns := &insert{ + v: child, + c: c, + } + return childIns.After(otherParams[paramCount-1]) +} + +func insertToArr(v *V, pos int, child *V) { + v.children.arr = append(v.children.arr, nil) + copy(v.children.arr[pos+1:], v.children.arr[pos:]) + v.children.arr[pos] = child +} + +// ================ APPEND ================ + +// MARK: APPEND + +// Appender type is for InTheEnd() or InTheBeginning() function. +// +// Appender 类型是用于 InTheEnd() 和 InTheBeginning() 函数的。使用者可以不用关注这个类型。 +// 并且这个类型只应当由 V.Append() 产生。 +type Appender interface { + InTheBeginning(params ...any) (*V, error) + InTheEnd(params ...any) (*V, error) +} + +type appender struct { + v *V + c *V // child + + err error +} + +// Append starts appending a child JSON value to a JSON array. +// +// Append 开始将一个 JSON 值添加到一个数组中。需结合 InTheEnd() 和 InTheBeginning() 函数使用。 +func (v *V) Append(child any) Appender { + var ch *V + var err error + + if child == nil { + ch = NewNull() + } else if childV, ok := child.(*V); ok { + ch = childV + } else { + ch, err = Import(child) + } + return &appender{ + v: v, + c: ch, + err: err, + } +} + +// AppendString is equivalent to Append(jsonvalue.NewString(s)) +// +// AppendString 等价于 Append(jsonvalue.NewString(s)) +func (v *V) AppendString(s string) Appender { + return v.Append(NewString(s)) +} + +// AppendBytes is equivalent to Append(jsonvalue.NewBytes(b)) +// +// AppendBytes 等价于 Append(jsonvalue.NewBytes(b)) +func (v *V) AppendBytes(b []byte) Appender { + return v.Append(NewBytes(b)) +} + +// AppendBool is equivalent to Append(jsonvalue.NewBool(b)) +// +// AppendBool 等价于 Append(jsonvalue.NewBool(b)) +func (v *V) AppendBool(b bool) Appender { + return v.Append(NewBool(b)) +} + +// AppendInt is equivalent to Append(jsonvalue.NewInt(b)) +// +// AppendInt 等价于 Append(jsonvalue.NewInt(b)) +func (v *V) AppendInt(i int) Appender { + return v.Append(NewInt(i)) +} + +// AppendInt64 is equivalent to Append(jsonvalue.NewInt64(b)) +// +// AppendInt64 等价于 Append(jsonvalue.NewInt64(b)) +func (v *V) AppendInt64(i int64) Appender { + return v.Append(NewInt64(i)) +} + +// AppendInt32 is equivalent to Append(jsonvalue.NewInt32(b)) +// +// AppendInt32 等价于 Append(jsonvalue.NewInt32(b)) +func (v *V) AppendInt32(i int32) Appender { + return v.Append(NewInt32(i)) +} + +// AppendUint is equivalent to Append(jsonvalue.NewUint(b)) +// +// AppendUint 等价于 Append(jsonvalue.NewUint(b)) +func (v *V) AppendUint(u uint) Appender { + return v.Append(NewUint(u)) +} + +// AppendUint64 is equivalent to Append(jsonvalue.NewUint64(b)) +// +// AppendUint64 等价于 Append(jsonvalue.NewUint64(b)) +func (v *V) AppendUint64(u uint64) Appender { + return v.Append(NewUint64(u)) +} + +// AppendUint32 is equivalent to Append(jsonvalue.NewUint32(b)) +// +// AppendUint32 等价于 Append(jsonvalue.NewUint32(b)) +func (v *V) AppendUint32(u uint32) Appender { + return v.Append(NewUint32(u)) +} + +// AppendFloat64 is equivalent to Append(jsonvalue.NewFloat64(b)) +// +// AppendUint32 等价于 Append(jsonvalue.NewUint32(b)) +func (v *V) AppendFloat64(f float64) Appender { + return v.Append(NewFloat64(f)) +} + +// AppendFloat32 is equivalent to Append(jsonvalue.NewFloat32(b)) +// +// AppendFloat32 等价于 Append(jsonvalue.NewFloat32(b)) +func (v *V) AppendFloat32(f float32) Appender { + return v.Append(NewFloat32(f)) +} + +// AppendNull is equivalent to Append(jsonvalue.NewNull()) +// +// AppendNull 等价于 Append(jsonvalue.NewNull()) +func (v *V) AppendNull() Appender { + return v.Append(NewNull()) +} + +// AppendObject is equivalent to Append(jsonvalue.NewObject()) +// +// AppendObject 等价于 Append(jsonvalue.NewObject()) +func (v *V) AppendObject() Appender { + return v.Append(NewObject()) +} + +// AppendArray is equivalent to Append(jsonvalue.NewArray()) +// +// AppendArray 等价于 Append(jsonvalue.NewArray()) +func (v *V) AppendArray() Appender { + return v.Append(NewArray()) +} + +// InTheBeginning completes the following operation of Append(). +// +// InTheBeginning 函数将 Append 函数指定的 JSON 值,添加到参数指定的数组的最前端 +func (apd *appender) InTheBeginning(params ...any) (*V, error) { + v := apd.v + c := apd.c + if nil == v || v.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + + // this is the last iteration + paramCount := len(params) + if paramCount == 0 { + if v.valueType != Array { + return &V{}, ErrNotArrayValue + } + if v.Len() == 0 { + appendToArr(v, c) + } else { + insertToArr(v, 0, c) + } + return c, nil + } + if ok, p := isSliceAndExtractJointParams(params[0]); ok { + if len(params) > 1 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return apd.InTheBeginning(p...) + } + + // this is not the last iteration + child, err := v.GetArray(params[0], params[1:paramCount]...) + if err != nil { + if !errors.Is(err, ErrNotFound) { + return &V{}, err + } + child, err = v.SetArray().At(params[0], params[1:]...) + if err != nil { + return &V{}, err + } + } + + if child.Len() == 0 { + appendToArr(child, c) + } else { + insertToArr(child, 0, c) + } + return c, nil +} + +// InTheEnd completes the following operation of Append(). +// +// InTheEnd 函数将 Append 函数指定的 JSON 值,添加到参数指定的数组的最后面 +func (apd *appender) InTheEnd(params ...any) (*V, error) { + v := apd.v + c := apd.c + if v.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + + // this is the last iteration + paramCount := len(params) + if paramCount == 0 { + if v.valueType != Array { + return &V{}, ErrNotArrayValue + } + + appendToArr(v, c) + return c, nil + } + if ok, p := isSliceAndExtractJointParams(params[0]); ok { + if len(params) > 1 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return apd.InTheEnd(p...) + } + + // this is not the last iteration + child, err := v.GetArray(params[0], params[1:paramCount]...) + if err != nil { + if !errors.Is(err, ErrNotFound) { + return &V{}, err + } + child, err = v.SetArray().At(params[0], params[1:]...) + if err != nil { + return &V{}, err + } + } + + appendToArr(child, c) + return c, nil +} + +// ================ DELETE ================ + +// MARK: DELETE + +func delFromObjectChildren(v *V, caseless bool, key string) (exist bool) { + _, exist = v.children.object[key] + if exist { + delete(v.children.object, key) + delCaselessKey(v, key) + return true + } + + if !caseless { + return false + } + + initCaselessStorage(v) + + lowerKey := strings.ToLower(key) + keys, exist := v.children.lowerCaseKeys[lowerKey] + if !exist { + return false + } + + for actualKey := range keys { + _, exist = v.children.object[actualKey] + if exist { + delete(v.children.object, actualKey) + delCaselessKey(v, actualKey) + return true + } + } + + return false +} + +// Delete deletes specified JSON value. For example, parameters ("data", "list") identifies deleting value in data.list. +// While ("list", 1) means deleting the second element from the "list" array. +// +// Delete 从 JSON 中删除参数指定的对象。比如参数 ("data", "list") 表示删除 data.list 值;参数 ("list", 1) 则表示删除 list +// 数组的第2(从1算起)个值。 +func (v *V) Delete(firstParam any, otherParams ...any) error { + return v.delete(false, firstParam, otherParams...) +} + +func (v *V) delete(caseless bool, firstParam any, otherParams ...any) error { + if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok { + if len(otherParams) > 0 { + return ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return v.delete(caseless, p1, p2...) + } + + paramCount := len(otherParams) + if paramCount == 0 { + return deleteInCurrentValue(v, caseless, firstParam) + } + + child, err := get(v, caseless, firstParam, otherParams[:paramCount-1]...) + if err != nil { + return err + } + // if child == nil { + // return ErrNotFound + // } + + return child.delete(caseless, otherParams[paramCount-1]) +} + +func deleteInCurrentValue(v *V, caseless bool, param any) error { + switch v.valueType { + case Object: + return deleteInCurrentObject(v, caseless, param) + case Array: + return deleteInCurrentArray(v, param) + default: + // else, this is an object value + return fmt.Errorf("%v type does not supports Delete()", v.valueType) + } +} + +func deleteInCurrentObject(v *V, caseless bool, param any) error { + // string expected + key, err := anyToString(param) + if err != nil { + return err + } + if exist := delFromObjectChildren(v, caseless, key); !exist { + return ErrNotFound + } + return nil +} + +func deleteInCurrentArray(v *V, param any) error { + // integer expected + pos, err := anyToInt(param) + if err != nil { + return err + } + pos = posAtIndexForRead(v, pos) + if pos < 0 { + return ErrOutOfRange + } + deleteInArr(v, pos) + return nil +} + +func deleteInArr(v *V, pos int) { + le := len(v.children.arr) + v.children.arr[pos] = nil + copy(v.children.arr[pos:], v.children.arr[pos+1:]) + v.children.arr = v.children.arr[:le-1] +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete_must.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete_must.go new file mode 100644 index 0000000..c21b6b1 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/insert_append_delete_must.go @@ -0,0 +1,245 @@ +package jsonvalue + +// ================ INSERT ================ + +// MustInserter is just like Inserter, but not returning sub-value or error. +type MustInserter interface { + // After completes the following operation of Insert(). It inserts value AFTER + // specified position. + // + // The last parameter identifies the position where a new JSON is inserted after, + // it should ba an integer, no matter signed or unsigned. If the position is + // zero or positive integer, it tells the index of an array. If the position + // is negative, it tells the backward index of an array. + // + // For example, 0 represents the first, and -2 represents the second last. + // + // After 结束并完成 Insert() 函数的后续插入操作,表示插入到指定位置的前面。 + // + // 在 Before 函数的最后一个参数指定了被插入的 JSON 数组的位置,这个参数应当是一个整型 + // (有无符号类型均可)。 + // 如果这个值等于0或者正整数,那么它指定的是在 JSON 数组中的位置(从0开始)。如果这个值是负数, + // 那么它指定的是 JSON 数组中从最后一个位置开始算起的位置。 + // + // 举例说明:0 表示第一个位置,而 -2 表示倒数第二个位置。 + After(firstParam any, otherParams ...any) + + // Before completes the following operation of Insert(). It inserts value BEFORE + // specified position. + // + // The last parameter identifies the position where a new JSON is inserted after, + // it should ba an integer, no matter signed or unsigned. If the position is + // zero or positive integer, it tells the index of an array. If the position + // is negative, it tells the backward index of an array. + // + // For example, 0 represents the first, and -2 represents the second last. + // + // Before 结束并完成 Insert() 函数的后续插入操作,表示插入到指定位置的后面。 + // + // 在 Before 函数的最后一个参数指定了被插入的 JSON 数组的位置,这个参数应当是一个整型 + // (有无符号类型均可)。 + // 如果这个值等于0或者正整数,那么它指定的是在 JSON 数组中的位置(从0开始)。如果这个值是负数, + // 那么它指定的是 JSON 数组中从最后一个位置开始算起的位置。 + // + // 举例说明:0 表示第一个位置,而 -2 表示倒数第二个位置。 + Before(firstParam any, otherParams ...any) +} + +type mInsert struct { + inserter Inserter +} + +// MustInsert is just like Insert, but not returning sub-value or error. +func (v *V) MustInsert(child any) MustInserter { + ins := v.Insert(child) + return &mInsert{ + inserter: ins, + } +} + +// MustInsertString is just like InsertString, but not returning sub-value or error. +func (v *V) MustInsertString(s string) MustInserter { + return v.MustInsert(NewString(s)) +} + +// MustInsertBool is just like InsertBool, but not returning sub-value or error. +func (v *V) MustInsertBool(b bool) MustInserter { + return v.MustInsert(NewBool(b)) +} + +// MustInsertInt is just like InsertInt, but not returning sub-value or error. +func (v *V) MustInsertInt(i int) MustInserter { + return v.MustInsert(NewInt(i)) +} + +// MustInsertInt64 is just like InsertInt64, but not returning sub-value or error. +func (v *V) MustInsertInt64(i int64) MustInserter { + return v.MustInsert(NewInt64(i)) +} + +// MustInsertInt32 is just like InsertInt32, but not returning sub-value or error. +func (v *V) MustInsertInt32(i int32) MustInserter { + return v.MustInsert(NewInt32(i)) +} + +// MustInsertUint is just like InsertUint, but not returning sub-value or error. +func (v *V) MustInsertUint(u uint) MustInserter { + return v.MustInsert(NewUint(u)) +} + +// MustInsertUint64 is just like InsertUint64, but not returning sub-value or error. +func (v *V) MustInsertUint64(u uint64) MustInserter { + return v.MustInsert(NewUint64(u)) +} + +// MustInsertUint32 is just like InsertUint32, but not returning sub-value or error. +func (v *V) MustInsertUint32(u uint32) MustInserter { + return v.MustInsert(NewUint32(u)) +} + +// MustInsertFloat64 is just like InsertFloat64, but not returning sub-value or error. +func (v *V) MustInsertFloat64(f float64) MustInserter { + return v.MustInsert(NewFloat64(f)) +} + +// MustInsertFloat32 is just like InsertFloat32, but not returning sub-value or error. +func (v *V) MustInsertFloat32(f float32) MustInserter { + return v.MustInsert(NewFloat32(f)) +} + +// MustInsertNull is just like InsertNull, but not returning sub-value or error. +func (v *V) MustInsertNull() MustInserter { + return v.MustInsert(NewNull()) +} + +// MustInsertObject is just like InsertObject, but not returning sub-value or error. +func (v *V) MustInsertObject() MustInserter { + return v.MustInsert(NewObject()) +} + +// MustInsertArray is just like InsertArray, but not returning sub-value or error. +func (v *V) MustInsertArray() MustInserter { + return v.MustInsert(NewArray()) +} + +func (ins *mInsert) Before(firstParam any, otherParams ...any) { + _, _ = ins.inserter.Before(firstParam, otherParams...) +} + +func (ins *mInsert) After(firstParam any, otherParams ...any) { + _, _ = ins.inserter.After(firstParam, otherParams...) +} + +// ================ APPEND ================ + +// MustAppender is just like Appender, but not returning sub-value or error. +type MustAppender interface { + // InTheBeginning completes the following operation of Append(). + // + // InTheBeginning 函数将 Append 函数指定的 JSON 值,添加到参数指定的数组的最前端 + InTheBeginning(params ...any) + + // InTheEnd completes the following operation of Append(). + // + // InTheEnd 函数将 Append 函数指定的 JSON 值,添加到参数指定的数组的最后面 + InTheEnd(params ...any) +} + +type mAppender struct { + appender Appender +} + +// Append starts appending a child JSON value to a JSON array. +// +// Append 开始将一个 JSON 值添加到一个数组中。需结合 InTheEnd() 和 InTheBeginning() 函数使用。 +func (v *V) MustAppend(child any) MustAppender { + a := v.Append(child) + return &mAppender{ + appender: a, + } +} + +// MustAppendString is just like AppendString, but not returning sub-value or error. +func (v *V) MustAppendString(s string) MustAppender { + return v.MustAppend(NewString(s)) +} + +// MustAppendBytes is just like AppendBytes, but not returning sub-value or error. +func (v *V) MustAppendBytes(b []byte) MustAppender { + return v.MustAppend(NewBytes(b)) +} + +// MustAppendBool is just like AppendBool, but not returning sub-value or error. +func (v *V) MustAppendBool(b bool) MustAppender { + return v.MustAppend(NewBool(b)) +} + +// MustAppendInt is just like AppendInt, but not returning sub-value or error. +func (v *V) MustAppendInt(i int) MustAppender { + return v.MustAppend(NewInt(i)) +} + +// MustAppendInt64 is just like AppendInt64, but not returning sub-value or error. +func (v *V) MustAppendInt64(i int64) MustAppender { + return v.MustAppend(NewInt64(i)) +} + +// MustAppendInt32 is just like AppendInt32, but not returning sub-value or error. +func (v *V) MustAppendInt32(i int32) MustAppender { + return v.MustAppend(NewInt32(i)) +} + +// MustAppendUint is just like AppendUint, but not returning sub-value or error. +func (v *V) MustAppendUint(u uint) MustAppender { + return v.MustAppend(NewUint(u)) +} + +// MustAppendUint64 is just like AppendUint64, but not returning sub-value or error. +func (v *V) MustAppendUint64(u uint64) MustAppender { + return v.MustAppend(NewUint64(u)) +} + +// MustAppendUint32 is just like AppendUint32, but not returning sub-value or error. +func (v *V) MustAppendUint32(u uint32) MustAppender { + return v.MustAppend(NewUint32(u)) +} + +// MustAppendFloat64 is just like AppendFloat64, but not returning sub-value or error. +func (v *V) MustAppendFloat64(f float64) MustAppender { + return v.MustAppend(NewFloat64(f)) +} + +// MustAppendFloat32 is just like AppendFloat32, but not returning sub-value or error. +func (v *V) MustAppendFloat32(f float32) MustAppender { + return v.MustAppend(NewFloat32(f)) +} + +// MustAppendNull is just like AppendNull, but not returning sub-value or error. +func (v *V) MustAppendNull() MustAppender { + return v.MustAppend(NewNull()) +} + +// MustAppendObject is just like AppendObject, but not returning sub-value or error. +func (v *V) MustAppendObject() MustAppender { + return v.MustAppend(NewObject()) +} + +// MustAppendArray is just like AppendArray, but not returning sub-value or error. +func (v *V) MustAppendArray() MustAppender { + return v.MustAppend(NewArray()) +} + +func (apd *mAppender) InTheBeginning(params ...any) { + _, _ = apd.appender.InTheBeginning(params...) +} + +func (apd *mAppender) InTheEnd(params ...any) { + _, _ = apd.appender.InTheEnd(params...) +} + +// ================ DELETE ================ + +// MustDelete is just like Delete, but not returning error. +func (v *V) MustDelete(firstParam any, otherParams ...any) { + _ = v.Delete(firstParam, otherParams...) +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer.go new file mode 100644 index 0000000..415958d --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer.go @@ -0,0 +1,18 @@ +// Package buffer implements a marshaling buffer for jsonvalue +package buffer + +// Buffer defines a buffer type +type Buffer interface { + WriteByte(byte) error + Write(d []byte) (int, error) + WriteString(s string) (int, error) + WriteRune(r rune) (int, error) + Bytes() []byte +} + +// NewBuffer returns a buffer +func NewBuffer() Buffer { + return &buffer{ + buff: make([]byte, 0, 4096), + } +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_impl.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_impl.go new file mode 100644 index 0000000..5a4be13 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_impl.go @@ -0,0 +1,32 @@ +package buffer + +import ( + "github.com/Andrew-M-C/go.jsonvalue/internal/unsafe" +) + +type buffer struct { + buff []byte +} + +func (b *buffer) WriteByte(c byte) error { + b.buff = append(b.buff, c) + return nil +} + +func (b *buffer) Write(d []byte) (int, error) { + b.buff = append(b.buff, d...) + return len(d), nil +} + +func (b *buffer) WriteString(s string) (int, error) { + d := unsafe.StoB(s) + return b.Write(d) +} + +func (b *buffer) WriteRune(r rune) (int, error) { + return b.WriteString(string(r)) +} + +func (b *buffer) Bytes() []byte { + return b.buff +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_internal.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_internal.go new file mode 100644 index 0000000..68027d1 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/buffer/buffer_internal.go @@ -0,0 +1 @@ +package buffer diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/unsafe/unsafe.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/unsafe/unsafe.go new file mode 100644 index 0000000..53f95ec --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/internal/unsafe/unsafe.go @@ -0,0 +1,18 @@ +package unsafe + +import ( + "reflect" + "unsafe" +) + +// BtoS []byte to string +func BtoS(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// StoB string to []byte +func StoB(s string) []byte { + sh := (*reflect.SliceHeader)(unsafe.Pointer(&s)) + sh.Cap = sh.Len + return *(*[]byte)(unsafe.Pointer(sh)) +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/iteration.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/iteration.go new file mode 100644 index 0000000..ca182a1 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/iteration.go @@ -0,0 +1,150 @@ +package jsonvalue + +import "sort" + +// Deprecated: ObjectIter is a deprecated type. +type ObjectIter struct { + K string + V *V +} + +// Deprecated: ArrayIter is a deprecated type. +type ArrayIter struct { + I int + V *V +} + +// RangeObjects goes through each children when this is an object value +// +// Return true in callback to continue range iteration, while false to break. +// +// 若当前 JSON 值是一个 object 类型时,RangeObjects 遍历所有的键值对。 +// +// 在回调函数中返回 true 表示继续迭代,返回 false 表示退出迭代 +func (v *V) RangeObjects(callback func(k string, v *V) bool) { + if !v.IsObject() { + return + } + if nil == callback { + return + } + + for k, c := range v.children.object { + ok := callback(k, c.v) + if !ok { + break + } + } +} + +// RangeObjectsBySetSequence acts just like RangeObjects, but the key sequence +// is arranged by when a key is set to the given object. +// +// RangeObjectsBySetSequence 类似于 RangeObjects 函数, 但是 key 的顺序会依照其被 set +// 进这个 object 的顺序传递。 +func (v *V) RangeObjectsBySetSequence(callback func(k string, v *V) bool) { + if !v.IsObject() { + return + } + if nil == callback { + return + } + + type keysAndID struct { + k string + id uint32 + v *V + } + + kvs := make([]keysAndID, 0, len(v.children.object)) + for k, child := range v.children.object { + kvs = append(kvs, keysAndID{ + k: k, + id: child.id, + v: child.v, + }) + } + sort.Slice(kvs, func(i, j int) bool { + return kvs[i].id <= kvs[j].id + }) + + for _, kv := range kvs { + ok := callback(kv.k, kv.v) + if !ok { + break + } + } +} + +// Deprecated: IterObjects is deprecated, please Use ForRangeObj() instead. +func (v *V) IterObjects() <-chan *ObjectIter { + ch := make(chan *ObjectIter, len(v.children.object)) + + go func() { + for k, c := range v.children.object { + ch <- &ObjectIter{ + K: k, + V: c.v, + } + } + close(ch) + }() + return ch +} + +// ForRangeObj returns a map which can be used in for - range block to iteration KVs in a JSON object value. +// +// ForRangeObj 返回一个 map 类型,用于使用 for - range 块迭代 JSON 对象类型的子成员。 +func (v *V) ForRangeObj() map[string]*V { + res := make(map[string]*V, len(v.children.object)) + for k, c := range v.children.object { + res[k] = c.v + } + return res +} + +// RangeArray goes through each children when this is an array value +// +// Return true in callback to continue range iteration, while false to break. +// +// 若当前 JSON 值是一个 array 类型时,RangeArray 遍历所有的数组成员。 +// +// 在回调函数中返回 true 表示继续迭代,返回 false 表示退出迭代 +func (v *V) RangeArray(callback func(i int, v *V) bool) { + if !v.IsArray() { + return + } + if nil == callback { + return + } + + for i, child := range v.children.arr { + if ok := callback(i, child); !ok { + break + } + } +} + +// Deprecated: IterArray is deprecated, please Use ForRangeArr() instead. +func (v *V) IterArray() <-chan *ArrayIter { + c := make(chan *ArrayIter, len(v.children.arr)) + + go func() { + for i, child := range v.children.arr { + c <- &ArrayIter{ + I: i, + V: child, + } + } + close(c) + }() + return c +} + +// ForRangeArr returns a slice which can be used in for - range block to iteration KVs in a JSON array value. +// +// ForRangeObj 返回一个切片,用于使用 for - range 块迭代 JSON 数组类型的子成员。 +func (v *V) ForRangeArr() []*V { + res := make([]*V, 0, len(v.children.arr)) + return append(res, v.children.arr...) +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue.go new file mode 100644 index 0000000..d9e64de --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue.go @@ -0,0 +1,598 @@ +// Package jsonvalue is for JSON parsing and setting. It is used in situations those +// Go structures cannot achieve, or "map[string]any" could not do properly. +// +// As a quick start: +// +// v := jsonvalue.NewObject() +// v.SetString("Hello, JSON").At("someObject", "someObject", "someObject", "message") // automatically create sub objects +// fmt.Println(v.MustMarshalString()) // marshal to string type. Use MustMarshal if you want []byte instead. +// // Output: +// // {"someObject":{"someObject":{"someObject":{"message":"Hello, JSON!"}}} +// +// If you want to parse raw JSON data, use Unmarshal() +// +// raw := []byte(`{"message":"hello, world"}`) +// v, err := jsonvalue.Unmarshal(raw) +// s, _ := v.GetString("message") +// fmt.Println(s) +// // Output: +// // hello, world +// +// jsonvalue 包用于 JSON 的解析(反序列化)和编码(序列化)。通常情况下我们用 struct 来处理 +// 结构化的 JSON,但是有时候使用 struct 不方便或者是功能不足的时候,go 一般而言使用的是 +// "map[string]any",但是后者也有很多不方便的地方。本包即是用于替代这些不方便的情况的。 +// +// 快速上手: +// +// v := jsonvalue.NewObject() +// v.SetString("Hello, JSON").At("someObject", "someObject", "someObject", "message") // 自动创建子成员 +// fmt.Println(v.MustMarshalString()) // 序列化为 string 类型,如果你要 []byte 类型,则使用 MustMarshal 函数。 +// // 输出: +// // {"someObject":{"someObject":{"someObject":{"message":"Hello, JSON!"}}} +// +// 如果要反序列化原始的 JSON 文本,则使用 Unmarshal(): +// +// raw := []byte(`{"message":"hello, world"}`) +// v, err := jsonvalue.Unmarshal(raw) +// s, _ := v.GetString("message") +// fmt.Println(s) +// // 输出: +// // hello, world +package jsonvalue + +import ( + "bytes" + "strconv" + "strings" + + "github.com/Andrew-M-C/go.jsonvalue/internal/unsafe" +) + +// ValueType identifying JSON value type +type ValueType int + +const ( + // NotExist type tells that this JSON value is not exist or legal + NotExist ValueType = iota + // String JSON string type + String + // Number JSON number type + Number + // Object JSON object type + Object + // Array JSON array type + Array + // Boolean JSON boolean type + Boolean + // Null JSON null type + Null + // Unknown unknown JSON type + Unknown +) + +var typeStr = [Unknown + 1]string{ + "illegal", + "string", + "number", + "object", + "array", + "boolean", + "null", + "unknown", +} + +// String show the type name of JSON +func (t ValueType) String() string { + if t > Unknown { + t = NotExist + } else if t < 0 { + t = NotExist + } + return typeStr[int(t)] +} + +// ValueType returns the type of this JSON value. +func (v *V) ValueType() ValueType { + return v.valueType +} + +// V is the main type of jsonvalue, representing a JSON value. +// +// V 是 jsonvalue 的主类型,表示一个 JSON 值。 +type V struct { + valueType ValueType + + srcByte []byte + + num num + valueStr string + valueBool bool + children children +} + +type num struct { + negative bool + floated bool + i64 int64 + u64 uint64 + f64 float64 +} + +type childWithProperty struct { + id uint32 + v *V +} + +type children struct { + incrID uint32 + arr []*V + object map[string]childWithProperty + + // As official json package supports caseless key accessing, I decide to do it as well + lowerCaseKeys map[string]map[string]struct{} +} + +func (c *children) deepCopy() children { + res := children{ + incrID: c.incrID, + } + + // if length or arr > 0, this must be an array type + if len(c.arr) > 0 { + for _, v := range c.arr { + res.arr = append(res.arr, v.deepCopy()) + } + return res + } + + // if this is an object? + if c.object != nil { + res.object = make(map[string]childWithProperty, len(c.object)) + for key, item := range c.object { + res.object[key] = childWithProperty{ + id: item.id, + v: item.v.deepCopy(), + } + } + } + + // no need to copy lowerCaseKeys because it could be rebuild after calling + // Caseless() next time + + return res +} + +func addCaselessKey(v *V, k string) { + if v.children.lowerCaseKeys == nil { + return + } + lowerK := strings.ToLower(k) + keys, exist := v.children.lowerCaseKeys[lowerK] + if !exist { + keys = make(map[string]struct{}) + v.children.lowerCaseKeys[lowerK] = keys + } + keys[k] = struct{}{} +} + +func delCaselessKey(v *V, k string) { + if v.children.lowerCaseKeys == nil { + return + } + lowerK := strings.ToLower(k) + keys, exist := v.children.lowerCaseKeys[lowerK] + if !exist { + return + } + + delete(keys, k) + + if len(keys) == 0 { + delete(v.children.lowerCaseKeys, lowerK) + } +} + +// MustUnmarshalString just like UnmarshalString(). If error occurs, a JSON value +// with "NotExist" type would be returned, which could do nothing and return nothing +// in later use. It is useful to shorten codes. +// +// MustUnmarshalString 的逻辑与 UnmarshalString() 相同,不过如果错误的话,会返回一个类型未 +// "NotExist" 的 JSON 值,这个值在后续的操作中将无法返回有效的数据,或者是执行任何有效的操作。 +// 但起码不会导致程序 panic,便于使用短代码实现一些默认逻辑。 +func MustUnmarshalString(s string) *V { + v, _ := UnmarshalString(s) + return v +} + +// UnmarshalString is equivalent to Unmarshal([]byte(b)), but more efficient. +// +// UnmarshalString 等效于 Unmarshal([]byte(b)),但效率更高。 +func UnmarshalString(s string) (*V, error) { + // reference: https://stackoverflow.com/questions/41591097/slice-bounds-out-of-range-when-using-unsafe-pointer + // sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) + // bh := reflect.SliceHeader{ + // Data: sh.Data, + // Len: sh.Len, + // Cap: sh.Len, + // } + // b := *(*[]byte)(unsafe.Pointer(&bh)) + b := []byte(s) + return UnmarshalNoCopy(b) +} + +// MustUnmarshal just like Unmarshal(). If error occurs, a JSON value with "NotExist" +// type would be returned, which could do nothing and return nothing in later use. It +// is useful to shorten codes. +// +// MustUnmarshal 的逻辑与 Unmarshal() 相同,不过如果错误的话,会返回一个类型未 "NotExist" 的 +// JSON 值,这个值在后续的操作中将无法返回有效的数据,或者是执行任何有效的操作。但起码不会导致程序 +// panic,便于使用短代码实现一些默认逻辑。 +func MustUnmarshal(b []byte) *V { + v, _ := Unmarshal(b) + return v +} + +// Unmarshal parse raw bytes(encoded in UTF-8 or pure AscII) and returns a *V instance. +// +// Unmarshal 解析原始的字节类型数据(以 UTF-8 或纯 AscII 编码),并返回一个 *V 对象。 +func Unmarshal(b []byte) (ret *V, err error) { + le := len(b) + if le == 0 { + return nil, ErrNilParameter + } + + trueB := make([]byte, len(b)) + copy(trueB, b) + it := iter(trueB) + p := newPool(len(b)) + ret, err = unmarshalWithIter(p, it, 0) + p.release() + return +} + +// MustUnmarshalNoCopy just like UnmarshalNoCopy(). If error occurs, a JSON value +// with "NotExist" type would be returned, which could do nothing and return nothing +// in later use. It is useful to shorten codes. +// +// MustUnmarshalNoCopy 的逻辑与 UnmarshalNoCopy() 相同,不过如果错误的话,会返回一个类型未 +// "NotExist" 的 JSON 值,这个值在后续的操作中将无法返回有效的数据,或者是执行任何有效的操作。 +// 但起码不会导致程序 panic,便于使用短代码实现一些默认逻辑。 +func MustUnmarshalNoCopy(b []byte) *V { + v, _ := UnmarshalNoCopy(b) + return v +} + +// UnmarshalNoCopy is same as Unmarshal, but it does not copy another []byte instance +// for saving CPU time. But pay attention that the input []byte may be used as buffer +// by jsonvalue and mey be modified. +// +// UnmarshalNoCopy 与 Unmarshal 相同,但是这个函数在解析过程中不会重新复制一个 []byte,对于大 +// json 的解析而言能够大大节省时间。但请注意传入的 []byte 变量可能会被 jsonvalue 用作缓冲区,并进行修改 +func UnmarshalNoCopy(b []byte) (ret *V, err error) { + le := len(b) + if le == 0 { + return &V{}, ErrNilParameter + } + p := newPool(len(b)) + ret, err = unmarshalWithIter(p, iter(b), 0) + p.release() + return +} + +// ==== type access ==== + +// IsObject tells whether value is an object +// +// IsObject 判断当前值是不是一个对象类型 +func (v *V) IsObject() bool { + return v.valueType == Object +} + +// IsArray tells whether value is an array +// +// IsArray 判断当前值是不是一个数组类型 +func (v *V) IsArray() bool { + return v.valueType == Array +} + +// IsString tells whether value is a string +// +// IsString 判断当前值是不是一个字符串类型 +func (v *V) IsString() bool { + return v.valueType == String +} + +// IsNumber tells whether value is a number +// +// IsNumber 判断当前值是不是一个数字类型 +func (v *V) IsNumber() bool { + return v.valueType == Number +} + +// IsFloat tells whether value is a float point number. If there is no decimal point +// in original text, it returns false while IsNumber returns true. +// +// IsFloat 判断当前值是不是一个浮点数类型。如果给定的数不包含小数点,那么即便是数字类型,该函数也会返回 false. +func (v *V) IsFloat() bool { + if v.valueType != Number { + return false + } + return v.num.floated +} + +// IsInteger tells whether value is a fix point integer +// +// IsNumber 判断当前值是不是一个定点数整型 +func (v *V) IsInteger() bool { + if v.valueType != Number { + return false + } + return !(v.num.floated) +} + +// IsNegative tells whether value is a negative number +// +// IsNegative 判断当前值是不是一个负数 +func (v *V) IsNegative() bool { + if v.valueType != Number { + return false + } + return v.num.negative +} + +// IsPositive tells whether value is a positive number +// +// IsPositive 判断当前值是不是一个正数 +func (v *V) IsPositive() bool { + if v.valueType != Number { + return false + } + return !(v.num.negative) +} + +// GreaterThanInt64Max return true when ALL conditions below are met: +// 1. It is a number value. +// 2. It is a positive integer. +// 3. Its value is greater than 0x7fffffffffffffff. +// +// GreaterThanInt64Max 判断当前值是否超出 int64 可表示的范围。当以下条件均成立时,返回 true, +// 否则返回 false: +// 1. 是一个数字类型值. +// 2. 是一个正整型数字. +// 3. 该正整数的值大于 0x7fffffffffffffff. +func (v *V) GreaterThanInt64Max() bool { + if v.valueType != Number { + return false + } + if v.num.negative { + return false + } + return v.num.u64 > 0x7fffffffffffffff +} + +// IsBoolean tells whether value is a boolean +// +// IsBoolean 判断当前值是不是一个布尔类型 +func (v *V) IsBoolean() bool { + return v.valueType == Boolean +} + +// IsNull tells whether value is a null +// +// IsBoolean 判断当前值是不是一个空类型 +func (v *V) IsNull() bool { + return v.valueType == Null +} + +// Bool returns represented bool value. If value is not boolean, returns false. +// +// Bool 返回布尔类型值。如果当前值不是布尔类型,则判断是否为 string,不是 string 返回 false; +// 是 string 的话则返回字面值是否等于 true +func (v *V) Bool() bool { + if v.valueType == Boolean { + return v.valueBool + } + b, _ := getBoolAndErrorFromValue(v) + return b.valueBool +} + +// Int returns represented int value. If value is not a number, returns zero. +// +// Int 返回 int 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Int() int { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Int() + } + return int(v.num.i64) +} + +// Uint returns represented uint value. If value is not a number, returns zero. +// +// Uint 返回 uint 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Uint() uint { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Uint() + } + return uint(v.num.u64) +} + +// Int64 returns represented int64 value. If value is not a number, returns zero. +// +// Int64 返回 int64 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Int64() int64 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Int64() + } + return int64(v.num.i64) +} + +// Uint64 returns represented uint64 value. If value is not a number, returns zero. +// +// Uint64 返回 uint64 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Uint64() uint64 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Uint64() + } + return uint64(v.num.u64) +} + +// Int32 returns represented int32 value. If value is not a number, returns zero. +// +// Int32 返回 int32 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Int32() int32 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Int32() + } + return int32(v.num.i64) +} + +// Uint32 returns represented uint32 value. If value is not a number, returns zero. +// +// Uint32 返回 uint32 类型值。如果当前值不是数字类型,则返回 0。 +func (v *V) Uint32() uint32 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Uint32() + } + return uint32(v.num.u64) +} + +// Float64 returns represented float64 value. If value is not a number, returns zero. +// +// Float64 返回 float64 类型值。如果当前值不是数字类型,则返回 0.0。 +func (v *V) Float64() float64 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Float64() + } + return v.num.f64 +} + +// Float32 returns represented float32 value. If value is not a number, returns zero. +// +// Float32 返回 float32 类型值。如果当前值不是数字类型,则返回 0.0。 +func (v *V) Float32() float32 { + if v.valueType != Number { + return getNumberFromNotNumberValue(v).Float32() + } + return float32(v.num.f64) +} + +// Bytes returns represented binary data which is encoded as Base64 string. []byte{} +// would be returned if value is +// not a string type or base64 decode failed. +// +// Bytes 返回以 Base64 编码在 string 类型中的二进制数据。如果当前值不是字符串类型,或者是 base64 +// 编码失败,则返回 []byte{}。 +func (v *V) Bytes() []byte { + if v.valueType != String { + return []byte{} + } + b, err := internal.b64.DecodeString(v.valueStr) + if err != nil { + return []byte{} + } + return b +} + +func (v *V) deepCopy() *V { + if v == nil { + return &V{} + } + + switch v.valueType { + default: + // fallthrough + // case NotExist, Unknown: + return &V{} + case String: + return NewString(v.String()) + case Number: + res := new(globalPool{}, Number) + res.num = v.num + res.srcByte = v.srcByte + return res + case Object: + res := new(globalPool{}, Object) + res.children = v.children.deepCopy() + return res + case Array: + res := new(globalPool{}, Array) + res.children = v.children.deepCopy() + return res + case Boolean: + return NewBool(v.Bool()) + case Null: + return NewNull() + } +} + +type deepCopier interface { + deepCopy() *V +} + +// String returns represented string value or the description for the jsonvalue.V +// instance if it is not a string. +// +// String 返回 string 类型值。如果当前值不是字符串类型,则返回当前 *V 类型的描述说明。 +func (v *V) String() string { + if v == nil { + return "nil" + } + switch v.valueType { + default: + return "" + case Null: + return "null" + case Number: + if len(v.srcByte) > 0 { + return unsafe.BtoS(v.srcByte) + } + return strconv.FormatFloat(v.num.f64, 'g', -1, 64) + case String: + return v.valueStr + case Boolean: + return formatBool(v.valueBool) + case Object: + return packObjChildren(v) + case Array: + return packArrChildren(v) + } +} + +func packObjChildren(v *V) string { + buf := bytes.Buffer{} + bufObjChildren(v, &buf) + return buf.String() +} + +func bufObjChildren(v *V, buf *bytes.Buffer) { + buf.WriteByte('{') + i := 0 + for k, v := range v.children.object { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(k) + buf.WriteString(": ") + buf.WriteString(v.v.String()) + i++ + } + buf.WriteByte('}') +} + +func packArrChildren(v *V) string { + buf := bytes.Buffer{} + bufArrChildren(v, &buf) + return buf.String() +} + +func bufArrChildren(v *V, buf *bytes.Buffer) { + buf.WriteByte('[') + v.RangeArray(func(i int, v *V) bool { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(v.String()) + return true + }) + buf.WriteByte(']') +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue_internal.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue_internal.go new file mode 100644 index 0000000..f797560 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/jsonvalue_internal.go @@ -0,0 +1,112 @@ +package jsonvalue + +import ( + "encoding" + "encoding/base64" + "encoding/json" + "reflect" + "sync/atomic" +) + +var internal = struct { + b64 *base64.Encoding + + defaultMarshalOption *Opt + + predict struct { + bytesPerValue uint64 + calcStorage uint64 // upper 32 bits - size; lower 32 bits - value count + } + + types struct { + JSONMarshaler reflect.Type + TextMarshaler reflect.Type + } +}{} + +func init() { + internal.b64 = base64.StdEncoding + internal.defaultMarshalOption = emptyOptions() + internalAddPredictSizePerValue(16, 1) + + internal.types.JSONMarshaler = reflect.TypeOf((*json.Marshaler)(nil)).Elem() + internal.types.TextMarshaler = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() +} + +func internalLoadPredictSizePerValue() int { + if n := atomic.LoadUint64(&internal.predict.bytesPerValue); n > 0 { + return int(n) + } + + v := atomic.LoadUint64(&internal.predict.calcStorage) + total := v >> 32 + num := v & 0xFFFFFFFF + return int(total / num) +} + +func internalAddPredictSizePerValue(total, num int) { + v := atomic.LoadUint64(&internal.predict.calcStorage) + preTotal := v >> 32 + preNum := v & 0xFFFFFFFF + + nextTotal := uint64(total) + preTotal + nextNum := uint64(num) + preNum + + if nextTotal < 0x7FFFFFFF { + v := (nextTotal << 32) + nextNum + atomic.StoreUint64(&internal.predict.calcStorage, v) + return + } + + per := nextTotal / nextNum + atomic.StoreUint64(&internal.predict.bytesPerValue, per) + atomic.StoreUint64(&internal.predict.calcStorage, (per<<32)+1) +} + +type pool interface { + get() *V +} + +type globalPool struct{} + +func (globalPool) get() *V { + return &V{} +} + +type poolImpl struct { + pool []V + + count int + actual int // actual counted values + + rawSize int +} + +func newPool(rawSize int) *poolImpl { + per := internalLoadPredictSizePerValue() + cnt := rawSize / per + + p := &poolImpl{ + pool: make([]V, cnt), + count: cnt, + actual: 0, + rawSize: rawSize, + } + + return p +} + +func (p *poolImpl) get() *V { + if p.actual < p.count { + v := &p.pool[p.actual] + p.actual++ + return v + } + + p.actual++ + return globalPool{}.get() +} + +func (p *poolImpl) release() { + internalAddPredictSizePerValue(p.rawSize, p.actual) +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/marshal.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/marshal.go new file mode 100644 index 0000000..405d9d4 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/marshal.go @@ -0,0 +1,325 @@ +package jsonvalue + +import ( + "encoding/json" + "fmt" + "math" + "strings" + + "github.com/Andrew-M-C/go.jsonvalue/internal/buffer" + "github.com/Andrew-M-C/go.jsonvalue/internal/unsafe" +) + +// MustMarshal is the same as Marshal. If error occurs, an empty byte slice will be returned. +// +// MustMarshal 与 Marshal 相同,但是当错误发生时,什么都不做,直接返回空数据 +func (v *V) MustMarshal(opts ...Option) []byte { + ret, err := v.Marshal(opts...) + if err != nil { + return []byte{} + } + return ret +} + +// MustMarshalString is the same as MarshalString, If error occurs, an empty byte slice will be returned. +// +// MustMarshalString 与 MarshalString 相同,但是当错误发生时,什么都不做,直接返回空数据 +func (v *V) MustMarshalString(opt ...Option) string { + ret, err := v.MarshalString(opt...) + if err != nil { + return "" + } + return ret +} + +// Marshal returns marshaled bytes. +// +// Marshal 返回序列化后的 JSON 字节序列。 +func (v *V) Marshal(opts ...Option) (b []byte, err error) { + if NotExist == v.valueType { + return nil, ErrValueUninitialized + } + + buf := buffer.NewBuffer() + opt := combineOptions(opts) + + err = marshalToBuffer(v, nil, buf, opt) + if err != nil { + return nil, err + } + + b = buf.Bytes() + return b, nil +} + +// MarshalString is same with Marshal, but returns string. It is much more efficient than string(b). +// +// MarshalString 与 Marshal 相同, 不同的是返回 string 类型。它比 string(b) 操作更高效。 +func (v *V) MarshalString(opts ...Option) (s string, err error) { + b, err := v.Marshal(opts...) + if err != nil { + return "", err + } + return unsafe.BtoS(b), nil +} + +func marshalToBuffer(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) (err error) { + switch v.valueType { + default: + // do nothing + case String: + marshalString(v, buf, opt) + case Boolean: + marshalBoolean(v, buf) + case Number: + err = marshalNumber(v, buf, opt) + case Null: + marshalNull(buf) + case Object: + marshalObject(v, parentInfo, buf, opt) + case Array: + marshalArray(v, parentInfo, buf, opt) + } + return err +} + +func marshalString(v *V, buf buffer.Buffer, opt *Opt) { + _ = buf.WriteByte('"') + escapeStringToBuff(v.valueStr, buf, opt) + _ = buf.WriteByte('"') +} + +func marshalBoolean(v *V, buf buffer.Buffer) { + _, _ = buf.WriteString(formatBool(v.valueBool)) +} + +func marshalNumber(v *V, buf buffer.Buffer, opt *Opt) error { + if b := v.srcByte; len(b) > 0 { + _, _ = buf.Write(b) + return nil + } + // else, +Inf or -Inf or NaN + if math.IsInf(v.num.f64, 1) { // +Inf + return marshalInfP(buf, opt) + } + if math.IsInf(v.num.f64, -1) { // -Inf + return marshalInfN(buf, opt) + } + + return marshalNaN(buf, opt) +} + +func marshalNaN(buf buffer.Buffer, opt *Opt) error { + switch opt.FloatNaNHandleType { + default: + fallthrough + case FloatNaNTreatAsError: + return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.NaN()) + + case FloatNaNConvertToFloat: + if !isValidFloat(opt.FloatNaNToFloat) { + return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, opt.FloatNaNToFloat) + } + b, _ := json.Marshal(opt.FloatNaNToFloat) + _, _ = buf.Write(b) + + case FloatNaNNull: + _, _ = buf.WriteString("null") + + case FloatNaNConvertToString: + if s := opt.FloatNaNToString; s == "" { + _, _ = buf.WriteString(`"NaN"`) + } else { + _ = buf.WriteByte('"') + escapeStringToBuff(s, buf, opt) + _ = buf.WriteByte('"') + } + } + + return nil +} + +func marshalInfP(buf buffer.Buffer, opt *Opt) error { + switch opt.FloatInfHandleType { + default: + fallthrough + case FloatInfTreatAsError: + return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.Inf(1)) + + case FloatInfConvertToFloat: + if !isValidFloat(opt.FloatInfToFloat) { + return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, opt.FloatInfToFloat) + } + b, _ := json.Marshal(opt.FloatInfToFloat) + _, _ = buf.Write(b) + + case FloatInfNull: + _, _ = buf.WriteString("null") + + case FloatInfConvertToString: + if s := opt.FloatInfPositiveToString; s == "" { + _, _ = buf.WriteString(`"+Inf"`) + } else { + _ = buf.WriteByte('"') + escapeStringToBuff(s, buf, opt) + _ = buf.WriteByte('"') + } + } + + return nil +} + +func marshalInfN(buf buffer.Buffer, opt *Opt) error { + switch opt.FloatInfHandleType { + default: + fallthrough + case FloatInfTreatAsError: + return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.Inf(-1)) + + case FloatInfConvertToFloat: + if !isValidFloat(opt.FloatInfToFloat) { + return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, -opt.FloatInfToFloat) + } + b, _ := json.Marshal(-opt.FloatInfToFloat) + _, _ = buf.Write(b) + + case FloatInfNull: + _, _ = buf.WriteString("null") + + case FloatInfConvertToString: + _ = buf.WriteByte('"') + if s := opt.FloatInfNegativeToString; s != "" { + escapeStringToBuff(s, buf, opt) + } else if opt.FloatInfPositiveToString != "" { + s = "-" + strings.TrimLeft(opt.FloatInfPositiveToString, "+") + escapeStringToBuff(s, buf, opt) + } else { + _, _ = buf.WriteString(`-Inf`) + } + _ = buf.WriteByte('"') + } + + return nil +} + +func isValidFloat(f float64) bool { + if math.IsNaN(f) { + return false + } + if math.IsInf(f, 0) { + return false + } + return true +} + +func marshalNull(buf buffer.Buffer) { + _, _ = buf.WriteString("null") +} + +func marshalObject(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) { + if len(v.children.object) == 0 { + _, _ = buf.WriteString("{}") + return + } + + opt.indent.cnt++ + _ = buf.WriteByte('{') + + if opt.MarshalLessFunc != nil { + sov := newSortObjectV(v, parentInfo, opt) + sov.marshalObjectWithLessFunc(buf, opt) + + } else if len(opt.MarshalKeySequence) > 0 { + sssv := newSortStringSliceV(v, opt) + sssv.marshalObjectWithStringSlice(buf, opt) + + } else if opt.marshalBySetSequence { + sssv := newSortStringSliceVBySetSeq(v) + sssv.marshalObjectWithStringSlice(buf, opt) + + } else { + writeObjectKVInRandomizedSequence(v, buf, opt) + } + + opt.indent.cnt-- + if opt.indent.enabled { + _ = buf.WriteByte('\n') + writeIndent(buf, opt) + } + _ = buf.WriteByte('}') +} + +func writeObjectKVInRandomizedSequence(v *V, buf buffer.Buffer, opt *Opt) { + firstWritten := false + for k, child := range v.children.object { + firstWritten = writeObjectChildren(nil, buf, !firstWritten, k, child.v, opt) + } +} + +func writeObjectChildren( + parentInfo *ParentInfo, buf buffer.Buffer, isFirstOne bool, key string, child *V, opt *Opt, +) (written bool) { + if child.IsNull() && opt.OmitNull { + return false + } + if !isFirstOne { + _ = buf.WriteByte(',') + } + + if opt.indent.enabled { + _ = buf.WriteByte('\n') + writeIndent(buf, opt) + } + + _ = buf.WriteByte('"') + escapeStringToBuff(key, buf, opt) + + if opt.indent.enabled { + _, _ = buf.WriteString("\": ") + } else { + _, _ = buf.WriteString("\":") + } + + _ = marshalToBuffer(child, parentInfo, buf, opt) + return true +} + +func writeIndent(buf buffer.Buffer, opt *Opt) { + _, _ = buf.WriteString(opt.indent.prefix) + for i := 0; i < opt.indent.cnt; i++ { + _, _ = buf.WriteString(opt.indent.indent) + } +} + +func marshalArray(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) { + if len(v.children.arr) == 0 { + _, _ = buf.WriteString("[]") + return + } + + opt.indent.cnt++ + _ = buf.WriteByte('[') + + v.RangeArray(func(i int, child *V) bool { + if i > 0 { + _ = buf.WriteByte(',') + } + if opt.indent.enabled { + _ = buf.WriteByte('\n') + writeIndent(buf, opt) + } + if opt.MarshalLessFunc == nil { + _ = marshalToBuffer(child, nil, buf, opt) + } else { + _ = marshalToBuffer(child, newParentInfo(v, parentInfo, intKey(i)), buf, opt) + } + return true + }) + + opt.indent.cnt-- + if opt.indent.enabled { + _ = buf.WriteByte('\n') + writeIndent(buf, opt) + } + _ = buf.WriteByte(']') +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/marshaler_unmarshaler.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/marshaler_unmarshaler.go new file mode 100644 index 0000000..400ccfe --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/marshaler_unmarshaler.go @@ -0,0 +1,44 @@ +package jsonvalue + +import ( + "encoding" + "encoding/json" +) + +var ( + _ json.Marshaler = (*V)(nil) + _ json.Unmarshaler = (*V)(nil) + + _ encoding.BinaryMarshaler = (*V)(nil) + _ encoding.BinaryUnmarshaler = (*V)(nil) +) + +// MarshalJSON implements json.Marshaler +func (v *V) MarshalJSON() ([]byte, error) { + return v.Marshal() +} + +// UnmarshalJSON implements json.Unmarshaler +func (v *V) UnmarshalJSON(b []byte) error { + res, err := Unmarshal(b) + if err != nil { + return err + } + *v = *res + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler +func (v *V) MarshalBinary() ([]byte, error) { + return v.Marshal() +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (v *V) UnmarshalBinary(b []byte) error { + res, err := Unmarshal(b) + if err != nil { + return err + } + *v = *res + return nil +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/new.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/new.go new file mode 100644 index 0000000..e2cb548 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/new.go @@ -0,0 +1,273 @@ +package jsonvalue + +import ( + "encoding/base64" + "fmt" + "math" + "reflect" + "strconv" +) + +// New generate a new jsonvalue type via given type. If given type is not supported, +// the returned type would equal to NotExist. If you are not sure whether given value +// type is OK in runtime, use Import() instead. +// +// New 函数按照给定参数类型创建一个 jsonvalue 类型。如果给定参数不是 JSON 支持的类型, 那么返回的 +// *V 对象的类型为 NotExist。如果在代码中无法确定入参是否是 JSON 支持的类型, 请改用函数 +// Import()。 +func New(value any) *V { + v, _ := Import(value) + return v +} + +// NewString returns an initialized string jsonvalue object +// +// NewString 用给定的 string 返回一个初始化好的字符串类型的 jsonvalue 值 +func NewString(s string) *V { + v := new(globalPool{}, String) + v.valueStr = s + return v +} + +// NewBytes returns an initialized string with Base64 string by given bytes +// +// NewBytes 用给定的字节串,返回一个初始化好的字符串类型的 jsonvalue 值,内容是字节串 Base64 之后的字符串。 +func NewBytes(b []byte) *V { + s := base64.StdEncoding.EncodeToString(b) + return NewString(s) +} + +// NewInt64 returns an initialized num jsonvalue object by int64 type +// +// NewInt64 用给定的 int64 返回一个初始化好的数字类型的 jsonvalue 值 +func NewInt64(i int64) *V { + v := new(globalPool{}, Number) + // v.num = &num{} + v.num.floated = false + v.num.negative = i < 0 + v.num.f64 = float64(i) + v.num.i64 = i + v.num.u64 = uint64(i) + s := strconv.FormatInt(v.num.i64, 10) + v.srcByte = []byte(s) + return v +} + +// NewUint64 returns an initialized num jsonvalue object by uint64 type +// +// NewUint64 用给定的 uint64 返回一个初始化好的数字类型的 jsonvalue 值 +func NewUint64(u uint64) *V { + v := new(globalPool{}, Number) + // v.num = &num{} + v.num.floated = false + v.num.negative = false + v.num.f64 = float64(u) + v.num.i64 = int64(u) + v.num.u64 = u + s := strconv.FormatUint(v.num.u64, 10) + v.srcByte = []byte(s) + return v +} + +// NewInt returns an initialized num jsonvalue object by int type +// +// NewInt 用给定的 int 返回一个初始化好的数字类型的 jsonvalue 值 +func NewInt(i int) *V { + return NewInt64(int64(i)) +} + +// NewUint returns an initialized num jsonvalue object by uint type +// +// NewUint 用给定的 uint 返回一个初始化好的数字类型的 jsonvalue 值 +func NewUint(u uint) *V { + return NewUint64(uint64(u)) +} + +// NewInt32 returns an initialized num jsonvalue object by int32 type +// +// NewInt32 用给定的 int32 返回一个初始化好的数字类型的 jsonvalue 值 +func NewInt32(i int32) *V { + return NewInt64(int64(i)) +} + +// NewUint32 returns an initialized num jsonvalue object by uint32 type +// +// NewUint32 用给定的 uint32 返回一个初始化好的数字类型的 jsonvalue 值 +func NewUint32(u uint32) *V { + return NewUint64(uint64(u)) +} + +// NewBool returns an initialized boolean jsonvalue object +// +// NewBool 用给定的 bool 返回一个初始化好的布尔类型的 jsonvalue 值 +func NewBool(b bool) *V { + v := new(globalPool{}, Boolean) + v.valueBool = b + return v +} + +// NewNull returns an initialized null jsonvalue object +// +// NewNull 返回一个初始化好的 null 类型的 jsonvalue 值 +func NewNull() *V { + v := new(globalPool{}, Null) + return v +} + +// NewObject returns an object-typed jsonvalue object. If keyValues is specified, it will also create some key-values in +// the object. Now we supports basic types only. Such as int/uint, int/int8/int16/int32/int64, +// uint/uint8/uint16/uint32/uint64 series, string, bool, nil. +// +// NewObject 返回一个初始化好的 object 类型的 jsonvalue 值。可以使用可选的 map[string]any 类型参数初始化该 object 的下一级键值对, +// 不过目前只支持基础类型,也就是: int/uint, int/int8/int16/int32/int64, uint/uint8/uint16/uint32/uint64, string, bool, nil。 +func NewObject(keyValues ...M) *V { + v := newObject(globalPool{}) + + if len(keyValues) > 0 { + kv := keyValues[0] + if kv != nil { + parseNewObjectKV(v, kv) + } + } + + return v +} + +// M is the alias of map[string]any +type M map[string]any + +func parseNewObjectKV(v *V, kv M) { + for k, val := range kv { + rv := reflect.ValueOf(val) + switch rv.Kind() { + case reflect.Invalid: + v.MustSetNull().At(k) + case reflect.Bool: + v.MustSetBool(rv.Bool()).At(k) + case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: + v.MustSetInt64(rv.Int()).At(k) + case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: + v.MustSetUint64(rv.Uint()).At(k) + case reflect.Float32, reflect.Float64: + v.MustSetFloat64(rv.Float()).At(k) + case reflect.String: + v.MustSetString(rv.String()).At(k) + // case reflect.Map: + // if rv.Type().Key().Kind() == reflect.String && rv.Type().Elem().Kind() == reflect.Interface { + // if m, ok := rv.Interface().(M); ok { + // sub := NewObject(m) + // if sub != nil { + // v.Set(sub).At(k) + // } + // } + // } + default: + // continue + } + } +} + +// NewArray returns an empty array-typed jsonvalue object +// +// NewArray 返回一个初始化好的 array 类型的 jsonvalue 值。 +func NewArray() *V { + return newArray(globalPool{}) +} + +// NewFloat64 returns an initialized num jsonvalue value by float64 type. The format and precision control is the same +// with encoding/json: https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L575 +// +// NewFloat64 根据指定的 flout64 类型返回一个初始化好的数字类型的 jsonvalue 值。数字转出来的字符串格式参照 encoding/json 中的逻辑。 +func NewFloat64(f float64) *V { + abs := math.Abs(f) + format := byte('f') + if abs < 1e-6 || abs >= 1e21 { + format = byte('e') + } + + return newFloat64f(globalPool{}, f, format, -1, 64) +} + +// NewFloat64f returns an initialized num jsonvalue value by float64 type. The format and prec parameter are used in +// strconv.FormatFloat(). Only 'f', 'E', 'e', 'G', 'g' formats are supported, other formats will be mapped to 'g'. +// +// NewFloat64f 根据指定的 float64 类型返回一个初始化好的数字类型的 jsonvalue 值。其中参数 format 和 prec 分别用于 +// strconv.FormatFloat() 函数. 只有 'f'、'E'、'e'、'G'、'g' 格式是支持的,其他配置均统一映射为 'g'。 +func NewFloat64f(f float64, format byte, prec int) *V { + if err := validateFloat64Format(format); err != nil { + format = 'g' + } + return newFloat64f(globalPool{}, f, format, prec, 64) +} + +// NewFloat32 returns an initialized num jsonvalue value by float32 type. The format and precision control is the same +// with encoding/json: https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L575 +// +// NewFloat32 根据指定的 float32 类型返回一个初始化好的数字类型的 jsonvalue 值。数字转出来的字符串格式参照 encoding/json 中的逻辑。 +func NewFloat32(f float32) *V { + f64 := float64(f) + abs := math.Abs(f64) + format := byte('f') + if abs < 1e-6 || abs >= 1e21 { + format = byte('e') + } + + return newFloat64f(globalPool{}, f64, format, -1, 32) +} + +// NewFloat32f returns an initialized num jsonvalue value by float64 type. The format and prec parameter are used in +// strconv.FormatFloat(). Only 'f', 'E', 'e', 'G', 'g' formats are supported, other formats will be mapped to 'g'. +// +// NewFloat32f 根据指定的 float64 类型返回一个初始化好的数字类型的 jsonvalue 值。其中参数 format 和 prec 分别用于 +// strconv.FormatFloat() 函数. 只有 'f'、'E'、'e'、'G'、'g' 格式是支持的,其他配置均统一映射为 'g'。 +func NewFloat32f(f float32, format byte, prec int) *V { + if err := validateFloat64Format(format); err != nil { + format = 'g' + } + return newFloat64f(globalPool{}, float64(f), format, prec, 64) +} + +// -------- internal functions -------- + +func new(p pool, t ValueType) *V { + v := pool.get(p) + v.valueType = t + return v +} + +func newObject(p pool) *V { + v := new(p, Object) + v.children.object = make(map[string]childWithProperty) + v.children.lowerCaseKeys = nil + return v +} + +func newArray(p pool) *V { + v := new(p, Array) + return v +} + +func newFloat64f(p pool, f float64, format byte, prec, bitSize int) *V { + v := new(p, Number) + // v.num = &num{} + v.num.negative = f < 0 + v.num.f64 = f + v.num.i64 = int64(f) + v.num.u64 = uint64(f) + + if isValidFloat(f) { + s := strconv.FormatFloat(f, format, prec, bitSize) + v.srcByte = []byte(s) + } + + return v +} + +func validateFloat64Format(f byte) error { + switch f { + case 'f', 'E', 'e', 'G', 'g': + return nil + default: + return fmt.Errorf("unsupported float value in option: %c", f) + } +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/option.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/option.go new file mode 100644 index 0000000..163bff1 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/option.go @@ -0,0 +1,634 @@ +package jsonvalue + +import "github.com/Andrew-M-C/go.jsonvalue/internal/buffer" + +const ( + initialArrayCapacity = 32 + asciiSize = 128 +) + +// Deprecated: Opt is the option of jsonvalue in marshaling. This type is deprecated, +// please use OptXxxx() functions instead. +// +// Opt 表示序列化当前 jsonvalue 类型时的参数。这个类型后续可能不再迭代新字段了,请改用 OptXxxx() 函数进行配置。 +type Opt struct { + // OmitNull tells how to handle null json value. The default value is false. + // If OmitNull is true, null value will be omitted when marshaling. + // + // OmitNull 表示是否忽略 JSON 中的 null 类型值。默认为 false. + OmitNull bool + + // ignoreJsonOmitempty ignore json tag "omitempty", which means that every data + // would be parsed into *jsonvalue.V + ignoreJsonOmitempty bool + + // MarshalLessFunc is used to handle sequences of marshaling. Since object is + // implemented by hash map, the sequence of keys is unexpectable. For situations + // those need settled JSON key-value sequence, please use MarshalLessFunc. + // + // Note: Elements in an array value would NOT trigger this function as they are + // already sorted. + // + // We provides a example DefaultStringSequence. It is quite useful when calculating + // idempotence of a JSON text, as key-value sequences should be fixed. + // + // MarshalLessFunc 用于处理序列化 JSON 对象类型时,键值对的顺序。由于 object 类型是采用 go + // 原生的 map 类型,采用哈希算法实现, + // 因此其键值对的顺序是不可控的。而为了提高效率,jsonvalue 的内部实现中并不会刻意保存键值对的顺序。 + // 如果有必要在序列化时固定键值对顺序的话, + // 可以使用这个函数。 + // + // 注意:array 类型中键值对的顺序不受这个函数的影响 + // + // 此外,我们提供了一个例子: DefaultStringSequence。当需要计算 JSON 文本的幂等值时, + // 由于需要不变的键值对顺序,因此这个函数是非常有用的。 + MarshalLessFunc MarshalLessFunc + + // MarshalKeySequence is used to handle sequance of marshaling. This is much simpler + // than MarshalLessFunc, just pass a string slice identifying key sequence. For keys + // those are not in this slice, they would be appended in the end according to result + // of Go string comparing. Therefore this parameter is useful for ensure idempotence. + // + // MarshalKeySequence 也用于处理序列化时的键值对顺序。与 MarshalLessFunc 不同,这个只需要用字符串 + // 切片的形式指定键的顺序即可,实现上更为简易和直观。对于那些不在指定切片中的键,那么将会统一放在结尾, + // 并且按照 go 字符串对比的结果排序。也可以保证幂等。 + MarshalKeySequence []string + keySequence map[string]int // generated from MarshalKeySequence + + // marshalBySetSequence enables object key sequence by when it is set. + // + // 按照 key 被设置的顺序处理序列化时的 marshal 顺序 + marshalBySetSequence bool + + // FloatNaNHandleType tells what to deal with float NaN. + // + // FloatNaNHandleType 表示当处理 float 的时候,如果遇到了 NaN 的话,要如何处理。 + FloatNaNHandleType FloatNaNHandleType + // FloatNaNToString works with FloatNaNHandleType = FloatNaNConvertToString. It tells + // what string to replace to with NaN. If not specified, NaN will be set as string "NaN". + // + // FloatNaNToString 搭配 FloatNaNHandleType = FloatNaNConvertToString 使用,表示将 NaN + // 映射为哪个字符串。这个值如果不指定,则默认会被设置为字符串 "NaN" + FloatNaNToString string + // FloatNaNToFloat works with FloatNaNHandleType = FloatNaNConvertToFloat. It tells + // what float number will be mapped to as for NaN. NaN, +Inf or -Inf are not allowed + // for this option. + // + // FloatNaNToFloat 搭配 FloatNaNHandleType = FloatNaNConvertToFloat 使用,表示将 NaN + // 映射为哪个 float64 值。不允许指定为 NaN, +Inf 或 -Inf。如果不指定,则映射为 0。 + FloatNaNToFloat float64 + + // FloatInfHandleType tells what to deal with float +Inf and -Inf. + // + // FloatInfHandleType 表示当处理 float 的时候,如果遇到了 +Inf 和 -Inf 的话,要如何处理。 + FloatInfHandleType FloatInfHandleType + // FloatInfPositiveToString works with FloatInfHandleType = FloatInfConvertToFloat. + // It tells what float number will be mapped to as for +Inf. If not specified, +Inf + // will be set as string "+Inf". + // + // FloatInfPositiveToString 搭配 FloatInfHandleType = FloatInfConvertToFloat 使用, + // 表示将 NaN 映射为哪个字符串。 + // 这个值如果不指定,则默认会被设置为字符串 "+Inf" + FloatInfPositiveToString string + // FloatInfNegativeToString works with FloatInfHandleType = FloatInfConvertToFloat. + // It tells what float number will be mapped to as for -Inf. If not specified, -Inf + // will be set as string "-" + strings.TrimLeft(FloatInfPositiveToString, "+"). + // + // FloatInfNegativeToString 搭配 FloatInfHandleType = FloatInfConvertToFloat 使用, + // 表示将 NaN 映射为哪个字符串。这个值如果不指定,则默认会被设置为字符串 "-" + strings.TrimLeft(FloatInfPositiveToString, "+") + FloatInfNegativeToString string + // FloatInfToFloat works with FloatInfHandleType = FloatInfConvertToFloat. It tells + // what float numbers will be mapped to as for +Inf. And -Inf will be specified as + // the negative value of this option. +Inf or -Inf are not allowed for this option. + // + // FloatInfToFloat 搭配 FloatInfHandleType = FloatInfConvertToFloat 使用,表示将 +Inf + // 映射为哪个 float64 值。而 -Inf 则会被映射为这个值的负数。不允许指定为 NaN, +Inf 或 -Inf。 + // 如果不指定,则映射为 0 + FloatInfToFloat float64 + + // unicodeEscapingFunc defines how to escaping a unicode greater than 0x7F to buffer. + unicodeEscapingFunc func(r rune, buf buffer.Buffer) + + // asciiCharEscapingFunc defines how to marshal bytes lower than 0x80. + asciiCharEscapingFunc [asciiSize]func(b byte, buf buffer.Buffer) + + // escProperties + escProperties escapingProperties + + // indent + indent struct { + enabled bool + prefix string + indent string + cnt int + } +} + +type FloatNaNHandleType uint8 + +const ( + // FloatNaNTreatAsError indicates that error will be returned when a float number + // is NaN when marshaling. + // + // FloatNaNTreatAsError 表示当 marshal 遇到 NaN 时,返回错误。这是默认选项。 + FloatNaNTreatAsError FloatNaNHandleType = 0 + // FloatNaNConvertToFloat indicates that NaN will be replaced as another float + // number when marshaling. This option works with option FloatNaNToFloat. + // + // FloatNaNConvertToFloat 表示当 marshal 遇到 NaN 时,将值置为另一个数。搭配 FloatNaNToFloat + // 选项使用。 + FloatNaNConvertToFloat FloatNaNHandleType = 1 + // FloatNaNNull indicates that NaN key-value pair will be set as null when marshaling. + // + // FloatNaNNull 表示当 marshal 遇到 NaN 时,则将值设置为 null + FloatNaNNull FloatNaNHandleType = 2 + // FloatNaNConvertToString indicates that NaN will be replaced as a string when + // marshaling. This option works with option FloatNaNToString. + // + // FloatNaNConvertToString 表示当 marshal 遇到 NaN 时,将值设置为一个字符串。搭配 + // FloatNaNToString 选项使用。 + FloatNaNConvertToString FloatNaNHandleType = 3 +) + +type FloatInfHandleType uint8 + +const ( + // FloatInfTreatAsError indicates that error will be returned when a float number + // is Inf or -Inf when marshaling. + // + // FloatInfTreatAsError 表示当 marshal 遇到 Inf 或 -Inf 时,返回错误。这是默认选项。 + FloatInfTreatAsError FloatInfHandleType = 0 + // FloatInfConvertToFloat indicates that Inf and -Inf will be replaced as another + // float number when marshaling. This option works with option FloatInfToFloat. + // + // FloatInfConvertToFloat 表示当 marshal 遇到 Inf 或 -Inf 时,将值置为另一个数。搭配 + // FloatInfToFloat 选项使用。 + FloatInfConvertToFloat FloatInfHandleType = 1 + // FloatInfNull indicates that Inf or -Inf key-value pair will be set as null + // when marshaling. + // + // FloatInfNull 表示当 marshal 遇到 Inf 和 -Inf 时,则将值设置为 null + FloatInfNull FloatInfHandleType = 2 + // FloatInfConvertToString indicates that Inf anf -Inf will be replaced as a + // string when marshaling. This option works with option FloatInfPositiveToString + // and FloatInfNegativeToString. + // + // FloatInfConvertToString 表示当 marshal 遇到 Inf 和 -Inf 时,将值设置为一个字符串。 + // 搭配 FloatInfPositiveToString FloatInfNegativeToString 选项使用。 + FloatInfConvertToString FloatInfHandleType = 3 +) + +// Option is used for additional options when marshaling. Can be either a Opt{} +// (not pointer to it) or other options generated by jsonvalue.OptXxxx() functions. +// +// Option 表示用于序列化的额外选项。可以是一个 Opt{} 结构体值(而不是它的指针),或者是使用 +// jsonvalue.OptXxxx() 函数生成的选项。 +type Option interface { + mergeTo(*Opt) +} + +func (o Opt) mergeTo(tgt *Opt) { + *tgt = o +} + +// SetDefaultMarshalOptions set default option for marshaling. It is quite +// useful to invoke this function once in certern init funciton. Or you can +// invoke it after main entry. It is goroutine-safe. +// +// Please keep in mind that it takes effect globally and affects ALL marshaling +// behaviors in the future until the process ends. Please ensure that these +// options are acceptable for ALL future marshaling. +// +// However, you can still add additional options in later marshaling. +// +// SetDefaultMarshalOptions 设置序列化时的默认参数。使用方可以在 init 函数阶段,或者是 +// main 函数启动后立刻调用该函数,以调整序列化时的默认行为。这个函数是协程安全的。 +// +// 请记住,这个函数影响的是后续所有的序列化行为,请确保这个配置对后续的其他操作是可行的。 +// +// 当然,你也可以在后续的操作中,基于原先配置的默认选项基础上,添加其他附加选项。 +func SetDefaultMarshalOptions(opts ...Option) { + opt := emptyOptions() + opt.combineOptionsFrom(opts) + internal.defaultMarshalOption = opt +} + +// ResetDefaultMarshalOptions reset default marshaling options to system default. +// +// ResetDefaultMarshalOptions 重设序列化时的默认选项为系统最原始的版本。 +func ResetDefaultMarshalOptions() { + internal.defaultMarshalOption = emptyOptions() +} + +func emptyOptions() *Opt { + return &Opt{} +} + +func getDefaultOptions() *Opt { + res := *internal.defaultMarshalOption + return &res +} + +func combineOptions(opts []Option) *Opt { + opt := getDefaultOptions() + opt.combineOptionsFrom(opts) + return opt +} + +func (opt *Opt) combineOptionsFrom(opts []Option) { + for _, o := range opts { + o.mergeTo(opt) + } + opt.parseEscapingFuncs() +} + +// ==== OmitNull ==== + +// OptOmitNull configures OmitNull field in Opt{}, identifying whether null values +// should be omitted when marshaling. +// +// OptOmitNull 配置 Opt{} 中的 OmitNull 字段,表示是否忽略 null 值。 +func OptOmitNull(b bool) Option { + return &optOmitNull{b: b} +} + +type optOmitNull struct { + b bool +} + +func (o *optOmitNull) mergeTo(opt *Opt) { + opt.OmitNull = o.b +} + +// ==== IgnoreOmitempty ==== + +// OptIgnoreOmitempty is used in Import() and New() function. This option tells +// jsonvalue to ignore json tag "omitempty", which means that every field would +// be parsed into *jsonvalue.V. +// +// OptIgnoreOmitempty 用在 Import 和 New() 函数中。这个选项将会忽略 json 标签中的 +// "omitempty" 参数。换句话说, 所有的字段都会被解析并包装到 *jsonvalue.V 值中。 +func OptIgnoreOmitempty() Option { + return optIgnoreOmitempty{} +} + +type optIgnoreOmitempty struct{} + +func (optIgnoreOmitempty) mergeTo(opt *Opt) { + opt.ignoreJsonOmitempty = true +} + +// ==== MarshalLessFunc === + +// OptKeySequenceWithLessFunc configures MarshalLessFunc field in Opt{}, which defines +// key sequence when marshaling. +// +// OptKeySequenceWithLessFunc 配置 Opt{} 中的 MarshalLessFunc 字段,配置序列化时的键顺序。 +func OptKeySequenceWithLessFunc(f MarshalLessFunc) Option { + return &optMarshalLessFunc{f: f} +} + +// OptDefaultStringSequence configures MarshalLessFunc field in Opt{} as jsonvalue.DefaultStringSequence, +// which is dictionary sequence. +// +// OptDefaultStringSequence 配置 Opt{} 中的 MarshalLessFunc 字段为 jsonvalue.DefaultStringSequence,也就是字典序。 +func OptDefaultStringSequence() Option { + return &optMarshalLessFunc{f: DefaultStringSequence} +} + +type optMarshalLessFunc struct { + f MarshalLessFunc +} + +func (o *optMarshalLessFunc) mergeTo(opt *Opt) { + if o.f != nil { + opt.MarshalLessFunc = o.f + opt.MarshalKeySequence = nil + opt.keySequence = nil + opt.marshalBySetSequence = false + } +} + +// ==== MarshalKeySequence ==== + +// OptKeySequence configures MarshalKeySequence field in Opt{}. +// +// OptKeySequence 配置 Opt{} 中的 MarshalKeySequence 字段。 +func OptKeySequence(seq []string) Option { + return &optMarshalKeySequence{seq: seq} +} + +type optMarshalKeySequence struct { + seq []string +} + +func (o *optMarshalKeySequence) mergeTo(opt *Opt) { + opt.MarshalLessFunc = nil + opt.MarshalKeySequence = o.seq + opt.keySequence = nil + opt.marshalBySetSequence = false +} + +// ==== marshalBySetSequence ==== + +// OptSetSequence tells that when marshaling an object, the key will be sorted by +// the time they are added into or refreshed in its parent object. The later a key +// +// is set or updated, the later it and its value will be marshaled. +// +// OptSetSequence 指定在序列化 object 时,按照一个 key 被设置时的顺序进行序列化。如果一个 +// key 越晚添加到 object 类型,则在序列化的时候越靠后。 +func OptSetSequence() Option { + return optSetSequence{} +} + +type optSetSequence struct{} + +func (optSetSequence) mergeTo(opt *Opt) { + opt.MarshalLessFunc = nil + opt.MarshalKeySequence = nil + opt.keySequence = nil + opt.marshalBySetSequence = true +} + +// ==== FloatNaNConvertToFloat ==== + +// OptFloatNaNToFloat tells that when marshaling float NaN, replace it as another +// valid float number. +// +// OptFloatNaNToFloat 指定当遇到 NaN 时,将值替换成一个有效的 float 值。 +func OptFloatNaNToFloat(f float64) Option { + return &optFloatNaNConvertToFloat{f: f} +} + +type optFloatNaNConvertToFloat struct { + f float64 +} + +func (o *optFloatNaNConvertToFloat) mergeTo(opt *Opt) { + opt.FloatNaNHandleType = FloatNaNConvertToFloat + opt.FloatNaNToFloat = o.f +} + +// ==== FloatNaNNull ==== + +// OptFloatNaNToNull will replace a float value to null if it is NaN. +// +// OptFloatNaNToNull 表示当遇到 NaN 时,将值替换成 null +func OptFloatNaNToNull() Option { + return optFloatNaNNull{} +} + +type optFloatNaNNull struct{} + +func (optFloatNaNNull) mergeTo(opt *Opt) { + opt.FloatNaNHandleType = FloatNaNNull +} + +// ==== FloatNaNConvertToString ==== + +// OptFloatNaNToStringNaN will replace a float value to string "NaN" if it is NaN. +// +// OptFloatNaNToStringNaN 表示遇到 NaN 时,将其替换成字符串 "NaN" +func OptFloatNaNToStringNaN() Option { + return &optFloatNaNConvertToString{s: "NaN"} +} + +// OptFloatNaNToString will replace a float value to specified string if it is NaN. +// If empty string is given, will replace as "NaN". +// +// OptFloatNaNToString 表示当遇到 NaN 时,将其替换成指定的字符串。如果指定空字符串,则替换成 "NaN" +func OptFloatNaNToString(s string) Option { + return &optFloatNaNConvertToString{s: s} +} + +type optFloatNaNConvertToString struct { + s string +} + +func (o *optFloatNaNConvertToString) mergeTo(opt *Opt) { + opt.FloatNaNHandleType = FloatNaNConvertToString + opt.FloatNaNToString = o.s +} + +// ==== FloatInfConvertToFloat ==== + +// OptFloatInfToFloat will replace a +Inf float value to specified f, while -f if +// the value is -Inf. +// +// OptFloatInfToFloat 表示当遇到 +Inf 时,将其替换成另一个 float 值;如果是 -Inf,则会替换成其取负数。 +func OptFloatInfToFloat(f float64) Option { + return &optFloatInfConvertToFloat{f: f} +} + +type optFloatInfConvertToFloat struct { + f float64 +} + +func (o *optFloatInfConvertToFloat) mergeTo(opt *Opt) { + opt.FloatInfHandleType = FloatInfConvertToFloat + opt.FloatInfToFloat = o.f +} + +// ==== FloatInfNull ==== + +// OptFloatInfToNull will replace a float value to null if it is +/-Inf. +// +// OptFloatInfToNull 表示当遇到 +/-Inf 时,将值替换成 null +func OptFloatInfToNull() Option { + return optFloatInfNull{} +} + +type optFloatInfNull struct{} + +func (optFloatInfNull) mergeTo(opt *Opt) { + opt.FloatInfHandleType = FloatInfNull +} + +// ==== FloatInfConvertToString ==== + +// OptFloatInfToStringInf will replace a +Inf value to string "+Inf", while -Inf to "-Inf" +// +// OptFloatInfToStringInf 表示遇到 +/-Inf 时,相应地将其替换成字符串 "+Inf" 和 "-Inf" +func OptFloatInfToStringInf() Option { + return &optFloatInfConvertToString{} +} + +// OptFloatInfToString tells what string to replace when marshaling +Inf and -Inf numbers. +// +// OptFloatInfToString 表示遇到 +/-Inf 时,将其替换成什么字符串。 +func OptFloatInfToString(positiveInf, negativeInf string) Option { + return &optFloatInfConvertToString{ + positive: positiveInf, + negative: negativeInf, + } +} + +type optFloatInfConvertToString struct { + positive string + negative string +} + +func (o *optFloatInfConvertToString) mergeTo(opt *Opt) { + opt.FloatInfHandleType = FloatInfConvertToString + opt.FloatInfPositiveToString = o.positive + opt.FloatInfNegativeToString = o.negative +} + +// ==== escapeHTML ==== + +// OptEscapeHTML specifies whether problematic HTML characters should be escaped +// inside JSON quoted strings. The default behavior is to escape &, <, and > to +// \u0026, \u003c, and \u003e to avoid certain safety problems that can arise when +// embedding JSON in HTML. If not specified, HTML symbols above will be escaped by +// default. +// +// OptEscapeHTML 指定部分 HTML 符号是否会被转义。相关的 HTML 符号为 &, <, > 三个。如无指定, +// 则默认会被转义 +func OptEscapeHTML(on bool) Option { + return optEscapeHTML(on) +} + +type optEscapeHTML bool + +func (o optEscapeHTML) mergeTo(opt *Opt) { + if o { + opt.escProperties = opt.escProperties.clear(escapeWithoutHTML) + } else { + opt.escProperties = opt.escProperties.set(escapeWithoutHTML) + } +} + +// ==== do or do not not use ASCII escaping ==== + +// OptUTF8 specifies that all unicodes greater than 0x7F, will NOT be escaped by +// \uXXXX format but UTF-8. +// +// OptUTF8 指定使用 UTF-8 编码。也就是说针对大于 0x7F 的 unicode 字符,将不会使用默认的 \uXXXX +// 格式进行编码,而是直接使用 UTF-8。 +func OptUTF8() Option { + return optUTF8(true) +} + +type optUTF8 bool + +func (o optUTF8) mergeTo(opt *Opt) { + opt.escProperties = opt.escProperties.set(escapeUTF8) +} + +// ==== ignore slash ==== + +// OptEscapeSlash specifies whether we should escape slash (/) symbol. In JSON standard, +// this character should be escaped as '\/'. But non-escaping will not affect anything. +// If not specfied, slash will be escaped by default. +// +// OptEscapeSlash 指定是否需要转移斜杠 (/) 符号。在 JSON 标准中这个符号是需要被转移为 '\/' 的, +// +// 但是不转义这个符号也不会带来什么问题。如无明确指定,如无指定,默认情况下,斜杠是会被转义的。 +func OptEscapeSlash(on bool) Option { + return optEscSlash(on) +} + +type optEscSlash bool + +func (o optEscSlash) mergeTo(opt *Opt) { + if o { + opt.escProperties = opt.escProperties.clear(escapeIgnoreSlash) + } else { + opt.escProperties = opt.escProperties.set(escapeIgnoreSlash) + } +} + +// escapingProperties is a bit mask, showing the option for escaping +// characters. +// +// escapingProperties 是一个位掩码,表明转义特殊字符的方法 +type escapingProperties uint8 + +const ( + escapeUTF8 = 0 + escapeWithoutHTML = 1 + escapeIgnoreSlash = 2 +) + +func (esc escapingProperties) set(mask escapingProperties) escapingProperties { + return esc | (1 << mask) +} + +func (esc escapingProperties) clear(mask escapingProperties) escapingProperties { + return esc & ^(1 << mask) +} + +func (esc escapingProperties) has(mask escapingProperties) bool { + return esc == esc.set(mask) +} + +// parseEscapingFuncs parse escaping functions by escapingProperties. +func (o *Opt) parseEscapingFuncs() { + // init bytes lower than 0x80 + for i := range o.asciiCharEscapingFunc { + o.asciiCharEscapingFunc[i] = escapeNothing + } + + iterate := func(from, to int, fu func(b byte, buf buffer.Buffer)) { + for i := from; i <= to; i++ { + o.asciiCharEscapingFunc[i] = fu + } + } + + // ASCII control bytes should always escaped + iterate(0x00, 0x07, escAsciiControlChar) + // 0x08 is \b, encoding/json marshal as \u0008, but according to JSON standard, it should be "\b" + iterate(0x0E, 0x1F, escAsciiControlChar) + o.asciiCharEscapingFunc[0x7F] = escAsciiControlChar // encoding/json does not escape DEL + + // ASCII characters always to be escaped + o.asciiCharEscapingFunc['"'] = escDoubleQuote + o.asciiCharEscapingFunc['/'] = escSlash + o.asciiCharEscapingFunc['\\'] = escBackslash + o.asciiCharEscapingFunc['\b'] = escBackspace + o.asciiCharEscapingFunc['\f'] = escVertTab + o.asciiCharEscapingFunc['\t'] = escTab + o.asciiCharEscapingFunc['\n'] = escNewLine + o.asciiCharEscapingFunc['\r'] = escReturn + o.asciiCharEscapingFunc['<'] = escLeftAngle + o.asciiCharEscapingFunc['>'] = escRightAngle + o.asciiCharEscapingFunc['&'] = escAnd + // o.asciiCharEscapingFunc['%'] = escPercent + + // unicodes >= 0x80 + if o.escProperties.has(escapeUTF8) { + o.unicodeEscapingFunc = escapeGreaterUnicodeToBuffByUTF8 + } else { + o.unicodeEscapingFunc = escapeGreaterUnicodeToBuffByUTF16 + } + + // ignore slash? + if o.escProperties.has(escapeIgnoreSlash) { + o.asciiCharEscapingFunc['/'] = escapeNothing + } + + // without HTML? + if o.escProperties.has(escapeWithoutHTML) { + o.asciiCharEscapingFunc['<'] = escapeNothing + o.asciiCharEscapingFunc['>'] = escapeNothing + o.asciiCharEscapingFunc['&'] = escapeNothing + } +} + +// ==== indent ==== + +// OptIndent appliesiIndent to format the output. +// +// OptIndent 指定序列化时的缩进。 +func OptIndent(prefix, indent string) Option { + return optionIndent{prefix, indent} +} + +type optionIndent [2]string + +func (o optionIndent) mergeTo(opt *Opt) { + opt.indent.enabled = true + opt.indent.prefix = o[0] + opt.indent.indent = o[1] +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/set.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/set.go new file mode 100644 index 0000000..3e4ba4a --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/set.go @@ -0,0 +1,412 @@ +package jsonvalue + +import ( + "fmt" + "reflect" +) + +// Setter type is for At() only. +// +// Setter 类型仅用于 At() 函数。 +type Setter interface { + // At completes the following operation of Set(). It defines position of value + // in Set() and return the new value set. + // + // The usage of At() is perhaps the most important. This function will recursively + // search for child value, and set the new value specified by Set() or SetXxx() + // series functions. Please unfold and read the following examples, they are important. + // + // At 完成 Set() 函数的后续操作并设置相应的子成员。其参数指定了应该在哪个位置设置子成员, + // 并且返回被设置的子成员对象。 + // + // 该函数的用法恐怕是 jsonvalue 中最重要的内容了:该函数会按照给定的可变参数递归地一层一层查找 + // JSON 值的子成员,并且设置到指定的位置上。设置的逻辑说明起来比较抽象,请打开以下的例子以了解, + // 这非常重要。 + At(firstParam any, otherParams ...any) (*V, error) +} + +type setter struct { + v *V + c *V // child + + err error +} + +// Set starts setting a child JSON value. Any legal JSON value typped parameter +// is accepted, such as string, int, float, bool, nil, *jsonvalue.V, or even +// a struct or map or slice. +// +// Please refer to examples of "func (set Setter) At(...)" +// +// https://godoc.org/github.com/Andrew-M-C/go.jsonvalue/#Set.At +// +// Set 开始设置一个 JSON 子成员。任何合法的 JSON 类型都可以作为参数, 比如 string, int, +// float, bool, nil, *jsonvalue.V 等类型, 甚至也支持结构体、map、切片、数组。 +// +// 请参见 "func (set Setter) At(...)" 例子. +// +// https://godoc.org/github.com/Andrew-M-C/go.jsonvalue/#Set.At +func (v *V) Set(child any) Setter { + var ch *V + var err error + + if child == nil { + ch = NewNull() + } else if childV, ok := child.(*V); ok { + ch = childV + } else { + ch, err = Import(child) + } + + return &setter{ + v: v, + c: ch, + err: err, + } +} + +// SetString is equivalent to Set(jsonvalue.NewString(s)) +// +// SetString 等效于 Set(jsonvalue.NewString(s)) +func (v *V) SetString(s string) Setter { + return v.Set(NewString(s)) +} + +// SetBytes is equivalent to Set(NewString(base64.StdEncoding.EncodeToString(b))) +// +// SetBytes 等效于 Set(NewString(base64.StdEncoding.EncodeToString(b))) +func (v *V) SetBytes(b []byte) Setter { + s := internal.b64.EncodeToString(b) + return v.SetString(s) +} + +// SetBool is equivalent to Set(jsonvalue.NewBool(b)) +// +// SetBool 等效于 Set(jsonvalue.NewBool(b)) +func (v *V) SetBool(b bool) Setter { + return v.Set(NewBool(b)) +} + +// SetInt is equivalent to Set(jsonvalue.NewInt(b)) +// +// SetInt 等效于 Set(jsonvalue.NewInt(b)) +func (v *V) SetInt(i int) Setter { + return v.Set(NewInt(i)) +} + +// SetInt64 is equivalent to Set(jsonvalue.NewInt64(b)) +// +// SetInt64 等效于 Set(jsonvalue.NewInt64(b)) +func (v *V) SetInt64(i int64) Setter { + return v.Set(NewInt64(i)) +} + +// SetInt32 is equivalent to Set(jsonvalue.NewInt32(b)) +// +// SetInt32 等效于 Set(jsonvalue.NewInt32(b)) +func (v *V) SetInt32(i int32) Setter { + return v.Set(NewInt32(i)) +} + +// SetUint is equivalent to Set(jsonvalue.NewUint(b)) +// +// SetUint 等效于 Set(jsonvalue.NewUint(b)) +func (v *V) SetUint(u uint) Setter { + return v.Set(NewUint(u)) +} + +// SetUint64 is equivalent to Set(jsonvalue.NewUint64(b)) +// +// SetUint64 is equivalent to Set(jsonvalue.NewUint64(b)) +func (v *V) SetUint64(u uint64) Setter { + return v.Set(NewUint64(u)) +} + +// SetUint32 is equivalent to Set(jsonvalue.NewUint32(b)) +// +// SetUint32 等效于 Set(jsonvalue.NewUint32(b)) +func (v *V) SetUint32(u uint32) Setter { + return v.Set(NewUint32(u)) +} + +// SetFloat64 is equivalent to Set(jsonvalue.NewFloat64(b)) +// +// SetFloat64 等效于 Set(jsonvalue.NewFloat64(b)) +func (v *V) SetFloat64(f float64) Setter { + return v.Set(NewFloat64(f)) +} + +// SetFloat32 is equivalent to Set(jsonvalue.NewFloat32(b)) +// +// SetFloat32 等效于 Set(jsonvalue.NewFloat32(b)) +func (v *V) SetFloat32(f float32) Setter { + return v.Set(NewFloat32(f)) +} + +// SetNull is equivalent to Set(jsonvalue.NewNull()) +// +// SetNull 等效于 Set(jsonvalue.NewNull()) +func (v *V) SetNull() Setter { + return v.Set(NewNull()) +} + +// SetObject is equivalent to Set(jsonvalue.NewObject()) +// +// SetObject 等效于 Set(jsonvalue.NewObject()) +func (v *V) SetObject() Setter { + return v.Set(NewObject()) +} + +// SetArray is equivalent to Set(jsonvalue.NewArray()) +// +// SetArray 等效于 Set(jsonvalue.NewArray()) +func (v *V) SetArray() Setter { + return v.Set(NewArray()) +} + +func setToObjectChildren(v *V, key string, child *V) { + v.children.incrID++ + v.children.object[key] = childWithProperty{ + id: v.children.incrID, + v: child, + } + addCaselessKey(v, key) +} + +func (s *setter) At(firstParam any, otherParams ...any) (*V, error) { + if s.err != nil { + return &V{}, s.err + } + if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok { + if len(otherParams) > 0 { + return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven + } + return s.At(p1, p2...) + } + v := s.v + c := s.c + if nil == v || v.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + if nil == c || c.valueType == NotExist { + return &V{}, ErrValueUninitialized + } + + // this is the last iteration + if len(otherParams) == 0 { + return s.atLastParam(firstParam) + } + + // this is not the last iterarion + if v.valueType == Object { + return s.atObject(firstParam, otherParams) + } + + // array type + if v.valueType == Array { + return s.atArray(firstParam, otherParams) + } + + // illegal type + return &V{}, fmt.Errorf("%v type does not supports Set()", v.valueType) +} + +func (s *setter) atLastParam(p any) (*V, error) { + v := s.v + c := s.c + switch v.valueType { + default: + return &V{}, fmt.Errorf("%v type does not supports Set()", v.valueType) + + case Object: + var k string + k, err := anyToString(p) + if err != nil { + return &V{}, err + } + setToObjectChildren(v, k, c) + return c, nil + + case Array: + pos, err := anyToInt(p) + if err != nil { + return &V{}, err + } + err = setAtIndex(v, c, pos) + if err != nil { + return &V{}, err + } + return c, nil + } +} + +func (s *setter) atObject(firstParam any, otherParams []any) (*V, error) { + v := s.v + c := s.c + k, err := anyToString(firstParam) + if err != nil { + return &V{}, err + } + child, exist := getFromObjectChildren(v, false, k) + if !exist { + if _, err := anyToString(otherParams[0]); err == nil { + child = NewObject() + } else if i, err := anyToInt(otherParams[0]); err == nil { + if i != 0 { + return &V{}, ErrOutOfRange + } + child = NewArray() + } else { + return &V{}, fmt.Errorf("unexpected type %v for Set()", reflect.TypeOf(otherParams[0])) + } + } + next := &setter{ + v: child, + c: c, + } + _, err = next.At(otherParams[0], otherParams[1:]...) + if err != nil { + return &V{}, err + } + if !exist { + setToObjectChildren(v, k, child) + } + return c, nil +} + +func (s *setter) atArray(firstParam any, otherParams []any) (*V, error) { + v := s.v + c := s.c + pos, err := anyToInt(firstParam) + if err != nil { + return &V{}, err + } + child, ok := childAtIndex(v, pos) + isNewChild := false + if !ok { + isNewChild = true + if _, err := anyToString(otherParams[0]); err == nil { + child = NewObject() + } else if i, err := anyToInt(otherParams[0]); err == nil { + if i != 0 { + return &V{}, ErrOutOfRange + } + child = NewArray() + } else { + return &V{}, fmt.Errorf("unexpected type %v for Set()", reflect.TypeOf(otherParams[0])) + } + } + next := &setter{ + v: child, + c: c, + } + _, err = next.At(otherParams[0], otherParams[1:]...) + if err != nil { + return &V{}, err + } + // OK to add this object + if isNewChild { + appendToArr(v, child) + } + return c, nil +} + +func posAtIndexForSet(v *V, pos int) (newPos int, appendToEnd bool) { + if pos == len(v.children.arr) { + return pos, true + } + pos = posAtIndexForRead(v, pos) + return pos, false +} + +func posAtIndexForInsertBefore(v *V, pos int) (newPos int) { + le := len(v.children.arr) + if le == 0 { + return -1 + } + + if pos == 0 { + return 0 + } + + if pos < 0 { + pos += le + if pos < 0 { + return -1 + } + return pos + } + + if pos >= le { + return -1 + } + + return pos +} + +func posAtIndexForInsertAfter(v *V, pos int) (newPos int, appendToEnd bool) { + le := len(v.children.arr) + if le == 0 { + return -1, false + } + + if pos == -1 { + return le, true + } + + if pos < 0 { + pos += le + if pos < 0 { + return -1, false + } + return pos + 1, false + } + + if pos >= le { + return -1, false + } + + return pos + 1, false +} + +func posAtIndexForRead(v *V, pos int) int { + le := len(v.children.arr) + if le == 0 { + return -1 + } + + if pos < 0 { + pos += le + if pos < 0 { + return -1 + } + return pos + } + + if pos >= le { + return -1 + } + + return pos +} + +func childAtIndex(v *V, pos int) (*V, bool) { // if nil returned, means that just push + pos = posAtIndexForRead(v, pos) + if pos < 0 { + return &V{}, false + } + return v.children.arr[pos], true +} + +func setAtIndex(v *V, child *V, pos int) error { + pos, appendToEnd := posAtIndexForSet(v, pos) + if pos < 0 { + return ErrOutOfRange + } + if appendToEnd { + v.children.arr = append(v.children.arr, child) + } else { + v.children.arr[pos] = child + } + return nil +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/set_must.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/set_must.go new file mode 100644 index 0000000..2e91325 --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/set_must.go @@ -0,0 +1,162 @@ +package jsonvalue + +// MARK: v.MustSet(xxx).At(xxx) + +// MustSetter is just like Setter, but not returning sub-value or error. +type MustSetter interface { + // At completes the following operation of Set(). It defines position of value + // in Set() and return the new value set. + // + // The usage of At() is perhaps the most important. This function will recursively + // search for child value, and set the new value specified by Set() or SetXxx() + // series functions. Please unfold and read the following examples, they are important. + // + // At 完成 Set() 函数的后续操作并设置相应的子成员。其参数指定了应该在哪个位置设置子成员, + // 并且返回被设置的子成员对象。 + // + // 该函数的用法恐怕是 jsonvalue 中最重要的内容了:该函数会按照给定的可变参数递归地一层一层查找 + // JSON 值的子成员,并且设置到指定的位置上。设置的逻辑说明起来比较抽象,请打开以下的例子以了解, + // 这非常重要。 + At(firstParam any, otherParams ...any) +} + +type mSetter struct { + setter Setter +} + +// MustSet is just like Set, but not returning sub-value or error. +func (v *V) MustSet(child any) MustSetter { + setter := v.Set(child) + return mSetter{ + setter: setter, + } +} + +// MustSetString is equivalent to Set(jsonvalue.NewString(s)) +// +// MustSetString 等效于 Set(jsonvalue.NewString(s)) +func (v *V) MustSetString(s string) MustSetter { + return v.MustSet(NewString(s)) +} + +// MustSetBytes is equivalent to Set(NewString(base64.StdEncoding.EncodeToString(b))) +// +// MustSetBytes 等效于 Set(NewString(base64.StdEncoding.EncodeToString(b))) +func (v *V) MustSetBytes(b []byte) MustSetter { + s := internal.b64.EncodeToString(b) + return v.MustSetString(s) +} + +// MustSetBool is equivalent to Set(jsonvalue.NewBool(b)) +// +// MustSetBool 等效于 Set(jsonvalue.NewBool(b)) +func (v *V) MustSetBool(b bool) MustSetter { + return v.MustSet(NewBool(b)) +} + +// MustSetInt is equivalent to Set(jsonvalue.NewInt(b)) +// +// MustSetInt 等效于 Set(jsonvalue.NewInt(b)) +func (v *V) MustSetInt(i int) MustSetter { + return v.MustSet(NewInt(i)) +} + +// MustSetInt64 is equivalent to Set(jsonvalue.NewInt64(b)) +// +// MustSetInt64 等效于 Set(jsonvalue.NewInt64(b)) +func (v *V) MustSetInt64(i int64) MustSetter { + return v.MustSet(NewInt64(i)) +} + +// MustSetInt32 is equivalent to Set(jsonvalue.NewInt32(b)) +// +// MustSetInt32 等效于 Set(jsonvalue.NewInt32(b)) +func (v *V) MustSetInt32(i int32) MustSetter { + return v.MustSet(NewInt32(i)) +} + +// MustSetUint is equivalent to Set(jsonvalue.NewUint(b)) +// +// MustSetUint 等效于 Set(jsonvalue.NewUint(b)) +func (v *V) MustSetUint(u uint) MustSetter { + return v.MustSet(NewUint(u)) +} + +// MustSetUint64 is equivalent to Set(jsonvalue.NewUint64(b)) +// +// MustSetUint64 is equivalent to Set(jsonvalue.NewUint64(b)) +func (v *V) MustSetUint64(u uint64) MustSetter { + return v.MustSet(NewUint64(u)) +} + +// MustSetUint32 is equivalent to Set(jsonvalue.NewUint32(b)) +// +// MustSetUint32 等效于 Set(jsonvalue.NewUint32(b)) +func (v *V) MustSetUint32(u uint32) MustSetter { + return v.MustSet(NewUint32(u)) +} + +// MustSetFloat64 is equivalent to Set(jsonvalue.NewFloat64(b)) +// +// MustSetFloat64 等效于 Set(jsonvalue.NewFloat64(b)) +func (v *V) MustSetFloat64(f float64) MustSetter { + return v.MustSet(NewFloat64(f)) +} + +// MustSetFloat32 is equivalent to Set(jsonvalue.NewFloat32(b)) +// +// MustSetFloat32 等效于 Set(jsonvalue.NewFloat32(b)) +func (v *V) MustSetFloat32(f float32) MustSetter { + return v.MustSet(NewFloat32(f)) +} + +// MustSetNull is equivalent to Set(jsonvalue.NewNull()) +// +// MustSetNull 等效于 Set(jsonvalue.NewNull()) +func (v *V) MustSetNull() MustSetter { + return v.MustSet(NewNull()) +} + +// MustSetObject is equivalent to Set(jsonvalue.NewObject()) +// +// MustSetObject 等效于 Set(jsonvalue.NewObject()) +func (v *V) MustSetObject() MustSetter { + return v.MustSet(NewObject()) +} + +// MustSetArray is equivalent to Set(jsonvalue.NewArray()) +// +// MustSetArray 等效于 Set(jsonvalue.NewArray()) +func (v *V) MustSetArray() MustSetter { + return v.MustSet(NewArray()) +} + +func (s mSetter) At(firstParam any, otherParams ...any) { + _, _ = s.setter.At(firstParam, otherParams...) +} + +// MARK: v.At(xxx).Set(xxx) + +// AtSetter works like v.MustSet(...).At(...), just with different sequence. +type AtSetter interface { + Set(subValue any) +} + +// At works like v.MustSet(...).At(...), just with different sequence. +func (v *V) At(firstParam any, otherParams ...any) AtSetter { + return atSetter{ + v: v, + firstParam: firstParam, + otherParams: otherParams, + } +} + +type atSetter struct { + v *V + firstParam any + otherParams []any +} + +func (a atSetter) Set(sub any) { + a.v.MustSet(sub).At(a.firstParam, a.otherParams...) +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/sort.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/sort.go new file mode 100644 index 0000000..0ebfc8e --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/sort.go @@ -0,0 +1,326 @@ +package jsonvalue + +import ( + "bytes" + "sort" + "strconv" + "strings" + + "github.com/Andrew-M-C/go.jsonvalue/internal/buffer" +) + +// ---------------- array sorting ---------------- + +// ArrayLessFunc is used in SortArray(), identifying which member is ahead. +// +// ArrayLessFunc 用于 SortArray() 函数中,指定两个成员谁在前面。 +type ArrayLessFunc func(v1, v2 *V) bool + +// SortArray is used to re-arrange sequence of the array. Invokers should pass less function for sorting. +// Nothing would happens either lessFunc is nil or v is not an array. +// +// SortArray 用于对 array 类型的 JSON 的子成员进行重新排序。基本逻辑与 sort.Sort 函数相同。当 lessFunc 为 nil,或者当前 JSON 不是一个 +// array 类型时,什么变化都不会发生。 +func (v *V) SortArray(lessFunc ArrayLessFunc) { + if nil == lessFunc { + return + } + if !v.IsArray() { + return + } + + sav := newSortV(v, lessFunc) + sav.Sort() +} + +type sortArrayV struct { + v *V + lessFunc ArrayLessFunc +} + +func newSortV(v *V, lessFunc ArrayLessFunc) *sortArrayV { + sav := sortArrayV{ + v: v, + lessFunc: lessFunc, + } + return &sav +} + +func (v *sortArrayV) Sort() { + sort.Sort(v) +} + +func (v *sortArrayV) Len() int { + return len(v.v.children.arr) +} + +func (v *sortArrayV) Less(i, j int) bool { + v1 := v.v.children.arr[i] + v2 := v.v.children.arr[j] + return v.lessFunc(v1, v2) +} + +func (v *sortArrayV) Swap(i, j int) { + v.v.children.arr[i], v.v.children.arr[j] = v.v.children.arr[j], v.v.children.arr[i] +} + +// ---------------- marshal sorting ---------------- + +// Key is the element of KeyPath +// +// Key 是 KeyPath 类型的成员 +type Key struct { + s string + i int +} + +func intKey(i int) Key { + return Key{i: i} +} + +func stringKey(s string) Key { + return Key{s: s} +} + +// String returns string value of a key +// +// String 返回当前键值对的键的描述 +func (k *Key) String() string { + if k.s != "" { + return k.s + } + return strconv.Itoa(k.i) +} + +// IsString tells if current key is a string, which indicates a child of an object. +// +// IsString 判断当前的键是不是一个 string 类型,如果是的话,那么它是一个 object JSON 的子成员。 +func (k *Key) IsString() bool { + return k.s != "" +} + +// Int returns int value of a key. +// +// Int 返回当前键值对的 int 值。 +func (k *Key) Int() int { + if k.s == "" { + return k.i + } + return 0 +} + +// IsInt tells if current key is a integer, which indicates a child of an array +// +// IsInt 判断当前的键是不是一个整型类型,如果是的话,那么它是一个 array JSON 的子成员。 +func (k *Key) IsInt() bool { + return k.s == "" +} + +// KeyPath identifies a full path of keys of object in jsonvalue. +// +// KeyPath 表示一个对象在指定 jsonvalue 中的完整的键路径。 +type KeyPath []*Key + +// String returns last element of key path. +// +// String 返回 KeyPath 的最后一个成员的描述。 +func (p KeyPath) String() (s string) { + buff := bytes.Buffer{} + buff.WriteRune('[') + + defer func() { + buff.WriteRune(']') + s = buff.String() + }() + + for i, k := range p { + if i > 0 { + buff.WriteRune(' ') + } + if k.IsInt() { + s := strconv.Itoa(k.Int()) + buff.WriteString(s) + } else { + buff.WriteRune('"') + escapeStringToBuff(k.String(), &buff, getDefaultOptions()) + buff.WriteRune('"') + } + } + + return +} + +// ParentInfo show information of parent of a JSON value. +// +// ParentInfo 表示一个 JSON 值的父节点信息。 +type ParentInfo struct { + Parent *V + KeyPath KeyPath +} + +func newParentInfo(v *V, nilableParentInfo *ParentInfo, key Key) *ParentInfo { + if nil == nilableParentInfo { + return &ParentInfo{ + Parent: v, + KeyPath: KeyPath{&key}, + } + } + + return &ParentInfo{ + Parent: v, + KeyPath: append(nilableParentInfo.KeyPath, &key), + } +} + +// MarshalLessFunc is used in marshaling, for sorting marshaled data. +// +// MarshalLessFunc 用于序列化,指定 object 类型的 JSON 的键值对顺序。 +type MarshalLessFunc func(nilableParent *ParentInfo, key1, key2 string, v1, v2 *V) bool + +// DefaultStringSequence simply use strings.Compare() to define the sequence +// of various key-value pairs of an object value. +// This function is used in Opt.MarshalLessFunc. +// +// DefaultStringSequence 使用 strings.Compare() 函数来判断键值对的顺序。用于 Opt.MarshalLessFunc。 +func DefaultStringSequence(parent *ParentInfo, key1, key2 string, v1, v2 *V) bool { + return strings.Compare(key1, key2) <= 0 +} + +func (sov *sortObjectV) marshalObjectWithLessFunc(buf buffer.Buffer, opt *Opt) { + // sort + sort.Sort(sov) + + // marshal + firstWritten := false + for i, key := range sov.keys { + child := sov.values[i] + par := newParentInfo(child, sov.parentInfo, stringKey(key)) + firstWritten = writeObjectChildren(par, buf, !firstWritten, key, child, opt) + } +} + +type sortObjectV struct { + parentInfo *ParentInfo + lessFunc MarshalLessFunc + keys []string + values []*V +} + +func (sov *sortObjectV) Len() int { + return len(sov.values) +} + +func (sov *sortObjectV) Less(i, j int) bool { + return sov.lessFunc(sov.parentInfo, sov.keys[i], sov.keys[j], sov.values[i], sov.values[j]) +} + +func (sov *sortObjectV) Swap(i, j int) { + sov.keys[i], sov.keys[j] = sov.keys[j], sov.keys[i] + sov.values[i], sov.values[j] = sov.values[j], sov.values[i] +} + +func newSortObjectV(v *V, parentInfo *ParentInfo, opt *Opt) *sortObjectV { + sov := sortObjectV{ + parentInfo: parentInfo, + lessFunc: opt.MarshalLessFunc, + keys: make([]string, 0, len(v.children.object)), + values: make([]*V, 0, len(v.children.object)), + } + for k, child := range v.children.object { + sov.keys = append(sov.keys, k) + sov.values = append(sov.values, child.v) + } + + return &sov +} + +// marshalObjectWithStringSlice use a slice to determine sequence of object +func (sssv *sortStringSliceV) marshalObjectWithStringSlice(buf buffer.Buffer, opt *Opt) { + // sort + sort.Sort(sssv) + + // marshal + firstWritten := false + for i, key := range sssv.keys { + child := sssv.values[i] + firstWritten = writeObjectChildren(nil, buf, !firstWritten, key, child, opt) + } +} + +type sortStringSliceV struct { + v *V + seq map[string]int + keys []string + values []*V +} + +func newSortStringSliceV(v *V, opt *Opt) *sortStringSliceV { + if nil == opt.keySequence { + opt.keySequence = make(map[string]int, len(opt.MarshalKeySequence)) + for i, str := range opt.MarshalKeySequence { + opt.keySequence[str] = i + } + } + + sssv := sortStringSliceV{ + v: v, + seq: opt.keySequence, + keys: make([]string, 0, v.Len()), + values: make([]*V, 0, v.Len()), + } + for k, child := range v.children.object { + sssv.keys = append(sssv.keys, k) + sssv.values = append(sssv.values, child.v) + } + + return &sssv +} + +func newSortStringSliceVBySetSeq(v *V) *sortStringSliceV { + keySequence := make(map[string]int, len(v.children.object)) + for k, child := range v.children.object { + keySequence[k] = int(child.id) + } + + sssv := sortStringSliceV{ + v: v, + seq: keySequence, + keys: make([]string, 0, v.Len()), + values: make([]*V, 0, v.Len()), + } + for k, child := range v.children.object { + sssv.keys = append(sssv.keys, k) + sssv.values = append(sssv.values, child.v) + } + + return &sssv +} + +func (sssv *sortStringSliceV) Len() int { + return len(sssv.values) +} + +func (sssv *sortStringSliceV) Less(i, j int) bool { + k1 := sssv.keys[i] + k2 := sssv.keys[j] + + seq1, exist1 := sssv.seq[k1] + seq2, exist2 := sssv.seq[k2] + + if exist1 { + if exist2 { + return seq1 < seq2 + } + return true + } + if exist2 { + return false + } + + return k1 <= k2 +} + +func (sssv *sortStringSliceV) Swap(i, j int) { + sssv.keys[i], sssv.keys[j] = sssv.keys[j], sssv.keys[i] + sssv.values[i], sssv.values[j] = sssv.values[j], sssv.values[i] +} diff --git a/vendor/github.com/Andrew-M-C/go.jsonvalue/unmarshal.go b/vendor/github.com/Andrew-M-C/go.jsonvalue/unmarshal.go new file mode 100644 index 0000000..249222a --- /dev/null +++ b/vendor/github.com/Andrew-M-C/go.jsonvalue/unmarshal.go @@ -0,0 +1,900 @@ +package jsonvalue + +import ( + "errors" + "fmt" + "strconv" + + "github.com/Andrew-M-C/go.jsonvalue/internal/unsafe" +) + +// ================ OUTER UNMARSHAL ================ + +// unmarshalWithIter parse bytes with unknown value type. +func unmarshalWithIter(p pool, it iter, offset int) (v *V, err error) { + end := len(it) + offset, reachEnd := it.skipBlanks(offset) + if reachEnd { + return &V{}, fmt.Errorf("%w, cannot find any symbol characters found", ErrRawBytesUnrecognized) + } + + chr := it[offset] + switch chr { + case '{': + v, offset, err = unmarshalObjectWithIterUnknownEnd(p, it, offset, end) + + case '[': + v, offset, err = unmarshalArrayWithIterUnknownEnd(p, it, offset, end) + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': + var n *V + n, offset, _, err = it.parseNumber(p, offset) + if err == nil { + v = n + } + + case '"': + var sectLenWithoutQuote int + var sectEnd int + sectLenWithoutQuote, sectEnd, err = it.parseStrFromBytesForwardWithQuote(offset) + if err == nil { + v, err = NewString(unsafe.BtoS(it[offset+1:offset+1+sectLenWithoutQuote])), nil + offset = sectEnd + } + + case 't': + offset, err = it.parseTrue(offset) + if err == nil { + v = NewBool(true) + } + + case 'f': + offset, err = it.parseFalse(offset) + if err == nil { + v = NewBool(false) + } + + case 'n': + offset, err = it.parseNull(offset) + if err == nil { + v = NewNull() + } + + default: + return &V{}, fmt.Errorf("%w, invalid character \\u%04X at Position %d", ErrRawBytesUnrecognized, chr, offset) + } + + if err != nil { + return &V{}, err + } + + if offset, reachEnd = it.skipBlanks(offset, end); !reachEnd { + return &V{}, fmt.Errorf("%w, unnecessary trailing data remains at Position %d", ErrRawBytesUnrecognized, offset) + } + + return v, nil +} + +// unmarshalArrayWithIterUnknownEnd is similar with unmarshalArrayWithIter, though should start with '[', +// but it does not known where its ']' is +func unmarshalArrayWithIterUnknownEnd(p pool, it iter, offset, right int) (_ *V, end int, err error) { + offset++ + arr := newArray(p) + + reachEnd := false + + for offset < right { + // search for ending ']' + offset, reachEnd = it.skipBlanks(offset, right) + if reachEnd { + // ']' not found + return nil, -1, fmt.Errorf("%w, cannot find ']'", ErrNotArrayValue) + } + + chr := it[offset] + switch chr { + case ']': + return arr, offset + 1, nil + + case ',': + offset++ + + case '{': + v, sectEnd, err := unmarshalObjectWithIterUnknownEnd(p, it, offset, right) + if err != nil { + return nil, -1, err + } + appendToArr(arr, v) + offset = sectEnd + + case '[': + v, sectEnd, err := unmarshalArrayWithIterUnknownEnd(p, it, offset, right) + if err != nil { + return nil, -1, err + } + appendToArr(arr, v) + offset = sectEnd + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': + var v *V + v, sectEnd, _, err := it.parseNumber(p, offset) + if err != nil { + return nil, -1, err + } + appendToArr(arr, v) + offset = sectEnd + + case '"': + sectLenWithoutQuote, sectEnd, err := it.parseStrFromBytesForwardWithQuote(offset) + if err != nil { + return nil, -1, err + } + v := NewString(unsafe.BtoS(it[offset+1 : offset+1+sectLenWithoutQuote])) + appendToArr(arr, v) + offset = sectEnd + + case 't': + sectEnd, err := it.parseTrue(offset) + if err != nil { + return nil, -1, err + } + appendToArr(arr, NewBool(true)) + offset = sectEnd + + case 'f': + sectEnd, err := it.parseFalse(offset) + if err != nil { + return nil, -1, err + } + appendToArr(arr, NewBool(false)) + offset = sectEnd + + case 'n': + sectEnd, err := it.parseNull(offset) + if err != nil { + return nil, -1, err + } + appendToArr(arr, NewNull()) + offset = sectEnd + + default: + return nil, -1, fmt.Errorf("%w, invalid character \\u%04X at Position %d", ErrRawBytesUnrecognized, chr, offset) + } + } + + return nil, -1, fmt.Errorf("%w, cannot find ']'", ErrNotArrayValue) +} + +func appendToArr(v *V, child *V) { + if v.children.arr == nil { + v.children.arr = make([]*V, 0, initialArrayCapacity) + } + v.children.arr = append(v.children.arr, child) +} + +// unmarshalObjectWithIterUnknownEnd unmarshal object from raw bytes. it[offset] must be '{' +func unmarshalObjectWithIterUnknownEnd(p pool, it iter, offset, right int) (_ *V, end int, err error) { + offset++ + obj := newObject(p) + + keyStart, keyEnd := 0, 0 + colonFound := false + + reachEnd := false + + keyNotFoundErr := func() error { + if keyEnd == 0 { + return fmt.Errorf( + "%w, missing key for another value at Position %d", ErrNotObjectValue, offset, + ) + } + if !colonFound { + return fmt.Errorf( + "%w, missing colon for key at Position %d", ErrNotObjectValue, offset, + ) + } + return nil + } + + valNotFoundErr := func() error { + if keyEnd > 0 { + return fmt.Errorf( + "%w, missing value for key '%s' at Position %d", + ErrNotObjectValue, unsafe.BtoS(it[keyStart:keyEnd]), keyStart, + ) + } + return nil + } + + for offset < right { + offset, reachEnd = it.skipBlanks(offset, right) + if reachEnd { + // '}' not found + return nil, -1, fmt.Errorf("%w, cannot find '}'", ErrNotObjectValue) + } + + chr := it[offset] + switch chr { + case '}': + if err = valNotFoundErr(); err != nil { + return nil, -1, err + } + return obj, offset + 1, nil + + case ',': + if err = valNotFoundErr(); err != nil { + return nil, -1, err + } + offset++ + // continue + + case ':': + if colonFound { + return nil, -1, fmt.Errorf("%w, duplicate colon at Position %d", ErrNotObjectValue, keyStart) + } + colonFound = true + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + offset++ + // continue + + case '{': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + v, sectEnd, err := unmarshalObjectWithIterUnknownEnd(p, it, offset, right) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), v) + keyEnd, colonFound = 0, false + offset = sectEnd + + case '[': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + v, sectEnd, err := unmarshalArrayWithIterUnknownEnd(p, it, offset, right) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), v) + keyEnd, colonFound = 0, false + offset = sectEnd + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + var v *V + v, sectEnd, _, err := it.parseNumber(p, offset) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), v) + keyEnd, colonFound = 0, false + offset = sectEnd + + case '"': + if keyEnd > 0 { + // string value + if !colonFound { + return nil, -1, fmt.Errorf("%w, missing value for key '%s' at Position %d", + ErrNotObjectValue, unsafe.BtoS(it[keyStart:keyEnd]), keyStart, + ) + } + sectLenWithoutQuote, sectEnd, err := it.parseStrFromBytesForwardWithQuote(offset) + if err != nil { + return nil, -1, err + } + v := NewString(unsafe.BtoS(it[offset+1 : offset+1+sectLenWithoutQuote])) + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), v) + keyEnd, colonFound = 0, false + offset = sectEnd + + } else { + // string key + sectLenWithoutQuote, sectEnd, err := it.parseStrFromBytesForwardWithQuote(offset) + if err != nil { + return nil, -1, err + } + keyStart, keyEnd = offset+1, offset+1+sectLenWithoutQuote + offset = sectEnd + } + + case 't': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + sectEnd, err := it.parseTrue(offset) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), NewBool(true)) + keyEnd, colonFound = 0, false + offset = sectEnd + + case 'f': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + sectEnd, err := it.parseFalse(offset) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), NewBool(false)) + keyEnd, colonFound = 0, false + offset = sectEnd + + case 'n': + if err = keyNotFoundErr(); err != nil { + return nil, -1, err + } + sectEnd, err := it.parseNull(offset) + if err != nil { + return nil, -1, err + } + setToObjectChildren(obj, unsafe.BtoS(it[keyStart:keyEnd]), NewNull()) + keyEnd, colonFound = 0, false + offset = sectEnd + + default: + return nil, -1, fmt.Errorf("%w, invalid character \\u%04X at Position %d", ErrRawBytesUnrecognized, chr, offset) + } + + } + + return nil, -1, fmt.Errorf("%w, cannot find '}'", ErrNotObjectValue) +} + +// parseNumber parse a number string. Reference: +// +// - [ECMA-404 The JSON Data Interchange Standard](https://www.json.org/json-en.html) +func parseNumber(v *V, p pool) (err error) { + it := iter(v.srcByte) + + parsed, end, reachEnd, err := it.parseNumber(p, 0) + if err != nil { + return err + } + if !reachEnd { + return fmt.Errorf("invalid character: 0x%02x", v.srcByte[end]) + } + + *v = *parsed + return nil +} + +// ==== simple object parsing ==== +func newFromNumber(p pool, b []byte) (ret *V, err error) { + v := new(p, Number) + v.srcByte = b + return v, nil +} + +// ================ GENERAL UNMARSHALING ================ + +// iter is used to iterate []byte text +type iter []byte + +func (it iter) parseStrFromBytesForwardWithQuote(offset int) (sectLenWithoutQuote int, sectEnd int, err error) { + offset++ // skip " + end := len(it) + sectEnd = offset + + shift := func(i *int, le int) { + if end-*i < le { + err = fmt.Errorf( + "%w, expect at least %d remaining bytes, but got %d at Position %d", + ErrIllegalString, end-*i, le, *i, + ) + return + } + it.memcpy(sectEnd, *i, le) + sectEnd += le + *i += le + } + + // iterate every byte + for i := offset; i < end; { + chr := it[i] + + // ACSII? + switch { + case chr == '\\': + err = it.handleEscapeStart(&i, §End) + case chr == '"': + // found end quote + return sectEnd - offset, i + 1, nil + case chr <= 0x7F: + // shift(&i, 1) + it[sectEnd] = it[i] + i++ + sectEnd++ + case runeIdentifyingBytes2(chr): + shift(&i, 2) + case runeIdentifyingBytes3(chr): + shift(&i, 3) + case runeIdentifyingBytes4(chr): + shift(&i, 4) + default: + err = fmt.Errorf("%w: illegal UTF8 string at Position %d", ErrIllegalString, i) + } + if err != nil { + return -1, -1, err + } + } + + err = errors.New("ending double quote of a string is not found") + return +} + +func (it iter) handleEscapeStart(i *int, sectEnd *int) error { + if len(it)-1-*i < 1 { + return errors.New("escape symbol not followed by another character") + } + + chr := it[*i+1] + switch chr { + default: + return fmt.Errorf("unrecognized character 0x%02X after escape symbol", chr) + case '"', '\'', '/', '\\': + it[*sectEnd] = chr + *sectEnd++ + *i += 2 + case 'b': + it[*sectEnd] = '\b' + *sectEnd++ + *i += 2 + case 'f': + it[*sectEnd] = '\f' + *sectEnd++ + *i += 2 + case 'r': + it[*sectEnd] = '\r' + *sectEnd++ + *i += 2 + case 'n': + it[*sectEnd] = '\n' + *sectEnd++ + *i += 2 + case 't': + it[*sectEnd] = '\t' + *sectEnd++ + *i += 2 + case 'u': + return it.handleEscapeUnicodeStartWithEnd(i, len(it)-1, sectEnd) + } + return nil +} + +func (it iter) handleEscapeUnicodeStartWithEnd(i *int, end int, sectEnd *int) (err error) { + if end-*i <= 5 { + return errors.New("escape symbol not followed by another character") + } + + b3 := chrToHex(it[*i+2], &err) + b2 := chrToHex(it[*i+3], &err) + b1 := chrToHex(it[*i+4], &err) + b0 := chrToHex(it[*i+5], &err) + if err != nil { + return + } + + r := (rune(b3) << 12) + (rune(b2) << 8) + (rune(b1) << 4) + rune(b0) + + // this rune is smaller than 0x10000 + if r <= 0xD7FF || r >= 0xE000 { + le := it.assignASCIICodedRune(*sectEnd, r) + *i += 6 + *sectEnd += le + return nil + } + + // reference: [JSON 序列化中的转义和 Unicode 编码](https://cloud.tencent.com/developer/article/1625557/) + // should get another unicode-escaped character + if end-*i <= 11 { + return fmt.Errorf("insufficient UTF-16 data at offset %d", *i) + } + if it[*i+6] != '\\' || it[*i+7] != 'u' { + return fmt.Errorf("expect unicode escape character at position %d but not", *i+6) + } + + ex3 := chrToHex(it[*i+8], &err) + ex2 := chrToHex(it[*i+9], &err) + ex1 := chrToHex(it[*i+10], &err) + ex0 := chrToHex(it[*i+11], &err) + if err != nil { + return + } + + ex := (rune(ex3) << 12) + (rune(ex2) << 8) + (rune(ex1) << 4) + rune(ex0) + if ex < 0xDC00 { + return fmt.Errorf( + "%w, expect second UTF-16 encoding but got 0x04%X at position %d", + ErrIllegalString, r, *i+8, + ) + } + ex -= 0xDC00 + if ex > 0x03FF { + return fmt.Errorf( + "%w, expect second UTF-16 encoding but got 0x04%X at position %d", + ErrIllegalString, r, *i+8, + ) + } + + r = ((r - 0xD800) << 10) + ex + 0x10000 + + le := it.assignASCIICodedRune(*sectEnd, r) + *i += 12 + *sectEnd += le + return nil +} + +func chrToHex(chr byte, errOut *error) byte { + if chr >= '0' && chr <= '9' { + return chr - '0' + } + if chr >= 'A' && chr <= 'F' { + return chr - 'A' + 10 + } + if chr >= 'a' && chr <= 'f' { + return chr - 'a' + 10 + } + *errOut = fmt.Errorf("invalid unicode value character: %c", rune(chr)) + return 0 +} + +func (it iter) memcpy(dst, src, length int) { + if dst == src { + return + } + copy(it[dst:dst+length], it[src:src+length]) + // ptr := unsafe.Pointer(&it[0]) + // C.memcpy( + // unsafe.Pointer(uintptr(ptr)+uintptr(dst)), + // unsafe.Pointer(uintptr(ptr)+uintptr(src)), + // C.size_t(length), + // ) +} + +func (it iter) assignASCIICodedRune(dst int, r rune) (offset int) { + switch { + // 0zzzzzzz ==> + // 0zzzzzzz + case r <= 0x7F: + it[dst+0] = byte(r) + return 1 + // 00000yyy yyzzzzzz ==> + // 110yyyyy 10zzzzzz + case r <= 0x7FF: + it[dst+1] = byte((r&0x03F)>>0) + 0x80 + it[dst+0] = byte((r&0x7C0)>>6) + 0xC0 + return 2 + // xxxxyyyy yyzzzzzz ==> + // 1110xxxx 10yyyyyy 10zzzzzz + case r <= 0xFFFF: + it[dst+2] = byte((r&0x003F)>>0) + 0x80 + it[dst+1] = byte((r&0x0FC0)>>6) + 0x80 + it[dst+0] = byte((r&0xF000)>>12) + 0xE0 + return 3 + // 000wwwxx xxxxyyyy yyzzzzzz ==> + // 11110www 10xxxxxx 10yyyyyy 10zzzzzz + default: + it[dst+3] = byte((r&0x00003F)>>0) + 0x80 + it[dst+2] = byte((r&0x000FC0)>>6) + 0x80 + it[dst+1] = byte((r&0x03F000)>>12) + 0x80 + it[dst+0] = byte((r&0x1C0000)>>18) + 0xF0 + return 4 + } +} + +func runeIdentifyingBytes2(chr byte) bool { + return (chr & 0xE0) == 0xC0 +} + +func runeIdentifyingBytes3(chr byte) bool { + return (chr & 0xF0) == 0xE0 +} + +func runeIdentifyingBytes4(chr byte) bool { + return (chr & 0xF8) == 0xF0 +} + +func (it iter) parseTrue(offset int) (end int, err error) { + if len(it)-offset < 4 { + return -1, fmt.Errorf("%w, insufficient character from Position %d", ErrNotValidBoolValue, offset) + } + + if it[offset] == 't' && + it[offset+1] == 'r' && + it[offset+2] == 'u' && + it[offset+3] == 'e' { + return offset + 4, nil + } + + return -1, fmt.Errorf("%w, not 'true' at Position %d", ErrNotValidBoolValue, offset) +} + +func (it iter) parseFalse(offset int) (end int, err error) { + if len(it)-offset < 5 { + return -1, fmt.Errorf("%w, insufficient character from Position %d", ErrNotValidBoolValue, offset) + } + + if it[offset] == 'f' && + it[offset+1] == 'a' && + it[offset+2] == 'l' && + it[offset+3] == 's' && + it[offset+4] == 'e' { + return offset + 5, nil + } + + return -1, fmt.Errorf("%w, not 'false' at Position %d", ErrNotValidBoolValue, offset) +} + +func (it iter) parseNull(offset int) (end int, err error) { + if len(it)-offset < 4 { + return -1, fmt.Errorf("%w, insufficient character from Position %d", ErrNotValidNullValue, offset) + } + + if it[offset] == 'n' && + it[offset+1] == 'u' && + it[offset+2] == 'l' && + it[offset+3] == 'l' { + return offset + 4, nil + } + + return -1, fmt.Errorf("%w, not 'null' at Position %d", ErrNotValidBoolValue, offset) +} + +// skipBlanks skip blank characters until end or reaching a non-blank character +func (it iter) skipBlanks(offset int, endPos ...int) (newOffset int, reachEnd bool) { + end := 0 + if len(endPos) > 0 { + end = endPos[0] + } else { + end = len(it) + } + + for offset < end { + chr := it[offset] + switch chr { + case ' ', '\r', '\n', '\t', '\b': + offset++ // continue + default: + return offset, false + } + } + + return end, true +} + +// ================ FLOAT UNMARSHALING ================ + +// For state machine chart, please refer to ./img/parse_float_state_chart.drawio + +func (it iter) parseNumber( + p pool, offset int, +) (v *V, end int, reachEnd bool, err error) { + + idx := offset + negative := false + floated := false + exponentGot := false + dotGot := false + intAfterDotGot := false + integer := uint64(0) + edgeFound := false + + // len(it)-idx means remain bytes + + for ; len(it)-idx > 0 && !edgeFound; idx++ { + b := it[idx] + + switch b { + default: + edgeFound = true + + case '0': + if idx == offset { + // OK + } else if exponentGot { + // OK + } else if dotGot { + intAfterDotGot = true + } else if negative { + if integer == 0 && idx != offset+1 { + err = it.numErrorf(idx, "unexpected zero") + return + } + } else if integer == 0 { + err = it.numErrorf(idx, "unexpected zero") + return + } + integer *= 10 + + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + if !floated { + integer = integer*10 + uint64(b) - '0' + } else if !exponentGot { + intAfterDotGot = true + } + + case 'e', 'E': + if exponentGot { + err = it.numErrorf(idx, "unexpected exponent symbol") + return + } + exponentGot = true + floated = true + + case '+': + if !exponentGot { + err = it.numErrorf(idx, "unexpected +") + return + } + // Codes below not needed because this error is caught in outer logic + // if !floated { + // err = it.numErrorf(idx, "unexpected positive symbol") + // return + // } + + case '-': + if !floated { + if idx != offset { + err = it.numErrorf(idx, "unexpected negative symbol") + return + } + negative = true + } + + case '.': + if idx == offset || floated || exponentGot || dotGot { + err = it.numErrorf(idx, "unexpected dot symbol") + return + } + dotGot = true + floated = true + } + } + + if edgeFound { + idx-- + } + + if floated { + if dotGot && !intAfterDotGot { + err = it.numErrorf(offset, "integer after dot missing") + return + } + v, err = it.parseFloatResult(p, offset, idx) + } else { + if integer > 0 && it[offset] == '0' { + err = it.numErrorf(offset, "non-zero integer should not start with zero") + return + } + + firstB := it[offset] + if idx-offset == 1 { + if firstB >= '0' && firstB <= '9' { + // OK + } else { + err = it.numErrorf(offset, "invalid number format") + return + } + } + + if negative { + v, err = it.parseNegativeIntResult(p, offset, idx, integer) + } else { + v, err = it.parsePositiveIntResult(p, offset, idx, integer) + } + } + + return v, idx, len(it)-idx == 0, err +} + +func (it iter) numErrorf(offset int, f string, a ...any) error { + a = append([]any{offset}, a...) + return fmt.Errorf("parsing number at index %d: "+f, a...) + + // debug ONLY below + + // getCaller := func(skip int) string { + // pc, _, _, ok := runtime.Caller(skip + 1) + // if !ok { + // return "" + // } + // ca := runtime.CallersFrames([]uintptr{pc}) + // fr, _ := ca.Next() + + // fu := filepath.Ext(fr.Function) + // fu = strings.TrimLeft(fu, ".") + // li := fr.Line + + // return fmt.Sprintf("%s(), Line %d", fu, li) + // } + // ca := getCaller(1) + + // a = append([]any{ca, string(it), offset}, a...) + // return fmt.Errorf("%s - parsing number \"%s\" at index %d: "+f, a...) +} + +const ( + uintMaxStr = "18446744073709551615" + uintMaxDigits = 10000000000000000000 + intMin = -9223372036854775808 + intMinStr = "-9223372036854775808" + intMinAbs = 9223372036854775808 +) + +func (it iter) parseFloatResult(p pool, start, end int) (*V, error) { + f, err := strconv.ParseFloat(unsafe.BtoS(it[start:end]), 64) + if err != nil { + return nil, it.numErrorf(start, "%w", err) + } + + v := new(p, Number) + v.srcByte = it[start:end] + + v.num.negative = f < 0 + v.num.floated = true + v.num.i64 = int64(f) + v.num.u64 = uint64(f) + v.num.f64 = f + + return v, nil +} + +func (it iter) parsePositiveIntResult(p pool, start, end int, integer uint64) (*V, error) { + le := end - start + + if le > len(uintMaxStr) { + return nil, it.numErrorf(start, "value too large") + } else if le == len(uintMaxStr) { + if integer < uintMaxDigits { + return nil, it.numErrorf(start, "value too large") + } + } + + v := new(p, Number) + v.srcByte = it[start:end] + + v.num.negative = false + v.num.floated = false + v.num.i64 = int64(integer) + v.num.u64 = uint64(integer) + v.num.f64 = float64(integer) + + return v, nil +} + +func (it iter) parseNegativeIntResult(p pool, start, end int, integer uint64) (*V, error) { + le := end - start + + if le > len(intMinStr) { + return nil, it.numErrorf(start, "absolute value too large") + } else if le == len(intMinStr) { + if integer > intMinAbs { + return nil, it.numErrorf(start, "absolute value too large") + } + } + + v := new(p, Number) + v.srcByte = it[start:end] + + v.num.negative = true + v.num.floated = false + + if integer == intMinAbs { + v.num.i64 = intMin + } else { + v.num.i64 = -int64(integer) + } + + v.num.u64 = uint64(v.num.i64) + v.num.f64 = -float64(integer) + + return v, nil +} diff --git a/vendor/github.com/shopspring/decimal/.gitignore b/vendor/github.com/shopspring/decimal/.gitignore new file mode 100644 index 0000000..ff36b98 --- /dev/null +++ b/vendor/github.com/shopspring/decimal/.gitignore @@ -0,0 +1,9 @@ +.git +*.swp + +# IntelliJ +.idea/ +*.iml + +# VS code +*.code-workspace diff --git a/vendor/github.com/shopspring/decimal/CHANGELOG.md b/vendor/github.com/shopspring/decimal/CHANGELOG.md new file mode 100644 index 0000000..432d0fd --- /dev/null +++ b/vendor/github.com/shopspring/decimal/CHANGELOG.md @@ -0,0 +1,76 @@ +## Decimal v1.4.0 +#### BREAKING +- Drop support for Go version older than 1.10 [#361](https://github.com/shopspring/decimal/pull/361) + +#### FEATURES +- Add implementation of natural logarithm [#339](https://github.com/shopspring/decimal/pull/339) [#357](https://github.com/shopspring/decimal/pull/357) +- Add improved implementation of power operation [#358](https://github.com/shopspring/decimal/pull/358) +- Add Compare method which forwards calls to Cmp [#346](https://github.com/shopspring/decimal/pull/346) +- Add NewFromBigRat constructor [#288](https://github.com/shopspring/decimal/pull/288) +- Add NewFromUint64 constructor [#352](https://github.com/shopspring/decimal/pull/352) + +#### ENHANCEMENTS +- Migrate to Github Actions [#245](https://github.com/shopspring/decimal/pull/245) [#340](https://github.com/shopspring/decimal/pull/340) +- Fix examples for RoundDown, RoundFloor, RoundUp, and RoundCeil [#285](https://github.com/shopspring/decimal/pull/285) [#328](https://github.com/shopspring/decimal/pull/328) [#341](https://github.com/shopspring/decimal/pull/341) +- Use Godoc standard to mark deprecated Equals and StringScaled methods [#342](https://github.com/shopspring/decimal/pull/342) +- Removed unnecessary min function for RescalePair method [#265](https://github.com/shopspring/decimal/pull/265) +- Avoid reallocation of initial slice in MarshalBinary (GobEncode) [#355](https://github.com/shopspring/decimal/pull/355) +- Optimize NumDigits method [#301](https://github.com/shopspring/decimal/pull/301) [#356](https://github.com/shopspring/decimal/pull/356) +- Optimize BigInt method [#359](https://github.com/shopspring/decimal/pull/359) +- Support scanning uint64 [#131](https://github.com/shopspring/decimal/pull/131) [#364](https://github.com/shopspring/decimal/pull/364) +- Add docs section with alternative libraries [#363](https://github.com/shopspring/decimal/pull/363) + +#### BUGFIXES +- Fix incorrect calculation of decimal modulo [#258](https://github.com/shopspring/decimal/pull/258) [#317](https://github.com/shopspring/decimal/pull/317) +- Allocate new(big.Int) in Copy method to deeply clone it [#278](https://github.com/shopspring/decimal/pull/278) +- Fix overflow edge case in QuoRem method [#322](https://github.com/shopspring/decimal/pull/322) + +## Decimal v1.3.1 + +#### ENHANCEMENTS +- Reduce memory allocation in case of initialization from big.Int [#252](https://github.com/shopspring/decimal/pull/252) + +#### BUGFIXES +- Fix binary marshalling of decimal zero value [#253](https://github.com/shopspring/decimal/pull/253) + +## Decimal v1.3.0 + +#### FEATURES +- Add NewFromFormattedString initializer [#184](https://github.com/shopspring/decimal/pull/184) +- Add NewNullDecimal initializer [#234](https://github.com/shopspring/decimal/pull/234) +- Add implementation of natural exponent function (Taylor, Hull-Abraham) [#229](https://github.com/shopspring/decimal/pull/229) +- Add RoundUp, RoundDown, RoundCeil, RoundFloor methods [#196](https://github.com/shopspring/decimal/pull/196) [#202](https://github.com/shopspring/decimal/pull/202) [#220](https://github.com/shopspring/decimal/pull/220) +- Add XML support for NullDecimal [#192](https://github.com/shopspring/decimal/pull/192) +- Add IsInteger method [#179](https://github.com/shopspring/decimal/pull/179) +- Add Copy helper method [#123](https://github.com/shopspring/decimal/pull/123) +- Add InexactFloat64 helper method [#205](https://github.com/shopspring/decimal/pull/205) +- Add CoefficientInt64 helper method [#244](https://github.com/shopspring/decimal/pull/244) + +#### ENHANCEMENTS +- Performance optimization of NewFromString init method [#198](https://github.com/shopspring/decimal/pull/198) +- Performance optimization of Abs and Round methods [#240](https://github.com/shopspring/decimal/pull/240) +- Additional tests (CI) for ppc64le architecture [#188](https://github.com/shopspring/decimal/pull/188) + +#### BUGFIXES +- Fix rounding in FormatFloat fallback path (roundShortest method, fix taken from Go main repository) [#161](https://github.com/shopspring/decimal/pull/161) +- Add slice range checks to UnmarshalBinary method [#232](https://github.com/shopspring/decimal/pull/232) + +## Decimal v1.2.0 + +#### BREAKING +- Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172) + +#### FEATURES +- Add NewFromInt and NewFromInt32 initializers [#72](https://github.com/shopspring/decimal/pull/72) +- Add support for Go modules [#157](https://github.com/shopspring/decimal/pull/157) +- Add BigInt, BigFloat helper methods [#171](https://github.com/shopspring/decimal/pull/171) + +#### ENHANCEMENTS +- Memory usage optimization [#160](https://github.com/shopspring/decimal/pull/160) +- Updated travis CI golang versions [#156](https://github.com/shopspring/decimal/pull/156) +- Update documentation [#173](https://github.com/shopspring/decimal/pull/173) +- Improve code quality [#174](https://github.com/shopspring/decimal/pull/174) + +#### BUGFIXES +- Revert remove insignificant digits [#159](https://github.com/shopspring/decimal/pull/159) +- Remove 15 interval for RoundCash [#166](https://github.com/shopspring/decimal/pull/166) diff --git a/vendor/github.com/shopspring/decimal/LICENSE b/vendor/github.com/shopspring/decimal/LICENSE new file mode 100644 index 0000000..ad2148a --- /dev/null +++ b/vendor/github.com/shopspring/decimal/LICENSE @@ -0,0 +1,45 @@ +The MIT License (MIT) + +Copyright (c) 2015 Spring, Inc. + +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. + +- Based on https://github.com/oguzbilgic/fpd, which has the following license: +""" +The MIT License (MIT) + +Copyright (c) 2013 Oguz Bilgic + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" diff --git a/vendor/github.com/shopspring/decimal/README.md b/vendor/github.com/shopspring/decimal/README.md new file mode 100644 index 0000000..318c9df --- /dev/null +++ b/vendor/github.com/shopspring/decimal/README.md @@ -0,0 +1,139 @@ +# decimal + +[![ci](https://github.com/shopspring/decimal/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/shopspring/decimal/actions/workflows/ci.yml) +[![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) +[![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal) + +Arbitrary-precision fixed-point decimal numbers in go. + +_Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point. + +## Features + + * The zero-value is 0, and is safe to use without initialization + * Addition, subtraction, multiplication with no loss of precision + * Division with specified precision + * Database/sql serialization/deserialization + * JSON and XML serialization/deserialization + +## Install + +Run `go get github.com/shopspring/decimal` + +## Requirements + +Decimal library requires Go version `>=1.10` + +## Documentation + +http://godoc.org/github.com/shopspring/decimal + + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/shopspring/decimal" +) + +func main() { + price, err := decimal.NewFromString("136.02") + if err != nil { + panic(err) + } + + quantity := decimal.NewFromInt(3) + + fee, _ := decimal.NewFromString(".035") + taxRate, _ := decimal.NewFromString(".08875") + + subtotal := price.Mul(quantity) + + preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1))) + + total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1))) + + fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06 + fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421 + fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375 + fmt.Println("Total:", total) // Total: 459.824961375 + fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875 +} +``` + +## Alternative libraries + +When working with decimal numbers, you might face problems this library is not perfectly suited for. +Fortunately, thanks to the wonderful community we have a dozen other libraries that you can choose from. +Explore other alternatives to find the one that best fits your needs :) + +* [cockroachdb/apd](https://github.com/cockroachdb/apd) - arbitrary precision, mutable and rich API similar to `big.Int`, more performant than this library +* [alpacahq/alpacadecimal](https://github.com/alpacahq/alpacadecimal) - high performance, low precision (12 digits), fully compatible API with this library +* [govalues/decimal](https://github.com/govalues/decimal) - high performance, zero-allocation, low precision (19 digits) +* [greatcloak/decimal](https://github.com/greatcloak/decimal) - fork focusing on billing and e-commerce web application related use cases, includes out-of-the-box BSON marshaling support + +## FAQ + +#### Why don't you just use float64? + +Because float64 (or any binary floating point type, actually) can't represent +numbers such as `0.1` exactly. + +Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that +it prints out `10`, but it actually prints `9.999999999999831`. Over time, +these small errors can really add up! + +#### Why don't you just use big.Rat? + +big.Rat is fine for representing rational numbers, but Decimal is better for +representing money. Why? Here's a (contrived) example: + +Let's say you use big.Rat, and you have two numbers, x and y, both +representing 1/3, and you have `z = 1 - x - y = 1/3`. If you print each one +out, the string output has to stop somewhere (let's say it stops at 3 decimal +digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did +the other 0.001 go? + +Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE + +With Decimal, the strings being printed out represent the number exactly. So, +if you have `x = y = 1/3` (with precision 3), they will actually be equal to +0.333, and when you do `z = 1 - x - y`, `z` will be equal to .334. No money is +unaccounted for! + +You still have to be careful. If you want to split a number `N` 3 ways, you +can't just send `N/3` to three different people. You have to pick one to send +`N - (2/3*N)` to. That person will receive the fraction of a penny remainder. + +But, it is much easier to be careful with Decimal than with big.Rat. + +#### Why isn't the API similar to big.Int's? + +big.Int's API is built to reduce the number of memory allocations for maximal +performance. This makes sense for its use-case, but the trade-off is that the +API is awkward and easy to misuse. + +For example, to add two big.Ints, you do: `z := new(big.Int).Add(x, y)`. A +developer unfamiliar with this API might try to do `z := a.Add(a, b)`. This +modifies `a` and sets `z` as an alias for `a`, which they might not expect. It +also modifies any other aliases to `a`. + +Here's an example of the subtle bugs you can introduce with big.Int's API: +https://play.golang.org/p/x2R_78pa8r + +In contrast, it's difficult to make such mistakes with decimal. Decimals +behave like other go numbers types: even though `a = b` will not deep copy +`b` into `a`, it is impossible to modify a Decimal, since all Decimal methods +return new Decimals and do not modify the originals. The downside is that +this causes extra allocations, so Decimal is less performant. My assumption +is that if you're using Decimals, you probably care more about correctness +than performance. + +## License + +The MIT License (MIT) + +This is a heavily modified fork of [fpd.Decimal](https://github.com/oguzbilgic/fpd), which was also released under the MIT License. diff --git a/vendor/github.com/shopspring/decimal/const.go b/vendor/github.com/shopspring/decimal/const.go new file mode 100644 index 0000000..e5d6fa8 --- /dev/null +++ b/vendor/github.com/shopspring/decimal/const.go @@ -0,0 +1,63 @@ +package decimal + +import ( + "strings" +) + +const ( + strLn10 = "2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286248633409525465082806756666287369098781689482907208325554680843799894826233198528393505308965377732628846163366222287698219886746543667474404243274365155048934314939391479619404400222105101714174800368808401264708068556774321622835522011480466371565912137345074785694768346361679210180644507064800027750268491674655058685693567342067058113642922455440575892572420824131469568901675894025677631135691929203337658714166023010570308963457207544037084746994016826928280848118428931484852494864487192780967627127577539702766860595249671667418348570442250719796500471495105049221477656763693866297697952211071826454973477266242570942932258279850258550978526538320760672631716430950599508780752371033310119785754733154142180842754386359177811705430982748238504564801909561029929182431823752535770975053956518769751037497088869218020518933950723853920514463419726528728696511086257149219884997874887377134568620916705849807828059751193854445009978131146915934666241071846692310107598438319191292230792503747298650929009880391941702654416816335727555703151596113564846546190897042819763365836983716328982174407366009162177850541779276367731145041782137660111010731042397832521894898817597921798666394319523936855916447118246753245630912528778330963604262982153040874560927760726641354787576616262926568298704957954913954918049209069438580790032763017941503117866862092408537949861264933479354871737451675809537088281067452440105892444976479686075120275724181874989395971643105518848195288330746699317814634930000321200327765654130472621883970596794457943468343218395304414844803701305753674262153675579814770458031413637793236291560128185336498466942261465206459942072917119370602444929358037007718981097362533224548366988505528285966192805098447175198503666680874970496982273220244823343097169111136813588418696549323714996941979687803008850408979618598756579894836445212043698216415292987811742973332588607915912510967187510929248475023930572665446276200923068791518135803477701295593646298412366497023355174586195564772461857717369368404676577047874319780573853271810933883496338813069945569399346101090745616033312247949360455361849123333063704751724871276379140924398331810164737823379692265637682071706935846394531616949411701841938119405416449466111274712819705817783293841742231409930022911502362192186723337268385688273533371925103412930705632544426611429765388301822384091026198582888433587455960453004548370789052578473166283701953392231047527564998119228742789713715713228319641003422124210082180679525276689858180956119208391760721080919923461516952599099473782780648128058792731993893453415320185969711021407542282796298237068941764740642225757212455392526179373652434440560595336591539160312524480149313234572453879524389036839236450507881731359711238145323701508413491122324390927681724749607955799151363982881058285740538000653371655553014196332241918087621018204919492651483892" +) + +var ( + ln10 = newConstApproximation(strLn10) +) + +type constApproximation struct { + exact Decimal + approximations []Decimal +} + +func newConstApproximation(value string) constApproximation { + parts := strings.Split(value, ".") + coeff, fractional := parts[0], parts[1] + + coeffLen := len(coeff) + maxPrecision := len(fractional) + + var approximations []Decimal + for p := 1; p < maxPrecision; p *= 2 { + r := RequireFromString(value[:coeffLen+p]) + approximations = append(approximations, r) + } + + return constApproximation{ + RequireFromString(value), + approximations, + } +} + +// Returns the smallest approximation available that's at least as precise +// as the passed precision (places after decimal point), i.e. Floor[ log2(precision) ] + 1 +func (c constApproximation) withPrecision(precision int32) Decimal { + i := 0 + + if precision >= 1 { + i++ + } + + for precision >= 16 { + precision /= 16 + i += 4 + } + + for precision >= 2 { + precision /= 2 + i++ + } + + if i >= len(c.approximations) { + return c.exact + } + + return c.approximations[i] +} diff --git a/vendor/github.com/shopspring/decimal/decimal-go.go b/vendor/github.com/shopspring/decimal/decimal-go.go new file mode 100644 index 0000000..9958d69 --- /dev/null +++ b/vendor/github.com/shopspring/decimal/decimal-go.go @@ -0,0 +1,415 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package decimal + +type decimal struct { + d [800]byte // digits, big-endian representation + nd int // number of digits used + dp int // decimal point + neg bool // negative flag + trunc bool // discarded nonzero digits beyond d[:nd] +} + +func (a *decimal) String() string { + n := 10 + a.nd + if a.dp > 0 { + n += a.dp + } + if a.dp < 0 { + n += -a.dp + } + + buf := make([]byte, n) + w := 0 + switch { + case a.nd == 0: + return "0" + + case a.dp <= 0: + // zeros fill space between decimal point and digits + buf[w] = '0' + w++ + buf[w] = '.' + w++ + w += digitZero(buf[w : w+-a.dp]) + w += copy(buf[w:], a.d[0:a.nd]) + + case a.dp < a.nd: + // decimal point in middle of digits + w += copy(buf[w:], a.d[0:a.dp]) + buf[w] = '.' + w++ + w += copy(buf[w:], a.d[a.dp:a.nd]) + + default: + // zeros fill space between digits and decimal point + w += copy(buf[w:], a.d[0:a.nd]) + w += digitZero(buf[w : w+a.dp-a.nd]) + } + return string(buf[0:w]) +} + +func digitZero(dst []byte) int { + for i := range dst { + dst[i] = '0' + } + return len(dst) +} + +// trim trailing zeros from number. +// (They are meaningless; the decimal point is tracked +// independent of the number of digits.) +func trim(a *decimal) { + for a.nd > 0 && a.d[a.nd-1] == '0' { + a.nd-- + } + if a.nd == 0 { + a.dp = 0 + } +} + +// Assign v to a. +func (a *decimal) Assign(v uint64) { + var buf [24]byte + + // Write reversed decimal in buf. + n := 0 + for v > 0 { + v1 := v / 10 + v -= 10 * v1 + buf[n] = byte(v + '0') + n++ + v = v1 + } + + // Reverse again to produce forward decimal in a.d. + a.nd = 0 + for n--; n >= 0; n-- { + a.d[a.nd] = buf[n] + a.nd++ + } + a.dp = a.nd + trim(a) +} + +// Maximum shift that we can do in one pass without overflow. +// A uint has 32 or 64 bits, and we have to be able to accommodate 9<> 63) +const maxShift = uintSize - 4 + +// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow. +func rightShift(a *decimal, k uint) { + r := 0 // read pointer + w := 0 // write pointer + + // Pick up enough leading digits to cover first shift. + var n uint + for ; n>>k == 0; r++ { + if r >= a.nd { + if n == 0 { + // a == 0; shouldn't get here, but handle anyway. + a.nd = 0 + return + } + for n>>k == 0 { + n = n * 10 + r++ + } + break + } + c := uint(a.d[r]) + n = n*10 + c - '0' + } + a.dp -= r - 1 + + var mask uint = (1 << k) - 1 + + // Pick up a digit, put down a digit. + for ; r < a.nd; r++ { + c := uint(a.d[r]) + dig := n >> k + n &= mask + a.d[w] = byte(dig + '0') + w++ + n = n*10 + c - '0' + } + + // Put down extra digits. + for n > 0 { + dig := n >> k + n &= mask + if w < len(a.d) { + a.d[w] = byte(dig + '0') + w++ + } else if dig > 0 { + a.trunc = true + } + n = n * 10 + } + + a.nd = w + trim(a) +} + +// Cheat sheet for left shift: table indexed by shift count giving +// number of new digits that will be introduced by that shift. +// +// For example, leftcheats[4] = {2, "625"}. That means that +// if we are shifting by 4 (multiplying by 16), it will add 2 digits +// when the string prefix is "625" through "999", and one fewer digit +// if the string prefix is "000" through "624". +// +// Credit for this trick goes to Ken. + +type leftCheat struct { + delta int // number of new digits + cutoff string // minus one digit if original < a. +} + +var leftcheats = []leftCheat{ + // Leading digits of 1/2^i = 5^i. + // 5^23 is not an exact 64-bit floating point number, + // so have to use bc for the math. + // Go up to 60 to be large enough for 32bit and 64bit platforms. + /* + seq 60 | sed 's/^/5^/' | bc | + awk 'BEGIN{ print "\t{ 0, \"\" }," } + { + log2 = log(2)/log(10) + printf("\t{ %d, \"%s\" },\t// * %d\n", + int(log2*NR+1), $0, 2**NR) + }' + */ + {0, ""}, + {1, "5"}, // * 2 + {1, "25"}, // * 4 + {1, "125"}, // * 8 + {2, "625"}, // * 16 + {2, "3125"}, // * 32 + {2, "15625"}, // * 64 + {3, "78125"}, // * 128 + {3, "390625"}, // * 256 + {3, "1953125"}, // * 512 + {4, "9765625"}, // * 1024 + {4, "48828125"}, // * 2048 + {4, "244140625"}, // * 4096 + {4, "1220703125"}, // * 8192 + {5, "6103515625"}, // * 16384 + {5, "30517578125"}, // * 32768 + {5, "152587890625"}, // * 65536 + {6, "762939453125"}, // * 131072 + {6, "3814697265625"}, // * 262144 + {6, "19073486328125"}, // * 524288 + {7, "95367431640625"}, // * 1048576 + {7, "476837158203125"}, // * 2097152 + {7, "2384185791015625"}, // * 4194304 + {7, "11920928955078125"}, // * 8388608 + {8, "59604644775390625"}, // * 16777216 + {8, "298023223876953125"}, // * 33554432 + {8, "1490116119384765625"}, // * 67108864 + {9, "7450580596923828125"}, // * 134217728 + {9, "37252902984619140625"}, // * 268435456 + {9, "186264514923095703125"}, // * 536870912 + {10, "931322574615478515625"}, // * 1073741824 + {10, "4656612873077392578125"}, // * 2147483648 + {10, "23283064365386962890625"}, // * 4294967296 + {10, "116415321826934814453125"}, // * 8589934592 + {11, "582076609134674072265625"}, // * 17179869184 + {11, "2910383045673370361328125"}, // * 34359738368 + {11, "14551915228366851806640625"}, // * 68719476736 + {12, "72759576141834259033203125"}, // * 137438953472 + {12, "363797880709171295166015625"}, // * 274877906944 + {12, "1818989403545856475830078125"}, // * 549755813888 + {13, "9094947017729282379150390625"}, // * 1099511627776 + {13, "45474735088646411895751953125"}, // * 2199023255552 + {13, "227373675443232059478759765625"}, // * 4398046511104 + {13, "1136868377216160297393798828125"}, // * 8796093022208 + {14, "5684341886080801486968994140625"}, // * 17592186044416 + {14, "28421709430404007434844970703125"}, // * 35184372088832 + {14, "142108547152020037174224853515625"}, // * 70368744177664 + {15, "710542735760100185871124267578125"}, // * 140737488355328 + {15, "3552713678800500929355621337890625"}, // * 281474976710656 + {15, "17763568394002504646778106689453125"}, // * 562949953421312 + {16, "88817841970012523233890533447265625"}, // * 1125899906842624 + {16, "444089209850062616169452667236328125"}, // * 2251799813685248 + {16, "2220446049250313080847263336181640625"}, // * 4503599627370496 + {16, "11102230246251565404236316680908203125"}, // * 9007199254740992 + {17, "55511151231257827021181583404541015625"}, // * 18014398509481984 + {17, "277555756156289135105907917022705078125"}, // * 36028797018963968 + {17, "1387778780781445675529539585113525390625"}, // * 72057594037927936 + {18, "6938893903907228377647697925567626953125"}, // * 144115188075855872 + {18, "34694469519536141888238489627838134765625"}, // * 288230376151711744 + {18, "173472347597680709441192448139190673828125"}, // * 576460752303423488 + {19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976 +} + +// Is the leading prefix of b lexicographically less than s? +func prefixIsLessThan(b []byte, s string) bool { + for i := 0; i < len(s); i++ { + if i >= len(b) { + return true + } + if b[i] != s[i] { + return b[i] < s[i] + } + } + return false +} + +// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow. +func leftShift(a *decimal, k uint) { + delta := leftcheats[k].delta + if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { + delta-- + } + + r := a.nd // read index + w := a.nd + delta // write index + + // Pick up a digit, put down a digit. + var n uint + for r--; r >= 0; r-- { + n += (uint(a.d[r]) - '0') << k + quo := n / 10 + rem := n - 10*quo + w-- + if w < len(a.d) { + a.d[w] = byte(rem + '0') + } else if rem != 0 { + a.trunc = true + } + n = quo + } + + // Put down extra digits. + for n > 0 { + quo := n / 10 + rem := n - 10*quo + w-- + if w < len(a.d) { + a.d[w] = byte(rem + '0') + } else if rem != 0 { + a.trunc = true + } + n = quo + } + + a.nd += delta + if a.nd >= len(a.d) { + a.nd = len(a.d) + } + a.dp += delta + trim(a) +} + +// Binary shift left (k > 0) or right (k < 0). +func (a *decimal) Shift(k int) { + switch { + case a.nd == 0: + // nothing to do: a == 0 + case k > 0: + for k > maxShift { + leftShift(a, maxShift) + k -= maxShift + } + leftShift(a, uint(k)) + case k < 0: + for k < -maxShift { + rightShift(a, maxShift) + k += maxShift + } + rightShift(a, uint(-k)) + } +} + +// If we chop a at nd digits, should we round up? +func shouldRoundUp(a *decimal, nd int) bool { + if nd < 0 || nd >= a.nd { + return false + } + if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even + // if we truncated, a little higher than what's recorded - always round up + if a.trunc { + return true + } + return nd > 0 && (a.d[nd-1]-'0')%2 != 0 + } + // not halfway - digit tells all + return a.d[nd] >= '5' +} + +// Round a to nd digits (or fewer). +// If nd is zero, it means we're rounding +// just to the left of the digits, as in +// 0.09 -> 0.1. +func (a *decimal) Round(nd int) { + if nd < 0 || nd >= a.nd { + return + } + if shouldRoundUp(a, nd) { + a.RoundUp(nd) + } else { + a.RoundDown(nd) + } +} + +// Round a down to nd digits (or fewer). +func (a *decimal) RoundDown(nd int) { + if nd < 0 || nd >= a.nd { + return + } + a.nd = nd + trim(a) +} + +// Round a up to nd digits (or fewer). +func (a *decimal) RoundUp(nd int) { + if nd < 0 || nd >= a.nd { + return + } + + // round up + for i := nd - 1; i >= 0; i-- { + c := a.d[i] + if c < '9' { // can stop after this digit + a.d[i]++ + a.nd = i + 1 + return + } + } + + // Number is all 9s. + // Change to single 1 with adjusted decimal point. + a.d[0] = '1' + a.nd = 1 + a.dp++ +} + +// Extract integer part, rounded appropriately. +// No guarantees about overflow. +func (a *decimal) RoundedInteger() uint64 { + if a.dp > 20 { + return 0xFFFFFFFFFFFFFFFF + } + var i int + n := uint64(0) + for i = 0; i < a.dp && i < a.nd; i++ { + n = n*10 + uint64(a.d[i]-'0') + } + for ; i < a.dp; i++ { + n *= 10 + } + if shouldRoundUp(a, a.dp) { + n++ + } + return n +} diff --git a/vendor/github.com/shopspring/decimal/decimal.go b/vendor/github.com/shopspring/decimal/decimal.go new file mode 100644 index 0000000..a37a230 --- /dev/null +++ b/vendor/github.com/shopspring/decimal/decimal.go @@ -0,0 +1,2339 @@ +// Package decimal implements an arbitrary precision fixed-point decimal. +// +// The zero-value of a Decimal is 0, as you would expect. +// +// The best way to create a new Decimal is to use decimal.NewFromString, ex: +// +// n, err := decimal.NewFromString("-123.4567") +// n.String() // output: "-123.4567" +// +// To use Decimal as part of a struct: +// +// type StructName struct { +// Number Decimal +// } +// +// Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point. +package decimal + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + "math" + "math/big" + "regexp" + "strconv" + "strings" +) + +// DivisionPrecision is the number of decimal places in the result when it +// doesn't divide exactly. +// +// Example: +// +// d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) +// d1.String() // output: "0.6666666666666667" +// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000)) +// d2.String() // output: "0.0000666666666667" +// d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3)) +// d3.String() // output: "6666.6666666666666667" +// decimal.DivisionPrecision = 3 +// d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) +// d4.String() // output: "0.667" +var DivisionPrecision = 16 + +// PowPrecisionNegativeExponent specifies the maximum precision of the result (digits after decimal point) +// when calculating decimal power. Only used for cases where the exponent is a negative number. +// This constant applies to Pow, PowInt32 and PowBigInt methods, PowWithPrecision method is not constrained by it. +// +// Example: +// +// d1, err := decimal.NewFromFloat(15.2).PowInt32(-2) +// d1.String() // output: "0.0043282548476454" +// +// decimal.PowPrecisionNegativeExponent = 24 +// d2, err := decimal.NewFromFloat(15.2).PowInt32(-2) +// d2.String() // output: "0.004328254847645429362881" +var PowPrecisionNegativeExponent = 16 + +// MarshalJSONWithoutQuotes should be set to true if you want the decimal to +// be JSON marshaled as a number, instead of as a string. +// WARNING: this is dangerous for decimals with many digits, since many JSON +// unmarshallers (ex: Javascript's) will unmarshal JSON numbers to IEEE 754 +// double-precision floating point numbers, which means you can potentially +// silently lose precision. +var MarshalJSONWithoutQuotes = false + +// ExpMaxIterations specifies the maximum number of iterations needed to calculate +// precise natural exponent value using ExpHullAbrham method. +var ExpMaxIterations = 1000 + +// Zero constant, to make computations faster. +// Zero should never be compared with == or != directly, please use decimal.Equal or decimal.Cmp instead. +var Zero = New(0, 1) + +var zeroInt = big.NewInt(0) +var oneInt = big.NewInt(1) +var twoInt = big.NewInt(2) +var fourInt = big.NewInt(4) +var fiveInt = big.NewInt(5) +var tenInt = big.NewInt(10) +var twentyInt = big.NewInt(20) + +var factorials = []Decimal{New(1, 0)} + +// Decimal represents a fixed-point decimal. It is immutable. +// number = value * 10 ^ exp +type Decimal struct { + value *big.Int + + // NOTE(vadim): this must be an int32, because we cast it to float64 during + // calculations. If exp is 64 bit, we might lose precision. + // If we cared about being able to represent every possible decimal, we + // could make exp a *big.Int but it would hurt performance and numbers + // like that are unrealistic. + exp int32 +} + +// New returns a new fixed-point decimal, value * 10 ^ exp. +func New(value int64, exp int32) Decimal { + return Decimal{ + value: big.NewInt(value), + exp: exp, + } +} + +// NewFromInt converts an int64 to Decimal. +// +// Example: +// +// NewFromInt(123).String() // output: "123" +// NewFromInt(-10).String() // output: "-10" +func NewFromInt(value int64) Decimal { + return Decimal{ + value: big.NewInt(value), + exp: 0, + } +} + +// NewFromInt32 converts an int32 to Decimal. +// +// Example: +// +// NewFromInt(123).String() // output: "123" +// NewFromInt(-10).String() // output: "-10" +func NewFromInt32(value int32) Decimal { + return Decimal{ + value: big.NewInt(int64(value)), + exp: 0, + } +} + +// NewFromUint64 converts an uint64 to Decimal. +// +// Example: +// +// NewFromUint64(123).String() // output: "123" +func NewFromUint64(value uint64) Decimal { + return Decimal{ + value: new(big.Int).SetUint64(value), + exp: 0, + } +} + +// NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp +func NewFromBigInt(value *big.Int, exp int32) Decimal { + return Decimal{ + value: new(big.Int).Set(value), + exp: exp, + } +} + +// NewFromBigRat returns a new Decimal from a big.Rat. The numerator and +// denominator are divided and rounded to the given precision. +// +// Example: +// +// d1 := NewFromBigRat(big.NewRat(0, 1), 0) // output: "0" +// d2 := NewFromBigRat(big.NewRat(4, 5), 1) // output: "0.8" +// d3 := NewFromBigRat(big.NewRat(1000, 3), 3) // output: "333.333" +// d4 := NewFromBigRat(big.NewRat(2, 7), 4) // output: "0.2857" +func NewFromBigRat(value *big.Rat, precision int32) Decimal { + return Decimal{ + value: new(big.Int).Set(value.Num()), + exp: 0, + }.DivRound(Decimal{ + value: new(big.Int).Set(value.Denom()), + exp: 0, + }, precision) +} + +// NewFromString returns a new Decimal from a string representation. +// Trailing zeroes are not trimmed. +// +// Example: +// +// d, err := NewFromString("-123.45") +// d2, err := NewFromString(".0001") +// d3, err := NewFromString("1.47000") +func NewFromString(value string) (Decimal, error) { + originalInput := value + var intString string + var exp int64 + + // Check if number is using scientific notation + eIndex := strings.IndexAny(value, "Ee") + if eIndex != -1 { + expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32) + if err != nil { + if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { + return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value) + } + return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent is not numeric", value) + } + value = value[:eIndex] + exp = expInt + } + + pIndex := -1 + vLen := len(value) + for i := 0; i < vLen; i++ { + if value[i] == '.' { + if pIndex > -1 { + return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value) + } + pIndex = i + } + } + + if pIndex == -1 { + // There is no decimal point, we can just parse the original string as + // an int + intString = value + } else { + if pIndex+1 < vLen { + intString = value[:pIndex] + value[pIndex+1:] + } else { + intString = value[:pIndex] + } + expInt := -len(value[pIndex+1:]) + exp += int64(expInt) + } + + var dValue *big.Int + // strconv.ParseInt is faster than new(big.Int).SetString so this is just a shortcut for strings we know won't overflow + if len(intString) <= 18 { + parsed64, err := strconv.ParseInt(intString, 10, 64) + if err != nil { + return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) + } + dValue = big.NewInt(parsed64) + } else { + dValue = new(big.Int) + _, ok := dValue.SetString(intString, 10) + if !ok { + return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) + } + } + + if exp < math.MinInt32 || exp > math.MaxInt32 { + // NOTE(vadim): I doubt a string could realistically be this long + return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput) + } + + return Decimal{ + value: dValue, + exp: int32(exp), + }, nil +} + +// NewFromFormattedString returns a new Decimal from a formatted string representation. +// The second argument - replRegexp, is a regular expression that is used to find characters that should be +// removed from given decimal string representation. All matched characters will be replaced with an empty string. +// +// Example: +// +// r := regexp.MustCompile("[$,]") +// d1, err := NewFromFormattedString("$5,125.99", r) +// +// r2 := regexp.MustCompile("[_]") +// d2, err := NewFromFormattedString("1_000_000", r2) +// +// r3 := regexp.MustCompile("[USD\\s]") +// d3, err := NewFromFormattedString("5000 USD", r3) +func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error) { + parsedValue := replRegexp.ReplaceAllString(value, "") + d, err := NewFromString(parsedValue) + if err != nil { + return Decimal{}, err + } + return d, nil +} + +// RequireFromString returns a new Decimal from a string representation +// or panics if NewFromString had returned an error. +// +// Example: +// +// d := RequireFromString("-123.45") +// d2 := RequireFromString(".0001") +func RequireFromString(value string) Decimal { + dec, err := NewFromString(value) + if err != nil { + panic(err) + } + return dec +} + +// NewFromFloat converts a float64 to Decimal. +// +// The converted number will contain the number of significant digits that can be +// represented in a float with reliable roundtrip. +// This is typically 15 digits, but may be more in some cases. +// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information. +// +// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms. +// +// NOTE: this will panic on NaN, +/-inf +func NewFromFloat(value float64) Decimal { + if value == 0 { + return New(0, 0) + } + return newFromFloat(value, math.Float64bits(value), &float64info) +} + +// NewFromFloat32 converts a float32 to Decimal. +// +// The converted number will contain the number of significant digits that can be +// represented in a float with reliable roundtrip. +// This is typically 6-8 digits depending on the input. +// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information. +// +// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms. +// +// NOTE: this will panic on NaN, +/-inf +func NewFromFloat32(value float32) Decimal { + if value == 0 { + return New(0, 0) + } + // XOR is workaround for https://github.com/golang/go/issues/26285 + a := math.Float32bits(value) ^ 0x80808080 + return newFromFloat(float64(value), uint64(a)^0x80808080, &float32info) +} + +func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal { + if math.IsNaN(val) || math.IsInf(val, 0) { + panic(fmt.Sprintf("Cannot create a Decimal from %v", val)) + } + exp := int(bits>>flt.mantbits) & (1<>(flt.expbits+flt.mantbits) != 0 + + roundShortest(&d, mant, exp, flt) + // If less than 19 digits, we can do calculation in an int64. + if d.nd < 19 { + tmp := int64(0) + m := int64(1) + for i := d.nd - 1; i >= 0; i-- { + tmp += m * int64(d.d[i]-'0') + m *= 10 + } + if d.neg { + tmp *= -1 + } + return Decimal{value: big.NewInt(tmp), exp: int32(d.dp) - int32(d.nd)} + } + dValue := new(big.Int) + dValue, ok := dValue.SetString(string(d.d[:d.nd]), 10) + if ok { + return Decimal{value: dValue, exp: int32(d.dp) - int32(d.nd)} + } + + return NewFromFloatWithExponent(val, int32(d.dp)-int32(d.nd)) +} + +// NewFromFloatWithExponent converts a float64 to Decimal, with an arbitrary +// number of fractional digits. +// +// Example: +// +// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46" +func NewFromFloatWithExponent(value float64, exp int32) Decimal { + if math.IsNaN(value) || math.IsInf(value, 0) { + panic(fmt.Sprintf("Cannot create a Decimal from %v", value)) + } + + bits := math.Float64bits(value) + mant := bits & (1<<52 - 1) + exp2 := int32((bits >> 52) & (1<<11 - 1)) + sign := bits >> 63 + + if exp2 == 0 { + // specials + if mant == 0 { + return Decimal{} + } + // subnormal + exp2++ + } else { + // normal + mant |= 1 << 52 + } + + exp2 -= 1023 + 52 + + // normalizing base-2 values + for mant&1 == 0 { + mant = mant >> 1 + exp2++ + } + + // maximum number of fractional base-10 digits to represent 2^N exactly cannot be more than -N if N<0 + if exp < 0 && exp < exp2 { + if exp2 < 0 { + exp = exp2 + } else { + exp = 0 + } + } + + // representing 10^M * 2^N as 5^M * 2^(M+N) + exp2 -= exp + + temp := big.NewInt(1) + dMant := big.NewInt(int64(mant)) + + // applying 5^M + if exp > 0 { + temp = temp.SetInt64(int64(exp)) + temp = temp.Exp(fiveInt, temp, nil) + } else if exp < 0 { + temp = temp.SetInt64(-int64(exp)) + temp = temp.Exp(fiveInt, temp, nil) + dMant = dMant.Mul(dMant, temp) + temp = temp.SetUint64(1) + } + + // applying 2^(M+N) + if exp2 > 0 { + dMant = dMant.Lsh(dMant, uint(exp2)) + } else if exp2 < 0 { + temp = temp.Lsh(temp, uint(-exp2)) + } + + // rounding and downscaling + if exp > 0 || exp2 < 0 { + halfDown := new(big.Int).Rsh(temp, 1) + dMant = dMant.Add(dMant, halfDown) + dMant = dMant.Quo(dMant, temp) + } + + if sign == 1 { + dMant = dMant.Neg(dMant) + } + + return Decimal{ + value: dMant, + exp: exp, + } +} + +// Copy returns a copy of decimal with the same value and exponent, but a different pointer to value. +func (d Decimal) Copy() Decimal { + d.ensureInitialized() + return Decimal{ + value: new(big.Int).Set(d.value), + exp: d.exp, + } +} + +// rescale returns a rescaled version of the decimal. Returned +// decimal may be less precise if the given exponent is bigger +// than the initial exponent of the Decimal. +// NOTE: this will truncate, NOT round +// +// Example: +// +// d := New(12345, -4) +// d2 := d.rescale(-1) +// d3 := d2.rescale(-4) +// println(d1) +// println(d2) +// println(d3) +// +// Output: +// +// 1.2345 +// 1.2 +// 1.2000 +func (d Decimal) rescale(exp int32) Decimal { + d.ensureInitialized() + + if d.exp == exp { + return Decimal{ + new(big.Int).Set(d.value), + d.exp, + } + } + + // NOTE(vadim): must convert exps to float64 before - to prevent overflow + diff := math.Abs(float64(exp) - float64(d.exp)) + value := new(big.Int).Set(d.value) + + expScale := new(big.Int).Exp(tenInt, big.NewInt(int64(diff)), nil) + if exp > d.exp { + value = value.Quo(value, expScale) + } else if exp < d.exp { + value = value.Mul(value, expScale) + } + + return Decimal{ + value: value, + exp: exp, + } +} + +// Abs returns the absolute value of the decimal. +func (d Decimal) Abs() Decimal { + if !d.IsNegative() { + return d + } + d.ensureInitialized() + d2Value := new(big.Int).Abs(d.value) + return Decimal{ + value: d2Value, + exp: d.exp, + } +} + +// Add returns d + d2. +func (d Decimal) Add(d2 Decimal) Decimal { + rd, rd2 := RescalePair(d, d2) + + d3Value := new(big.Int).Add(rd.value, rd2.value) + return Decimal{ + value: d3Value, + exp: rd.exp, + } +} + +// Sub returns d - d2. +func (d Decimal) Sub(d2 Decimal) Decimal { + rd, rd2 := RescalePair(d, d2) + + d3Value := new(big.Int).Sub(rd.value, rd2.value) + return Decimal{ + value: d3Value, + exp: rd.exp, + } +} + +// Neg returns -d. +func (d Decimal) Neg() Decimal { + d.ensureInitialized() + val := new(big.Int).Neg(d.value) + return Decimal{ + value: val, + exp: d.exp, + } +} + +// Mul returns d * d2. +func (d Decimal) Mul(d2 Decimal) Decimal { + d.ensureInitialized() + d2.ensureInitialized() + + expInt64 := int64(d.exp) + int64(d2.exp) + if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 { + // NOTE(vadim): better to panic than give incorrect results, as + // Decimals are usually used for money + panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64)) + } + + d3Value := new(big.Int).Mul(d.value, d2.value) + return Decimal{ + value: d3Value, + exp: int32(expInt64), + } +} + +// Shift shifts the decimal in base 10. +// It shifts left when shift is positive and right if shift is negative. +// In simpler terms, the given value for shift is added to the exponent +// of the decimal. +func (d Decimal) Shift(shift int32) Decimal { + d.ensureInitialized() + return Decimal{ + value: new(big.Int).Set(d.value), + exp: d.exp + shift, + } +} + +// Div returns d / d2. If it doesn't divide exactly, the result will have +// DivisionPrecision digits after the decimal point. +func (d Decimal) Div(d2 Decimal) Decimal { + return d.DivRound(d2, int32(DivisionPrecision)) +} + +// QuoRem does division with remainder +// d.QuoRem(d2,precision) returns quotient q and remainder r such that +// +// d = d2 * q + r, q an integer multiple of 10^(-precision) +// 0 <= r < abs(d2) * 10 ^(-precision) if d>=0 +// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0 +// +// Note that precision<0 is allowed as input. +func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) { + d.ensureInitialized() + d2.ensureInitialized() + if d2.value.Sign() == 0 { + panic("decimal division by 0") + } + scale := -precision + e := int64(d.exp) - int64(d2.exp) - int64(scale) + if e > math.MaxInt32 || e < math.MinInt32 { + panic("overflow in decimal QuoRem") + } + var aa, bb, expo big.Int + var scalerest int32 + // d = a 10^ea + // d2 = b 10^eb + if e < 0 { + aa = *d.value + expo.SetInt64(-e) + bb.Exp(tenInt, &expo, nil) + bb.Mul(d2.value, &bb) + scalerest = d.exp + // now aa = a + // bb = b 10^(scale + eb - ea) + } else { + expo.SetInt64(e) + aa.Exp(tenInt, &expo, nil) + aa.Mul(d.value, &aa) + bb = *d2.value + scalerest = scale + d2.exp + // now aa = a ^ (ea - eb - scale) + // bb = b + } + var q, r big.Int + q.QuoRem(&aa, &bb, &r) + dq := Decimal{value: &q, exp: scale} + dr := Decimal{value: &r, exp: scalerest} + return dq, dr +} + +// DivRound divides and rounds to a given precision +// i.e. to an integer multiple of 10^(-precision) +// +// for a positive quotient digit 5 is rounded up, away from 0 +// if the quotient is negative then digit 5 is rounded down, away from 0 +// +// Note that precision<0 is allowed as input. +func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal { + // QuoRem already checks initialization + q, r := d.QuoRem(d2, precision) + // the actual rounding decision is based on comparing r*10^precision and d2/2 + // instead compare 2 r 10 ^precision and d2 + var rv2 big.Int + rv2.Abs(r.value) + rv2.Lsh(&rv2, 1) + // now rv2 = abs(r.value) * 2 + r2 := Decimal{value: &rv2, exp: r.exp + precision} + // r2 is now 2 * r * 10 ^ precision + var c = r2.Cmp(d2.Abs()) + + if c < 0 { + return q + } + + if d.value.Sign()*d2.value.Sign() < 0 { + return q.Sub(New(1, -precision)) + } + + return q.Add(New(1, -precision)) +} + +// Mod returns d % d2. +func (d Decimal) Mod(d2 Decimal) Decimal { + _, r := d.QuoRem(d2, 0) + return r +} + +// Pow returns d to the power of d2. +// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point. +// +// Pow returns 0 (zero-value of Decimal) instead of error for power operation edge cases, to handle those edge cases use PowWithPrecision +// Edge cases not handled by Pow: +// - 0 ** 0 => undefined value +// - 0 ** y, where y < 0 => infinity +// - x ** y, where x < 0 and y is non-integer decimal => imaginary value +// +// Example: +// +// d1 := decimal.NewFromFloat(4.0) +// d2 := decimal.NewFromFloat(4.0) +// res1 := d1.Pow(d2) +// res1.String() // output: "256" +// +// d3 := decimal.NewFromFloat(5.0) +// d4 := decimal.NewFromFloat(5.73) +// res2 := d3.Pow(d4) +// res2.String() // output: "10118.08037125" +func (d Decimal) Pow(d2 Decimal) Decimal { + baseSign := d.Sign() + expSign := d2.Sign() + + if baseSign == 0 { + if expSign == 0 { + return Decimal{} + } + if expSign == 1 { + return Decimal{zeroInt, 0} + } + if expSign == -1 { + return Decimal{} + } + } + + if expSign == 0 { + return Decimal{oneInt, 0} + } + + // TODO: optimize extraction of fractional part + one := Decimal{oneInt, 0} + expIntPart, expFracPart := d2.QuoRem(one, 0) + + if baseSign == -1 && !expFracPart.IsZero() { + return Decimal{} + } + + intPartPow, _ := d.PowBigInt(expIntPart.value) + + // if exponent is an integer we don't need to calculate d1**frac(d2) + if expFracPart.value.Sign() == 0 { + return intPartPow + } + + // TODO: optimize NumDigits for more performant precision adjustment + digitsBase := d.NumDigits() + digitsExponent := d2.NumDigits() + + precision := digitsBase + + if digitsExponent > precision { + precision += digitsExponent + } + + precision += 6 + + // Calculate x ** frac(y), where + // x ** frac(y) = exp(ln(x ** frac(y)) = exp(ln(x) * frac(y)) + fracPartPow, err := d.Abs().Ln(-d.exp + int32(precision)) + if err != nil { + return Decimal{} + } + + fracPartPow = fracPartPow.Mul(expFracPart) + + fracPartPow, err = fracPartPow.ExpTaylor(-d.exp + int32(precision)) + if err != nil { + return Decimal{} + } + + // Join integer and fractional part, + // base ** (expBase + expFrac) = base ** expBase * base ** expFrac + res := intPartPow.Mul(fracPartPow) + + return res +} + +// PowWithPrecision returns d to the power of d2. +// Precision parameter specifies minimum precision of the result (digits after decimal point). +// Returned decimal is not rounded to 'precision' places after decimal point. +// +// PowWithPrecision returns error when: +// - 0 ** 0 => undefined value +// - 0 ** y, where y < 0 => infinity +// - x ** y, where x < 0 and y is non-integer decimal => imaginary value +// +// Example: +// +// d1 := decimal.NewFromFloat(4.0) +// d2 := decimal.NewFromFloat(4.0) +// res1, err := d1.PowWithPrecision(d2, 2) +// res1.String() // output: "256" +// +// d3 := decimal.NewFromFloat(5.0) +// d4 := decimal.NewFromFloat(5.73) +// res2, err := d3.PowWithPrecision(d4, 5) +// res2.String() // output: "10118.080371595015625" +// +// d5 := decimal.NewFromFloat(-3.0) +// d6 := decimal.NewFromFloat(-6.0) +// res3, err := d5.PowWithPrecision(d6, 10) +// res3.String() // output: "0.0013717421" +func (d Decimal) PowWithPrecision(d2 Decimal, precision int32) (Decimal, error) { + baseSign := d.Sign() + expSign := d2.Sign() + + if baseSign == 0 { + if expSign == 0 { + return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0") + } + if expSign == 1 { + return Decimal{zeroInt, 0}, nil + } + if expSign == -1 { + return Decimal{}, fmt.Errorf("cannot represent infinity value of 0 ** y, where y < 0") + } + } + + if expSign == 0 { + return Decimal{oneInt, 0}, nil + } + + // TODO: optimize extraction of fractional part + one := Decimal{oneInt, 0} + expIntPart, expFracPart := d2.QuoRem(one, 0) + + if baseSign == -1 && !expFracPart.IsZero() { + return Decimal{}, fmt.Errorf("cannot represent imaginary value of x ** y, where x < 0 and y is non-integer decimal") + } + + intPartPow, _ := d.powBigIntWithPrecision(expIntPart.value, precision) + + // if exponent is an integer we don't need to calculate d1**frac(d2) + if expFracPart.value.Sign() == 0 { + return intPartPow, nil + } + + // TODO: optimize NumDigits for more performant precision adjustment + digitsBase := d.NumDigits() + digitsExponent := d2.NumDigits() + + if int32(digitsBase) > precision { + precision = int32(digitsBase) + } + if int32(digitsExponent) > precision { + precision += int32(digitsExponent) + } + // increase precision by 10 to compensate for errors in further calculations + precision += 10 + + // Calculate x ** frac(y), where + // x ** frac(y) = exp(ln(x ** frac(y)) = exp(ln(x) * frac(y)) + fracPartPow, err := d.Abs().Ln(precision) + if err != nil { + return Decimal{}, err + } + + fracPartPow = fracPartPow.Mul(expFracPart) + + fracPartPow, err = fracPartPow.ExpTaylor(precision) + if err != nil { + return Decimal{}, err + } + + // Join integer and fractional part, + // base ** (expBase + expFrac) = base ** expBase * base ** expFrac + res := intPartPow.Mul(fracPartPow) + + return res, nil +} + +// PowInt32 returns d to the power of exp, where exp is int32. +// Only returns error when d and exp is 0, thus result is undefined. +// +// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point. +// +// Example: +// +// d1, err := decimal.NewFromFloat(4.0).PowInt32(4) +// d1.String() // output: "256" +// +// d2, err := decimal.NewFromFloat(3.13).PowInt32(5) +// d2.String() // output: "300.4150512793" +func (d Decimal) PowInt32(exp int32) (Decimal, error) { + if d.IsZero() && exp == 0 { + return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0") + } + + isExpNeg := exp < 0 + exp = abs(exp) + + n, result := d, New(1, 0) + + for exp > 0 { + if exp%2 == 1 { + result = result.Mul(n) + } + exp /= 2 + + if exp > 0 { + n = n.Mul(n) + } + } + + if isExpNeg { + return New(1, 0).DivRound(result, int32(PowPrecisionNegativeExponent)), nil + } + + return result, nil +} + +// PowBigInt returns d to the power of exp, where exp is big.Int. +// Only returns error when d and exp is 0, thus result is undefined. +// +// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point. +// +// Example: +// +// d1, err := decimal.NewFromFloat(3.0).PowBigInt(big.NewInt(3)) +// d1.String() // output: "27" +// +// d2, err := decimal.NewFromFloat(629.25).PowBigInt(big.NewInt(5)) +// d2.String() // output: "98654323103449.5673828125" +func (d Decimal) PowBigInt(exp *big.Int) (Decimal, error) { + return d.powBigIntWithPrecision(exp, int32(PowPrecisionNegativeExponent)) +} + +func (d Decimal) powBigIntWithPrecision(exp *big.Int, precision int32) (Decimal, error) { + if d.IsZero() && exp.Sign() == 0 { + return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0") + } + + tmpExp := new(big.Int).Set(exp) + isExpNeg := exp.Sign() < 0 + + if isExpNeg { + tmpExp.Abs(tmpExp) + } + + n, result := d, New(1, 0) + + for tmpExp.Sign() > 0 { + if tmpExp.Bit(0) == 1 { + result = result.Mul(n) + } + tmpExp.Rsh(tmpExp, 1) + + if tmpExp.Sign() > 0 { + n = n.Mul(n) + } + } + + if isExpNeg { + return New(1, 0).DivRound(result, precision), nil + } + + return result, nil +} + +// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm. +// OverallPrecision argument specifies the overall precision of the result (integer part + decimal part). +// +// ExpHullAbrham is faster than ExpTaylor for small precision values, but it is much slower for large precision values. +// +// Example: +// +// NewFromFloat(26.1).ExpHullAbrham(2).String() // output: "220000000000" +// NewFromFloat(26.1).ExpHullAbrham(20).String() // output: "216314672147.05767284" +func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) { + // Algorithm based on Variable precision exponential function. + // ACM Transactions on Mathematical Software by T. E. Hull & A. Abrham. + if d.IsZero() { + return Decimal{oneInt, 0}, nil + } + + currentPrecision := overallPrecision + + // Algorithm does not work if currentPrecision * 23 < |x|. + // Precision is automatically increased in such cases, so the value can be calculated precisely. + // If newly calculated precision is higher than ExpMaxIterations the currentPrecision will not be changed. + f := d.Abs().InexactFloat64() + if ncp := f / 23; ncp > float64(currentPrecision) && ncp < float64(ExpMaxIterations) { + currentPrecision = uint32(math.Ceil(ncp)) + } + + // fail if abs(d) beyond an over/underflow threshold + overflowThreshold := New(23*int64(currentPrecision), 0) + if d.Abs().Cmp(overflowThreshold) > 0 { + return Decimal{}, fmt.Errorf("over/underflow threshold, exp(x) cannot be calculated precisely") + } + + // Return 1 if abs(d) small enough; this also avoids later over/underflow + overflowThreshold2 := New(9, -int32(currentPrecision)-1) + if d.Abs().Cmp(overflowThreshold2) <= 0 { + return Decimal{oneInt, d.exp}, nil + } + + // t is the smallest integer >= 0 such that the corresponding abs(d/k) < 1 + t := d.exp + int32(d.NumDigits()) // Add d.NumDigits because the paper assumes that d.value [0.1, 1) + + if t < 0 { + t = 0 + } + + k := New(1, t) // reduction factor + r := Decimal{new(big.Int).Set(d.value), d.exp - t} // reduced argument + p := int32(currentPrecision) + t + 2 // precision for calculating the sum + + // Determine n, the number of therms for calculating sum + // use first Newton step (1.435p - 1.182) / log10(p/abs(r)) + // for solving appropriate equation, along with directed + // roundings and simple rational bound for log10(p/abs(r)) + rf := r.Abs().InexactFloat64() + pf := float64(p) + nf := math.Ceil((1.453*pf - 1.182) / math.Log10(pf/rf)) + if nf > float64(ExpMaxIterations) || math.IsNaN(nf) { + return Decimal{}, fmt.Errorf("exact value cannot be calculated in <=ExpMaxIterations iterations") + } + n := int64(nf) + + tmp := New(0, 0) + sum := New(1, 0) + one := New(1, 0) + for i := n - 1; i > 0; i-- { + tmp.value.SetInt64(i) + sum = sum.Mul(r.DivRound(tmp, p)) + sum = sum.Add(one) + } + + ki := k.IntPart() + res := New(1, 0) + for i := ki; i > 0; i-- { + res = res.Mul(sum) + } + + resNumDigits := int32(res.NumDigits()) + + var roundDigits int32 + if resNumDigits > abs(res.exp) { + roundDigits = int32(currentPrecision) - resNumDigits - res.exp + } else { + roundDigits = int32(currentPrecision) + } + + res = res.Round(roundDigits) + + return res, nil +} + +// ExpTaylor calculates the natural exponent of decimal (e to the power of d) using Taylor series expansion. +// Precision argument specifies how precise the result must be (number of digits after decimal point). +// Negative precision is allowed. +// +// ExpTaylor is much faster for large precision values than ExpHullAbrham. +// +// Example: +// +// d, err := NewFromFloat(26.1).ExpTaylor(2).String() +// d.String() // output: "216314672147.06" +// +// NewFromFloat(26.1).ExpTaylor(20).String() +// d.String() // output: "216314672147.05767284062928674083" +// +// NewFromFloat(26.1).ExpTaylor(-10).String() +// d.String() // output: "220000000000" +func (d Decimal) ExpTaylor(precision int32) (Decimal, error) { + // Note(mwoss): Implementation can be optimized by exclusively using big.Int API only + if d.IsZero() { + return Decimal{oneInt, 0}.Round(precision), nil + } + + var epsilon Decimal + var divPrecision int32 + if precision < 0 { + epsilon = New(1, -1) + divPrecision = 8 + } else { + epsilon = New(1, -precision-1) + divPrecision = precision + 1 + } + + decAbs := d.Abs() + pow := d.Abs() + factorial := New(1, 0) + + result := New(1, 0) + + for i := int64(1); ; { + step := pow.DivRound(factorial, divPrecision) + result = result.Add(step) + + // Stop Taylor series when current step is smaller than epsilon + if step.Cmp(epsilon) < 0 { + break + } + + pow = pow.Mul(decAbs) + + i++ + + // Calculate next factorial number or retrieve cached value + if len(factorials) >= int(i) && !factorials[i-1].IsZero() { + factorial = factorials[i-1] + } else { + // To avoid any race conditions, firstly the zero value is appended to a slice to create + // a spot for newly calculated factorial. After that, the zero value is replaced by calculated + // factorial using the index notation. + factorial = factorials[i-2].Mul(New(i, 0)) + factorials = append(factorials, Zero) + factorials[i-1] = factorial + } + } + + if d.Sign() < 0 { + result = New(1, 0).DivRound(result, precision+1) + } + + result = result.Round(precision) + return result, nil +} + +// Ln calculates natural logarithm of d. +// Precision argument specifies how precise the result must be (number of digits after decimal point). +// Negative precision is allowed. +// +// Example: +// +// d1, err := NewFromFloat(13.3).Ln(2) +// d1.String() // output: "2.59" +// +// d2, err := NewFromFloat(579.161).Ln(10) +// d2.String() // output: "6.3615805046" +func (d Decimal) Ln(precision int32) (Decimal, error) { + // Algorithm based on The Use of Iteration Methods for Approximating the Natural Logarithm, + // James F. Epperson, The American Mathematical Monthly, Vol. 96, No. 9, November 1989, pp. 831-835. + if d.IsNegative() { + return Decimal{}, fmt.Errorf("cannot calculate natural logarithm for negative decimals") + } + + if d.IsZero() { + return Decimal{}, fmt.Errorf("cannot represent natural logarithm of 0, result: -infinity") + } + + calcPrecision := precision + 2 + z := d.Copy() + + var comp1, comp3, comp2, comp4, reduceAdjust Decimal + comp1 = z.Sub(Decimal{oneInt, 0}) + comp3 = Decimal{oneInt, -1} + + // for decimal in range [0.9, 1.1] where ln(d) is close to 0 + usePowerSeries := false + + if comp1.Abs().Cmp(comp3) <= 0 { + usePowerSeries = true + } else { + // reduce input decimal to range [0.1, 1) + expDelta := int32(z.NumDigits()) + z.exp + z.exp -= expDelta + + // Input decimal was reduced by factor of 10^expDelta, thus we will need to add + // ln(10^expDelta) = expDelta * ln(10) + // to the result to compensate that + ln10 := ln10.withPrecision(calcPrecision) + reduceAdjust = NewFromInt32(expDelta) + reduceAdjust = reduceAdjust.Mul(ln10) + + comp1 = z.Sub(Decimal{oneInt, 0}) + + if comp1.Abs().Cmp(comp3) <= 0 { + usePowerSeries = true + } else { + // initial estimate using floats + zFloat := z.InexactFloat64() + comp1 = NewFromFloat(math.Log(zFloat)) + } + } + + epsilon := Decimal{oneInt, -calcPrecision} + + if usePowerSeries { + // Power Series - https://en.wikipedia.org/wiki/Logarithm#Power_series + // Calculating n-th term of formula: ln(z+1) = 2 sum [ 1 / (2n+1) * (z / (z+2))^(2n+1) ] + // until the difference between current and next term is smaller than epsilon. + // Coverage quite fast for decimals close to 1.0 + + // z + 2 + comp2 = comp1.Add(Decimal{twoInt, 0}) + // z / (z + 2) + comp3 = comp1.DivRound(comp2, calcPrecision) + // 2 * (z / (z + 2)) + comp1 = comp3.Add(comp3) + comp2 = comp1.Copy() + + for n := 1; ; n++ { + // 2 * (z / (z+2))^(2n+1) + comp2 = comp2.Mul(comp3).Mul(comp3) + + // 1 / (2n+1) * 2 * (z / (z+2))^(2n+1) + comp4 = NewFromInt(int64(2*n + 1)) + comp4 = comp2.DivRound(comp4, calcPrecision) + + // comp1 = 2 sum [ 1 / (2n+1) * (z / (z+2))^(2n+1) ] + comp1 = comp1.Add(comp4) + + if comp4.Abs().Cmp(epsilon) <= 0 { + break + } + } + } else { + // Halley's Iteration. + // Calculating n-th term of formula: a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z), + // until the difference between current and next term is smaller than epsilon + var prevStep Decimal + maxIters := calcPrecision*2 + 10 + + for i := int32(0); i < maxIters; i++ { + // exp(a_n) + comp3, _ = comp1.ExpTaylor(calcPrecision) + // exp(a_n) - z + comp2 = comp3.Sub(z) + // 2 * (exp(a_n) - z) + comp2 = comp2.Add(comp2) + // exp(a_n) + z + comp4 = comp3.Add(z) + // 2 * (exp(a_n) - z) / (exp(a_n) + z) + comp3 = comp2.DivRound(comp4, calcPrecision) + // comp1 = a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z) + comp1 = comp1.Sub(comp3) + + if prevStep.Add(comp3).IsZero() { + // If iteration steps oscillate we should return early and prevent an infinity loop + // NOTE(mwoss): This should be quite a rare case, returning error is not necessary + break + } + + if comp3.Abs().Cmp(epsilon) <= 0 { + break + } + + prevStep = comp3 + } + } + + comp1 = comp1.Add(reduceAdjust) + + return comp1.Round(precision), nil +} + +// NumDigits returns the number of digits of the decimal coefficient (d.Value) +func (d Decimal) NumDigits() int { + if d.value == nil { + return 1 + } + + if d.value.IsInt64() { + i64 := d.value.Int64() + // restrict fast path to integers with exact conversion to float64 + if i64 <= (1<<53) && i64 >= -(1<<53) { + if i64 == 0 { + return 1 + } + return int(math.Log10(math.Abs(float64(i64)))) + 1 + } + } + + estimatedNumDigits := int(float64(d.value.BitLen()) / math.Log2(10)) + + // estimatedNumDigits (lg10) may be off by 1, need to verify + digitsBigInt := big.NewInt(int64(estimatedNumDigits)) + errorCorrectionUnit := digitsBigInt.Exp(tenInt, digitsBigInt, nil) + + if d.value.CmpAbs(errorCorrectionUnit) >= 0 { + return estimatedNumDigits + 1 + } + + return estimatedNumDigits +} + +// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false. +func (d Decimal) IsInteger() bool { + // The most typical case, all decimal with exponent higher or equal 0 can be represented as integer + if d.exp >= 0 { + return true + } + // When the exponent is negative we have to check every number after the decimal place + // If all of them are zeroes, we are sure that given decimal can be represented as an integer + var r big.Int + q := new(big.Int).Set(d.value) + for z := abs(d.exp); z > 0; z-- { + q.QuoRem(q, tenInt, &r) + if r.Cmp(zeroInt) != 0 { + return false + } + } + return true +} + +// Abs calculates absolute value of any int32. Used for calculating absolute value of decimal's exponent. +func abs(n int32) int32 { + if n < 0 { + return -n + } + return n +} + +// Cmp compares the numbers represented by d and d2 and returns: +// +// -1 if d < d2 +// 0 if d == d2 +// +1 if d > d2 +func (d Decimal) Cmp(d2 Decimal) int { + d.ensureInitialized() + d2.ensureInitialized() + + if d.exp == d2.exp { + return d.value.Cmp(d2.value) + } + + rd, rd2 := RescalePair(d, d2) + + return rd.value.Cmp(rd2.value) +} + +// Compare compares the numbers represented by d and d2 and returns: +// +// -1 if d < d2 +// 0 if d == d2 +// +1 if d > d2 +func (d Decimal) Compare(d2 Decimal) int { + return d.Cmp(d2) +} + +// Equal returns whether the numbers represented by d and d2 are equal. +func (d Decimal) Equal(d2 Decimal) bool { + return d.Cmp(d2) == 0 +} + +// Deprecated: Equals is deprecated, please use Equal method instead. +func (d Decimal) Equals(d2 Decimal) bool { + return d.Equal(d2) +} + +// GreaterThan (GT) returns true when d is greater than d2. +func (d Decimal) GreaterThan(d2 Decimal) bool { + return d.Cmp(d2) == 1 +} + +// GreaterThanOrEqual (GTE) returns true when d is greater than or equal to d2. +func (d Decimal) GreaterThanOrEqual(d2 Decimal) bool { + cmp := d.Cmp(d2) + return cmp == 1 || cmp == 0 +} + +// LessThan (LT) returns true when d is less than d2. +func (d Decimal) LessThan(d2 Decimal) bool { + return d.Cmp(d2) == -1 +} + +// LessThanOrEqual (LTE) returns true when d is less than or equal to d2. +func (d Decimal) LessThanOrEqual(d2 Decimal) bool { + cmp := d.Cmp(d2) + return cmp == -1 || cmp == 0 +} + +// Sign returns: +// +// -1 if d < 0 +// 0 if d == 0 +// +1 if d > 0 +func (d Decimal) Sign() int { + if d.value == nil { + return 0 + } + return d.value.Sign() +} + +// IsPositive return +// +// true if d > 0 +// false if d == 0 +// false if d < 0 +func (d Decimal) IsPositive() bool { + return d.Sign() == 1 +} + +// IsNegative return +// +// true if d < 0 +// false if d == 0 +// false if d > 0 +func (d Decimal) IsNegative() bool { + return d.Sign() == -1 +} + +// IsZero return +// +// true if d == 0 +// false if d > 0 +// false if d < 0 +func (d Decimal) IsZero() bool { + return d.Sign() == 0 +} + +// Exponent returns the exponent, or scale component of the decimal. +func (d Decimal) Exponent() int32 { + return d.exp +} + +// Coefficient returns the coefficient of the decimal. It is scaled by 10^Exponent() +func (d Decimal) Coefficient() *big.Int { + d.ensureInitialized() + // we copy the coefficient so that mutating the result does not mutate the Decimal. + return new(big.Int).Set(d.value) +} + +// CoefficientInt64 returns the coefficient of the decimal as int64. It is scaled by 10^Exponent() +// If coefficient cannot be represented in an int64, the result will be undefined. +func (d Decimal) CoefficientInt64() int64 { + d.ensureInitialized() + return d.value.Int64() +} + +// IntPart returns the integer component of the decimal. +func (d Decimal) IntPart() int64 { + scaledD := d.rescale(0) + return scaledD.value.Int64() +} + +// BigInt returns integer component of the decimal as a BigInt. +func (d Decimal) BigInt() *big.Int { + scaledD := d.rescale(0) + return scaledD.value +} + +// BigFloat returns decimal as BigFloat. +// Be aware that casting decimal to BigFloat might cause a loss of precision. +func (d Decimal) BigFloat() *big.Float { + f := &big.Float{} + f.SetString(d.String()) + return f +} + +// Rat returns a rational number representation of the decimal. +func (d Decimal) Rat() *big.Rat { + d.ensureInitialized() + if d.exp <= 0 { + // NOTE(vadim): must negate after casting to prevent int32 overflow + denom := new(big.Int).Exp(tenInt, big.NewInt(-int64(d.exp)), nil) + return new(big.Rat).SetFrac(d.value, denom) + } + + mul := new(big.Int).Exp(tenInt, big.NewInt(int64(d.exp)), nil) + num := new(big.Int).Mul(d.value, mul) + return new(big.Rat).SetFrac(num, oneInt) +} + +// Float64 returns the nearest float64 value for d and a bool indicating +// whether f represents d exactly. +// For more details, see the documentation for big.Rat.Float64 +func (d Decimal) Float64() (f float64, exact bool) { + return d.Rat().Float64() +} + +// InexactFloat64 returns the nearest float64 value for d. +// It doesn't indicate if the returned value represents d exactly. +func (d Decimal) InexactFloat64() float64 { + f, _ := d.Float64() + return f +} + +// String returns the string representation of the decimal +// with the fixed point. +// +// Example: +// +// d := New(-12345, -3) +// println(d.String()) +// +// Output: +// +// -12.345 +func (d Decimal) String() string { + return d.string(true) +} + +// StringFixed returns a rounded fixed-point string with places digits after +// the decimal point. +// +// Example: +// +// NewFromFloat(0).StringFixed(2) // output: "0.00" +// NewFromFloat(0).StringFixed(0) // output: "0" +// NewFromFloat(5.45).StringFixed(0) // output: "5" +// NewFromFloat(5.45).StringFixed(1) // output: "5.5" +// NewFromFloat(5.45).StringFixed(2) // output: "5.45" +// NewFromFloat(5.45).StringFixed(3) // output: "5.450" +// NewFromFloat(545).StringFixed(-1) // output: "550" +func (d Decimal) StringFixed(places int32) string { + rounded := d.Round(places) + return rounded.string(false) +} + +// StringFixedBank returns a banker rounded fixed-point string with places digits +// after the decimal point. +// +// Example: +// +// NewFromFloat(0).StringFixedBank(2) // output: "0.00" +// NewFromFloat(0).StringFixedBank(0) // output: "0" +// NewFromFloat(5.45).StringFixedBank(0) // output: "5" +// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4" +// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45" +// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450" +// NewFromFloat(545).StringFixedBank(-1) // output: "540" +func (d Decimal) StringFixedBank(places int32) string { + rounded := d.RoundBank(places) + return rounded.string(false) +} + +// StringFixedCash returns a Swedish/Cash rounded fixed-point string. For +// more details see the documentation at function RoundCash. +func (d Decimal) StringFixedCash(interval uint8) string { + rounded := d.RoundCash(interval) + return rounded.string(false) +} + +// Round rounds the decimal to places decimal places. +// If places < 0, it will round the integer part to the nearest 10^(-places). +// +// Example: +// +// NewFromFloat(5.45).Round(1).String() // output: "5.5" +// NewFromFloat(545).Round(-1).String() // output: "550" +func (d Decimal) Round(places int32) Decimal { + if d.exp == -places { + return d + } + // truncate to places + 1 + ret := d.rescale(-places - 1) + + // add sign(d) * 0.5 + if ret.value.Sign() < 0 { + ret.value.Sub(ret.value, fiveInt) + } else { + ret.value.Add(ret.value, fiveInt) + } + + // floor for positive numbers, ceil for negative numbers + _, m := ret.value.DivMod(ret.value, tenInt, new(big.Int)) + ret.exp++ + if ret.value.Sign() < 0 && m.Cmp(zeroInt) != 0 { + ret.value.Add(ret.value, oneInt) + } + + return ret +} + +// RoundCeil rounds the decimal towards +infinity. +// +// Example: +// +// NewFromFloat(545).RoundCeil(-2).String() // output: "600" +// NewFromFloat(500).RoundCeil(-2).String() // output: "500" +// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11" +// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.4" +func (d Decimal) RoundCeil(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + + if d.value.Sign() > 0 { + rescaled.value.Add(rescaled.value, oneInt) + } + + return rescaled +} + +// RoundFloor rounds the decimal towards -infinity. +// +// Example: +// +// NewFromFloat(545).RoundFloor(-2).String() // output: "500" +// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500" +// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1" +// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.5" +func (d Decimal) RoundFloor(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + + if d.value.Sign() < 0 { + rescaled.value.Sub(rescaled.value, oneInt) + } + + return rescaled +} + +// RoundUp rounds the decimal away from zero. +// +// Example: +// +// NewFromFloat(545).RoundUp(-2).String() // output: "600" +// NewFromFloat(500).RoundUp(-2).String() // output: "500" +// NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11" +// NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.5" +func (d Decimal) RoundUp(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + + if d.value.Sign() > 0 { + rescaled.value.Add(rescaled.value, oneInt) + } else if d.value.Sign() < 0 { + rescaled.value.Sub(rescaled.value, oneInt) + } + + return rescaled +} + +// RoundDown rounds the decimal towards zero. +// +// Example: +// +// NewFromFloat(545).RoundDown(-2).String() // output: "500" +// NewFromFloat(-500).RoundDown(-2).String() // output: "-500" +// NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1" +// NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.4" +func (d Decimal) RoundDown(places int32) Decimal { + if d.exp >= -places { + return d + } + + rescaled := d.rescale(-places) + if d.Equal(rescaled) { + return d + } + return rescaled +} + +// RoundBank rounds the decimal to places decimal places. +// If the final digit to round is equidistant from the nearest two integers the +// rounded value is taken as the even number +// +// If places < 0, it will round the integer part to the nearest 10^(-places). +// +// Examples: +// +// NewFromFloat(5.45).RoundBank(1).String() // output: "5.4" +// NewFromFloat(545).RoundBank(-1).String() // output: "540" +// NewFromFloat(5.46).RoundBank(1).String() // output: "5.5" +// NewFromFloat(546).RoundBank(-1).String() // output: "550" +// NewFromFloat(5.55).RoundBank(1).String() // output: "5.6" +// NewFromFloat(555).RoundBank(-1).String() // output: "560" +func (d Decimal) RoundBank(places int32) Decimal { + + round := d.Round(places) + remainder := d.Sub(round).Abs() + + half := New(5, -places-1) + if remainder.Cmp(half) == 0 && round.value.Bit(0) != 0 { + if round.value.Sign() < 0 { + round.value.Add(round.value, oneInt) + } else { + round.value.Sub(round.value, oneInt) + } + } + + return round +} + +// RoundCash aka Cash/Penny/öre rounding rounds decimal to a specific +// interval. The amount payable for a cash transaction is rounded to the nearest +// multiple of the minimum currency unit available. The following intervals are +// available: 5, 10, 25, 50 and 100; any other number throws a panic. +// +// 5: 5 cent rounding 3.43 => 3.45 +// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up) +// 25: 25 cent rounding 3.41 => 3.50 +// 50: 50 cent rounding 3.75 => 4.00 +// 100: 100 cent rounding 3.50 => 4.00 +// +// For more details: https://en.wikipedia.org/wiki/Cash_rounding +func (d Decimal) RoundCash(interval uint8) Decimal { + var iVal *big.Int + switch interval { + case 5: + iVal = twentyInt + case 10: + iVal = tenInt + case 25: + iVal = fourInt + case 50: + iVal = twoInt + case 100: + iVal = oneInt + default: + panic(fmt.Sprintf("Decimal does not support this Cash rounding interval `%d`. Supported: 5, 10, 25, 50, 100", interval)) + } + dVal := Decimal{ + value: iVal, + } + + // TODO: optimize those calculations to reduce the high allocations (~29 allocs). + return d.Mul(dVal).Round(0).Div(dVal).Truncate(2) +} + +// Floor returns the nearest integer value less than or equal to d. +func (d Decimal) Floor() Decimal { + d.ensureInitialized() + + if d.exp >= 0 { + return d + } + + exp := big.NewInt(10) + + // NOTE(vadim): must negate after casting to prevent int32 overflow + exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) + + z := new(big.Int).Div(d.value, exp) + return Decimal{value: z, exp: 0} +} + +// Ceil returns the nearest integer value greater than or equal to d. +func (d Decimal) Ceil() Decimal { + d.ensureInitialized() + + if d.exp >= 0 { + return d + } + + exp := big.NewInt(10) + + // NOTE(vadim): must negate after casting to prevent int32 overflow + exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) + + z, m := new(big.Int).DivMod(d.value, exp, new(big.Int)) + if m.Cmp(zeroInt) != 0 { + z.Add(z, oneInt) + } + return Decimal{value: z, exp: 0} +} + +// Truncate truncates off digits from the number, without rounding. +// +// NOTE: precision is the last digit that will not be truncated (must be >= 0). +// +// Example: +// +// decimal.NewFromString("123.456").Truncate(2).String() // "123.45" +func (d Decimal) Truncate(precision int32) Decimal { + d.ensureInitialized() + if precision >= 0 && -precision > d.exp { + return d.rescale(-precision) + } + return d +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error { + if string(decimalBytes) == "null" { + return nil + } + + str, err := unquoteIfQuoted(decimalBytes) + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", decimalBytes, err) + } + + decimal, err := NewFromString(str) + *d = decimal + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", str, err) + } + return nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (d Decimal) MarshalJSON() ([]byte, error) { + var str string + if MarshalJSONWithoutQuotes { + str = d.String() + } else { + str = "\"" + d.String() + "\"" + } + return []byte(str), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation +// is already used when encoding to text, this method stores that string as []byte +func (d *Decimal) UnmarshalBinary(data []byte) error { + // Verify we have at least 4 bytes for the exponent. The GOB encoded value + // may be empty. + if len(data) < 4 { + return fmt.Errorf("error decoding binary %v: expected at least 4 bytes, got %d", data, len(data)) + } + + // Extract the exponent + d.exp = int32(binary.BigEndian.Uint32(data[:4])) + + // Extract the value + d.value = new(big.Int) + if err := d.value.GobDecode(data[4:]); err != nil { + return fmt.Errorf("error decoding binary %v: %s", data, err) + } + + return nil +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d Decimal) MarshalBinary() (data []byte, err error) { + // exp is written first, but encode value first to know output size + var valueData []byte + if valueData, err = d.value.GobEncode(); err != nil { + return nil, err + } + + // Write the exponent in front, since it's a fixed size + expData := make([]byte, 4, len(valueData)+4) + binary.BigEndian.PutUint32(expData, uint32(d.exp)) + + // Return the byte array + return append(expData, valueData...), nil +} + +// Scan implements the sql.Scanner interface for database deserialization. +func (d *Decimal) Scan(value interface{}) error { + // first try to see if the data is stored in database as a Numeric datatype + switch v := value.(type) { + + case float32: + *d = NewFromFloat(float64(v)) + return nil + + case float64: + // numeric in sqlite3 sends us float64 + *d = NewFromFloat(v) + return nil + + case int64: + // at least in sqlite3 when the value is 0 in db, the data is sent + // to us as an int64 instead of a float64 ... + *d = New(v, 0) + return nil + + case uint64: + // while clickhouse may send 0 in db as uint64 + *d = NewFromUint64(v) + return nil + + default: + // default is trying to interpret value stored as string + str, err := unquoteIfQuoted(v) + if err != nil { + return err + } + *d, err = NewFromString(str) + return err + } +} + +// Value implements the driver.Valuer interface for database serialization. +func (d Decimal) Value() (driver.Value, error) { + return d.String(), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for XML +// deserialization. +func (d *Decimal) UnmarshalText(text []byte) error { + str := string(text) + + dec, err := NewFromString(str) + *d = dec + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", str, err) + } + + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface for XML +// serialization. +func (d Decimal) MarshalText() (text []byte, err error) { + return []byte(d.String()), nil +} + +// GobEncode implements the gob.GobEncoder interface for gob serialization. +func (d Decimal) GobEncode() ([]byte, error) { + return d.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface for gob serialization. +func (d *Decimal) GobDecode(data []byte) error { + return d.UnmarshalBinary(data) +} + +// StringScaled first scales the decimal then calls .String() on it. +// +// Deprecated: buggy and unintuitive. Use StringFixed instead. +func (d Decimal) StringScaled(exp int32) string { + return d.rescale(exp).String() +} + +func (d Decimal) string(trimTrailingZeros bool) string { + if d.exp >= 0 { + return d.rescale(0).value.String() + } + + abs := new(big.Int).Abs(d.value) + str := abs.String() + + var intPart, fractionalPart string + + // NOTE(vadim): this cast to int will cause bugs if d.exp == INT_MIN + // and you are on a 32-bit machine. Won't fix this super-edge case. + dExpInt := int(d.exp) + if len(str) > -dExpInt { + intPart = str[:len(str)+dExpInt] + fractionalPart = str[len(str)+dExpInt:] + } else { + intPart = "0" + + num0s := -dExpInt - len(str) + fractionalPart = strings.Repeat("0", num0s) + str + } + + if trimTrailingZeros { + i := len(fractionalPart) - 1 + for ; i >= 0; i-- { + if fractionalPart[i] != '0' { + break + } + } + fractionalPart = fractionalPart[:i+1] + } + + number := intPart + if len(fractionalPart) > 0 { + number += "." + fractionalPart + } + + if d.value.Sign() < 0 { + return "-" + number + } + + return number +} + +func (d *Decimal) ensureInitialized() { + if d.value == nil { + d.value = new(big.Int) + } +} + +// Min returns the smallest Decimal that was passed in the arguments. +// +// To call this function with an array, you must do: +// +// Min(arr[0], arr[1:]...) +// +// This makes it harder to accidentally call Min with 0 arguments. +func Min(first Decimal, rest ...Decimal) Decimal { + ans := first + for _, item := range rest { + if item.Cmp(ans) < 0 { + ans = item + } + } + return ans +} + +// Max returns the largest Decimal that was passed in the arguments. +// +// To call this function with an array, you must do: +// +// Max(arr[0], arr[1:]...) +// +// This makes it harder to accidentally call Max with 0 arguments. +func Max(first Decimal, rest ...Decimal) Decimal { + ans := first + for _, item := range rest { + if item.Cmp(ans) > 0 { + ans = item + } + } + return ans +} + +// Sum returns the combined total of the provided first and rest Decimals +func Sum(first Decimal, rest ...Decimal) Decimal { + total := first + for _, item := range rest { + total = total.Add(item) + } + + return total +} + +// Avg returns the average value of the provided first and rest Decimals +func Avg(first Decimal, rest ...Decimal) Decimal { + count := New(int64(len(rest)+1), 0) + sum := Sum(first, rest...) + return sum.Div(count) +} + +// RescalePair rescales two decimals to common exponential value (minimal exp of both decimals) +func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) { + d1.ensureInitialized() + d2.ensureInitialized() + + if d1.exp < d2.exp { + return d1, d2.rescale(d1.exp) + } else if d1.exp > d2.exp { + return d1.rescale(d2.exp), d2 + } + + return d1, d2 +} + +func unquoteIfQuoted(value interface{}) (string, error) { + var bytes []byte + + switch v := value.(type) { + case string: + bytes = []byte(v) + case []byte: + bytes = v + default: + return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'", value, value) + } + + // If the amount is quoted, strip the quotes + if len(bytes) > 2 && bytes[0] == '"' && bytes[len(bytes)-1] == '"' { + bytes = bytes[1 : len(bytes)-1] + } + return string(bytes), nil +} + +// NullDecimal represents a nullable decimal with compatibility for +// scanning null values from the database. +type NullDecimal struct { + Decimal Decimal + Valid bool +} + +func NewNullDecimal(d Decimal) NullDecimal { + return NullDecimal{ + Decimal: d, + Valid: true, + } +} + +// Scan implements the sql.Scanner interface for database deserialization. +func (d *NullDecimal) Scan(value interface{}) error { + if value == nil { + d.Valid = false + return nil + } + d.Valid = true + return d.Decimal.Scan(value) +} + +// Value implements the driver.Valuer interface for database serialization. +func (d NullDecimal) Value() (driver.Value, error) { + if !d.Valid { + return nil, nil + } + return d.Decimal.Value() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error { + if string(decimalBytes) == "null" { + d.Valid = false + return nil + } + d.Valid = true + return d.Decimal.UnmarshalJSON(decimalBytes) +} + +// MarshalJSON implements the json.Marshaler interface. +func (d NullDecimal) MarshalJSON() ([]byte, error) { + if !d.Valid { + return []byte("null"), nil + } + return d.Decimal.MarshalJSON() +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for XML +// deserialization +func (d *NullDecimal) UnmarshalText(text []byte) error { + str := string(text) + + // check for empty XML or XML without body e.g., + if str == "" { + d.Valid = false + return nil + } + if err := d.Decimal.UnmarshalText(text); err != nil { + d.Valid = false + return err + } + d.Valid = true + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface for XML +// serialization. +func (d NullDecimal) MarshalText() (text []byte, err error) { + if !d.Valid { + return []byte{}, nil + } + return d.Decimal.MarshalText() +} + +// Trig functions + +// Atan returns the arctangent, in radians, of x. +func (d Decimal) Atan() Decimal { + if d.Equal(NewFromFloat(0.0)) { + return d + } + if d.GreaterThan(NewFromFloat(0.0)) { + return d.satan() + } + return d.Neg().satan().Neg() +} + +func (d Decimal) xatan() Decimal { + P0 := NewFromFloat(-8.750608600031904122785e-01) + P1 := NewFromFloat(-1.615753718733365076637e+01) + P2 := NewFromFloat(-7.500855792314704667340e+01) + P3 := NewFromFloat(-1.228866684490136173410e+02) + P4 := NewFromFloat(-6.485021904942025371773e+01) + Q0 := NewFromFloat(2.485846490142306297962e+01) + Q1 := NewFromFloat(1.650270098316988542046e+02) + Q2 := NewFromFloat(4.328810604912902668951e+02) + Q3 := NewFromFloat(4.853903996359136964868e+02) + Q4 := NewFromFloat(1.945506571482613964425e+02) + z := d.Mul(d) + b1 := P0.Mul(z).Add(P1).Mul(z).Add(P2).Mul(z).Add(P3).Mul(z).Add(P4).Mul(z) + b2 := z.Add(Q0).Mul(z).Add(Q1).Mul(z).Add(Q2).Mul(z).Add(Q3).Mul(z).Add(Q4) + z = b1.Div(b2) + z = d.Mul(z).Add(d) + return z +} + +// satan reduces its argument (known to be positive) +// to the range [0, 0.66] and calls xatan. +func (d Decimal) satan() Decimal { + Morebits := NewFromFloat(6.123233995736765886130e-17) // pi/2 = PIO2 + Morebits + Tan3pio8 := NewFromFloat(2.41421356237309504880) // tan(3*pi/8) + pi := NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459) + + if d.LessThanOrEqual(NewFromFloat(0.66)) { + return d.xatan() + } + if d.GreaterThan(Tan3pio8) { + return pi.Div(NewFromFloat(2.0)).Sub(NewFromFloat(1.0).Div(d).xatan()).Add(Morebits) + } + return pi.Div(NewFromFloat(4.0)).Add((d.Sub(NewFromFloat(1.0)).Div(d.Add(NewFromFloat(1.0)))).xatan()).Add(NewFromFloat(0.5).Mul(Morebits)) +} + +// sin coefficients +var _sin = [...]Decimal{ + NewFromFloat(1.58962301576546568060e-10), // 0x3de5d8fd1fd19ccd + NewFromFloat(-2.50507477628578072866e-8), // 0xbe5ae5e5a9291f5d + NewFromFloat(2.75573136213857245213e-6), // 0x3ec71de3567d48a1 + NewFromFloat(-1.98412698295895385996e-4), // 0xbf2a01a019bfdf03 + NewFromFloat(8.33333333332211858878e-3), // 0x3f8111111110f7d0 + NewFromFloat(-1.66666666666666307295e-1), // 0xbfc5555555555548 +} + +// Sin returns the sine of the radian argument x. +func (d Decimal) Sin() Decimal { + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + if d.Equal(NewFromFloat(0.0)) { + return d + } + // make argument positive but save the sign + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + sign = true + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + // reflect in x axis + if j > 3 { + sign = !sign + j -= 4 + } + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if j == 1 || j == 2 { + w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) + y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w) + } else { + y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) + } + if sign { + y = y.Neg() + } + return y +} + +// cos coefficients +var _cos = [...]Decimal{ + NewFromFloat(-1.13585365213876817300e-11), // 0xbda8fa49a0861a9b + NewFromFloat(2.08757008419747316778e-9), // 0x3e21ee9d7b4e3f05 + NewFromFloat(-2.75573141792967388112e-7), // 0xbe927e4f7eac4bc6 + NewFromFloat(2.48015872888517045348e-5), // 0x3efa01a019c844f5 + NewFromFloat(-1.38888888888730564116e-3), // 0xbf56c16c16c14f91 + NewFromFloat(4.16666666666665929218e-2), // 0x3fa555555555554b +} + +// Cos returns the cosine of the radian argument x. +func (d Decimal) Cos() Decimal { + + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + // make argument positive + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + // reflect in x axis + if j > 3 { + sign = !sign + j -= 4 + } + if j > 1 { + sign = !sign + } + + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if j == 1 || j == 2 { + y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) + } else { + w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) + y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w) + } + if sign { + y = y.Neg() + } + return y +} + +var _tanP = [...]Decimal{ + NewFromFloat(-1.30936939181383777646e+4), // 0xc0c992d8d24f3f38 + NewFromFloat(1.15351664838587416140e+6), // 0x413199eca5fc9ddd + NewFromFloat(-1.79565251976484877988e+7), // 0xc1711fead3299176 +} +var _tanQ = [...]Decimal{ + NewFromFloat(1.00000000000000000000e+0), + NewFromFloat(1.36812963470692954678e+4), //0x40cab8a5eeb36572 + NewFromFloat(-1.32089234440210967447e+6), //0xc13427bc582abc96 + NewFromFloat(2.50083801823357915839e+7), //0x4177d98fc2ead8ef + NewFromFloat(-5.38695755929454629881e+7), //0xc189afe03cbe5a31 +} + +// Tan returns the tangent of the radian argument x. +func (d Decimal) Tan() Decimal { + + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + if d.Equal(NewFromFloat(0.0)) { + return d + } + + // make argument positive but save the sign + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + sign = true + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if zz.GreaterThan(NewFromFloat(1e-14)) { + w := zz.Mul(_tanP[0].Mul(zz).Add(_tanP[1]).Mul(zz).Add(_tanP[2])) + x := zz.Add(_tanQ[1]).Mul(zz).Add(_tanQ[2]).Mul(zz).Add(_tanQ[3]).Mul(zz).Add(_tanQ[4]) + y = z.Add(z.Mul(w.Div(x))) + } else { + y = z + } + if j&2 == 2 { + y = NewFromFloat(-1.0).Div(y) + } + if sign { + y = y.Neg() + } + return y +} diff --git a/vendor/github.com/shopspring/decimal/rounding.go b/vendor/github.com/shopspring/decimal/rounding.go new file mode 100644 index 0000000..d4b0cd0 --- /dev/null +++ b/vendor/github.com/shopspring/decimal/rounding.go @@ -0,0 +1,160 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package decimal + +type floatInfo struct { + mantbits uint + expbits uint + bias int +} + +var float32info = floatInfo{23, 8, -127} +var float64info = floatInfo{52, 11, -1023} + +// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely reconstructed. +func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { + // If mantissa is zero, the number is zero; stop now. + if mant == 0 { + d.nd = 0 + return + } + + // Compute upper and lower such that any decimal number + // between upper and lower (possibly inclusive) + // will round to the original floating point number. + + // We may see at once that the number is already shortest. + // + // Suppose d is not denormal, so that 2^exp <= d < 10^dp. + // The closest shorter number is at least 10^(dp-nd) away. + // The lower/upper bounds computed below are at distance + // at most 2^(exp-mantbits). + // + // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), + // or equivalently log2(10)*(dp-nd) > exp-mantbits. + // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). + minexp := flt.bias + 1 // minimum possible exponent + if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { + // The number is already shortest. + return + } + + // d = mant << (exp - mantbits) + // Next highest floating point number is mant+1 << exp-mantbits. + // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. + upper := new(decimal) + upper.Assign(mant*2 + 1) + upper.Shift(exp - int(flt.mantbits) - 1) + + // d = mant << (exp - mantbits) + // Next lowest floating point number is mant-1 << exp-mantbits, + // unless mant-1 drops the significant bit and exp is not the minimum exp, + // in which case the next lowest is mant*2-1 << exp-mantbits-1. + // Either way, call it mantlo << explo-mantbits. + // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. + var mantlo uint64 + var explo int + if mant > 1<= d.nd { + break + } + li := ui - upper.dp + lower.dp + l := byte('0') // lower digit + if li >= 0 && li < lower.nd { + l = lower.d[li] + } + m := byte('0') // middle digit + if mi >= 0 { + m = d.d[mi] + } + u := byte('0') // upper digit + if ui < upper.nd { + u = upper.d[ui] + } + + // Okay to round down (truncate) if lower has a different digit + // or if lower is inclusive and is exactly the result of rounding + // down (i.e., and we have reached the final digit of lower). + okdown := l != m || inclusive && li+1 == lower.nd + + switch { + case upperdelta == 0 && m+1 < u: + // Example: + // m = 12345xxx + // u = 12347xxx + upperdelta = 2 + case upperdelta == 0 && m != u: + // Example: + // m = 12345xxx + // u = 12346xxx + upperdelta = 1 + case upperdelta == 1 && (m != '9' || u != '0'): + // Example: + // m = 1234598x + // u = 1234600x + upperdelta = 2 + } + // Okay to round up if upper has a different digit and either upper + // is inclusive or upper is bigger than the result of rounding up. + okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd) + + // If it's okay to do either, then round to the nearest one. + // If it's okay to do only one, do it. + switch { + case okdown && okup: + d.Round(mi + 1) + return + case okdown: + d.RoundDown(mi + 1) + return + case okup: + d.RoundUp(mi + 1) + return + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d67e4e0..465e84b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2,6 +2,11 @@ ## explicit; go 1.20 filippo.io/edwards25519 filippo.io/edwards25519/field +# github.com/Andrew-M-C/go.jsonvalue v1.4.1 +## explicit; go 1.13 +github.com/Andrew-M-C/go.jsonvalue +github.com/Andrew-M-C/go.jsonvalue/internal/buffer +github.com/Andrew-M-C/go.jsonvalue/internal/unsafe # github.com/Esword618/unioffice v1.4.1 ## explicit; go 1.12 github.com/Esword618/unioffice @@ -76,6 +81,9 @@ github.com/lxn/win ## explicit; go 1.22 github.com/playwright-community/playwright-go github.com/playwright-community/playwright-go/internal/safe +# github.com/shopspring/decimal v1.4.0 +## explicit; go 1.10 +github.com/shopspring/decimal # github.com/stretchr/testify v1.9.0 ## explicit; go 1.17 # github.com/super-l/machine-code v0.0.0-20241121142923-4cb40646deba