mirror of
https://salsa.debian.org/mdosch/feed-to-muc.git
synced 2024-11-22 22:18:39 +01:00
Remove vendor folder.
This commit is contained in:
parent
a9a81b1832
commit
eaccc166c1
288 changed files with 0 additions and 204152 deletions
1
vendor/github.com/PuerkitoBio/goquery/.gitattributes
generated
vendored
1
vendor/github.com/PuerkitoBio/goquery/.gitattributes
generated
vendored
|
@ -1 +0,0 @@
|
||||||
testdata/* linguist-vendored
|
|
16
vendor/github.com/PuerkitoBio/goquery/.gitignore
generated
vendored
16
vendor/github.com/PuerkitoBio/goquery/.gitignore
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
# editor temporary files
|
|
||||||
*.sublime-*
|
|
||||||
.DS_Store
|
|
||||||
*.swp
|
|
||||||
#*.*#
|
|
||||||
tags
|
|
||||||
|
|
||||||
# direnv config
|
|
||||||
.env*
|
|
||||||
|
|
||||||
# test binaries
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# coverage and profilte outputs
|
|
||||||
*.out
|
|
||||||
|
|
12
vendor/github.com/PuerkitoBio/goquery/LICENSE
generated
vendored
12
vendor/github.com/PuerkitoBio/goquery/LICENSE
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
Copyright (c) 2012-2021, Martin Angers & Contributors
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
195
vendor/github.com/PuerkitoBio/goquery/README.md
generated
vendored
195
vendor/github.com/PuerkitoBio/goquery/README.md
generated
vendored
|
@ -1,195 +0,0 @@
|
||||||
# goquery - a little like that j-thing, only in Go
|
|
||||||
|
|
||||||
[![Build Status](https://github.com/PuerkitoBio/goquery/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/PuerkitoBio/goquery/actions)
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/PuerkitoBio/goquery.svg)](https://pkg.go.dev/github.com/PuerkitoBio/goquery)
|
|
||||||
[![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
|
|
||||||
|
|
||||||
goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
|
|
||||||
|
|
||||||
Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this.
|
|
||||||
|
|
||||||
Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
* [Installation](#installation)
|
|
||||||
* [Changelog](#changelog)
|
|
||||||
* [API](#api)
|
|
||||||
* [Examples](#examples)
|
|
||||||
* [Related Projects](#related-projects)
|
|
||||||
* [Support](#support)
|
|
||||||
* [License](#license)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Please note that because of the net/html dependency, goquery requires Go1.1+ and is tested on Go1.7+.
|
|
||||||
|
|
||||||
$ go get github.com/PuerkitoBio/goquery
|
|
||||||
|
|
||||||
(optional) To run unit tests:
|
|
||||||
|
|
||||||
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
|
|
||||||
$ go test
|
|
||||||
|
|
||||||
(optional) To run benchmarks (warning: it runs for a few minutes):
|
|
||||||
|
|
||||||
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
|
|
||||||
$ go test -bench=".*"
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
**Note that goquery's API is now stable, and will not break.**
|
|
||||||
|
|
||||||
* **2021-10-25 (v1.8.0)** : Add `Render` function to render a `Selection` to an `io.Writer` (thanks [@anthonygedeon](https://github.com/anthonygedeon)).
|
|
||||||
* **2021-07-11 (v1.7.1)** : Update go.mod dependencies and add dependabot config (thanks [@jauderho](https://github.com/jauderho)).
|
|
||||||
* **2021-06-14 (v1.7.0)** : Add `Single` and `SingleMatcher` functions to optimize first-match selection (thanks [@gdollardollar](https://github.com/gdollardollar)).
|
|
||||||
* **2021-01-11 (v1.6.1)** : Fix panic when calling `{Prepend,Append,Set}Html` on a `Selection` that contains non-Element nodes.
|
|
||||||
* **2020-10-08 (v1.6.0)** : Parse html in context of the container node for all functions that deal with html strings (`AfterHtml`, `AppendHtml`, etc.). Thanks to [@thiemok][thiemok] and [@davidjwilkins][djw] for their work on this.
|
|
||||||
* **2020-02-04 (v1.5.1)** : Update module dependencies.
|
|
||||||
* **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
|
|
||||||
* **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
|
|
||||||
* **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
|
|
||||||
* **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
|
|
||||||
* **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
|
|
||||||
* **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
|
|
||||||
* **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
|
|
||||||
* **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
|
|
||||||
* **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
|
|
||||||
* **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
|
|
||||||
* **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see [doc][] for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
|
|
||||||
* **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
|
|
||||||
* **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
|
|
||||||
* **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone].
|
|
||||||
* **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used.
|
|
||||||
* **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s.
|
|
||||||
* **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader.
|
|
||||||
* **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response.
|
|
||||||
* **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility.
|
|
||||||
* **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out).
|
|
||||||
* **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method.
|
|
||||||
* **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases).
|
|
||||||
* **v0.1.0** : Initial release.
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate.
|
|
||||||
|
|
||||||
jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention:
|
|
||||||
|
|
||||||
* When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`)
|
|
||||||
* When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`)
|
|
||||||
* The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`)
|
|
||||||
* The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`)
|
|
||||||
* The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`)
|
|
||||||
* The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`)
|
|
||||||
|
|
||||||
Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour).
|
|
||||||
|
|
||||||
The complete [package reference documentation can be found here][doc].
|
|
||||||
|
|
||||||
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
|
|
||||||
|
|
||||||
* `Find("~")` returns an empty selection because the selector string doesn't match anything.
|
|
||||||
* `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
|
|
||||||
* `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
|
|
||||||
* `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
See some tips and tricks in the [wiki][].
|
|
||||||
|
|
||||||
Adapted from example_test.go:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleScrape() {
|
|
||||||
// Request the HTML page.
|
|
||||||
res, err := http.Get("http://metalsucks.net")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode != 200 {
|
|
||||||
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the HTML document
|
|
||||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the review items
|
|
||||||
doc.Find(".left-content article .post-title").Each(func(i int, s *goquery.Selection) {
|
|
||||||
// For each item found, get the title
|
|
||||||
title := s.Find("a").Text()
|
|
||||||
fmt.Printf("Review %d: %s\n", i, title)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ExampleScrape()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related Projects
|
|
||||||
|
|
||||||
- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
|
|
||||||
- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
|
|
||||||
- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
|
|
||||||
- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
|
|
||||||
- [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
|
|
||||||
- [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
|
|
||||||
- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
|
|
||||||
- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.
|
|
||||||
- [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
|
|
||||||
- [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags.
|
|
||||||
- [stitcherd](https://github.com/vhodges/stitcherd), A server for doing server side includes using css selectors and DOM updates.
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
There are a number of ways you can support the project:
|
|
||||||
|
|
||||||
* Use it, star it, build something with it, spread the word!
|
|
||||||
- If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section!
|
|
||||||
* Raise issues to improve the project (note: doc typos and clarifications are issues too!)
|
|
||||||
- Please search existing issues before opening a new one - it may have already been adressed.
|
|
||||||
* Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
|
|
||||||
- Make sure new code is tested.
|
|
||||||
- Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
|
|
||||||
* Sponsor the developer
|
|
||||||
- See the Github Sponsor button at the top of the repo on github
|
|
||||||
- or via BuyMeACoffee.com, below
|
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/mna" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
|
|
||||||
|
|
||||||
[jquery]: http://jquery.com/
|
|
||||||
[go]: http://golang.org/
|
|
||||||
[cascadia]: https://github.com/andybalholm/cascadia
|
|
||||||
[cascadiacli]: https://github.com/suntong/cascadia
|
|
||||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
|
||||||
[golic]: http://golang.org/LICENSE
|
|
||||||
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
|
|
||||||
[doc]: https://pkg.go.dev/github.com/PuerkitoBio/goquery
|
|
||||||
[index]: http://api.jquery.com/index/
|
|
||||||
[gonet]: https://github.com/golang/net/
|
|
||||||
[html]: https://pkg.go.dev/golang.org/x/net/html
|
|
||||||
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
|
|
||||||
[thatguystone]: https://github.com/thatguystone
|
|
||||||
[piotr]: https://github.com/piotrkowalczuk
|
|
||||||
[goq]: https://github.com/andrewstuart/goq
|
|
||||||
[thiemok]: https://github.com/thiemok
|
|
||||||
[djw]: https://github.com/davidjwilkins
|
|
124
vendor/github.com/PuerkitoBio/goquery/array.go
generated
vendored
124
vendor/github.com/PuerkitoBio/goquery/array.go
generated
vendored
|
@ -1,124 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxUint = ^uint(0)
|
|
||||||
maxInt = int(maxUint >> 1)
|
|
||||||
|
|
||||||
// ToEnd is a special index value that can be used as end index in a call
|
|
||||||
// to Slice so that all elements are selected until the end of the Selection.
|
|
||||||
// It is equivalent to passing (*Selection).Length().
|
|
||||||
ToEnd = maxInt
|
|
||||||
)
|
|
||||||
|
|
||||||
// First reduces the set of matched elements to the first in the set.
|
|
||||||
// It returns a new Selection object, and an empty Selection object if the
|
|
||||||
// the selection is empty.
|
|
||||||
func (s *Selection) First() *Selection {
|
|
||||||
return s.Eq(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Last reduces the set of matched elements to the last in the set.
|
|
||||||
// It returns a new Selection object, and an empty Selection object if
|
|
||||||
// the selection is empty.
|
|
||||||
func (s *Selection) Last() *Selection {
|
|
||||||
return s.Eq(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eq reduces the set of matched elements to the one at the specified index.
|
|
||||||
// If a negative index is given, it counts backwards starting at the end of the
|
|
||||||
// set. It returns a new Selection object, and an empty Selection object if the
|
|
||||||
// index is invalid.
|
|
||||||
func (s *Selection) Eq(index int) *Selection {
|
|
||||||
if index < 0 {
|
|
||||||
index += len(s.Nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if index >= len(s.Nodes) || index < 0 {
|
|
||||||
return newEmptySelection(s.document)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Slice(index, index+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice reduces the set of matched elements to a subset specified by a range
|
|
||||||
// of indices. The start index is 0-based and indicates the index of the first
|
|
||||||
// element to select. The end index is 0-based and indicates the index at which
|
|
||||||
// the elements stop being selected (the end index is not selected).
|
|
||||||
//
|
|
||||||
// The indices may be negative, in which case they represent an offset from the
|
|
||||||
// end of the selection.
|
|
||||||
//
|
|
||||||
// The special value ToEnd may be specified as end index, in which case all elements
|
|
||||||
// until the end are selected. This works both for a positive and negative start
|
|
||||||
// index.
|
|
||||||
func (s *Selection) Slice(start, end int) *Selection {
|
|
||||||
if start < 0 {
|
|
||||||
start += len(s.Nodes)
|
|
||||||
}
|
|
||||||
if end == ToEnd {
|
|
||||||
end = len(s.Nodes)
|
|
||||||
} else if end < 0 {
|
|
||||||
end += len(s.Nodes)
|
|
||||||
}
|
|
||||||
return pushStack(s, s.Nodes[start:end])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the underlying node at the specified index.
|
|
||||||
// Get without parameter is not implemented, since the node array is available
|
|
||||||
// on the Selection object.
|
|
||||||
func (s *Selection) Get(index int) *html.Node {
|
|
||||||
if index < 0 {
|
|
||||||
index += len(s.Nodes) // Negative index gets from the end
|
|
||||||
}
|
|
||||||
return s.Nodes[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index returns the position of the first element within the Selection object
|
|
||||||
// relative to its sibling elements.
|
|
||||||
func (s *Selection) Index() int {
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexSelector returns the position of the first element within the
|
|
||||||
// Selection object relative to the elements matched by the selector, or -1 if
|
|
||||||
// not found.
|
|
||||||
func (s *Selection) IndexSelector(selector string) int {
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
sel := s.document.Find(selector)
|
|
||||||
return indexInSlice(sel.Nodes, s.Nodes[0])
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexMatcher returns the position of the first element within the
|
|
||||||
// Selection object relative to the elements matched by the matcher, or -1 if
|
|
||||||
// not found.
|
|
||||||
func (s *Selection) IndexMatcher(m Matcher) int {
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
sel := s.document.FindMatcher(m)
|
|
||||||
return indexInSlice(sel.Nodes, s.Nodes[0])
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexOfNode returns the position of the specified node within the Selection
|
|
||||||
// object, or -1 if not found.
|
|
||||||
func (s *Selection) IndexOfNode(node *html.Node) int {
|
|
||||||
return indexInSlice(s.Nodes, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexOfSelection returns the position of the first node in the specified
|
|
||||||
// Selection object within this Selection object, or -1 if not found.
|
|
||||||
func (s *Selection) IndexOfSelection(sel *Selection) int {
|
|
||||||
if sel != nil && len(sel.Nodes) > 0 {
|
|
||||||
return indexInSlice(s.Nodes, sel.Nodes[0])
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
123
vendor/github.com/PuerkitoBio/goquery/doc.go
generated
vendored
123
vendor/github.com/PuerkitoBio/goquery/doc.go
generated
vendored
|
@ -1,123 +0,0 @@
|
||||||
// Copyright (c) 2012-2016, Martin Angers & Contributors
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
// are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
// other materials provided with the distribution.
|
|
||||||
// * Neither the name of the author nor the names of its contributors may be used to
|
|
||||||
// endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
|
||||||
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
||||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
||||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
||||||
// WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package goquery implements features similar to jQuery, including the chainable
|
|
||||||
syntax, to manipulate and query an HTML document.
|
|
||||||
|
|
||||||
It brings a syntax and a set of features similar to jQuery to the Go language.
|
|
||||||
It is based on Go's net/html package and the CSS Selector library cascadia.
|
|
||||||
Since the net/html parser returns nodes, and not a full-featured DOM
|
|
||||||
tree, jQuery's stateful manipulation functions (like height(), css(), detach())
|
|
||||||
have been left off.
|
|
||||||
|
|
||||||
Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
|
|
||||||
the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
|
|
||||||
See the repository's wiki for various options on how to do this.
|
|
||||||
|
|
||||||
Syntax-wise, it is as close as possible to jQuery, with the same method names when
|
|
||||||
possible, and that warm and fuzzy chainable interface. jQuery being the
|
|
||||||
ultra-popular library that it is, writing a similar HTML-manipulating
|
|
||||||
library was better to follow its API than to start anew (in the same spirit as
|
|
||||||
Go's fmt package), even though some of its methods are less than intuitive (looking
|
|
||||||
at you, index()...).
|
|
||||||
|
|
||||||
It is hosted on GitHub, along with additional documentation in the README.md
|
|
||||||
file: https://github.com/puerkitobio/goquery
|
|
||||||
|
|
||||||
Please note that because of the net/html dependency, goquery requires Go1.1+.
|
|
||||||
|
|
||||||
The various methods are split into files based on the category of behavior.
|
|
||||||
The three dots (...) indicate that various "overloads" are available.
|
|
||||||
|
|
||||||
* array.go : array-like positional manipulation of the selection.
|
|
||||||
- Eq()
|
|
||||||
- First()
|
|
||||||
- Get()
|
|
||||||
- Index...()
|
|
||||||
- Last()
|
|
||||||
- Slice()
|
|
||||||
|
|
||||||
* expand.go : methods that expand or augment the selection's set.
|
|
||||||
- Add...()
|
|
||||||
- AndSelf()
|
|
||||||
- Union(), which is an alias for AddSelection()
|
|
||||||
|
|
||||||
* filter.go : filtering methods, that reduce the selection's set.
|
|
||||||
- End()
|
|
||||||
- Filter...()
|
|
||||||
- Has...()
|
|
||||||
- Intersection(), which is an alias of FilterSelection()
|
|
||||||
- Not...()
|
|
||||||
|
|
||||||
* iteration.go : methods to loop over the selection's nodes.
|
|
||||||
- Each()
|
|
||||||
- EachWithBreak()
|
|
||||||
- Map()
|
|
||||||
|
|
||||||
* manipulation.go : methods for modifying the document
|
|
||||||
- After...()
|
|
||||||
- Append...()
|
|
||||||
- Before...()
|
|
||||||
- Clone()
|
|
||||||
- Empty()
|
|
||||||
- Prepend...()
|
|
||||||
- Remove...()
|
|
||||||
- ReplaceWith...()
|
|
||||||
- Unwrap()
|
|
||||||
- Wrap...()
|
|
||||||
- WrapAll...()
|
|
||||||
- WrapInner...()
|
|
||||||
|
|
||||||
* property.go : methods that inspect and get the node's properties values.
|
|
||||||
- Attr*(), RemoveAttr(), SetAttr()
|
|
||||||
- AddClass(), HasClass(), RemoveClass(), ToggleClass()
|
|
||||||
- Html()
|
|
||||||
- Length()
|
|
||||||
- Size(), which is an alias for Length()
|
|
||||||
- Text()
|
|
||||||
|
|
||||||
* query.go : methods that query, or reflect, a node's identity.
|
|
||||||
- Contains()
|
|
||||||
- Is...()
|
|
||||||
|
|
||||||
* traversal.go : methods to traverse the HTML document tree.
|
|
||||||
- Children...()
|
|
||||||
- Contents()
|
|
||||||
- Find...()
|
|
||||||
- Next...()
|
|
||||||
- Parent[s]...()
|
|
||||||
- Prev...()
|
|
||||||
- Siblings...()
|
|
||||||
|
|
||||||
* type.go : definition of the types exposed by goquery.
|
|
||||||
- Document
|
|
||||||
- Selection
|
|
||||||
- Matcher
|
|
||||||
|
|
||||||
* utilities.go : definition of helper functions (and not methods on a *Selection)
|
|
||||||
that are not part of jQuery, but are useful to goquery.
|
|
||||||
- NodeName
|
|
||||||
- OuterHtml
|
|
||||||
*/
|
|
||||||
package goquery
|
|
70
vendor/github.com/PuerkitoBio/goquery/expand.go
generated
vendored
70
vendor/github.com/PuerkitoBio/goquery/expand.go
generated
vendored
|
@ -1,70 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import "golang.org/x/net/html"
|
|
||||||
|
|
||||||
// Add adds the selector string's matching nodes to those in the current
|
|
||||||
// selection and returns a new Selection object.
|
|
||||||
// The selector string is run in the context of the document of the current
|
|
||||||
// Selection object.
|
|
||||||
func (s *Selection) Add(selector string) *Selection {
|
|
||||||
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMatcher adds the matcher's matching nodes to those in the current
|
|
||||||
// selection and returns a new Selection object.
|
|
||||||
// The matcher is run in the context of the document of the current
|
|
||||||
// Selection object.
|
|
||||||
func (s *Selection) AddMatcher(m Matcher) *Selection {
|
|
||||||
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSelection adds the specified Selection object's nodes to those in the
|
|
||||||
// current selection and returns a new Selection object.
|
|
||||||
func (s *Selection) AddSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.AddNodes()
|
|
||||||
}
|
|
||||||
return s.AddNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Union is an alias for AddSelection.
|
|
||||||
func (s *Selection) Union(sel *Selection) *Selection {
|
|
||||||
return s.AddSelection(sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNodes adds the specified nodes to those in the
|
|
||||||
// current selection and returns a new Selection object.
|
|
||||||
func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AndSelf adds the previous set of elements on the stack to the current set.
|
|
||||||
// It returns a new Selection object containing the current Selection combined
|
|
||||||
// with the previous one.
|
|
||||||
// Deprecated: This function has been deprecated and is now an alias for AddBack().
|
|
||||||
func (s *Selection) AndSelf() *Selection {
|
|
||||||
return s.AddBack()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBack adds the previous set of elements on the stack to the current set.
|
|
||||||
// It returns a new Selection object containing the current Selection combined
|
|
||||||
// with the previous one.
|
|
||||||
func (s *Selection) AddBack() *Selection {
|
|
||||||
return s.AddSelection(s.prevSel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBackFiltered reduces the previous set of elements on the stack to those that
|
|
||||||
// match the selector string, and adds them to the current set.
|
|
||||||
// It returns a new Selection object containing the current Selection combined
|
|
||||||
// with the filtered previous one
|
|
||||||
func (s *Selection) AddBackFiltered(selector string) *Selection {
|
|
||||||
return s.AddSelection(s.prevSel.Filter(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBackMatcher reduces the previous set of elements on the stack to those that match
|
|
||||||
// the mateher, and adds them to the curernt set.
|
|
||||||
// It returns a new Selection object containing the current Selection combined
|
|
||||||
// with the filtered previous one
|
|
||||||
func (s *Selection) AddBackMatcher(m Matcher) *Selection {
|
|
||||||
return s.AddSelection(s.prevSel.FilterMatcher(m))
|
|
||||||
}
|
|
163
vendor/github.com/PuerkitoBio/goquery/filter.go
generated
vendored
163
vendor/github.com/PuerkitoBio/goquery/filter.go
generated
vendored
|
@ -1,163 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import "golang.org/x/net/html"
|
|
||||||
|
|
||||||
// Filter reduces the set of matched elements to those that match the selector string.
|
|
||||||
// It returns a new Selection object for this subset of matching elements.
|
|
||||||
func (s *Selection) Filter(selector string) *Selection {
|
|
||||||
return s.FilterMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterMatcher reduces the set of matched elements to those that match
|
|
||||||
// the given matcher. It returns a new Selection object for this subset
|
|
||||||
// of matching elements.
|
|
||||||
func (s *Selection) FilterMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, winnow(s, m, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not removes elements from the Selection that match the selector string.
|
|
||||||
// It returns a new Selection object with the matching elements removed.
|
|
||||||
func (s *Selection) Not(selector string) *Selection {
|
|
||||||
return s.NotMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotMatcher removes elements from the Selection that match the given matcher.
|
|
||||||
// It returns a new Selection object with the matching elements removed.
|
|
||||||
func (s *Selection) NotMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, winnow(s, m, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterFunction reduces the set of matched elements to those that pass the function's test.
|
|
||||||
// It returns a new Selection object for this subset of elements.
|
|
||||||
func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
|
|
||||||
return pushStack(s, winnowFunction(s, f, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFunction removes elements from the Selection that pass the function's test.
|
|
||||||
// It returns a new Selection object with the matching elements removed.
|
|
||||||
func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
|
|
||||||
return pushStack(s, winnowFunction(s, f, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterNodes reduces the set of matched elements to those that match the specified nodes.
|
|
||||||
// It returns a new Selection object for this subset of elements.
|
|
||||||
func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, winnowNodes(s, nodes, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotNodes removes elements from the Selection that match the specified nodes.
|
|
||||||
// It returns a new Selection object with the matching elements removed.
|
|
||||||
func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, winnowNodes(s, nodes, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterSelection reduces the set of matched elements to those that match a
|
|
||||||
// node in the specified Selection object.
|
|
||||||
// It returns a new Selection object for this subset of elements.
|
|
||||||
func (s *Selection) FilterSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return pushStack(s, winnowNodes(s, nil, true))
|
|
||||||
}
|
|
||||||
return pushStack(s, winnowNodes(s, sel.Nodes, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotSelection removes elements from the Selection that match a node in the specified
|
|
||||||
// Selection object. It returns a new Selection object with the matching elements removed.
|
|
||||||
func (s *Selection) NotSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return pushStack(s, winnowNodes(s, nil, false))
|
|
||||||
}
|
|
||||||
return pushStack(s, winnowNodes(s, sel.Nodes, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection is an alias for FilterSelection.
|
|
||||||
func (s *Selection) Intersection(sel *Selection) *Selection {
|
|
||||||
return s.FilterSelection(sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has reduces the set of matched elements to those that have a descendant
|
|
||||||
// that matches the selector.
|
|
||||||
// It returns a new Selection object with the matching elements.
|
|
||||||
func (s *Selection) Has(selector string) *Selection {
|
|
||||||
return s.HasSelection(s.document.Find(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasMatcher reduces the set of matched elements to those that have a descendant
|
|
||||||
// that matches the matcher.
|
|
||||||
// It returns a new Selection object with the matching elements.
|
|
||||||
func (s *Selection) HasMatcher(m Matcher) *Selection {
|
|
||||||
return s.HasSelection(s.document.FindMatcher(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasNodes reduces the set of matched elements to those that have a
|
|
||||||
// descendant that matches one of the nodes.
|
|
||||||
// It returns a new Selection object with the matching elements.
|
|
||||||
func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return s.FilterFunction(func(_ int, sel *Selection) bool {
|
|
||||||
// Add all nodes that contain one of the specified nodes
|
|
||||||
for _, n := range nodes {
|
|
||||||
if sel.Contains(n) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasSelection reduces the set of matched elements to those that have a
|
|
||||||
// descendant that matches one of the nodes of the specified Selection object.
|
|
||||||
// It returns a new Selection object with the matching elements.
|
|
||||||
func (s *Selection) HasSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.HasNodes()
|
|
||||||
}
|
|
||||||
return s.HasNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End ends the most recent filtering operation in the current chain and
|
|
||||||
// returns the set of matched elements to its previous state.
|
|
||||||
func (s *Selection) End() *Selection {
|
|
||||||
if s.prevSel != nil {
|
|
||||||
return s.prevSel
|
|
||||||
}
|
|
||||||
return newEmptySelection(s.document)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter based on the matcher, and the indicator to keep (Filter) or
|
|
||||||
// to get rid of (Not) the matching elements.
|
|
||||||
func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
|
|
||||||
// Optimize if keep is requested
|
|
||||||
if keep {
|
|
||||||
return m.Filter(sel.Nodes)
|
|
||||||
}
|
|
||||||
// Use grep
|
|
||||||
return grep(sel, func(i int, s *Selection) bool {
|
|
||||||
return !m.Match(s.Get(0))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter based on an array of nodes, and the indicator to keep (Filter) or
|
|
||||||
// to get rid of (Not) the matching elements.
|
|
||||||
func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
|
|
||||||
if len(nodes)+len(sel.Nodes) < minNodesForSet {
|
|
||||||
return grep(sel, func(i int, s *Selection) bool {
|
|
||||||
return isInSlice(nodes, s.Get(0)) == keep
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
set := make(map[*html.Node]bool)
|
|
||||||
for _, n := range nodes {
|
|
||||||
set[n] = true
|
|
||||||
}
|
|
||||||
return grep(sel, func(i int, s *Selection) bool {
|
|
||||||
return set[s.Get(0)] == keep
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter based on a function test, and the indicator to keep (Filter) or
|
|
||||||
// to get rid of (Not) the matching elements.
|
|
||||||
func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
|
|
||||||
return grep(sel, func(i int, s *Selection) bool {
|
|
||||||
return f(i, s) == keep
|
|
||||||
})
|
|
||||||
}
|
|
8
vendor/github.com/PuerkitoBio/goquery/go.mod
generated
vendored
8
vendor/github.com/PuerkitoBio/goquery/go.mod
generated
vendored
|
@ -1,8 +0,0 @@
|
||||||
module github.com/PuerkitoBio/goquery
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/andybalholm/cascadia v1.3.1
|
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
|
|
||||||
)
|
|
||||||
|
|
||||||
go 1.13
|
|
9
vendor/github.com/PuerkitoBio/goquery/go.sum
generated
vendored
9
vendor/github.com/PuerkitoBio/goquery/go.sum
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
|
||||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
39
vendor/github.com/PuerkitoBio/goquery/iteration.go
generated
vendored
39
vendor/github.com/PuerkitoBio/goquery/iteration.go
generated
vendored
|
@ -1,39 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
// Each iterates over a Selection object, executing a function for each
|
|
||||||
// matched element. It returns the current Selection object. The function
|
|
||||||
// f is called for each element in the selection with the index of the
|
|
||||||
// element in that selection starting at 0, and a *Selection that contains
|
|
||||||
// only that element.
|
|
||||||
func (s *Selection) Each(f func(int, *Selection)) *Selection {
|
|
||||||
for i, n := range s.Nodes {
|
|
||||||
f(i, newSingleSelection(n, s.document))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachWithBreak iterates over a Selection object, executing a function for each
|
|
||||||
// matched element. It is identical to Each except that it is possible to break
|
|
||||||
// out of the loop by returning false in the callback function. It returns the
|
|
||||||
// current Selection object.
|
|
||||||
func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
|
|
||||||
for i, n := range s.Nodes {
|
|
||||||
if !f(i, newSingleSelection(n, s.document)) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map passes each element in the current matched set through a function,
|
|
||||||
// producing a slice of string holding the returned values. The function
|
|
||||||
// f is called for each element in the selection with the index of the
|
|
||||||
// element in that selection starting at 0, and a *Selection that contains
|
|
||||||
// only that element.
|
|
||||||
func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
|
|
||||||
for i, n := range s.Nodes {
|
|
||||||
result = append(result, f(i, newSingleSelection(n, s.document)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
679
vendor/github.com/PuerkitoBio/goquery/manipulation.go
generated
vendored
679
vendor/github.com/PuerkitoBio/goquery/manipulation.go
generated
vendored
|
@ -1,679 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
// After applies the selector from the root document and inserts the matched elements
|
|
||||||
// after the elements in the set of matched elements.
|
|
||||||
//
|
|
||||||
// If one of the matched elements in the selection is not currently in the
|
|
||||||
// document, it's impossible to insert nodes after it, so it will be ignored.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) After(selector string) *Selection {
|
|
||||||
return s.AfterMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterMatcher applies the matcher from the root document and inserts the matched elements
|
|
||||||
// after the elements in the set of matched elements.
|
|
||||||
//
|
|
||||||
// If one of the matched elements in the selection is not currently in the
|
|
||||||
// document, it's impossible to insert nodes after it, so it will be ignored.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AfterMatcher(m Matcher) *Selection {
|
|
||||||
return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterSelection inserts the elements in the selection after each element in the set of matched
|
|
||||||
// elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AfterSelection(sel *Selection) *Selection {
|
|
||||||
return s.AfterNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterHtml parses the html and inserts it after the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AfterHtml(htmlStr string) *Selection {
|
|
||||||
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
nextSibling := node.NextSibling
|
|
||||||
for _, n := range nodes {
|
|
||||||
if node.Parent != nil {
|
|
||||||
node.Parent.InsertBefore(n, nextSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterNodes inserts the nodes after each element in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
|
|
||||||
return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
|
|
||||||
if sn.Parent != nil {
|
|
||||||
sn.Parent.InsertBefore(n, sn.NextSibling)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append appends the elements specified by the selector to the end of each element
|
|
||||||
// in the set of matched elements, following those rules:
|
|
||||||
//
|
|
||||||
// 1) The selector is applied to the root document.
|
|
||||||
//
|
|
||||||
// 2) Elements that are part of the document will be moved to the new location.
|
|
||||||
//
|
|
||||||
// 3) If there are multiple locations to append to, cloned nodes will be
|
|
||||||
// appended to all target locations except the last one, which will be moved
|
|
||||||
// as noted in (2).
|
|
||||||
func (s *Selection) Append(selector string) *Selection {
|
|
||||||
return s.AppendMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendMatcher appends the elements specified by the matcher to the end of each element
|
|
||||||
// in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AppendMatcher(m Matcher) *Selection {
|
|
||||||
return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendSelection appends the elements in the selection to the end of each element
|
|
||||||
// in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AppendSelection(sel *Selection) *Selection {
|
|
||||||
return s.AppendNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendHtml parses the html and appends it to the set of matched elements.
|
|
||||||
func (s *Selection) AppendHtml(htmlStr string) *Selection {
|
|
||||||
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
for _, n := range nodes {
|
|
||||||
node.AppendChild(n)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendNodes appends the specified nodes to each node in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
|
|
||||||
return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
|
|
||||||
sn.AppendChild(n)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before inserts the matched elements before each element in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) Before(selector string) *Selection {
|
|
||||||
return s.BeforeMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeMatcher inserts the matched elements before each element in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) BeforeMatcher(m Matcher) *Selection {
|
|
||||||
return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeSelection inserts the elements in the selection before each element in the set of matched
|
|
||||||
// elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) BeforeSelection(sel *Selection) *Selection {
|
|
||||||
return s.BeforeNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeHtml parses the html and inserts it before the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) BeforeHtml(htmlStr string) *Selection {
|
|
||||||
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
for _, n := range nodes {
|
|
||||||
if node.Parent != nil {
|
|
||||||
node.Parent.InsertBefore(n, node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeNodes inserts the nodes before each element in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
|
|
||||||
return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
|
|
||||||
if sn.Parent != nil {
|
|
||||||
sn.Parent.InsertBefore(n, sn)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone creates a deep copy of the set of matched nodes. The new nodes will not be
|
|
||||||
// attached to the document.
|
|
||||||
func (s *Selection) Clone() *Selection {
|
|
||||||
ns := newEmptySelection(s.document)
|
|
||||||
ns.Nodes = cloneNodes(s.Nodes)
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty removes all children nodes from the set of matched elements.
|
|
||||||
// It returns the children nodes in a new Selection.
|
|
||||||
func (s *Selection) Empty() *Selection {
|
|
||||||
var nodes []*html.Node
|
|
||||||
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
for c := n.FirstChild; c != nil; c = n.FirstChild {
|
|
||||||
n.RemoveChild(c)
|
|
||||||
nodes = append(nodes, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pushStack(s, nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepend prepends the elements specified by the selector to each element in
|
|
||||||
// the set of matched elements, following the same rules as Append.
|
|
||||||
func (s *Selection) Prepend(selector string) *Selection {
|
|
||||||
return s.PrependMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrependMatcher prepends the elements specified by the matcher to each
|
|
||||||
// element in the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) PrependMatcher(m Matcher) *Selection {
|
|
||||||
return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrependSelection prepends the elements in the selection to each element in
|
|
||||||
// the set of matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) PrependSelection(sel *Selection) *Selection {
|
|
||||||
return s.PrependNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrependHtml parses the html and prepends it to the set of matched elements.
|
|
||||||
func (s *Selection) PrependHtml(htmlStr string) *Selection {
|
|
||||||
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
firstChild := node.FirstChild
|
|
||||||
for _, n := range nodes {
|
|
||||||
node.InsertBefore(n, firstChild)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrependNodes prepends the specified nodes to each node in the set of
|
|
||||||
// matched elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
|
|
||||||
return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
|
|
||||||
// sn.FirstChild may be nil, in which case this functions like
|
|
||||||
// sn.AppendChild()
|
|
||||||
sn.InsertBefore(n, sn.FirstChild)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the set of matched elements from the document.
|
|
||||||
// It returns the same selection, now consisting of nodes not in the document.
|
|
||||||
func (s *Selection) Remove() *Selection {
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
if n.Parent != nil {
|
|
||||||
n.Parent.RemoveChild(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveFiltered removes from the current set of matched elements those that
|
|
||||||
// match the selector filter. It returns the Selection of removed nodes.
|
|
||||||
//
|
|
||||||
// For example if the selection s contains "<h1>", "<h2>" and "<h3>"
|
|
||||||
// and s.RemoveFiltered("h2") is called, only the "<h2>" node is removed
|
|
||||||
// (and returned), while "<h1>" and "<h3>" are kept in the document.
|
|
||||||
func (s *Selection) RemoveFiltered(selector string) *Selection {
|
|
||||||
return s.RemoveMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveMatcher removes from the current set of matched elements those that
|
|
||||||
// match the Matcher filter. It returns the Selection of removed nodes.
|
|
||||||
// See RemoveFiltered for additional information.
|
|
||||||
func (s *Selection) RemoveMatcher(m Matcher) *Selection {
|
|
||||||
return s.FilterMatcher(m).Remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceWith replaces each element in the set of matched elements with the
|
|
||||||
// nodes matched by the given selector.
|
|
||||||
// It returns the removed elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) ReplaceWith(selector string) *Selection {
|
|
||||||
return s.ReplaceWithMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceWithMatcher replaces each element in the set of matched elements with
|
|
||||||
// the nodes matched by the given Matcher.
|
|
||||||
// It returns the removed elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
|
|
||||||
return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceWithSelection replaces each element in the set of matched elements with
|
|
||||||
// the nodes from the given Selection.
|
|
||||||
// It returns the removed elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
|
|
||||||
return s.ReplaceWithNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceWithHtml replaces each element in the set of matched elements with
|
|
||||||
// the parsed HTML.
|
|
||||||
// It returns the removed elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection {
|
|
||||||
s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
nextSibling := node.NextSibling
|
|
||||||
for _, n := range nodes {
|
|
||||||
if node.Parent != nil {
|
|
||||||
node.Parent.InsertBefore(n, nextSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return s.Remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceWithNodes replaces each element in the set of matched elements with
|
|
||||||
// the given nodes.
|
|
||||||
// It returns the removed elements.
|
|
||||||
//
|
|
||||||
// This follows the same rules as Selection.Append.
|
|
||||||
func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
|
|
||||||
s.AfterNodes(ns...)
|
|
||||||
return s.Remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHtml sets the html content of each element in the selection to
|
|
||||||
// specified html string.
|
|
||||||
func (s *Selection) SetHtml(htmlStr string) *Selection {
|
|
||||||
for _, context := range s.Nodes {
|
|
||||||
for c := context.FirstChild; c != nil; c = context.FirstChild {
|
|
||||||
context.RemoveChild(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
|
|
||||||
for _, n := range nodes {
|
|
||||||
node.AppendChild(n)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetText sets the content of each element in the selection to specified content.
|
|
||||||
// The provided text string is escaped.
|
|
||||||
func (s *Selection) SetText(text string) *Selection {
|
|
||||||
return s.SetHtml(html.EscapeString(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap removes the parents of the set of matched elements, leaving the matched
|
|
||||||
// elements (and their siblings, if any) in their place.
|
|
||||||
// It returns the original selection.
|
|
||||||
func (s *Selection) Unwrap() *Selection {
|
|
||||||
s.Parent().Each(func(i int, ss *Selection) {
|
|
||||||
// For some reason, jquery allows unwrap to remove the <head> element, so
|
|
||||||
// allowing it here too. Same for <html>. Why it allows those elements to
|
|
||||||
// be unwrapped while not allowing body is a mystery to me.
|
|
||||||
if ss.Nodes[0].Data != "body" {
|
|
||||||
ss.ReplaceWithSelection(ss.Contents())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap wraps each element in the set of matched elements inside the first
|
|
||||||
// element matched by the given selector. The matched child is cloned before
|
|
||||||
// being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) Wrap(selector string) *Selection {
|
|
||||||
return s.WrapMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapMatcher wraps each element in the set of matched elements inside the
|
|
||||||
// first element matched by the given matcher. The matched child is cloned
|
|
||||||
// before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapMatcher(m Matcher) *Selection {
|
|
||||||
return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapSelection wraps each element in the set of matched elements inside the
|
|
||||||
// first element in the given Selection. The element is cloned before being
|
|
||||||
// inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapSelection(sel *Selection) *Selection {
|
|
||||||
return s.wrapNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapHtml wraps each element in the set of matched elements inside the inner-
|
|
||||||
// most child of the given HTML.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapHtml(htmlStr string) *Selection {
|
|
||||||
nodesMap := make(map[string][]*html.Node)
|
|
||||||
for _, context := range s.Nodes {
|
|
||||||
var parent *html.Node
|
|
||||||
if context.Parent != nil {
|
|
||||||
parent = context.Parent
|
|
||||||
} else {
|
|
||||||
parent = &html.Node{Type: html.ElementNode}
|
|
||||||
}
|
|
||||||
nodes, found := nodesMap[nodeName(parent)]
|
|
||||||
if !found {
|
|
||||||
nodes = parseHtmlWithContext(htmlStr, parent)
|
|
||||||
nodesMap[nodeName(parent)] = nodes
|
|
||||||
}
|
|
||||||
newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapNode wraps each element in the set of matched elements inside the inner-
|
|
||||||
// most child of the given node. The given node is copied before being inserted
|
|
||||||
// into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapNode(n *html.Node) *Selection {
|
|
||||||
return s.wrapNodes(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
|
|
||||||
s.Each(func(i int, ss *Selection) {
|
|
||||||
ss.wrapAllNodes(ns...)
|
|
||||||
})
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapAll wraps a single HTML structure, matched by the given selector, around
|
|
||||||
// all elements in the set of matched elements. The matched child is cloned
|
|
||||||
// before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapAll(selector string) *Selection {
|
|
||||||
return s.WrapAllMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
|
|
||||||
// around all elements in the set of matched elements. The matched child is
|
|
||||||
// cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
|
|
||||||
return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapAllSelection wraps a single HTML structure, the first node of the given
|
|
||||||
// Selection, around all elements in the set of matched elements. The matched
|
|
||||||
// child is cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
|
|
||||||
return s.wrapAllNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapAllHtml wraps the given HTML structure around all elements in the set of
|
|
||||||
// matched elements. The matched child is cloned before being inserted into the
|
|
||||||
// document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapAllHtml(htmlStr string) *Selection {
|
|
||||||
var context *html.Node
|
|
||||||
var nodes []*html.Node
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
context = s.Nodes[0]
|
|
||||||
if context.Parent != nil {
|
|
||||||
nodes = parseHtmlWithContext(htmlStr, context)
|
|
||||||
} else {
|
|
||||||
nodes = parseHtml(htmlStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.wrapAllNodes(nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
|
|
||||||
if len(ns) > 0 {
|
|
||||||
return s.WrapAllNode(ns[0])
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapAllNode wraps the given node around the first element in the Selection,
|
|
||||||
// making all other nodes in the Selection children of the given node. The node
|
|
||||||
// is cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapAllNode(n *html.Node) *Selection {
|
|
||||||
if s.Size() == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
wrap := cloneNode(n)
|
|
||||||
|
|
||||||
first := s.Nodes[0]
|
|
||||||
if first.Parent != nil {
|
|
||||||
first.Parent.InsertBefore(wrap, first)
|
|
||||||
first.Parent.RemoveChild(first)
|
|
||||||
}
|
|
||||||
|
|
||||||
for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
|
|
||||||
wrap = c
|
|
||||||
}
|
|
||||||
|
|
||||||
newSingleSelection(wrap, s.document).AppendSelection(s)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInner wraps an HTML structure, matched by the given selector, around the
|
|
||||||
// content of element in the set of matched elements. The matched child is
|
|
||||||
// cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapInner(selector string) *Selection {
|
|
||||||
return s.WrapInnerMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInnerMatcher wraps an HTML structure, matched by the given selector,
|
|
||||||
// around the content of element in the set of matched elements. The matched
|
|
||||||
// child is cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
|
|
||||||
return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInnerSelection wraps an HTML structure, matched by the given selector,
|
|
||||||
// around the content of element in the set of matched elements. The matched
|
|
||||||
// child is cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
|
|
||||||
return s.wrapInnerNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInnerHtml wraps an HTML structure, matched by the given selector, around
|
|
||||||
// the content of element in the set of matched elements. The matched child is
|
|
||||||
// cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapInnerHtml(htmlStr string) *Selection {
|
|
||||||
nodesMap := make(map[string][]*html.Node)
|
|
||||||
for _, context := range s.Nodes {
|
|
||||||
nodes, found := nodesMap[nodeName(context)]
|
|
||||||
if !found {
|
|
||||||
nodes = parseHtmlWithContext(htmlStr, context)
|
|
||||||
nodesMap[nodeName(context)] = nodes
|
|
||||||
}
|
|
||||||
newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInnerNode wraps an HTML structure, matched by the given selector, around
|
|
||||||
// the content of element in the set of matched elements. The matched child is
|
|
||||||
// cloned before being inserted into the document.
|
|
||||||
//
|
|
||||||
// It returns the original set of elements.
|
|
||||||
func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
|
|
||||||
return s.wrapInnerNodes(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
|
|
||||||
if len(ns) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Each(func(i int, s *Selection) {
|
|
||||||
contents := s.Contents()
|
|
||||||
|
|
||||||
if contents.Size() > 0 {
|
|
||||||
contents.wrapAllNodes(ns...)
|
|
||||||
} else {
|
|
||||||
s.AppendNodes(cloneNode(ns[0]))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseHtml(h string) []*html.Node {
|
|
||||||
// Errors are only returned when the io.Reader returns any error besides
|
|
||||||
// EOF, but strings.Reader never will
|
|
||||||
nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
|
|
||||||
if err != nil {
|
|
||||||
panic("goquery: failed to parse HTML: " + err.Error())
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseHtmlWithContext(h string, context *html.Node) []*html.Node {
|
|
||||||
// Errors are only returned when the io.Reader returns any error besides
|
|
||||||
// EOF, but strings.Reader never will
|
|
||||||
nodes, err := html.ParseFragment(strings.NewReader(h), context)
|
|
||||||
if err != nil {
|
|
||||||
panic("goquery: failed to parse HTML: " + err.Error())
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first child that is an ElementNode
|
|
||||||
func getFirstChildEl(n *html.Node) *html.Node {
|
|
||||||
c := n.FirstChild
|
|
||||||
for c != nil && c.Type != html.ElementNode {
|
|
||||||
c = c.NextSibling
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deep copy a slice of nodes.
|
|
||||||
func cloneNodes(ns []*html.Node) []*html.Node {
|
|
||||||
cns := make([]*html.Node, 0, len(ns))
|
|
||||||
|
|
||||||
for _, n := range ns {
|
|
||||||
cns = append(cns, cloneNode(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
return cns
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deep copy a node. The new node has clones of all the original node's
|
|
||||||
// children but none of its parents or siblings.
|
|
||||||
func cloneNode(n *html.Node) *html.Node {
|
|
||||||
nn := &html.Node{
|
|
||||||
Type: n.Type,
|
|
||||||
DataAtom: n.DataAtom,
|
|
||||||
Data: n.Data,
|
|
||||||
Attr: make([]html.Attribute, len(n.Attr)),
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(nn.Attr, n.Attr)
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
nn.AppendChild(cloneNode(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
|
|
||||||
f func(sn *html.Node, n *html.Node)) *Selection {
|
|
||||||
|
|
||||||
lasti := s.Size() - 1
|
|
||||||
|
|
||||||
// net.Html doesn't provide document fragments for insertion, so to get
|
|
||||||
// things in the correct order with After() and Prepend(), the callback
|
|
||||||
// needs to be called on the reverse of the nodes.
|
|
||||||
if reverse {
|
|
||||||
for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
ns[i], ns[j] = ns[j], ns[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, sn := range s.Nodes {
|
|
||||||
for _, n := range ns {
|
|
||||||
if i != lasti {
|
|
||||||
f(sn, cloneNode(n))
|
|
||||||
} else {
|
|
||||||
if n.Parent != nil {
|
|
||||||
n.Parent.RemoveChild(n)
|
|
||||||
}
|
|
||||||
f(sn, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// eachNodeHtml parses the given html string and inserts the resulting nodes in the dom with the mergeFn.
|
|
||||||
// The parsed nodes are inserted for each element of the selection.
|
|
||||||
// isParent can be used to indicate that the elements of the selection should be treated as the parent for the parsed html.
|
|
||||||
// A cache is used to avoid parsing the html multiple times should the elements of the selection result in the same context.
|
|
||||||
func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n *html.Node, nodes []*html.Node)) *Selection {
|
|
||||||
// cache to avoid parsing the html for the same context multiple times
|
|
||||||
nodeCache := make(map[string][]*html.Node)
|
|
||||||
var context *html.Node
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
if isParent {
|
|
||||||
context = n.Parent
|
|
||||||
} else {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
context = n
|
|
||||||
}
|
|
||||||
if context != nil {
|
|
||||||
nodes, found := nodeCache[nodeName(context)]
|
|
||||||
if !found {
|
|
||||||
nodes = parseHtmlWithContext(htmlStr, context)
|
|
||||||
nodeCache[nodeName(context)] = nodes
|
|
||||||
}
|
|
||||||
mergeFn(n, cloneNodes(nodes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
275
vendor/github.com/PuerkitoBio/goquery/property.go
generated
vendored
275
vendor/github.com/PuerkitoBio/goquery/property.go
generated
vendored
|
@ -1,275 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rxClassTrim = regexp.MustCompile("[\t\r\n]")
|
|
||||||
|
|
||||||
// Attr gets the specified attribute's value for the first element in the
|
|
||||||
// Selection. To get the value for each element individually, use a looping
|
|
||||||
// construct such as Each or Map method.
|
|
||||||
func (s *Selection) Attr(attrName string) (val string, exists bool) {
|
|
||||||
if len(s.Nodes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return getAttributeValue(attrName, s.Nodes[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttrOr works like Attr but returns default value if attribute is not present.
|
|
||||||
func (s *Selection) AttrOr(attrName, defaultValue string) string {
|
|
||||||
if len(s.Nodes) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, exists := getAttributeValue(attrName, s.Nodes[0])
|
|
||||||
if !exists {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAttr removes the named attribute from each element in the set of matched elements.
|
|
||||||
func (s *Selection) RemoveAttr(attrName string) *Selection {
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
removeAttr(n, attrName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAttr sets the given attribute on each element in the set of matched elements.
|
|
||||||
func (s *Selection) SetAttr(attrName, val string) *Selection {
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
attr := getAttributePtr(attrName, n)
|
|
||||||
if attr == nil {
|
|
||||||
n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
|
|
||||||
} else {
|
|
||||||
attr.Val = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text gets the combined text contents of each element in the set of matched
|
|
||||||
// elements, including their descendants.
|
|
||||||
func (s *Selection) Text() string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
// Slightly optimized vs calling Each: no single selection object created
|
|
||||||
var f func(*html.Node)
|
|
||||||
f = func(n *html.Node) {
|
|
||||||
if n.Type == html.TextNode {
|
|
||||||
// Keep newlines and spaces, like jQuery
|
|
||||||
buf.WriteString(n.Data)
|
|
||||||
}
|
|
||||||
if n.FirstChild != nil {
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
f(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
f(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size is an alias for Length.
|
|
||||||
func (s *Selection) Size() int {
|
|
||||||
return s.Length()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length returns the number of elements in the Selection object.
|
|
||||||
func (s *Selection) Length() int {
|
|
||||||
return len(s.Nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Html gets the HTML contents of the first element in the set of matched
|
|
||||||
// elements. It includes text and comment nodes.
|
|
||||||
func (s *Selection) Html() (ret string, e error) {
|
|
||||||
// Since there is no .innerHtml, the HTML content must be re-created from
|
|
||||||
// the nodes using html.Render.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
e = html.Render(&buf, c)
|
|
||||||
if e != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddClass adds the given class(es) to each element in the set of matched elements.
|
|
||||||
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
|
||||||
func (s *Selection) AddClass(class ...string) *Selection {
|
|
||||||
classStr := strings.TrimSpace(strings.Join(class, " "))
|
|
||||||
|
|
||||||
if classStr == "" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
tcls := getClassesSlice(classStr)
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
curClasses, attr := getClassesAndAttr(n, true)
|
|
||||||
for _, newClass := range tcls {
|
|
||||||
if !strings.Contains(curClasses, " "+newClass+" ") {
|
|
||||||
curClasses += newClass + " "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setClasses(n, attr, curClasses)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasClass determines whether any of the matched elements are assigned the
|
|
||||||
// given class.
|
|
||||||
func (s *Selection) HasClass(class string) bool {
|
|
||||||
class = " " + class + " "
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
classes, _ := getClassesAndAttr(n, false)
|
|
||||||
if strings.Contains(classes, class) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveClass removes the given class(es) from each element in the set of matched elements.
|
|
||||||
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
|
||||||
// If no class name is provided, all classes are removed.
|
|
||||||
func (s *Selection) RemoveClass(class ...string) *Selection {
|
|
||||||
var rclasses []string
|
|
||||||
|
|
||||||
classStr := strings.TrimSpace(strings.Join(class, " "))
|
|
||||||
remove := classStr == ""
|
|
||||||
|
|
||||||
if !remove {
|
|
||||||
rclasses = getClassesSlice(classStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
if remove {
|
|
||||||
removeAttr(n, "class")
|
|
||||||
} else {
|
|
||||||
classes, attr := getClassesAndAttr(n, true)
|
|
||||||
for _, rcl := range rclasses {
|
|
||||||
classes = strings.Replace(classes, " "+rcl+" ", " ", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
setClasses(n, attr, classes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
|
|
||||||
// Multiple class names can be specified, separated by a space or via multiple arguments.
|
|
||||||
func (s *Selection) ToggleClass(class ...string) *Selection {
|
|
||||||
classStr := strings.TrimSpace(strings.Join(class, " "))
|
|
||||||
|
|
||||||
if classStr == "" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
tcls := getClassesSlice(classStr)
|
|
||||||
|
|
||||||
for _, n := range s.Nodes {
|
|
||||||
classes, attr := getClassesAndAttr(n, true)
|
|
||||||
for _, tcl := range tcls {
|
|
||||||
if strings.Contains(classes, " "+tcl+" ") {
|
|
||||||
classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
|
|
||||||
} else {
|
|
||||||
classes += tcl + " "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setClasses(n, attr, classes)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
|
|
||||||
if n == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, a := range n.Attr {
|
|
||||||
if a.Key == attrName {
|
|
||||||
return &n.Attr[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private function to get the specified attribute's value from a node.
|
|
||||||
func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) {
|
|
||||||
if a := getAttributePtr(attrName, n); a != nil {
|
|
||||||
val = a.Val
|
|
||||||
exists = true
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get and normalize the "class" attribute from the node.
|
|
||||||
func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
|
|
||||||
// Applies only to element nodes
|
|
||||||
if n.Type == html.ElementNode {
|
|
||||||
attr = getAttributePtr("class", n)
|
|
||||||
if attr == nil && create {
|
|
||||||
n.Attr = append(n.Attr, html.Attribute{
|
|
||||||
Key: "class",
|
|
||||||
Val: "",
|
|
||||||
})
|
|
||||||
attr = &n.Attr[len(n.Attr)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if attr == nil {
|
|
||||||
classes = " "
|
|
||||||
} else {
|
|
||||||
classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getClassesSlice(classes string) []string {
|
|
||||||
return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeAttr(n *html.Node, attrName string) {
|
|
||||||
for i, a := range n.Attr {
|
|
||||||
if a.Key == attrName {
|
|
||||||
n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr =
|
|
||||||
n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setClasses(n *html.Node, attr *html.Attribute, classes string) {
|
|
||||||
classes = strings.TrimSpace(classes)
|
|
||||||
if classes == "" {
|
|
||||||
removeAttr(n, "class")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
attr.Val = classes
|
|
||||||
}
|
|
49
vendor/github.com/PuerkitoBio/goquery/query.go
generated
vendored
49
vendor/github.com/PuerkitoBio/goquery/query.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import "golang.org/x/net/html"
|
|
||||||
|
|
||||||
// Is checks the current matched set of elements against a selector and
|
|
||||||
// returns true if at least one of these elements matches.
|
|
||||||
func (s *Selection) Is(selector string) bool {
|
|
||||||
return s.IsMatcher(compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMatcher checks the current matched set of elements against a matcher and
|
|
||||||
// returns true if at least one of these elements matches.
|
|
||||||
func (s *Selection) IsMatcher(m Matcher) bool {
|
|
||||||
if len(s.Nodes) > 0 {
|
|
||||||
if len(s.Nodes) == 1 {
|
|
||||||
return m.Match(s.Nodes[0])
|
|
||||||
}
|
|
||||||
return len(m.Filter(s.Nodes)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFunction checks the current matched set of elements against a predicate and
|
|
||||||
// returns true if at least one of these elements matches.
|
|
||||||
func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
|
|
||||||
return s.FilterFunction(f).Length() > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSelection checks the current matched set of elements against a Selection object
|
|
||||||
// and returns true if at least one of these elements matches.
|
|
||||||
func (s *Selection) IsSelection(sel *Selection) bool {
|
|
||||||
return s.FilterSelection(sel).Length() > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNodes checks the current matched set of elements against the specified nodes
|
|
||||||
// and returns true if at least one of these elements matches.
|
|
||||||
func (s *Selection) IsNodes(nodes ...*html.Node) bool {
|
|
||||||
return s.FilterNodes(nodes...).Length() > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains returns true if the specified Node is within,
|
|
||||||
// at any depth, one of the nodes in the Selection object.
|
|
||||||
// It is NOT inclusive, to behave like jQuery's implementation, and
|
|
||||||
// unlike Javascript's .contains, so if the contained
|
|
||||||
// node is itself in the selection, it returns false.
|
|
||||||
func (s *Selection) Contains(n *html.Node) bool {
|
|
||||||
return sliceContains(s.Nodes, n)
|
|
||||||
}
|
|
698
vendor/github.com/PuerkitoBio/goquery/traversal.go
generated
vendored
698
vendor/github.com/PuerkitoBio/goquery/traversal.go
generated
vendored
|
@ -1,698 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import "golang.org/x/net/html"
|
|
||||||
|
|
||||||
type siblingType int
|
|
||||||
|
|
||||||
// Sibling type, used internally when iterating over children at the same
|
|
||||||
// level (siblings) to specify which nodes are requested.
|
|
||||||
const (
|
|
||||||
siblingPrevUntil siblingType = iota - 3
|
|
||||||
siblingPrevAll
|
|
||||||
siblingPrev
|
|
||||||
siblingAll
|
|
||||||
siblingNext
|
|
||||||
siblingNextAll
|
|
||||||
siblingNextUntil
|
|
||||||
siblingAllIncludingNonElements
|
|
||||||
)
|
|
||||||
|
|
||||||
// Find gets the descendants of each element in the current set of matched
|
|
||||||
// elements, filtered by a selector. It returns a new Selection object
|
|
||||||
// containing these matched elements.
|
|
||||||
func (s *Selection) Find(selector string) *Selection {
|
|
||||||
return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMatcher gets the descendants of each element in the current set of matched
|
|
||||||
// elements, filtered by the matcher. It returns a new Selection object
|
|
||||||
// containing these matched elements.
|
|
||||||
func (s *Selection) FindMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, findWithMatcher(s.Nodes, m))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindSelection gets the descendants of each element in the current
|
|
||||||
// Selection, filtered by a Selection. It returns a new Selection object
|
|
||||||
// containing these matched elements.
|
|
||||||
func (s *Selection) FindSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return pushStack(s, nil)
|
|
||||||
}
|
|
||||||
return s.FindNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindNodes gets the descendants of each element in the current
|
|
||||||
// Selection, filtered by some nodes. It returns a new Selection object
|
|
||||||
// containing these matched elements.
|
|
||||||
func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
if sliceContains(s.Nodes, n) {
|
|
||||||
return []*html.Node{n}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contents gets the children of each element in the Selection,
|
|
||||||
// including text and comment nodes. It returns a new Selection object
|
|
||||||
// containing these elements.
|
|
||||||
func (s *Selection) Contents() *Selection {
|
|
||||||
return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentsFiltered gets the children of each element in the Selection,
|
|
||||||
// filtered by the specified selector. It returns a new Selection
|
|
||||||
// object containing these elements. Since selectors only act on Element nodes,
|
|
||||||
// this function is an alias to ChildrenFiltered unless the selector is empty,
|
|
||||||
// in which case it is an alias to Contents.
|
|
||||||
func (s *Selection) ContentsFiltered(selector string) *Selection {
|
|
||||||
if selector != "" {
|
|
||||||
return s.ChildrenFiltered(selector)
|
|
||||||
}
|
|
||||||
return s.Contents()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentsMatcher gets the children of each element in the Selection,
|
|
||||||
// filtered by the specified matcher. It returns a new Selection
|
|
||||||
// object containing these elements. Since matchers only act on Element nodes,
|
|
||||||
// this function is an alias to ChildrenMatcher.
|
|
||||||
func (s *Selection) ContentsMatcher(m Matcher) *Selection {
|
|
||||||
return s.ChildrenMatcher(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Children gets the child elements of each element in the Selection.
|
|
||||||
// It returns a new Selection object containing these elements.
|
|
||||||
func (s *Selection) Children() *Selection {
|
|
||||||
return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChildrenFiltered gets the child elements of each element in the Selection,
|
|
||||||
// filtered by the specified selector. It returns a new
|
|
||||||
// Selection object containing these elements.
|
|
||||||
func (s *Selection) ChildrenFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChildrenMatcher gets the child elements of each element in the Selection,
|
|
||||||
// filtered by the specified matcher. It returns a new
|
|
||||||
// Selection object containing these elements.
|
|
||||||
func (s *Selection) ChildrenMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent gets the parent of each element in the Selection. It returns a
|
|
||||||
// new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) Parent() *Selection {
|
|
||||||
return pushStack(s, getParentNodes(s.Nodes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentFiltered gets the parent of each element in the Selection filtered by a
|
|
||||||
// selector. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentMatcher gets the parent of each element in the Selection filtered by a
|
|
||||||
// matcher. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getParentNodes(s.Nodes), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closest gets the first element that matches the selector by testing the
|
|
||||||
// element itself and traversing up through its ancestors in the DOM tree.
|
|
||||||
func (s *Selection) Closest(selector string) *Selection {
|
|
||||||
cs := compileMatcher(selector)
|
|
||||||
return s.ClosestMatcher(cs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClosestMatcher gets the first element that matches the matcher by testing the
|
|
||||||
// element itself and traversing up through its ancestors in the DOM tree.
|
|
||||||
func (s *Selection) ClosestMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
// For each node in the selection, test the node itself, then each parent
|
|
||||||
// until a match is found.
|
|
||||||
for ; n != nil; n = n.Parent {
|
|
||||||
if m.Match(n) {
|
|
||||||
return []*html.Node{n}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClosestNodes gets the first element that matches one of the nodes by testing the
|
|
||||||
// element itself and traversing up through its ancestors in the DOM tree.
|
|
||||||
func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
|
|
||||||
set := make(map[*html.Node]bool)
|
|
||||||
for _, n := range nodes {
|
|
||||||
set[n] = true
|
|
||||||
}
|
|
||||||
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
// For each node in the selection, test the node itself, then each parent
|
|
||||||
// until a match is found.
|
|
||||||
for ; n != nil; n = n.Parent {
|
|
||||||
if set[n] {
|
|
||||||
return []*html.Node{n}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClosestSelection gets the first element that matches one of the nodes in the
|
|
||||||
// Selection by testing the element itself and traversing up through its ancestors
|
|
||||||
// in the DOM tree.
|
|
||||||
func (s *Selection) ClosestSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return pushStack(s, nil)
|
|
||||||
}
|
|
||||||
return s.ClosestNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parents gets the ancestors of each element in the current Selection. It
|
|
||||||
// returns a new Selection object with the matched elements.
|
|
||||||
func (s *Selection) Parents() *Selection {
|
|
||||||
return pushStack(s, getParentsNodes(s.Nodes, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFiltered gets the ancestors of each element in the current
|
|
||||||
// Selection. It returns a new Selection object with the matched elements.
|
|
||||||
func (s *Selection) ParentsFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsMatcher gets the ancestors of each element in the current
|
|
||||||
// Selection. It returns a new Selection object with the matched elements.
|
|
||||||
func (s *Selection) ParentsMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsUntil gets the ancestors of each element in the Selection, up to but
|
|
||||||
// not including the element matched by the selector. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsUntil(selector string) *Selection {
|
|
||||||
return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
|
|
||||||
// not including the element matched by the matcher. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, getParentsNodes(s.Nodes, m, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsUntilSelection gets the ancestors of each element in the Selection,
|
|
||||||
// up to but not including the elements in the specified Selection. It returns a
|
|
||||||
// new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.Parents()
|
|
||||||
}
|
|
||||||
return s.ParentsUntilNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsUntilNodes gets the ancestors of each element in the Selection,
|
|
||||||
// up to but not including the specified nodes. It returns a
|
|
||||||
// new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, getParentsNodes(s.Nodes, nil, nodes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFilteredUntil is like ParentsUntil, with the option to filter the
|
|
||||||
// results based on a selector string. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
|
|
||||||
// results based on a matcher. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
|
||||||
return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.ParentsMatcher(filter)
|
|
||||||
}
|
|
||||||
return s.ParentsMatcherUntilNodes(filter, sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Siblings gets the siblings of each element in the Selection. It returns
|
|
||||||
// a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) Siblings() *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiblingsFiltered gets the siblings of each element in the Selection
|
|
||||||
// filtered by a selector. It returns a new Selection object containing the
|
|
||||||
// matched elements.
|
|
||||||
func (s *Selection) SiblingsFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiblingsMatcher gets the siblings of each element in the Selection
|
|
||||||
// filtered by a matcher. It returns a new Selection object containing the
|
|
||||||
// matched elements.
|
|
||||||
func (s *Selection) SiblingsMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next gets the immediately following sibling of each element in the
|
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) Next() *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextFiltered gets the immediately following sibling of each element in the
|
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) NextFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextMatcher gets the immediately following sibling of each element in the
|
|
||||||
// Selection filtered by a matcher. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) NextMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextAll gets all the following siblings of each element in the
|
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextAll() *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextAllFiltered gets all the following siblings of each element in the
|
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) NextAllFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextAllMatcher gets all the following siblings of each element in the
|
|
||||||
// Selection filtered by a matcher. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) NextAllMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prev gets the immediately preceding sibling of each element in the
|
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) Prev() *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevFiltered gets the immediately preceding sibling of each element in the
|
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) PrevFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevMatcher gets the immediately preceding sibling of each element in the
|
|
||||||
// Selection filtered by a matcher. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) PrevMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevAll gets all the preceding siblings of each element in the
|
|
||||||
// Selection. It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevAll() *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevAllFiltered gets all the preceding siblings of each element in the
|
|
||||||
// Selection filtered by a selector. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) PrevAllFiltered(selector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevAllMatcher gets all the preceding siblings of each element in the
|
|
||||||
// Selection filtered by a matcher. It returns a new Selection object
|
|
||||||
// containing the matched elements.
|
|
||||||
func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextUntil gets all following siblings of each element up to but not
|
|
||||||
// including the element matched by the selector. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) NextUntil(selector string) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
compileMatcher(selector), nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextUntilMatcher gets all following siblings of each element up to but not
|
|
||||||
// including the element matched by the matcher. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) NextUntilMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
m, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextUntilSelection gets all following siblings of each element up to but not
|
|
||||||
// including the element matched by the Selection. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.NextAll()
|
|
||||||
}
|
|
||||||
return s.NextUntilNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextUntilNodes gets all following siblings of each element up to but not
|
|
||||||
// including the element matched by the nodes. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
nil, nodes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevUntil gets all preceding siblings of each element up to but not
|
|
||||||
// including the element matched by the selector. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) PrevUntil(selector string) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
compileMatcher(selector), nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevUntilMatcher gets all preceding siblings of each element up to but not
|
|
||||||
// including the element matched by the matcher. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) PrevUntilMatcher(m Matcher) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
m, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevUntilSelection gets all preceding siblings of each element up to but not
|
|
||||||
// including the element matched by the Selection. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.PrevAll()
|
|
||||||
}
|
|
||||||
return s.PrevUntilNodes(sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevUntilNodes gets all preceding siblings of each element up to but not
|
|
||||||
// including the element matched by the nodes. It returns a new Selection
|
|
||||||
// object containing the matched elements.
|
|
||||||
func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
|
|
||||||
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
nil, nodes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextFilteredUntil is like NextUntil, with the option to filter
|
|
||||||
// the results based on a selector string.
|
|
||||||
// It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
|
|
||||||
// the results based on a matcher.
|
|
||||||
// It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
until, nil), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextFilteredUntilSelection is like NextUntilSelection, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
|
||||||
return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextMatcherUntilSelection is like NextUntilSelection, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.NextMatcher(filter)
|
|
||||||
}
|
|
||||||
return s.NextMatcherUntilNodes(filter, sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextFilteredUntilNodes is like NextUntilNodes, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
nil, nodes), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextMatcherUntilNodes is like NextUntilNodes, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
|
|
||||||
nil, nodes), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevFilteredUntil is like PrevUntil, with the option to filter
|
|
||||||
// the results based on a selector string.
|
|
||||||
// It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
|
|
||||||
// the results based on a matcher.
|
|
||||||
// It returns a new Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
until, nil), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevFilteredUntilSelection is like PrevUntilSelection, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
|
|
||||||
return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevMatcherUntilSelection is like PrevUntilSelection, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
|
|
||||||
if sel == nil {
|
|
||||||
return s.PrevMatcher(filter)
|
|
||||||
}
|
|
||||||
return s.PrevMatcherUntilNodes(filter, sel.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevFilteredUntilNodes is like PrevUntilNodes, with the
|
|
||||||
// option to filter the results based on a selector string. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
nil, nodes), compileMatcher(filterSelector))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevMatcherUntilNodes is like PrevUntilNodes, with the
|
|
||||||
// option to filter the results based on a matcher. It returns a new
|
|
||||||
// Selection object containing the matched elements.
|
|
||||||
func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
|
|
||||||
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
|
|
||||||
nil, nodes), filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter and push filters the nodes based on a matcher, and pushes the results
|
|
||||||
// on the stack, with the srcSel as previous selection.
|
|
||||||
func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection {
|
|
||||||
// Create a temporary Selection with the specified nodes to filter using winnow
|
|
||||||
sel := &Selection{nodes, srcSel.document, nil}
|
|
||||||
// Filter based on matcher and push on stack
|
|
||||||
return pushStack(srcSel, winnow(sel, m, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal implementation of Find that return raw nodes.
|
|
||||||
func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node {
|
|
||||||
// Map nodes to find the matches within the children of each node
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
|
||||||
// Go down one level, becausejQuery's Find selects only within descendants
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if c.Type == html.ElementNode {
|
|
||||||
result = append(result, m.MatchAll(c)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal implementation to get all parent nodes, stopping at the specified
|
|
||||||
// node (or nil if no stop).
|
|
||||||
func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node {
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
|
|
||||||
for p := n.Parent; p != nil; p = p.Parent {
|
|
||||||
sel := newSingleSelection(p, nil)
|
|
||||||
if stopm != nil {
|
|
||||||
if sel.IsMatcher(stopm) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if len(stopNodes) > 0 {
|
|
||||||
if sel.IsNodes(stopNodes...) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.Type == html.ElementNode {
|
|
||||||
result = append(result, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal implementation of sibling nodes that return a raw slice of matches.
|
|
||||||
func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node {
|
|
||||||
var f func(*html.Node) bool
|
|
||||||
|
|
||||||
// If the requested siblings are ...Until, create the test function to
|
|
||||||
// determine if the until condition is reached (returns true if it is)
|
|
||||||
if st == siblingNextUntil || st == siblingPrevUntil {
|
|
||||||
f = func(n *html.Node) bool {
|
|
||||||
if untilm != nil {
|
|
||||||
// Matcher-based condition
|
|
||||||
sel := newSingleSelection(n, nil)
|
|
||||||
return sel.IsMatcher(untilm)
|
|
||||||
} else if len(untilNodes) > 0 {
|
|
||||||
// Nodes-based condition
|
|
||||||
sel := newSingleSelection(n, nil)
|
|
||||||
return sel.IsNodes(untilNodes...)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
return getChildrenWithSiblingType(n.Parent, st, n, f)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the children nodes of each node in the specified slice of nodes,
|
|
||||||
// based on the sibling type request.
|
|
||||||
func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
return getChildrenWithSiblingType(n, st, nil, nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the children of the specified parent, based on the requested sibling
|
|
||||||
// type, skipping a specified node if required.
|
|
||||||
func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
|
|
||||||
untilFunc func(*html.Node) bool) (result []*html.Node) {
|
|
||||||
|
|
||||||
// Create the iterator function
|
|
||||||
var iter = func(cur *html.Node) (ret *html.Node) {
|
|
||||||
// Based on the sibling type requested, iterate the right way
|
|
||||||
for {
|
|
||||||
switch st {
|
|
||||||
case siblingAll, siblingAllIncludingNonElements:
|
|
||||||
if cur == nil {
|
|
||||||
// First iteration, start with first child of parent
|
|
||||||
// Skip node if required
|
|
||||||
if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
|
|
||||||
ret = skipNode.NextSibling
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Skip node if required
|
|
||||||
if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
|
|
||||||
ret = skipNode.NextSibling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case siblingPrev, siblingPrevAll, siblingPrevUntil:
|
|
||||||
if cur == nil {
|
|
||||||
// Start with previous sibling of the skip node
|
|
||||||
ret = skipNode.PrevSibling
|
|
||||||
} else {
|
|
||||||
ret = cur.PrevSibling
|
|
||||||
}
|
|
||||||
case siblingNext, siblingNextAll, siblingNextUntil:
|
|
||||||
if cur == nil {
|
|
||||||
// Start with next sibling of the skip node
|
|
||||||
ret = skipNode.NextSibling
|
|
||||||
} else {
|
|
||||||
ret = cur.NextSibling
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("Invalid sibling type.")
|
|
||||||
}
|
|
||||||
if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Not a valid node, try again from this one
|
|
||||||
cur = ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for c := iter(nil); c != nil; c = iter(c) {
|
|
||||||
// If this is an ...Until case, test before append (returns true
|
|
||||||
// if the until condition is reached)
|
|
||||||
if st == siblingNextUntil || st == siblingPrevUntil {
|
|
||||||
if untilFunc(c) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = append(result, c)
|
|
||||||
if st == siblingNext || st == siblingPrev {
|
|
||||||
// Only one node was requested (immediate next or previous), so exit
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal implementation of parent nodes that return a raw slice of Nodes.
|
|
||||||
func getParentNodes(nodes []*html.Node) []*html.Node {
|
|
||||||
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
|
|
||||||
if n.Parent != nil && n.Parent.Type == html.ElementNode {
|
|
||||||
return []*html.Node{n.Parent}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal map function used by many traversing methods. Takes the source nodes
|
|
||||||
// to iterate on and the mapping function that returns an array of nodes.
|
|
||||||
// Returns an array of nodes mapped by calling the callback function once for
|
|
||||||
// each node in the source nodes.
|
|
||||||
func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
|
|
||||||
set := make(map[*html.Node]bool)
|
|
||||||
for i, n := range nodes {
|
|
||||||
if vals := f(i, n); len(vals) > 0 {
|
|
||||||
result = appendWithoutDuplicates(result, vals, set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
203
vendor/github.com/PuerkitoBio/goquery/type.go
generated
vendored
203
vendor/github.com/PuerkitoBio/goquery/type.go
generated
vendored
|
@ -1,203 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/andybalholm/cascadia"
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Document represents an HTML document to be manipulated. Unlike jQuery, which
|
|
||||||
// is loaded as part of a DOM document, and thus acts upon its containing
|
|
||||||
// document, GoQuery doesn't know which HTML document to act upon. So it needs
|
|
||||||
// to be told, and that's what the Document class is for. It holds the root
|
|
||||||
// document node to manipulate, and can make selections on this document.
|
|
||||||
type Document struct {
|
|
||||||
*Selection
|
|
||||||
Url *url.URL
|
|
||||||
rootNode *html.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDocumentFromNode is a Document constructor that takes a root html Node
|
|
||||||
// as argument.
|
|
||||||
func NewDocumentFromNode(root *html.Node) *Document {
|
|
||||||
return newDocument(root, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDocument is a Document constructor that takes a string URL as argument.
|
|
||||||
// It loads the specified document, parses it, and stores the root Document
|
|
||||||
// node, ready to be manipulated.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the net/http standard library package to make the request
|
|
||||||
// and validate the response before calling goquery.NewDocumentFromReader
|
|
||||||
// with the response's body.
|
|
||||||
func NewDocument(url string) (*Document, error) {
|
|
||||||
// Load the URL
|
|
||||||
res, e := http.Get(url)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return NewDocumentFromResponse(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDocumentFromReader returns a Document from an io.Reader.
|
|
||||||
// It returns an error as second value if the reader's data cannot be parsed
|
|
||||||
// as html. It does not check if the reader is also an io.Closer, the
|
|
||||||
// provided reader is never closed by this call. It is the responsibility
|
|
||||||
// of the caller to close it if required.
|
|
||||||
func NewDocumentFromReader(r io.Reader) (*Document, error) {
|
|
||||||
root, e := html.Parse(r)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return newDocument(root, nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDocumentFromResponse is another Document constructor that takes an http response as argument.
|
|
||||||
// It loads the specified response's document, parses it, and stores the root Document
|
|
||||||
// node, ready to be manipulated. The response's body is closed on return.
|
|
||||||
//
|
|
||||||
// Deprecated: Use goquery.NewDocumentFromReader with the response's body.
|
|
||||||
func NewDocumentFromResponse(res *http.Response) (*Document, error) {
|
|
||||||
if res == nil {
|
|
||||||
return nil, errors.New("Response is nil")
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.Request == nil {
|
|
||||||
return nil, errors.New("Response.Request is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the HTML into nodes
|
|
||||||
root, e := html.Parse(res.Body)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and fill the document
|
|
||||||
return newDocument(root, res.Request.URL), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloneDocument creates a deep-clone of a document.
|
|
||||||
func CloneDocument(doc *Document) *Document {
|
|
||||||
return newDocument(cloneNode(doc.rootNode), doc.Url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private constructor, make sure all fields are correctly filled.
|
|
||||||
func newDocument(root *html.Node, url *url.URL) *Document {
|
|
||||||
// Create and fill the document
|
|
||||||
d := &Document{nil, url, root}
|
|
||||||
d.Selection = newSingleSelection(root, d)
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selection represents a collection of nodes matching some criteria. The
|
|
||||||
// initial Selection can be created by using Document.Find, and then
|
|
||||||
// manipulated using the jQuery-like chainable syntax and methods.
|
|
||||||
type Selection struct {
|
|
||||||
Nodes []*html.Node
|
|
||||||
document *Document
|
|
||||||
prevSel *Selection
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper constructor to create an empty selection
|
|
||||||
func newEmptySelection(doc *Document) *Selection {
|
|
||||||
return &Selection{nil, doc, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper constructor to create a selection of only one node
|
|
||||||
func newSingleSelection(node *html.Node, doc *Document) *Selection {
|
|
||||||
return &Selection{[]*html.Node{node}, doc, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matcher is an interface that defines the methods to match
|
|
||||||
// HTML nodes against a compiled selector string. Cascadia's
|
|
||||||
// Selector implements this interface.
|
|
||||||
type Matcher interface {
|
|
||||||
Match(*html.Node) bool
|
|
||||||
MatchAll(*html.Node) []*html.Node
|
|
||||||
Filter([]*html.Node) []*html.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single compiles a selector string to a Matcher that stops after the first
|
|
||||||
// match is found.
|
|
||||||
//
|
|
||||||
// By default, Selection.Find and other functions that accept a selector string
|
|
||||||
// to select nodes will use all matches corresponding to that selector. By
|
|
||||||
// using the Matcher returned by Single, at most the first match will be
|
|
||||||
// selected.
|
|
||||||
//
|
|
||||||
// For example, those two statements are semantically equivalent:
|
|
||||||
//
|
|
||||||
// sel1 := doc.Find("a").First()
|
|
||||||
// sel2 := doc.FindMatcher(goquery.Single("a"))
|
|
||||||
//
|
|
||||||
// The one using Single is optimized to be potentially much faster on large
|
|
||||||
// documents.
|
|
||||||
//
|
|
||||||
// Only the behaviour of the MatchAll method of the Matcher interface is
|
|
||||||
// altered compared to standard Matchers. This means that the single-selection
|
|
||||||
// property of the Matcher only applies for Selection methods where the Matcher
|
|
||||||
// is used to select nodes, not to filter or check if a node matches the
|
|
||||||
// Matcher - in those cases, the behaviour of the Matcher is unchanged (e.g.
|
|
||||||
// FilterMatcher(Single("div")) will still result in a Selection with multiple
|
|
||||||
// "div"s if there were many "div"s in the Selection to begin with).
|
|
||||||
func Single(selector string) Matcher {
|
|
||||||
return singleMatcher{compileMatcher(selector)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SingleMatcher returns a Matcher matches the same nodes as m, but that stops
|
|
||||||
// after the first match is found.
|
|
||||||
//
|
|
||||||
// See the documentation of function Single for more details.
|
|
||||||
func SingleMatcher(m Matcher) Matcher {
|
|
||||||
if _, ok := m.(singleMatcher); ok {
|
|
||||||
// m is already a singleMatcher
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
return singleMatcher{m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compileMatcher compiles the selector string s and returns
|
|
||||||
// the corresponding Matcher. If s is an invalid selector string,
|
|
||||||
// it returns a Matcher that fails all matches.
|
|
||||||
func compileMatcher(s string) Matcher {
|
|
||||||
cs, err := cascadia.Compile(s)
|
|
||||||
if err != nil {
|
|
||||||
return invalidMatcher{}
|
|
||||||
}
|
|
||||||
return cs
|
|
||||||
}
|
|
||||||
|
|
||||||
type singleMatcher struct {
|
|
||||||
Matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m singleMatcher) MatchAll(n *html.Node) []*html.Node {
|
|
||||||
// Optimized version - stops finding at the first match (cascadia-compiled
|
|
||||||
// matchers all use this code path).
|
|
||||||
if mm, ok := m.Matcher.(interface{ MatchFirst(*html.Node) *html.Node }); ok {
|
|
||||||
node := mm.MatchFirst(n)
|
|
||||||
if node == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return []*html.Node{node}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback version, for e.g. test mocks that don't provide the MatchFirst
|
|
||||||
// method.
|
|
||||||
nodes := m.Matcher.MatchAll(n)
|
|
||||||
if len(nodes) > 0 {
|
|
||||||
return nodes[:1:1]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidMatcher is a Matcher that always fails to match.
|
|
||||||
type invalidMatcher struct{}
|
|
||||||
|
|
||||||
func (invalidMatcher) Match(n *html.Node) bool { return false }
|
|
||||||
func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil }
|
|
||||||
func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil }
|
|
178
vendor/github.com/PuerkitoBio/goquery/utilities.go
generated
vendored
178
vendor/github.com/PuerkitoBio/goquery/utilities.go
generated
vendored
|
@ -1,178 +0,0 @@
|
||||||
package goquery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
// used to determine if a set (map[*html.Node]bool) should be used
|
|
||||||
// instead of iterating over a slice. The set uses more memory and
|
|
||||||
// is slower than slice iteration for small N.
|
|
||||||
const minNodesForSet = 1000
|
|
||||||
|
|
||||||
var nodeNames = []string{
|
|
||||||
html.ErrorNode: "#error",
|
|
||||||
html.TextNode: "#text",
|
|
||||||
html.DocumentNode: "#document",
|
|
||||||
html.CommentNode: "#comment",
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeName returns the node name of the first element in the selection.
|
|
||||||
// It tries to behave in a similar way as the DOM's nodeName property
|
|
||||||
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
|
|
||||||
//
|
|
||||||
// Go's net/html package defines the following node types, listed with
|
|
||||||
// the corresponding returned value from this function:
|
|
||||||
//
|
|
||||||
// ErrorNode : #error
|
|
||||||
// TextNode : #text
|
|
||||||
// DocumentNode : #document
|
|
||||||
// ElementNode : the element's tag name
|
|
||||||
// CommentNode : #comment
|
|
||||||
// DoctypeNode : the name of the document type
|
|
||||||
//
|
|
||||||
func NodeName(s *Selection) string {
|
|
||||||
if s.Length() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return nodeName(s.Get(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeName returns the node name of the given html node.
|
|
||||||
// See NodeName for additional details on behaviour.
|
|
||||||
func nodeName(node *html.Node) string {
|
|
||||||
if node == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node.Type {
|
|
||||||
case html.ElementNode, html.DoctypeNode:
|
|
||||||
return node.Data
|
|
||||||
default:
|
|
||||||
if int(node.Type) < len(nodeNames) {
|
|
||||||
return nodeNames[node.Type]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render renders the html of the first element from selector and writes it to
|
|
||||||
// the writer. It behaves the same as OuterHtml but writes to w instead of
|
|
||||||
// returning the string.
|
|
||||||
func Render(w io.Writer, s *Selection) error {
|
|
||||||
if s.Length() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
n := s.Get(0)
|
|
||||||
return html.Render(w, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OuterHtml returns the outer HTML rendering of the first item in
|
|
||||||
// the selection - that is, the HTML including the first element's
|
|
||||||
// tag and attributes.
|
|
||||||
//
|
|
||||||
// Unlike InnerHtml, this is a function and not a method on the Selection,
|
|
||||||
// because this is not a jQuery method (in javascript-land, this is
|
|
||||||
// a property provided by the DOM).
|
|
||||||
func OuterHtml(s *Selection) (string, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := Render(&buf, s); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through all container nodes to search for the target node.
|
|
||||||
func sliceContains(container []*html.Node, contained *html.Node) bool {
|
|
||||||
for _, n := range container {
|
|
||||||
if nodeContains(n, contained) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the contained node is within the container node.
|
|
||||||
func nodeContains(container *html.Node, contained *html.Node) bool {
|
|
||||||
// Check if the parent of the contained node is the container node, traversing
|
|
||||||
// upward until the top is reached, or the container is found.
|
|
||||||
for contained = contained.Parent; contained != nil; contained = contained.Parent {
|
|
||||||
if container == contained {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the target node is in the slice of nodes.
|
|
||||||
func isInSlice(slice []*html.Node, node *html.Node) bool {
|
|
||||||
return indexInSlice(slice, node) > -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index of the target node in the slice, or -1.
|
|
||||||
func indexInSlice(slice []*html.Node, node *html.Node) int {
|
|
||||||
if node != nil {
|
|
||||||
for i, n := range slice {
|
|
||||||
if n == node {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends the new nodes to the target slice, making sure no duplicate is added.
|
|
||||||
// There is no check to the original state of the target slice, so it may still
|
|
||||||
// contain duplicates. The target slice is returned because append() may create
|
|
||||||
// a new underlying array. If targetSet is nil, a local set is created with the
|
|
||||||
// target if len(target) + len(nodes) is greater than minNodesForSet.
|
|
||||||
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
|
|
||||||
// if there are not that many nodes, don't use the map, faster to just use nested loops
|
|
||||||
// (unless a non-nil targetSet is passed, in which case the caller knows better).
|
|
||||||
if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
|
|
||||||
for _, n := range nodes {
|
|
||||||
if !isInSlice(target, n) {
|
|
||||||
target = append(target, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
|
|
||||||
// if a targetSet is passed, then assume it is reliable, otherwise create one
|
|
||||||
// and initialize it with the current target contents.
|
|
||||||
if targetSet == nil {
|
|
||||||
targetSet = make(map[*html.Node]bool, len(target))
|
|
||||||
for _, n := range target {
|
|
||||||
targetSet[n] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, n := range nodes {
|
|
||||||
if !targetSet[n] {
|
|
||||||
target = append(target, n)
|
|
||||||
targetSet[n] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through a selection, returning only those nodes that pass the predicate
|
|
||||||
// function.
|
|
||||||
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
|
|
||||||
for i, n := range sel.Nodes {
|
|
||||||
if predicate(i, newSingleSelection(n, sel.document)) {
|
|
||||||
result = append(result, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Selection object based on the specified nodes, and keeps the
|
|
||||||
// source Selection object on the stack (linked list).
|
|
||||||
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
|
|
||||||
result := &Selection{nodes, fromSel.document, fromSel}
|
|
||||||
return result
|
|
||||||
}
|
|
14
vendor/github.com/andybalholm/cascadia/.travis.yml
generated
vendored
14
vendor/github.com/andybalholm/cascadia/.travis.yml
generated
vendored
|
@ -1,14 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get github.com/andybalholm/cascadia
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
24
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
24
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
|
@ -1,24 +0,0 @@
|
||||||
Copyright (c) 2011 Andy Balholm. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
9
vendor/github.com/andybalholm/cascadia/README.md
generated
vendored
9
vendor/github.com/andybalholm/cascadia/README.md
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
# cascadia
|
|
||||||
|
|
||||||
[![](https://travis-ci.org/andybalholm/cascadia.svg)](https://travis-ci.org/andybalholm/cascadia)
|
|
||||||
|
|
||||||
The Cascadia package implements CSS selectors for use with the parse trees produced by the html package.
|
|
||||||
|
|
||||||
To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package.
|
|
||||||
|
|
||||||
[Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia).
|
|
5
vendor/github.com/andybalholm/cascadia/go.mod
generated
vendored
5
vendor/github.com/andybalholm/cascadia/go.mod
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
module github.com/andybalholm/cascadia
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
|
|
7
vendor/github.com/andybalholm/cascadia/go.sum
generated
vendored
7
vendor/github.com/andybalholm/cascadia/go.sum
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
887
vendor/github.com/andybalholm/cascadia/parser.go
generated
vendored
887
vendor/github.com/andybalholm/cascadia/parser.go
generated
vendored
|
@ -1,887 +0,0 @@
|
||||||
// Package cascadia is an implementation of CSS selectors.
|
|
||||||
package cascadia
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// a parser for CSS selectors
|
|
||||||
type parser struct {
|
|
||||||
s string // the source text
|
|
||||||
i int // the current position
|
|
||||||
|
|
||||||
// if `false`, parsing a pseudo-element
|
|
||||||
// returns an error.
|
|
||||||
acceptPseudoElements bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseEscape parses a backslash escape.
|
|
||||||
func (p *parser) parseEscape() (result string, err error) {
|
|
||||||
if len(p.s) < p.i+2 || p.s[p.i] != '\\' {
|
|
||||||
return "", errors.New("invalid escape sequence")
|
|
||||||
}
|
|
||||||
|
|
||||||
start := p.i + 1
|
|
||||||
c := p.s[start]
|
|
||||||
switch {
|
|
||||||
case c == '\r' || c == '\n' || c == '\f':
|
|
||||||
return "", errors.New("escaped line ending outside string")
|
|
||||||
case hexDigit(c):
|
|
||||||
// unicode escape (hex)
|
|
||||||
var i int
|
|
||||||
for i = start; i < start+6 && i < len(p.s) && hexDigit(p.s[i]); i++ {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
v, _ := strconv.ParseUint(p.s[start:i], 16, 64)
|
|
||||||
if len(p.s) > i {
|
|
||||||
switch p.s[i] {
|
|
||||||
case '\r':
|
|
||||||
i++
|
|
||||||
if len(p.s) > i && p.s[i] == '\n' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
case ' ', '\t', '\n', '\f':
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.i = i
|
|
||||||
return string(rune(v)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the literal character after the backslash.
|
|
||||||
result = p.s[start : start+1]
|
|
||||||
p.i += 2
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// toLowerASCII returns s with all ASCII capital letters lowercased.
|
|
||||||
func toLowerASCII(s string) string {
|
|
||||||
var b []byte
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if c := s[i]; 'A' <= c && c <= 'Z' {
|
|
||||||
if b == nil {
|
|
||||||
b = make([]byte, len(s))
|
|
||||||
copy(b, s)
|
|
||||||
}
|
|
||||||
b[i] = s[i] + ('a' - 'A')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b == nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hexDigit(c byte) bool {
|
|
||||||
return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
|
|
||||||
}
|
|
||||||
|
|
||||||
// nameStart returns whether c can be the first character of an identifier
|
|
||||||
// (not counting an initial hyphen, or an escape sequence).
|
|
||||||
func nameStart(c byte) bool {
|
|
||||||
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127
|
|
||||||
}
|
|
||||||
|
|
||||||
// nameChar returns whether c can be a character within an identifier
|
|
||||||
// (not counting an escape sequence).
|
|
||||||
func nameChar(c byte) bool {
|
|
||||||
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 ||
|
|
||||||
c == '-' || '0' <= c && c <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseIdentifier parses an identifier.
|
|
||||||
func (p *parser) parseIdentifier() (result string, err error) {
|
|
||||||
startingDash := false
|
|
||||||
if len(p.s) > p.i && p.s[p.i] == '-' {
|
|
||||||
startingDash = true
|
|
||||||
p.i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(p.s) <= p.i {
|
|
||||||
return "", errors.New("expected identifier, found EOF instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := p.s[p.i]; !(nameStart(c) || c == '\\') {
|
|
||||||
return "", fmt.Errorf("expected identifier, found %c instead", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err = p.parseName()
|
|
||||||
if startingDash && err == nil {
|
|
||||||
result = "-" + result
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseName parses a name (which is like an identifier, but doesn't have
|
|
||||||
// extra restrictions on the first character).
|
|
||||||
func (p *parser) parseName() (result string, err error) {
|
|
||||||
i := p.i
|
|
||||||
loop:
|
|
||||||
for i < len(p.s) {
|
|
||||||
c := p.s[i]
|
|
||||||
switch {
|
|
||||||
case nameChar(c):
|
|
||||||
start := i
|
|
||||||
for i < len(p.s) && nameChar(p.s[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
result += p.s[start:i]
|
|
||||||
case c == '\\':
|
|
||||||
p.i = i
|
|
||||||
val, err := p.parseEscape()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
i = p.i
|
|
||||||
result += val
|
|
||||||
default:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if result == "" {
|
|
||||||
return "", errors.New("expected name, found EOF instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
p.i = i
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseString parses a single- or double-quoted string.
|
|
||||||
func (p *parser) parseString() (result string, err error) {
|
|
||||||
i := p.i
|
|
||||||
if len(p.s) < i+2 {
|
|
||||||
return "", errors.New("expected string, found EOF instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
quote := p.s[i]
|
|
||||||
i++
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for i < len(p.s) {
|
|
||||||
switch p.s[i] {
|
|
||||||
case '\\':
|
|
||||||
if len(p.s) > i+1 {
|
|
||||||
switch c := p.s[i+1]; c {
|
|
||||||
case '\r':
|
|
||||||
if len(p.s) > i+2 && p.s[i+2] == '\n' {
|
|
||||||
i += 3
|
|
||||||
continue loop
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case '\n', '\f':
|
|
||||||
i += 2
|
|
||||||
continue loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.i = i
|
|
||||||
val, err := p.parseEscape()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
i = p.i
|
|
||||||
result += val
|
|
||||||
case quote:
|
|
||||||
break loop
|
|
||||||
case '\r', '\n', '\f':
|
|
||||||
return "", errors.New("unexpected end of line in string")
|
|
||||||
default:
|
|
||||||
start := i
|
|
||||||
for i < len(p.s) {
|
|
||||||
if c := p.s[i]; c == quote || c == '\\' || c == '\r' || c == '\n' || c == '\f' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
result += p.s[start:i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i >= len(p.s) {
|
|
||||||
return "", errors.New("EOF in string")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume the final quote.
|
|
||||||
i++
|
|
||||||
|
|
||||||
p.i = i
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseRegex parses a regular expression; the end is defined by encountering an
|
|
||||||
// unmatched closing ')' or ']' which is not consumed
|
|
||||||
func (p *parser) parseRegex() (rx *regexp.Regexp, err error) {
|
|
||||||
i := p.i
|
|
||||||
if len(p.s) < i+2 {
|
|
||||||
return nil, errors.New("expected regular expression, found EOF instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
// number of open parens or brackets;
|
|
||||||
// when it becomes negative, finished parsing regex
|
|
||||||
open := 0
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for i < len(p.s) {
|
|
||||||
switch p.s[i] {
|
|
||||||
case '(', '[':
|
|
||||||
open++
|
|
||||||
case ')', ']':
|
|
||||||
open--
|
|
||||||
if open < 0 {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i >= len(p.s) {
|
|
||||||
return nil, errors.New("EOF in regular expression")
|
|
||||||
}
|
|
||||||
rx, err = regexp.Compile(p.s[p.i:i])
|
|
||||||
p.i = i
|
|
||||||
return rx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// skipWhitespace consumes whitespace characters and comments.
|
|
||||||
// It returns true if there was actually anything to skip.
|
|
||||||
func (p *parser) skipWhitespace() bool {
|
|
||||||
i := p.i
|
|
||||||
for i < len(p.s) {
|
|
||||||
switch p.s[i] {
|
|
||||||
case ' ', '\t', '\r', '\n', '\f':
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
case '/':
|
|
||||||
if strings.HasPrefix(p.s[i:], "/*") {
|
|
||||||
end := strings.Index(p.s[i+len("/*"):], "*/")
|
|
||||||
if end != -1 {
|
|
||||||
i += end + len("/**/")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if i > p.i {
|
|
||||||
p.i = i
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeParenthesis consumes an opening parenthesis and any following
|
|
||||||
// whitespace. It returns true if there was actually a parenthesis to skip.
|
|
||||||
func (p *parser) consumeParenthesis() bool {
|
|
||||||
if p.i < len(p.s) && p.s[p.i] == '(' {
|
|
||||||
p.i++
|
|
||||||
p.skipWhitespace()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeClosingParenthesis consumes a closing parenthesis and any preceding
|
|
||||||
// whitespace. It returns true if there was actually a parenthesis to skip.
|
|
||||||
func (p *parser) consumeClosingParenthesis() bool {
|
|
||||||
i := p.i
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i < len(p.s) && p.s[p.i] == ')' {
|
|
||||||
p.i++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
p.i = i
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseTypeSelector parses a type selector (one that matches by tag name).
|
|
||||||
func (p *parser) parseTypeSelector() (result tagSelector, err error) {
|
|
||||||
tag, err := p.parseIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return tagSelector{tag: toLowerASCII(tag)}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseIDSelector parses a selector that matches by id attribute.
|
|
||||||
func (p *parser) parseIDSelector() (idSelector, error) {
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead")
|
|
||||||
}
|
|
||||||
if p.s[p.i] != '#' {
|
|
||||||
return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.i++
|
|
||||||
id, err := p.parseName()
|
|
||||||
if err != nil {
|
|
||||||
return idSelector{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return idSelector{id: id}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseClassSelector parses a selector that matches by class attribute.
|
|
||||||
func (p *parser) parseClassSelector() (classSelector, error) {
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead")
|
|
||||||
}
|
|
||||||
if p.s[p.i] != '.' {
|
|
||||||
return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.i++
|
|
||||||
class, err := p.parseIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return classSelector{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return classSelector{class: class}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseAttributeSelector parses a selector that matches by attribute value.
|
|
||||||
func (p *parser) parseAttributeSelector() (attrSelector, error) {
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead")
|
|
||||||
}
|
|
||||||
if p.s[p.i] != '[' {
|
|
||||||
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.i++
|
|
||||||
p.skipWhitespace()
|
|
||||||
key, err := p.parseIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return attrSelector{}, err
|
|
||||||
}
|
|
||||||
key = toLowerASCII(key)
|
|
||||||
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.s[p.i] == ']' {
|
|
||||||
p.i++
|
|
||||||
return attrSelector{key: key, operation: ""}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.i+2 >= len(p.s) {
|
|
||||||
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
|
||||||
}
|
|
||||||
|
|
||||||
op := p.s[p.i : p.i+2]
|
|
||||||
if op[0] == '=' {
|
|
||||||
op = "="
|
|
||||||
} else if op[1] != '=' {
|
|
||||||
return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op)
|
|
||||||
}
|
|
||||||
p.i += len(op)
|
|
||||||
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
|
||||||
}
|
|
||||||
var val string
|
|
||||||
var rx *regexp.Regexp
|
|
||||||
if op == "#=" {
|
|
||||||
rx, err = p.parseRegex()
|
|
||||||
} else {
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '\'', '"':
|
|
||||||
val, err = p.parseString()
|
|
||||||
default:
|
|
||||||
val, err = p.parseIdentifier()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return attrSelector{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the attribute contains an ignore case flag
|
|
||||||
ignoreCase := false
|
|
||||||
if p.s[p.i] == 'i' || p.s[p.i] == 'I' {
|
|
||||||
ignoreCase = true
|
|
||||||
p.i++
|
|
||||||
}
|
|
||||||
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.s[p.i] != ']' {
|
|
||||||
return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i])
|
|
||||||
}
|
|
||||||
p.i++
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=":
|
|
||||||
return attrSelector{key: key, val: val, operation: op, regexp: rx, insensitive: ignoreCase}, nil
|
|
||||||
default:
|
|
||||||
return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errExpectedParenthesis = errors.New("expected '(' but didn't find it")
|
|
||||||
errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it")
|
|
||||||
errUnmatchedParenthesis = errors.New("unmatched '('")
|
|
||||||
)
|
|
||||||
|
|
||||||
// parsePseudoclassSelector parses a pseudoclass selector like :not(p) or a pseudo-element
|
|
||||||
// For backwards compatibility, both ':' and '::' prefix are allowed for pseudo-elements.
|
|
||||||
// https://drafts.csswg.org/selectors-3/#pseudo-elements
|
|
||||||
// Returning a nil `Sel` (and a nil `error`) means we found a pseudo-element.
|
|
||||||
func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err error) {
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return nil, "", fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead")
|
|
||||||
}
|
|
||||||
if p.s[p.i] != ':' {
|
|
||||||
return nil, "", fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.i++
|
|
||||||
var mustBePseudoElement bool
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return nil, "", fmt.Errorf("got empty pseudoclass (or pseudoelement)")
|
|
||||||
}
|
|
||||||
if p.s[p.i] == ':' { // we found a pseudo-element
|
|
||||||
mustBePseudoElement = true
|
|
||||||
p.i++
|
|
||||||
}
|
|
||||||
|
|
||||||
name, err := p.parseIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name = toLowerASCII(name)
|
|
||||||
if mustBePseudoElement && (name != "after" && name != "backdrop" && name != "before" &&
|
|
||||||
name != "cue" && name != "first-letter" && name != "first-line" && name != "grammar-error" &&
|
|
||||||
name != "marker" && name != "placeholder" && name != "selection" && name != "spelling-error") {
|
|
||||||
return out, "", fmt.Errorf("unknown pseudoelement :%s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch name {
|
|
||||||
case "not", "has", "haschild":
|
|
||||||
if !p.consumeParenthesis() {
|
|
||||||
return out, "", errExpectedParenthesis
|
|
||||||
}
|
|
||||||
sel, parseErr := p.parseSelectorGroup()
|
|
||||||
if parseErr != nil {
|
|
||||||
return out, "", parseErr
|
|
||||||
}
|
|
||||||
if !p.consumeClosingParenthesis() {
|
|
||||||
return out, "", errExpectedClosingParenthesis
|
|
||||||
}
|
|
||||||
|
|
||||||
out = relativePseudoClassSelector{name: name, match: sel}
|
|
||||||
|
|
||||||
case "contains", "containsown":
|
|
||||||
if !p.consumeParenthesis() {
|
|
||||||
return out, "", errExpectedParenthesis
|
|
||||||
}
|
|
||||||
if p.i == len(p.s) {
|
|
||||||
return out, "", errUnmatchedParenthesis
|
|
||||||
}
|
|
||||||
var val string
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '\'', '"':
|
|
||||||
val, err = p.parseString()
|
|
||||||
default:
|
|
||||||
val, err = p.parseIdentifier()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return out, "", err
|
|
||||||
}
|
|
||||||
val = strings.ToLower(val)
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return out, "", errors.New("unexpected EOF in pseudo selector")
|
|
||||||
}
|
|
||||||
if !p.consumeClosingParenthesis() {
|
|
||||||
return out, "", errExpectedClosingParenthesis
|
|
||||||
}
|
|
||||||
|
|
||||||
out = containsPseudoClassSelector{own: name == "containsown", value: val}
|
|
||||||
|
|
||||||
case "matches", "matchesown":
|
|
||||||
if !p.consumeParenthesis() {
|
|
||||||
return out, "", errExpectedParenthesis
|
|
||||||
}
|
|
||||||
rx, err := p.parseRegex()
|
|
||||||
if err != nil {
|
|
||||||
return out, "", err
|
|
||||||
}
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return out, "", errors.New("unexpected EOF in pseudo selector")
|
|
||||||
}
|
|
||||||
if !p.consumeClosingParenthesis() {
|
|
||||||
return out, "", errExpectedClosingParenthesis
|
|
||||||
}
|
|
||||||
|
|
||||||
out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx}
|
|
||||||
|
|
||||||
case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type":
|
|
||||||
if !p.consumeParenthesis() {
|
|
||||||
return out, "", errExpectedParenthesis
|
|
||||||
}
|
|
||||||
a, b, err := p.parseNth()
|
|
||||||
if err != nil {
|
|
||||||
return out, "", err
|
|
||||||
}
|
|
||||||
if !p.consumeClosingParenthesis() {
|
|
||||||
return out, "", errExpectedClosingParenthesis
|
|
||||||
}
|
|
||||||
last := name == "nth-last-child" || name == "nth-last-of-type"
|
|
||||||
ofType := name == "nth-of-type" || name == "nth-last-of-type"
|
|
||||||
out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType}
|
|
||||||
|
|
||||||
case "first-child":
|
|
||||||
out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false}
|
|
||||||
case "last-child":
|
|
||||||
out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true}
|
|
||||||
case "first-of-type":
|
|
||||||
out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false}
|
|
||||||
case "last-of-type":
|
|
||||||
out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true}
|
|
||||||
case "only-child":
|
|
||||||
out = onlyChildPseudoClassSelector{ofType: false}
|
|
||||||
case "only-of-type":
|
|
||||||
out = onlyChildPseudoClassSelector{ofType: true}
|
|
||||||
case "input":
|
|
||||||
out = inputPseudoClassSelector{}
|
|
||||||
case "empty":
|
|
||||||
out = emptyElementPseudoClassSelector{}
|
|
||||||
case "root":
|
|
||||||
out = rootPseudoClassSelector{}
|
|
||||||
case "link":
|
|
||||||
out = linkPseudoClassSelector{}
|
|
||||||
case "lang":
|
|
||||||
if !p.consumeParenthesis() {
|
|
||||||
return out, "", errExpectedParenthesis
|
|
||||||
}
|
|
||||||
if p.i == len(p.s) {
|
|
||||||
return out, "", errUnmatchedParenthesis
|
|
||||||
}
|
|
||||||
val, err := p.parseIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return out, "", err
|
|
||||||
}
|
|
||||||
val = strings.ToLower(val)
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return out, "", errors.New("unexpected EOF in pseudo selector")
|
|
||||||
}
|
|
||||||
if !p.consumeClosingParenthesis() {
|
|
||||||
return out, "", errExpectedClosingParenthesis
|
|
||||||
}
|
|
||||||
out = langPseudoClassSelector{lang: val}
|
|
||||||
case "enabled":
|
|
||||||
out = enabledPseudoClassSelector{}
|
|
||||||
case "disabled":
|
|
||||||
out = disabledPseudoClassSelector{}
|
|
||||||
case "checked":
|
|
||||||
out = checkedPseudoClassSelector{}
|
|
||||||
case "visited", "hover", "active", "focus", "target":
|
|
||||||
// Not applicable in a static context: never match.
|
|
||||||
out = neverMatchSelector{value: ":" + name}
|
|
||||||
case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error":
|
|
||||||
return nil, name, nil
|
|
||||||
default:
|
|
||||||
return out, "", fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseInteger parses a decimal integer.
|
|
||||||
func (p *parser) parseInteger() (int, error) {
|
|
||||||
i := p.i
|
|
||||||
start := i
|
|
||||||
for i < len(p.s) && '0' <= p.s[i] && p.s[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == start {
|
|
||||||
return 0, errors.New("expected integer, but didn't find it")
|
|
||||||
}
|
|
||||||
p.i = i
|
|
||||||
|
|
||||||
val, err := strconv.Atoi(p.s[start:i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseNth parses the argument for :nth-child (normally of the form an+b).
|
|
||||||
func (p *parser) parseNth() (a, b int, err error) {
|
|
||||||
// initial state
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
goto eof
|
|
||||||
}
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '-':
|
|
||||||
p.i++
|
|
||||||
goto negativeA
|
|
||||||
case '+':
|
|
||||||
p.i++
|
|
||||||
goto positiveA
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
goto positiveA
|
|
||||||
case 'n', 'N':
|
|
||||||
a = 1
|
|
||||||
p.i++
|
|
||||||
goto readN
|
|
||||||
case 'o', 'O', 'e', 'E':
|
|
||||||
id, nameErr := p.parseName()
|
|
||||||
if nameErr != nil {
|
|
||||||
return 0, 0, nameErr
|
|
||||||
}
|
|
||||||
id = toLowerASCII(id)
|
|
||||||
if id == "odd" {
|
|
||||||
return 2, 1, nil
|
|
||||||
}
|
|
||||||
if id == "even" {
|
|
||||||
return 2, 0, nil
|
|
||||||
}
|
|
||||||
return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", id)
|
|
||||||
default:
|
|
||||||
goto invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
positiveA:
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
goto eof
|
|
||||||
}
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
a, err = p.parseInteger()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
goto readA
|
|
||||||
case 'n', 'N':
|
|
||||||
a = 1
|
|
||||||
p.i++
|
|
||||||
goto readN
|
|
||||||
default:
|
|
||||||
goto invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
negativeA:
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
goto eof
|
|
||||||
}
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
a, err = p.parseInteger()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
a = -a
|
|
||||||
goto readA
|
|
||||||
case 'n', 'N':
|
|
||||||
a = -1
|
|
||||||
p.i++
|
|
||||||
goto readN
|
|
||||||
default:
|
|
||||||
goto invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
readA:
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
goto eof
|
|
||||||
}
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case 'n', 'N':
|
|
||||||
p.i++
|
|
||||||
goto readN
|
|
||||||
default:
|
|
||||||
// The number we read as a is actually b.
|
|
||||||
return 0, a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
readN:
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
goto eof
|
|
||||||
}
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '+':
|
|
||||||
p.i++
|
|
||||||
p.skipWhitespace()
|
|
||||||
b, err = p.parseInteger()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return a, b, nil
|
|
||||||
case '-':
|
|
||||||
p.i++
|
|
||||||
p.skipWhitespace()
|
|
||||||
b, err = p.parseInteger()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return a, -b, nil
|
|
||||||
default:
|
|
||||||
return a, 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
eof:
|
|
||||||
return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b")
|
|
||||||
|
|
||||||
invalid:
|
|
||||||
return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b")
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSimpleSelectorSequence parses a selector sequence that applies to
|
|
||||||
// a single element.
|
|
||||||
func (p *parser) parseSimpleSelectorSequence() (Sel, error) {
|
|
||||||
var selectors []Sel
|
|
||||||
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return nil, errors.New("expected selector, found EOF instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '*':
|
|
||||||
// It's the universal selector. Just skip over it, since it doesn't affect the meaning.
|
|
||||||
p.i++
|
|
||||||
if p.i+2 < len(p.s) && p.s[p.i:p.i+2] == "|*" { // other version of universal selector
|
|
||||||
p.i += 2
|
|
||||||
}
|
|
||||||
case '#', '.', '[', ':':
|
|
||||||
// There's no type selector. Wait to process the other till the main loop.
|
|
||||||
default:
|
|
||||||
r, err := p.parseTypeSelector()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
selectors = append(selectors, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pseudoElement string
|
|
||||||
loop:
|
|
||||||
for p.i < len(p.s) {
|
|
||||||
var (
|
|
||||||
ns Sel
|
|
||||||
newPseudoElement string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '#':
|
|
||||||
ns, err = p.parseIDSelector()
|
|
||||||
case '.':
|
|
||||||
ns, err = p.parseClassSelector()
|
|
||||||
case '[':
|
|
||||||
ns, err = p.parseAttributeSelector()
|
|
||||||
case ':':
|
|
||||||
ns, newPseudoElement, err = p.parsePseudoclassSelector()
|
|
||||||
default:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// From https://drafts.csswg.org/selectors-3/#pseudo-elements :
|
|
||||||
// "Only one pseudo-element may appear per selector, and if present
|
|
||||||
// it must appear after the sequence of simple selectors that
|
|
||||||
// represents the subjects of the selector.""
|
|
||||||
if ns == nil { // we found a pseudo-element
|
|
||||||
if pseudoElement != "" {
|
|
||||||
return nil, fmt.Errorf("only one pseudo-element is accepted per selector, got %s and %s", pseudoElement, newPseudoElement)
|
|
||||||
}
|
|
||||||
if !p.acceptPseudoElements {
|
|
||||||
return nil, fmt.Errorf("pseudo-element %s found, but pseudo-elements support is disabled", newPseudoElement)
|
|
||||||
}
|
|
||||||
pseudoElement = newPseudoElement
|
|
||||||
} else {
|
|
||||||
if pseudoElement != "" {
|
|
||||||
return nil, fmt.Errorf("pseudo-element %s must be at the end of selector", pseudoElement)
|
|
||||||
}
|
|
||||||
selectors = append(selectors, ns)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(selectors) == 1 && pseudoElement == "" { // no need wrap the selectors in compoundSelector
|
|
||||||
return selectors[0], nil
|
|
||||||
}
|
|
||||||
return compoundSelector{selectors: selectors, pseudoElement: pseudoElement}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSelector parses a selector that may include combinators.
|
|
||||||
func (p *parser) parseSelector() (Sel, error) {
|
|
||||||
p.skipWhitespace()
|
|
||||||
result, err := p.parseSimpleSelectorSequence()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
combinator byte
|
|
||||||
c Sel
|
|
||||||
)
|
|
||||||
if p.skipWhitespace() {
|
|
||||||
combinator = ' '
|
|
||||||
}
|
|
||||||
if p.i >= len(p.s) {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch p.s[p.i] {
|
|
||||||
case '+', '>', '~':
|
|
||||||
combinator = p.s[p.i]
|
|
||||||
p.i++
|
|
||||||
p.skipWhitespace()
|
|
||||||
case ',', ')':
|
|
||||||
// These characters can't begin a selector, but they can legally occur after one.
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if combinator == 0 {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err = p.parseSimpleSelectorSequence()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = combinedSelector{first: result, combinator: combinator, second: c}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSelectorGroup parses a group of selectors, separated by commas.
|
|
||||||
func (p *parser) parseSelectorGroup() (SelectorGroup, error) {
|
|
||||||
current, err := p.parseSelector()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := SelectorGroup{current}
|
|
||||||
|
|
||||||
for p.i < len(p.s) {
|
|
||||||
if p.s[p.i] != ',' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.i++
|
|
||||||
c, err := p.parseSelector()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = append(result, c)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
474
vendor/github.com/andybalholm/cascadia/pseudo_classes.go
generated
vendored
474
vendor/github.com/andybalholm/cascadia/pseudo_classes.go
generated
vendored
|
@ -1,474 +0,0 @@
|
||||||
package cascadia
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
"golang.org/x/net/html/atom"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file implements the pseudo classes selectors,
|
|
||||||
// which share the implementation of PseudoElement() and Specificity()
|
|
||||||
|
|
||||||
type abstractPseudoClass struct{}
|
|
||||||
|
|
||||||
func (s abstractPseudoClass) Specificity() Specificity {
|
|
||||||
return Specificity{0, 1, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c abstractPseudoClass) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type relativePseudoClassSelector struct {
|
|
||||||
name string // one of "not", "has", "haschild"
|
|
||||||
match SelectorGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s relativePseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch s.name {
|
|
||||||
case "not":
|
|
||||||
// matches elements that do not match a.
|
|
||||||
return !s.match.Match(n)
|
|
||||||
case "has":
|
|
||||||
// matches elements with any descendant that matches a.
|
|
||||||
return hasDescendantMatch(n, s.match)
|
|
||||||
case "haschild":
|
|
||||||
// matches elements with a child that matches a.
|
|
||||||
return hasChildMatch(n, s.match)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unsupported relative pseudo class selector : %s", s.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasChildMatch returns whether n has any child that matches a.
|
|
||||||
func hasChildMatch(n *html.Node, a Matcher) bool {
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if a.Match(c) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasDescendantMatch performs a depth-first search of n's descendants,
|
|
||||||
// testing whether any of them match a. It returns true as soon as a match is
|
|
||||||
// found, or false if no match is found.
|
|
||||||
func hasDescendantMatch(n *html.Node, a Matcher) bool {
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if a.Match(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specificity returns the specificity of the most specific selectors
|
|
||||||
// in the pseudo-class arguments.
|
|
||||||
// See https://www.w3.org/TR/selectors/#specificity-rules
|
|
||||||
func (s relativePseudoClassSelector) Specificity() Specificity {
|
|
||||||
var max Specificity
|
|
||||||
for _, sel := range s.match {
|
|
||||||
newSpe := sel.Specificity()
|
|
||||||
if max.Less(newSpe) {
|
|
||||||
max = newSpe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c relativePseudoClassSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type containsPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
value string
|
|
||||||
own bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s containsPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
var text string
|
|
||||||
if s.own {
|
|
||||||
// matches nodes that directly contain the given text
|
|
||||||
text = strings.ToLower(nodeOwnText(n))
|
|
||||||
} else {
|
|
||||||
// matches nodes that contain the given text.
|
|
||||||
text = strings.ToLower(nodeText(n))
|
|
||||||
}
|
|
||||||
return strings.Contains(text, s.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
type regexpPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
regexp *regexp.Regexp
|
|
||||||
own bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s regexpPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
var text string
|
|
||||||
if s.own {
|
|
||||||
// matches nodes whose text directly matches the specified regular expression
|
|
||||||
text = nodeOwnText(n)
|
|
||||||
} else {
|
|
||||||
// matches nodes whose text matches the specified regular expression
|
|
||||||
text = nodeText(n)
|
|
||||||
}
|
|
||||||
return s.regexp.MatchString(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeNodeText writes the text contained in n and its descendants to b.
|
|
||||||
func writeNodeText(n *html.Node, b *bytes.Buffer) {
|
|
||||||
switch n.Type {
|
|
||||||
case html.TextNode:
|
|
||||||
b.WriteString(n.Data)
|
|
||||||
case html.ElementNode:
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
writeNodeText(c, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeText returns the text contained in n and its descendants.
|
|
||||||
func nodeText(n *html.Node) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
writeNodeText(n, &b)
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeOwnText returns the contents of the text nodes that are direct
|
|
||||||
// children of n.
|
|
||||||
func nodeOwnText(n *html.Node) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if c.Type == html.TextNode {
|
|
||||||
b.WriteString(c.Data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type nthPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
a, b int
|
|
||||||
last, ofType bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s nthPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if s.a == 0 {
|
|
||||||
if s.last {
|
|
||||||
return simpleNthLastChildMatch(s.b, s.ofType, n)
|
|
||||||
} else {
|
|
||||||
return simpleNthChildMatch(s.b, s.ofType, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nthChildMatch(s.a, s.b, s.last, s.ofType, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nthChildMatch implements :nth-child(an+b).
|
|
||||||
// If last is true, implements :nth-last-child instead.
|
|
||||||
// If ofType is true, implements :nth-of-type instead.
|
|
||||||
func nthChildMatch(a, b int, last, ofType bool, n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := n.Parent
|
|
||||||
if parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if parent.Type == html.DocumentNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
i := -1
|
|
||||||
count := 0
|
|
||||||
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
if c == n {
|
|
||||||
i = count
|
|
||||||
if !last {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == -1 {
|
|
||||||
// This shouldn't happen, since n should always be one of its parent's children.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if last {
|
|
||||||
i = count - i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
i -= b
|
|
||||||
if a == 0 {
|
|
||||||
return i == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return i%a == 0 && i/a >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleNthChildMatch implements :nth-child(b).
|
|
||||||
// If ofType is true, implements :nth-of-type instead.
|
|
||||||
func simpleNthChildMatch(b int, ofType bool, n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := n.Parent
|
|
||||||
if parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if parent.Type == html.DocumentNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
if c == n {
|
|
||||||
return count == b
|
|
||||||
}
|
|
||||||
if count >= b {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleNthLastChildMatch implements :nth-last-child(b).
|
|
||||||
// If ofType is true, implements :nth-last-of-type instead.
|
|
||||||
func simpleNthLastChildMatch(b int, ofType bool, n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := n.Parent
|
|
||||||
if parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if parent.Type == html.DocumentNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
for c := parent.LastChild; c != nil; c = c.PrevSibling {
|
|
||||||
if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
if c == n {
|
|
||||||
return count == b
|
|
||||||
}
|
|
||||||
if count >= b {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type onlyChildPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
ofType bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match implements :only-child.
|
|
||||||
// If `ofType` is true, it implements :only-of-type instead.
|
|
||||||
func (s onlyChildPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := n.Parent
|
|
||||||
if parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if parent.Type == html.DocumentNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
for c := parent.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if (c.Type != html.ElementNode) || (s.ofType && c.Data != n.Data) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
if count > 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type inputPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches input, select, textarea and button elements.
|
|
||||||
func (s inputPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
return n.Type == html.ElementNode && (n.Data == "input" || n.Data == "select" || n.Data == "textarea" || n.Data == "button")
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyElementPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches empty elements.
|
|
||||||
func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
switch c.Type {
|
|
||||||
case html.ElementNode:
|
|
||||||
return false
|
|
||||||
case html.TextNode:
|
|
||||||
if strings.TrimSpace(nodeText(c)) == "" {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type rootPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match implements :root
|
|
||||||
func (s rootPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if n.Parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n.Parent.Type == html.DocumentNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasAttr(n *html.Node, attr string) bool {
|
|
||||||
return matchAttribute(n, attr, func(string) bool { return true })
|
|
||||||
}
|
|
||||||
|
|
||||||
type linkPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match implements :link
|
|
||||||
func (s linkPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
return (n.DataAtom == atom.A || n.DataAtom == atom.Area || n.DataAtom == atom.Link) && hasAttr(n, "href")
|
|
||||||
}
|
|
||||||
|
|
||||||
type langPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
lang string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s langPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
own := matchAttribute(n, "lang", func(val string) bool {
|
|
||||||
return val == s.lang || strings.HasPrefix(val, s.lang+"-")
|
|
||||||
})
|
|
||||||
if n.Parent == nil {
|
|
||||||
return own
|
|
||||||
}
|
|
||||||
return own || s.Match(n.Parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
type enabledPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s enabledPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch n.DataAtom {
|
|
||||||
case atom.A, atom.Area, atom.Link:
|
|
||||||
return hasAttr(n, "href")
|
|
||||||
case atom.Optgroup, atom.Menuitem, atom.Fieldset:
|
|
||||||
return !hasAttr(n, "disabled")
|
|
||||||
case atom.Button, atom.Input, atom.Select, atom.Textarea, atom.Option:
|
|
||||||
return !hasAttr(n, "disabled") && !inDisabledFieldset(n)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type disabledPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s disabledPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch n.DataAtom {
|
|
||||||
case atom.Optgroup, atom.Menuitem, atom.Fieldset:
|
|
||||||
return hasAttr(n, "disabled")
|
|
||||||
case atom.Button, atom.Input, atom.Select, atom.Textarea, atom.Option:
|
|
||||||
return hasAttr(n, "disabled") || inDisabledFieldset(n)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasLegendInPreviousSiblings(n *html.Node) bool {
|
|
||||||
for s := n.PrevSibling; s != nil; s = s.PrevSibling {
|
|
||||||
if s.DataAtom == atom.Legend {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func inDisabledFieldset(n *html.Node) bool {
|
|
||||||
if n.Parent == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if n.Parent.DataAtom == atom.Fieldset && hasAttr(n.Parent, "disabled") &&
|
|
||||||
(n.DataAtom != atom.Legend || hasLegendInPreviousSiblings(n)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return inDisabledFieldset(n.Parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
type checkedPseudoClassSelector struct {
|
|
||||||
abstractPseudoClass
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s checkedPseudoClassSelector) Match(n *html.Node) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch n.DataAtom {
|
|
||||||
case atom.Input, atom.Menuitem:
|
|
||||||
return hasAttr(n, "checked") && matchAttribute(n, "type", func(val string) bool {
|
|
||||||
t := toLowerASCII(val)
|
|
||||||
return t == "checkbox" || t == "radio"
|
|
||||||
})
|
|
||||||
case atom.Option:
|
|
||||||
return hasAttr(n, "selected")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
586
vendor/github.com/andybalholm/cascadia/selector.go
generated
vendored
586
vendor/github.com/andybalholm/cascadia/selector.go
generated
vendored
|
@ -1,586 +0,0 @@
|
||||||
package cascadia
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Matcher is the interface for basic selector functionality.
|
|
||||||
// Match returns whether a selector matches n.
|
|
||||||
type Matcher interface {
|
|
||||||
Match(n *html.Node) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sel is the interface for all the functionality provided by selectors.
|
|
||||||
type Sel interface {
|
|
||||||
Matcher
|
|
||||||
Specificity() Specificity
|
|
||||||
|
|
||||||
// Returns a CSS input compiling to this selector.
|
|
||||||
String() string
|
|
||||||
|
|
||||||
// Returns a pseudo-element, or an empty string.
|
|
||||||
PseudoElement() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses a selector. Use `ParseWithPseudoElement`
|
|
||||||
// if you need support for pseudo-elements.
|
|
||||||
func Parse(sel string) (Sel, error) {
|
|
||||||
p := &parser{s: sel}
|
|
||||||
compiled, err := p.parseSelector()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.i < len(sel) {
|
|
||||||
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return compiled, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseWithPseudoElement parses a single selector,
|
|
||||||
// with support for pseudo-element.
|
|
||||||
func ParseWithPseudoElement(sel string) (Sel, error) {
|
|
||||||
p := &parser{s: sel, acceptPseudoElements: true}
|
|
||||||
compiled, err := p.parseSelector()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.i < len(sel) {
|
|
||||||
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return compiled, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseGroup parses a selector, or a group of selectors separated by commas.
|
|
||||||
// Use `ParseGroupWithPseudoElements`
|
|
||||||
// if you need support for pseudo-elements.
|
|
||||||
func ParseGroup(sel string) (SelectorGroup, error) {
|
|
||||||
p := &parser{s: sel}
|
|
||||||
compiled, err := p.parseSelectorGroup()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.i < len(sel) {
|
|
||||||
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return compiled, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseGroupWithPseudoElements parses a selector, or a group of selectors separated by commas.
|
|
||||||
// It supports pseudo-elements.
|
|
||||||
func ParseGroupWithPseudoElements(sel string) (SelectorGroup, error) {
|
|
||||||
p := &parser{s: sel, acceptPseudoElements: true}
|
|
||||||
compiled, err := p.parseSelectorGroup()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.i < len(sel) {
|
|
||||||
return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return compiled, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Selector is a function which tells whether a node matches or not.
|
|
||||||
//
|
|
||||||
// This type is maintained for compatibility; I recommend using the newer and
|
|
||||||
// more idiomatic interfaces Sel and Matcher.
|
|
||||||
type Selector func(*html.Node) bool
|
|
||||||
|
|
||||||
// Compile parses a selector and returns, if successful, a Selector object
|
|
||||||
// that can be used to match against html.Node objects.
|
|
||||||
func Compile(sel string) (Selector, error) {
|
|
||||||
compiled, err := ParseGroup(sel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Selector(compiled.Match), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustCompile is like Compile, but panics instead of returning an error.
|
|
||||||
func MustCompile(sel string) Selector {
|
|
||||||
compiled, err := Compile(sel)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return compiled
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchAll returns a slice of the nodes that match the selector,
|
|
||||||
// from n and its children.
|
|
||||||
func (s Selector) MatchAll(n *html.Node) []*html.Node {
|
|
||||||
return s.matchAllInto(n, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Selector) matchAllInto(n *html.Node, storage []*html.Node) []*html.Node {
|
|
||||||
if s(n) {
|
|
||||||
storage = append(storage, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
|
||||||
storage = s.matchAllInto(child, storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryInto(n *html.Node, m Matcher, storage []*html.Node) []*html.Node {
|
|
||||||
for child := n.FirstChild; child != nil; child = child.NextSibling {
|
|
||||||
if m.Match(child) {
|
|
||||||
storage = append(storage, child)
|
|
||||||
}
|
|
||||||
storage = queryInto(child, m, storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryAll returns a slice of all the nodes that match m, from the descendants
|
|
||||||
// of n.
|
|
||||||
func QueryAll(n *html.Node, m Matcher) []*html.Node {
|
|
||||||
return queryInto(n, m, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match returns true if the node matches the selector.
|
|
||||||
func (s Selector) Match(n *html.Node) bool {
|
|
||||||
return s(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchFirst returns the first node that matches s, from n and its children.
|
|
||||||
func (s Selector) MatchFirst(n *html.Node) *html.Node {
|
|
||||||
if s.Match(n) {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
m := s.MatchFirst(c)
|
|
||||||
if m != nil {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query returns the first node that matches m, from the descendants of n.
|
|
||||||
// If none matches, it returns nil.
|
|
||||||
func Query(n *html.Node, m Matcher) *html.Node {
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if m.Match(c) {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if matched := Query(c, m); matched != nil {
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter returns the nodes in nodes that match the selector.
|
|
||||||
func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) {
|
|
||||||
for _, n := range nodes {
|
|
||||||
if s(n) {
|
|
||||||
result = append(result, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter returns the nodes that match m.
|
|
||||||
func Filter(nodes []*html.Node, m Matcher) (result []*html.Node) {
|
|
||||||
for _, n := range nodes {
|
|
||||||
if m.Match(n) {
|
|
||||||
result = append(result, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
type tagSelector struct {
|
|
||||||
tag string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches elements with a given tag name.
|
|
||||||
func (t tagSelector) Match(n *html.Node) bool {
|
|
||||||
return n.Type == html.ElementNode && n.Data == t.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c tagSelector) Specificity() Specificity {
|
|
||||||
return Specificity{0, 0, 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c tagSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type classSelector struct {
|
|
||||||
class string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches elements by class attribute.
|
|
||||||
func (t classSelector) Match(n *html.Node) bool {
|
|
||||||
return matchAttribute(n, "class", func(s string) bool {
|
|
||||||
return matchInclude(t.class, s, false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c classSelector) Specificity() Specificity {
|
|
||||||
return Specificity{0, 1, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c classSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type idSelector struct {
|
|
||||||
id string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches elements by id attribute.
|
|
||||||
func (t idSelector) Match(n *html.Node) bool {
|
|
||||||
return matchAttribute(n, "id", func(s string) bool {
|
|
||||||
return s == t.id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c idSelector) Specificity() Specificity {
|
|
||||||
return Specificity{1, 0, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c idSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type attrSelector struct {
|
|
||||||
key, val, operation string
|
|
||||||
regexp *regexp.Regexp
|
|
||||||
insensitive bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches elements by attribute value.
|
|
||||||
func (t attrSelector) Match(n *html.Node) bool {
|
|
||||||
switch t.operation {
|
|
||||||
case "":
|
|
||||||
return matchAttribute(n, t.key, func(string) bool { return true })
|
|
||||||
case "=":
|
|
||||||
return matchAttribute(n, t.key, func(s string) bool { return matchInsensitiveValue(s, t.val, t.insensitive) })
|
|
||||||
case "!=":
|
|
||||||
return attributeNotEqualMatch(t.key, t.val, n, t.insensitive)
|
|
||||||
case "~=":
|
|
||||||
// matches elements where the attribute named key is a whitespace-separated list that includes val.
|
|
||||||
return matchAttribute(n, t.key, func(s string) bool { return matchInclude(t.val, s, t.insensitive) })
|
|
||||||
case "|=":
|
|
||||||
return attributeDashMatch(t.key, t.val, n, t.insensitive)
|
|
||||||
case "^=":
|
|
||||||
return attributePrefixMatch(t.key, t.val, n, t.insensitive)
|
|
||||||
case "$=":
|
|
||||||
return attributeSuffixMatch(t.key, t.val, n, t.insensitive)
|
|
||||||
case "*=":
|
|
||||||
return attributeSubstringMatch(t.key, t.val, n, t.insensitive)
|
|
||||||
case "#=":
|
|
||||||
return attributeRegexMatch(t.key, t.regexp, n)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unsuported operation : %s", t.operation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches elements where we ignore (or not) the case of the attribute value
|
|
||||||
// the user attribute is the value set by the user to match elements
|
|
||||||
// the real attribute is the attribute value found in the code parsed
|
|
||||||
func matchInsensitiveValue(userAttr string, realAttr string, ignoreCase bool) bool {
|
|
||||||
if ignoreCase {
|
|
||||||
return strings.EqualFold(userAttr, realAttr)
|
|
||||||
}
|
|
||||||
return userAttr == realAttr
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches elements where the attribute named key satisifes the function f.
|
|
||||||
func matchAttribute(n *html.Node, key string, f func(string) bool) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, a := range n.Attr {
|
|
||||||
if a.Key == key && f(a.Val) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributeNotEqualMatch matches elements where
|
|
||||||
// the attribute named key does not have the value val.
|
|
||||||
func attributeNotEqualMatch(key, val string, n *html.Node, ignoreCase bool) bool {
|
|
||||||
if n.Type != html.ElementNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, a := range n.Attr {
|
|
||||||
if a.Key == key && matchInsensitiveValue(a.Val, val, ignoreCase) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if s is a whitespace-separated list that includes val.
|
|
||||||
func matchInclude(val string, s string, ignoreCase bool) bool {
|
|
||||||
for s != "" {
|
|
||||||
i := strings.IndexAny(s, " \t\r\n\f")
|
|
||||||
if i == -1 {
|
|
||||||
return matchInsensitiveValue(s, val, ignoreCase)
|
|
||||||
}
|
|
||||||
if matchInsensitiveValue(s[:i], val, ignoreCase) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
s = s[i+1:]
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches elements where the attribute named key equals val or starts with val plus a hyphen.
|
|
||||||
func attributeDashMatch(key, val string, n *html.Node, ignoreCase bool) bool {
|
|
||||||
return matchAttribute(n, key,
|
|
||||||
func(s string) bool {
|
|
||||||
if matchInsensitiveValue(s, val, ignoreCase) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(s) <= len(val) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if matchInsensitiveValue(s[:len(val)], val, ignoreCase) && s[len(val)] == '-' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributePrefixMatch returns a Selector that matches elements where
|
|
||||||
// the attribute named key starts with val.
|
|
||||||
func attributePrefixMatch(key, val string, n *html.Node, ignoreCase bool) bool {
|
|
||||||
return matchAttribute(n, key,
|
|
||||||
func(s string) bool {
|
|
||||||
if strings.TrimSpace(s) == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ignoreCase {
|
|
||||||
return strings.HasPrefix(strings.ToLower(s), strings.ToLower(val))
|
|
||||||
}
|
|
||||||
return strings.HasPrefix(s, val)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributeSuffixMatch matches elements where
|
|
||||||
// the attribute named key ends with val.
|
|
||||||
func attributeSuffixMatch(key, val string, n *html.Node, ignoreCase bool) bool {
|
|
||||||
return matchAttribute(n, key,
|
|
||||||
func(s string) bool {
|
|
||||||
if strings.TrimSpace(s) == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ignoreCase {
|
|
||||||
return strings.HasSuffix(strings.ToLower(s), strings.ToLower(val))
|
|
||||||
}
|
|
||||||
return strings.HasSuffix(s, val)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributeSubstringMatch matches nodes where
|
|
||||||
// the attribute named key contains val.
|
|
||||||
func attributeSubstringMatch(key, val string, n *html.Node, ignoreCase bool) bool {
|
|
||||||
return matchAttribute(n, key,
|
|
||||||
func(s string) bool {
|
|
||||||
if strings.TrimSpace(s) == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ignoreCase {
|
|
||||||
return strings.Contains(strings.ToLower(s), strings.ToLower(val))
|
|
||||||
}
|
|
||||||
return strings.Contains(s, val)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributeRegexMatch matches nodes where
|
|
||||||
// the attribute named key matches the regular expression rx
|
|
||||||
func attributeRegexMatch(key string, rx *regexp.Regexp, n *html.Node) bool {
|
|
||||||
return matchAttribute(n, key,
|
|
||||||
func(s string) bool {
|
|
||||||
return rx.MatchString(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c attrSelector) Specificity() Specificity {
|
|
||||||
return Specificity{0, 1, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c attrSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// see pseudo_classes.go for pseudo classes selectors
|
|
||||||
|
|
||||||
// on a static context, some selectors can't match anything
|
|
||||||
type neverMatchSelector struct {
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s neverMatchSelector) Match(n *html.Node) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s neverMatchSelector) Specificity() Specificity {
|
|
||||||
return Specificity{0, 0, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c neverMatchSelector) PseudoElement() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type compoundSelector struct {
|
|
||||||
selectors []Sel
|
|
||||||
pseudoElement string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches elements if each sub-selectors matches.
|
|
||||||
func (t compoundSelector) Match(n *html.Node) bool {
|
|
||||||
if len(t.selectors) == 0 {
|
|
||||||
return n.Type == html.ElementNode
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sel := range t.selectors {
|
|
||||||
if !sel.Match(n) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s compoundSelector) Specificity() Specificity {
|
|
||||||
var out Specificity
|
|
||||||
for _, sel := range s.selectors {
|
|
||||||
out = out.Add(sel.Specificity())
|
|
||||||
}
|
|
||||||
if s.pseudoElement != "" {
|
|
||||||
// https://drafts.csswg.org/selectors-3/#specificity
|
|
||||||
out = out.Add(Specificity{0, 0, 1})
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c compoundSelector) PseudoElement() string {
|
|
||||||
return c.pseudoElement
|
|
||||||
}
|
|
||||||
|
|
||||||
type combinedSelector struct {
|
|
||||||
first Sel
|
|
||||||
combinator byte
|
|
||||||
second Sel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t combinedSelector) Match(n *html.Node) bool {
|
|
||||||
if t.first == nil {
|
|
||||||
return false // maybe we should panic
|
|
||||||
}
|
|
||||||
switch t.combinator {
|
|
||||||
case 0:
|
|
||||||
return t.first.Match(n)
|
|
||||||
case ' ':
|
|
||||||
return descendantMatch(t.first, t.second, n)
|
|
||||||
case '>':
|
|
||||||
return childMatch(t.first, t.second, n)
|
|
||||||
case '+':
|
|
||||||
return siblingMatch(t.first, t.second, true, n)
|
|
||||||
case '~':
|
|
||||||
return siblingMatch(t.first, t.second, false, n)
|
|
||||||
default:
|
|
||||||
panic("unknown combinator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches an element if it matches d and has an ancestor that matches a.
|
|
||||||
func descendantMatch(a, d Matcher, n *html.Node) bool {
|
|
||||||
if !d.Match(n) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for p := n.Parent; p != nil; p = p.Parent {
|
|
||||||
if a.Match(p) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches an element if it matches d and its parent matches a.
|
|
||||||
func childMatch(a, d Matcher, n *html.Node) bool {
|
|
||||||
return d.Match(n) && n.Parent != nil && a.Match(n.Parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches an element if it matches s2 and is preceded by an element that matches s1.
|
|
||||||
// If adjacent is true, the sibling must be immediately before the element.
|
|
||||||
func siblingMatch(s1, s2 Matcher, adjacent bool, n *html.Node) bool {
|
|
||||||
if !s2.Match(n) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if adjacent {
|
|
||||||
for n = n.PrevSibling; n != nil; n = n.PrevSibling {
|
|
||||||
if n.Type == html.TextNode || n.Type == html.CommentNode {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return s1.Match(n)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk backwards looking for element that matches s1
|
|
||||||
for c := n.PrevSibling; c != nil; c = c.PrevSibling {
|
|
||||||
if s1.Match(c) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s combinedSelector) Specificity() Specificity {
|
|
||||||
spec := s.first.Specificity()
|
|
||||||
if s.second != nil {
|
|
||||||
spec = spec.Add(s.second.Specificity())
|
|
||||||
}
|
|
||||||
return spec
|
|
||||||
}
|
|
||||||
|
|
||||||
// on combinedSelector, a pseudo-element only makes sens on the last
|
|
||||||
// selector, although others increase specificity.
|
|
||||||
func (c combinedSelector) PseudoElement() string {
|
|
||||||
if c.second == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return c.second.PseudoElement()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A SelectorGroup is a list of selectors, which matches if any of the
|
|
||||||
// individual selectors matches.
|
|
||||||
type SelectorGroup []Sel
|
|
||||||
|
|
||||||
// Match returns true if the node matches one of the single selectors.
|
|
||||||
func (s SelectorGroup) Match(n *html.Node) bool {
|
|
||||||
for _, sel := range s {
|
|
||||||
if sel.Match(n) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
176
vendor/github.com/andybalholm/cascadia/serialize.go
generated
vendored
176
vendor/github.com/andybalholm/cascadia/serialize.go
generated
vendored
|
@ -1,176 +0,0 @@
|
||||||
package cascadia
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// implements the reverse operation Sel -> string
|
|
||||||
|
|
||||||
var specialCharReplacer *strings.Replacer
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var pairs []string
|
|
||||||
for _, s := range ",!\"#$%&'()*+ -./:;<=>?@[\\]^`{|}~" {
|
|
||||||
pairs = append(pairs, string(s), "\\"+string(s))
|
|
||||||
}
|
|
||||||
specialCharReplacer = strings.NewReplacer(pairs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// espace special CSS char
|
|
||||||
func escape(s string) string { return specialCharReplacer.Replace(s) }
|
|
||||||
|
|
||||||
func (c tagSelector) String() string {
|
|
||||||
return c.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c idSelector) String() string {
|
|
||||||
return "#" + escape(c.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c classSelector) String() string {
|
|
||||||
return "." + escape(c.class)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c attrSelector) String() string {
|
|
||||||
val := c.val
|
|
||||||
if c.operation == "#=" {
|
|
||||||
val = c.regexp.String()
|
|
||||||
} else if c.operation != "" {
|
|
||||||
val = fmt.Sprintf(`"%s"`, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
ignoreCase := ""
|
|
||||||
|
|
||||||
if c.insensitive {
|
|
||||||
ignoreCase = " i"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(`[%s%s%s%s]`, c.key, c.operation, val, ignoreCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c relativePseudoClassSelector) String() string {
|
|
||||||
return fmt.Sprintf(":%s(%s)", c.name, c.match.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c containsPseudoClassSelector) String() string {
|
|
||||||
s := "contains"
|
|
||||||
if c.own {
|
|
||||||
s += "Own"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(`:%s("%s")`, s, c.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c regexpPseudoClassSelector) String() string {
|
|
||||||
s := "matches"
|
|
||||||
if c.own {
|
|
||||||
s += "Own"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(":%s(%s)", s, c.regexp.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c nthPseudoClassSelector) String() string {
|
|
||||||
if c.a == 0 && c.b == 1 { // special cases
|
|
||||||
s := ":first-"
|
|
||||||
if c.last {
|
|
||||||
s = ":last-"
|
|
||||||
}
|
|
||||||
if c.ofType {
|
|
||||||
s += "of-type"
|
|
||||||
} else {
|
|
||||||
s += "child"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
var name string
|
|
||||||
switch [2]bool{c.last, c.ofType} {
|
|
||||||
case [2]bool{true, true}:
|
|
||||||
name = "nth-last-of-type"
|
|
||||||
case [2]bool{true, false}:
|
|
||||||
name = "nth-last-child"
|
|
||||||
case [2]bool{false, true}:
|
|
||||||
name = "nth-of-type"
|
|
||||||
case [2]bool{false, false}:
|
|
||||||
name = "nth-child"
|
|
||||||
}
|
|
||||||
s := fmt.Sprintf("+%d", c.b)
|
|
||||||
if c.b < 0 { // avoid +-8 invalid syntax
|
|
||||||
s = strconv.Itoa(c.b)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(":%s(%dn%s)", name, c.a, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c onlyChildPseudoClassSelector) String() string {
|
|
||||||
if c.ofType {
|
|
||||||
return ":only-of-type"
|
|
||||||
}
|
|
||||||
return ":only-child"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c inputPseudoClassSelector) String() string {
|
|
||||||
return ":input"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c emptyElementPseudoClassSelector) String() string {
|
|
||||||
return ":empty"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c rootPseudoClassSelector) String() string {
|
|
||||||
return ":root"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c linkPseudoClassSelector) String() string {
|
|
||||||
return ":link"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c langPseudoClassSelector) String() string {
|
|
||||||
return fmt.Sprintf(":lang(%s)", c.lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c neverMatchSelector) String() string {
|
|
||||||
return c.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c enabledPseudoClassSelector) String() string {
|
|
||||||
return ":enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c disabledPseudoClassSelector) String() string {
|
|
||||||
return ":disabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c checkedPseudoClassSelector) String() string {
|
|
||||||
return ":checked"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c compoundSelector) String() string {
|
|
||||||
if len(c.selectors) == 0 && c.pseudoElement == "" {
|
|
||||||
return "*"
|
|
||||||
}
|
|
||||||
chunks := make([]string, len(c.selectors))
|
|
||||||
for i, sel := range c.selectors {
|
|
||||||
chunks[i] = sel.String()
|
|
||||||
}
|
|
||||||
s := strings.Join(chunks, "")
|
|
||||||
if c.pseudoElement != "" {
|
|
||||||
s += "::" + c.pseudoElement
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c combinedSelector) String() string {
|
|
||||||
start := c.first.String()
|
|
||||||
if c.second != nil {
|
|
||||||
start += fmt.Sprintf(" %s %s", string(c.combinator), c.second.String())
|
|
||||||
}
|
|
||||||
return start
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c SelectorGroup) String() string {
|
|
||||||
ck := make([]string, len(c))
|
|
||||||
for i, s := range c {
|
|
||||||
ck[i] = s.String()
|
|
||||||
}
|
|
||||||
return strings.Join(ck, ", ")
|
|
||||||
}
|
|
26
vendor/github.com/andybalholm/cascadia/specificity.go
generated
vendored
26
vendor/github.com/andybalholm/cascadia/specificity.go
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
package cascadia
|
|
||||||
|
|
||||||
// Specificity is the CSS specificity as defined in
|
|
||||||
// https://www.w3.org/TR/selectors/#specificity-rules
|
|
||||||
// with the convention Specificity = [A,B,C].
|
|
||||||
type Specificity [3]int
|
|
||||||
|
|
||||||
// returns `true` if s < other (strictly), false otherwise
|
|
||||||
func (s Specificity) Less(other Specificity) bool {
|
|
||||||
for i := range s {
|
|
||||||
if s[i] < other[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if s[i] > other[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Specificity) Add(other Specificity) Specificity {
|
|
||||||
for i, sp := range other {
|
|
||||||
s[i] += sp
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
15
vendor/github.com/chilts/sid/.codeclimate.yml
generated
vendored
15
vendor/github.com/chilts/sid/.codeclimate.yml
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
engines:
|
|
||||||
golint:
|
|
||||||
enabled: true
|
|
||||||
govet:
|
|
||||||
enabled: true
|
|
||||||
gofmt:
|
|
||||||
enabled: true
|
|
||||||
fixme:
|
|
||||||
enabled: true
|
|
||||||
ratings:
|
|
||||||
paths:
|
|
||||||
- "**.go"
|
|
||||||
exclude_paths:
|
|
||||||
- "*_test.go"
|
|
11
vendor/github.com/chilts/sid/.travis.yml
generated
vendored
11
vendor/github.com/chilts/sid/.travis.yml
generated
vendored
|
@ -1,11 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.x
|
|
||||||
- master
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: 'master'
|
|
||||||
fast_finish: true
|
|
22
vendor/github.com/chilts/sid/LICENSE
generated
vendored
22
vendor/github.com/chilts/sid/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2017 Andrew Chilton <andychilton@gmail.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
48
vendor/github.com/chilts/sid/ReadMe.md
generated
vendored
48
vendor/github.com/chilts/sid/ReadMe.md
generated
vendored
|
@ -1,48 +0,0 @@
|
||||||
# sid : generate sortable identifiers
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/chilts/sid?status.svg)](https://godoc.org/github.com/chilts/sid)
|
|
||||||
[![Build Status](https://travis-ci.org/chilts/sid.svg?branch=master)](https://travis-ci.org/chilts/sid)
|
|
||||||
[![Code Climate](https://codeclimate.com/github/chilts/sid/badges/gpa.svg)](https://codeclimate.com/github/chilts/sid)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/chilts/sid)](https://goreportcard.com/report/github.com/chilts/sid)
|
|
||||||
|
|
||||||
This package is simple and only provides one function. The aim here is not pure speed, it is for an easy use-case
|
|
||||||
without having to worry about goroutines and locking.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/chilts/sid
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```
|
|
||||||
id1 := sid.Id()
|
|
||||||
id2 := sid.IdHex()
|
|
||||||
id3 := sid.IdBase32()
|
|
||||||
id4 := sid.IdBase64()
|
|
||||||
|
|
||||||
fmt.Printf("id1 = %s\n", id1)
|
|
||||||
fmt.Printf("id2 = %s\n", id2)
|
|
||||||
fmt.Printf("id3 = %s\n", id3)
|
|
||||||
fmt.Printf("id4 = %s\n", id4)
|
|
||||||
|
|
||||||
// -> "id1 = 1559872035903071353-1186579057231285506"
|
|
||||||
// -> "id2 = 15a5cf57e7d2a837-6eaafe687e7b3ec3"
|
|
||||||
// -> "id3 = 1b9efqnl51jj7-4u66ikpfq9ugm"
|
|
||||||
// -> "id4 = 1IeSBAWW9kK-0cDG64GQgGJ"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Author
|
|
||||||
|
|
||||||
By [Andrew Chilton](https://chilts.org/), [@twitter](https://twitter.com/andychilton).
|
|
||||||
|
|
||||||
For [AppsAttic](https://appsattic.com/), [@AppsAttic](https://twitter.com/AppsAttic).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](https://chilts.mit-license.org/2017/)
|
|
||||||
|
|
||||||
(Ends)
|
|
3
vendor/github.com/chilts/sid/go.mod
generated
vendored
3
vendor/github.com/chilts/sid/go.mod
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
module github.com/chilts/sid
|
|
||||||
|
|
||||||
go 1.12
|
|
186
vendor/github.com/chilts/sid/sid.go
generated
vendored
186
vendor/github.com/chilts/sid/sid.go
generated
vendored
|
@ -1,186 +0,0 @@
|
||||||
// Package sid provides the ability to generate Sortable Identifiers. These are also universally unique.
|
|
||||||
//
|
|
||||||
// id1 := sid.Id()
|
|
||||||
// id2 := sid.Id()
|
|
||||||
// fmt.Printf("id1 = %s\n", id1)
|
|
||||||
// fmt.Printf("id2 = %s\n", id2)
|
|
||||||
// // -> "id1 = 1IeSBAWW83j-2wgJ4PUtlAr"
|
|
||||||
// // -> "id2 = 1IeSBAWW9kK-0cDG64GQgGJ"
|
|
||||||
//
|
|
||||||
// This package is simple and only provides one function. The aim here is not pure speed, it is for an easy use-case
|
|
||||||
// without having to worry about goroutines and locking.
|
|
||||||
package sid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember the lastTime so that if (by chance) we get the same NanoSecond, we just incrememt the last random number.
|
|
||||||
var mu = &sync.Mutex{}
|
|
||||||
var lastTime int64
|
|
||||||
var lastRand int64
|
|
||||||
var chars = make([]string, 11, 11)
|
|
||||||
|
|
||||||
// 64 chars but ordered by ASCII
|
|
||||||
const base64 string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
|
|
||||||
|
|
||||||
func toStr(n int64) string {
|
|
||||||
// now do the generation (backwards, so we just %64 then /64 along the way)
|
|
||||||
for i := 10; i >= 0; i-- {
|
|
||||||
index := n % 64
|
|
||||||
chars[i] = string(base64[index])
|
|
||||||
n = n / 64
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(chars, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func toBase32(n int64) string {
|
|
||||||
b32 := strconv.FormatInt(n, 32)
|
|
||||||
|
|
||||||
for len(b32) < 13 {
|
|
||||||
b32 = "0" + b32
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Printf("b32=%s\n", b32)
|
|
||||||
|
|
||||||
return b32
|
|
||||||
}
|
|
||||||
|
|
||||||
func toHex(n int64) string {
|
|
||||||
hex := fmt.Sprintf("%x", n)
|
|
||||||
|
|
||||||
for len(hex) < 16 {
|
|
||||||
hex = "0" + hex
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdBase64 returns a 23 char string based on timestamp and a random number. The format is "XXXXXXXXXXX-YYYYYYYYYYY"
|
|
||||||
// where X is the timestamp and Y is the random number. If (by any chance) this is called in the same nanosecond, the
|
|
||||||
// random number is incremented instead of a new one being generated. This makes sure that two consecutive Ids
|
|
||||||
// generated in the same goroutine also ensure those Ids are also sortable.
|
|
||||||
//
|
|
||||||
// It is safe to call from different goroutines since it has it's own locking.
|
|
||||||
func IdBase64() string {
|
|
||||||
// lock for lastTime, lastRand, and chars
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
now := time.Now().UTC().UnixNano()
|
|
||||||
var r int64
|
|
||||||
|
|
||||||
// if we have the same time, just inc lastRand, else create a new one
|
|
||||||
if now == lastTime {
|
|
||||||
lastRand++
|
|
||||||
} else {
|
|
||||||
lastRand = rand.Int63()
|
|
||||||
}
|
|
||||||
r = lastRand
|
|
||||||
|
|
||||||
// remember this for next time
|
|
||||||
lastTime = now
|
|
||||||
|
|
||||||
return toStr(now) + "-" + toStr(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdBase32 returns a 27 char string based on timestamp and a random number. The format is
|
|
||||||
// "XXXXXXXXXXXXX-YYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance) this is called
|
|
||||||
// in the same nanosecond, the random number is incremented instead of a new one being generated. This makes sure that
|
|
||||||
// two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
|
||||||
//
|
|
||||||
// It is safe to call from different goroutines since it has it's own locking.
|
|
||||||
func IdBase32() string {
|
|
||||||
// lock for lastTime, lastRand, and chars
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
now := time.Now().UTC().UnixNano()
|
|
||||||
var r int64
|
|
||||||
|
|
||||||
// if we have the same time, just inc lastRand, else create a new one
|
|
||||||
if now == lastTime {
|
|
||||||
lastRand++
|
|
||||||
} else {
|
|
||||||
lastRand = rand.Int63()
|
|
||||||
}
|
|
||||||
r = lastRand
|
|
||||||
|
|
||||||
// remember this for next time
|
|
||||||
lastTime = now
|
|
||||||
|
|
||||||
return toBase32(now) + "-" + toBase32(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdHex returns a char string based on timestamp and a random number. The format is
|
|
||||||
// "XXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance) this is
|
|
||||||
// called in the same nanosecond, the random number is incremented instead of a new one being generated. This makes
|
|
||||||
// sure that two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
|
||||||
//
|
|
||||||
// It is safe to call from different goroutines since it has it's own locking.
|
|
||||||
func IdHex() string {
|
|
||||||
// lock for lastTime, lastRand, and chars
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
now := time.Now().UTC().UnixNano()
|
|
||||||
var r int64
|
|
||||||
|
|
||||||
// if we have the same time, just inc lastRand, else create a new one
|
|
||||||
if now == lastTime {
|
|
||||||
lastRand++
|
|
||||||
} else {
|
|
||||||
lastRand = rand.Int63()
|
|
||||||
}
|
|
||||||
r = lastRand
|
|
||||||
|
|
||||||
// remember this for next time
|
|
||||||
lastTime = now
|
|
||||||
|
|
||||||
return toHex(now) + "-" + toHex(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Id returns a 39 char string based on timestamp and a random number. The format is
|
|
||||||
// "XXXXXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance)
|
|
||||||
// this is called in the same nanosecond, the random number is incremented instead of a new one being generated. This
|
|
||||||
// makes sure that two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
|
||||||
//
|
|
||||||
// It is safe to call from different goroutines since it has it's own locking.
|
|
||||||
func Id() string {
|
|
||||||
// lock for lastTime, lastRand, and chars
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
now := time.Now().UTC().UnixNano()
|
|
||||||
var r int64
|
|
||||||
|
|
||||||
// if we have the same time, just inc lastRand, else create a new one
|
|
||||||
if now == lastTime {
|
|
||||||
lastRand++
|
|
||||||
} else {
|
|
||||||
lastRand = rand.Int63()
|
|
||||||
}
|
|
||||||
r = lastRand
|
|
||||||
|
|
||||||
// remember this for next time
|
|
||||||
lastTime = now
|
|
||||||
|
|
||||||
nowStr := strconv.FormatInt(now, 10)
|
|
||||||
rStr := strconv.FormatInt(r, 10)
|
|
||||||
|
|
||||||
for len(rStr) < 19 {
|
|
||||||
rStr = "0" + rStr
|
|
||||||
}
|
|
||||||
|
|
||||||
return nowStr + "-" + rStr
|
|
||||||
}
|
|
24
vendor/github.com/jaytaylor/html2text/.gitignore
generated
vendored
24
vendor/github.com/jaytaylor/html2text/.gitignore
generated
vendored
|
@ -1,24 +0,0 @@
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
||||||
*.prof
|
|
13
vendor/github.com/jaytaylor/html2text/.travis.yml
generated
vendored
13
vendor/github.com/jaytaylor/html2text/.travis.yml
generated
vendored
|
@ -1,13 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
# n.b. For golang release history, see https://golang.org/doc/devel/release.html
|
|
||||||
- tip
|
|
||||||
- "1.13.8"
|
|
||||||
- "1.12.17"
|
|
||||||
- "1.11.13"
|
|
||||||
- "1.10.8"
|
|
||||||
- "1.9.7"
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
on_success: change
|
|
||||||
on_failure: always
|
|
22
vendor/github.com/jaytaylor/html2text/LICENSE
generated
vendored
22
vendor/github.com/jaytaylor/html2text/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Jay Taylor
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
143
vendor/github.com/jaytaylor/html2text/README.md
generated
vendored
143
vendor/github.com/jaytaylor/html2text/README.md
generated
vendored
|
@ -1,143 +0,0 @@
|
||||||
# html2text
|
|
||||||
|
|
||||||
[![Documentation](https://godoc.org/github.com/jaytaylor/html2text?status.svg)](https://godoc.org/github.com/jaytaylor/html2text)
|
|
||||||
[![Build Status](https://travis-ci.org/jaytaylor/html2text.svg?branch=master)](https://travis-ci.org/jaytaylor/html2text)
|
|
||||||
[![Report Card](https://goreportcard.com/badge/github.com/jaytaylor/html2text)](https://goreportcard.com/report/github.com/jaytaylor/html2text)
|
|
||||||
|
|
||||||
### Converts HTML into text of the markdown-flavored variety
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Ensure your emails are readable by all!
|
|
||||||
|
|
||||||
Turns HTML into raw text, useful for sending fancy HTML emails with an equivalently nicely formatted TXT document as a fallback (e.g. for people who don't allow HTML emails or have other display issues).
|
|
||||||
|
|
||||||
html2text is a simple golang package for rendering HTML into plaintext.
|
|
||||||
|
|
||||||
There are still lots of improvements to be had, but FWIW this has worked fine for my [basic] HTML-2-text needs.
|
|
||||||
|
|
||||||
It requires go 1.x or newer ;)
|
|
||||||
|
|
||||||
## Download the package
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get jaytaylor.com/html2text
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example usage
|
|
||||||
|
|
||||||
### Library
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"jaytaylor.com/html2text"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
inputHTML := `
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>My Mega Service</title>
|
|
||||||
<link rel=\"stylesheet\" href=\"main.css\">
|
|
||||||
<style type=\"text/css\">body { color: #fff; }</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="logo">
|
|
||||||
<a href="http://jaytaylor.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>Welcome to your new account on my service!</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Here is some more information:
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Link 1: <a href="https://example.com">Example.com</a></li>
|
|
||||||
<li>Link 2: <a href="https://example2.com">Example2.com</a></li>
|
|
||||||
<li>Something else</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>Header 1</th><th>Header 2</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tfoot>
|
|
||||||
<tr><td>Footer 1</td><td>Footer 2</td></tr>
|
|
||||||
</tfoot>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Row 1 Col 1</td><td>Row 1 Col 2</td></tr>
|
|
||||||
<tr><td>Row 2 Col 1</td><td>Row 2 Col 2</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>`
|
|
||||||
|
|
||||||
text, err := html2text.FromString(inputHTML, html2text.Options{PrettyTables: true})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(text)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```
|
|
||||||
Mega Service ( http://jaytaylor.com/ )
|
|
||||||
|
|
||||||
******************************************
|
|
||||||
Welcome to your new account on my service!
|
|
||||||
******************************************
|
|
||||||
|
|
||||||
Here is some more information:
|
|
||||||
|
|
||||||
* Link 1: Example.com ( https://example.com )
|
|
||||||
* Link 2: Example2.com ( https://example2.com )
|
|
||||||
* Something else
|
|
||||||
|
|
||||||
+-------------+-------------+
|
|
||||||
| HEADER 1 | HEADER 2 |
|
|
||||||
+-------------+-------------+
|
|
||||||
| Row 1 Col 1 | Row 1 Col 2 |
|
|
||||||
| Row 2 Col 1 | Row 2 Col 2 |
|
|
||||||
+-------------+-------------+
|
|
||||||
| FOOTER 1 | FOOTER 2 |
|
|
||||||
+-------------+-------------+
|
|
||||||
```
|
|
||||||
|
|
||||||
### Command line
|
|
||||||
|
|
||||||
```
|
|
||||||
echo '<div>hi</div>' | html2text
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unit-tests
|
|
||||||
|
|
||||||
Running the unit-tests is straightforward and standard:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
Permissive MIT license.
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
|
|
||||||
You are more than welcome to open issues and send pull requests if you find a bug or want a new feature.
|
|
||||||
|
|
||||||
If you appreciate this library please feel free to drop me a line and tell me! It's always nice to hear from people who have benefitted from my work.
|
|
||||||
|
|
||||||
Email: jay at (my github username).com
|
|
||||||
|
|
||||||
Twitter: [@jtaylor](https://twitter.com/jtaylor)
|
|
||||||
|
|
||||||
# Alternatives
|
|
||||||
|
|
||||||
https://github.com/k3a/html2text - Lightweight
|
|
548
vendor/github.com/jaytaylor/html2text/html2text.go
generated
vendored
548
vendor/github.com/jaytaylor/html2text/html2text.go
generated
vendored
|
@ -1,548 +0,0 @@
|
||||||
package html2text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
"github.com/ssor/bom"
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
"golang.org/x/net/html/atom"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options provide toggles and overrides to control specific rendering behaviors.
|
|
||||||
type Options struct {
|
|
||||||
PrettyTables bool // Turns on pretty ASCII rendering for table elements.
|
|
||||||
PrettyTablesOptions *PrettyTablesOptions // Configures pretty ASCII rendering for table elements.
|
|
||||||
OmitLinks bool // Turns on omitting links
|
|
||||||
TextOnly bool // Returns only plain text
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyTablesOptions overrides tablewriter behaviors
|
|
||||||
type PrettyTablesOptions struct {
|
|
||||||
AutoFormatHeader bool
|
|
||||||
AutoWrapText bool
|
|
||||||
ReflowDuringAutoWrap bool
|
|
||||||
ColWidth int
|
|
||||||
ColumnSeparator string
|
|
||||||
RowSeparator string
|
|
||||||
CenterSeparator string
|
|
||||||
HeaderAlignment int
|
|
||||||
FooterAlignment int
|
|
||||||
Alignment int
|
|
||||||
ColumnAlignment []int
|
|
||||||
NewLine string
|
|
||||||
HeaderLine bool
|
|
||||||
RowLine bool
|
|
||||||
AutoMergeCells bool
|
|
||||||
Borders tablewriter.Border
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrettyTablesOptions creates PrettyTablesOptions with default settings
|
|
||||||
func NewPrettyTablesOptions() *PrettyTablesOptions {
|
|
||||||
return &PrettyTablesOptions{
|
|
||||||
AutoFormatHeader: true,
|
|
||||||
AutoWrapText: true,
|
|
||||||
ReflowDuringAutoWrap: true,
|
|
||||||
ColWidth: tablewriter.MAX_ROW_WIDTH,
|
|
||||||
ColumnSeparator: tablewriter.COLUMN,
|
|
||||||
RowSeparator: tablewriter.ROW,
|
|
||||||
CenterSeparator: tablewriter.CENTER,
|
|
||||||
HeaderAlignment: tablewriter.ALIGN_DEFAULT,
|
|
||||||
FooterAlignment: tablewriter.ALIGN_DEFAULT,
|
|
||||||
Alignment: tablewriter.ALIGN_DEFAULT,
|
|
||||||
ColumnAlignment: []int{},
|
|
||||||
NewLine: tablewriter.NEWLINE,
|
|
||||||
HeaderLine: true,
|
|
||||||
RowLine: false,
|
|
||||||
AutoMergeCells: false,
|
|
||||||
Borders: tablewriter.Border{Left: true, Right: true, Bottom: true, Top: true},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromHTMLNode renders text output from a pre-parsed HTML document.
|
|
||||||
func FromHTMLNode(doc *html.Node, o ...Options) (string, error) {
|
|
||||||
var options Options
|
|
||||||
if len(o) > 0 {
|
|
||||||
options = o[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := textifyTraverseContext{
|
|
||||||
buf: bytes.Buffer{},
|
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
if err := ctx.traverse(doc); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
text := strings.TrimSpace(newlineRe.ReplaceAllString(
|
|
||||||
strings.Replace(ctx.buf.String(), "\n ", "\n", -1), "\n\n"),
|
|
||||||
)
|
|
||||||
return text, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromReader renders text output after parsing HTML for the specified
|
|
||||||
// io.Reader.
|
|
||||||
func FromReader(reader io.Reader, options ...Options) (string, error) {
|
|
||||||
newReader, err := bom.NewReaderWithoutBom(reader)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
doc, err := html.Parse(newReader)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return FromHTMLNode(doc, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromString parses HTML from the input string, then renders the text form.
|
|
||||||
func FromString(input string, options ...Options) (string, error) {
|
|
||||||
bs := bom.CleanBom([]byte(input))
|
|
||||||
text, err := FromReader(bytes.NewReader(bs), options...)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return text, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
spacingRe = regexp.MustCompile(`[ \r\n\t]+`)
|
|
||||||
newlineRe = regexp.MustCompile(`\n\n+`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// traverseTableCtx holds text-related context.
|
|
||||||
type textifyTraverseContext struct {
|
|
||||||
buf bytes.Buffer
|
|
||||||
|
|
||||||
prefix string
|
|
||||||
tableCtx tableTraverseContext
|
|
||||||
options Options
|
|
||||||
endsWithSpace bool
|
|
||||||
justClosedDiv bool
|
|
||||||
blockquoteLevel int
|
|
||||||
lineLength int
|
|
||||||
isPre bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// tableTraverseContext holds table ASCII-form related context.
|
|
||||||
type tableTraverseContext struct {
|
|
||||||
header []string
|
|
||||||
body [][]string
|
|
||||||
footer []string
|
|
||||||
tmpRow int
|
|
||||||
isInFooter bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tableCtx *tableTraverseContext) init() {
|
|
||||||
tableCtx.body = [][]string{}
|
|
||||||
tableCtx.header = []string{}
|
|
||||||
tableCtx.footer = []string{}
|
|
||||||
tableCtx.isInFooter = false
|
|
||||||
tableCtx.tmpRow = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) handleElement(node *html.Node) error {
|
|
||||||
ctx.justClosedDiv = false
|
|
||||||
|
|
||||||
switch node.DataAtom {
|
|
||||||
case atom.Br:
|
|
||||||
return ctx.emit("\n")
|
|
||||||
|
|
||||||
case atom.H1, atom.H2, atom.H3:
|
|
||||||
subCtx := textifyTraverseContext{}
|
|
||||||
if err := subCtx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
str := subCtx.buf.String()
|
|
||||||
if ctx.options.TextOnly {
|
|
||||||
return ctx.emit(str + ".\n\n")
|
|
||||||
}
|
|
||||||
dividerLen := 0
|
|
||||||
for _, line := range strings.Split(str, "\n") {
|
|
||||||
if lineLen := len([]rune(line)); lineLen-1 > dividerLen {
|
|
||||||
dividerLen = lineLen - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var divider string
|
|
||||||
if node.DataAtom == atom.H1 {
|
|
||||||
divider = strings.Repeat("*", dividerLen)
|
|
||||||
} else {
|
|
||||||
divider = strings.Repeat("-", dividerLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.DataAtom == atom.H3 {
|
|
||||||
return ctx.emit("\n\n" + str + "\n" + divider + "\n\n")
|
|
||||||
}
|
|
||||||
return ctx.emit("\n\n" + divider + "\n" + str + "\n" + divider + "\n\n")
|
|
||||||
|
|
||||||
case atom.Blockquote:
|
|
||||||
ctx.blockquoteLevel++
|
|
||||||
if !ctx.options.TextOnly {
|
|
||||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel) + " "
|
|
||||||
}
|
|
||||||
if err := ctx.emit("\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ctx.blockquoteLevel == 1 {
|
|
||||||
if err := ctx.emit("\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ctx.blockquoteLevel--
|
|
||||||
if !ctx.options.TextOnly {
|
|
||||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel)
|
|
||||||
}
|
|
||||||
if ctx.blockquoteLevel > 0 {
|
|
||||||
ctx.prefix += " "
|
|
||||||
}
|
|
||||||
return ctx.emit("\n\n")
|
|
||||||
|
|
||||||
case atom.Div:
|
|
||||||
if ctx.lineLength > 0 {
|
|
||||||
if err := ctx.emit("\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if !ctx.justClosedDiv {
|
|
||||||
err = ctx.emit("\n")
|
|
||||||
}
|
|
||||||
ctx.justClosedDiv = true
|
|
||||||
return err
|
|
||||||
|
|
||||||
case atom.Li:
|
|
||||||
if !ctx.options.TextOnly {
|
|
||||||
if err := ctx.emit("* "); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.emit("\n")
|
|
||||||
|
|
||||||
case atom.B, atom.Strong:
|
|
||||||
subCtx := textifyTraverseContext{}
|
|
||||||
subCtx.endsWithSpace = true
|
|
||||||
if err := subCtx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
str := subCtx.buf.String()
|
|
||||||
if ctx.options.TextOnly {
|
|
||||||
return ctx.emit(str + ".")
|
|
||||||
}
|
|
||||||
return ctx.emit("*" + str + "*")
|
|
||||||
|
|
||||||
case atom.A:
|
|
||||||
linkText := ""
|
|
||||||
// For simple link element content with single text node only, peek at the link text.
|
|
||||||
if node.FirstChild != nil && node.FirstChild.NextSibling == nil && node.FirstChild.Type == html.TextNode {
|
|
||||||
linkText = node.FirstChild.Data
|
|
||||||
}
|
|
||||||
|
|
||||||
// If image is the only child, take its alt text as the link text.
|
|
||||||
if img := node.FirstChild; img != nil && node.LastChild == img && img.DataAtom == atom.Img {
|
|
||||||
if altText := getAttrVal(img, "alt"); altText != "" {
|
|
||||||
if err := ctx.emit(altText); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hrefLink := ""
|
|
||||||
if attrVal := getAttrVal(node, "href"); attrVal != "" {
|
|
||||||
attrVal = ctx.normalizeHrefLink(attrVal)
|
|
||||||
// Don't print link href if it matches link element content or if the link is empty.
|
|
||||||
if (!ctx.options.OmitLinks && attrVal != "" && linkText != attrVal) || !ctx.options.TextOnly {
|
|
||||||
hrefLink = "( " + attrVal + " )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.emit(hrefLink)
|
|
||||||
|
|
||||||
case atom.P, atom.Ul:
|
|
||||||
return ctx.paragraphHandler(node)
|
|
||||||
|
|
||||||
case atom.Table, atom.Tfoot, atom.Th, atom.Tr, atom.Td:
|
|
||||||
if ctx.options.PrettyTables {
|
|
||||||
return ctx.handleTableElement(node)
|
|
||||||
} else if node.DataAtom == atom.Table {
|
|
||||||
return ctx.paragraphHandler(node)
|
|
||||||
}
|
|
||||||
return ctx.traverseChildren(node)
|
|
||||||
|
|
||||||
case atom.Pre:
|
|
||||||
ctx.isPre = true
|
|
||||||
err := ctx.traverseChildren(node)
|
|
||||||
ctx.isPre = false
|
|
||||||
return err
|
|
||||||
|
|
||||||
case atom.Style, atom.Script, atom.Head:
|
|
||||||
// Ignore the subtree.
|
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ctx.traverseChildren(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// paragraphHandler renders node children surrounded by double newlines.
|
|
||||||
func (ctx *textifyTraverseContext) paragraphHandler(node *html.Node) error {
|
|
||||||
if err := ctx.emit("\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ctx.emit("\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleTableElement is only to be invoked when options.PrettyTables is active.
|
|
||||||
func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error {
|
|
||||||
if !ctx.options.PrettyTables {
|
|
||||||
panic("handleTableElement invoked when PrettyTables not active")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node.DataAtom {
|
|
||||||
case atom.Table:
|
|
||||||
if err := ctx.emit("\n\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-intialize all table context.
|
|
||||||
ctx.tableCtx.init()
|
|
||||||
|
|
||||||
// Browse children, enriching context with table data.
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
table := tablewriter.NewWriter(buf)
|
|
||||||
if ctx.options.PrettyTablesOptions != nil {
|
|
||||||
options := ctx.options.PrettyTablesOptions
|
|
||||||
table.SetAutoFormatHeaders(options.AutoFormatHeader)
|
|
||||||
table.SetAutoWrapText(options.AutoWrapText)
|
|
||||||
table.SetReflowDuringAutoWrap(options.ReflowDuringAutoWrap)
|
|
||||||
table.SetColWidth(options.ColWidth)
|
|
||||||
table.SetColumnSeparator(options.ColumnSeparator)
|
|
||||||
table.SetRowSeparator(options.RowSeparator)
|
|
||||||
table.SetCenterSeparator(options.CenterSeparator)
|
|
||||||
table.SetHeaderAlignment(options.HeaderAlignment)
|
|
||||||
table.SetFooterAlignment(options.FooterAlignment)
|
|
||||||
table.SetAlignment(options.Alignment)
|
|
||||||
table.SetColumnAlignment(options.ColumnAlignment)
|
|
||||||
table.SetNewLine(options.NewLine)
|
|
||||||
table.SetHeaderLine(options.HeaderLine)
|
|
||||||
table.SetRowLine(options.RowLine)
|
|
||||||
table.SetAutoMergeCells(options.AutoMergeCells)
|
|
||||||
table.SetBorders(options.Borders)
|
|
||||||
}
|
|
||||||
table.SetHeader(ctx.tableCtx.header)
|
|
||||||
table.SetFooter(ctx.tableCtx.footer)
|
|
||||||
table.AppendBulk(ctx.tableCtx.body)
|
|
||||||
|
|
||||||
// Render the table using ASCII.
|
|
||||||
table.Render()
|
|
||||||
if err := ctx.emit(buf.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.emit("\n\n")
|
|
||||||
|
|
||||||
case atom.Tfoot:
|
|
||||||
ctx.tableCtx.isInFooter = true
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ctx.tableCtx.isInFooter = false
|
|
||||||
|
|
||||||
case atom.Tr:
|
|
||||||
ctx.tableCtx.body = append(ctx.tableCtx.body, []string{})
|
|
||||||
if err := ctx.traverseChildren(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ctx.tableCtx.tmpRow++
|
|
||||||
|
|
||||||
case atom.Th:
|
|
||||||
res, err := ctx.renderEachChild(node)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.tableCtx.header = append(ctx.tableCtx.header, res)
|
|
||||||
|
|
||||||
case atom.Td:
|
|
||||||
res, err := ctx.renderEachChild(node)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.tableCtx.isInFooter {
|
|
||||||
ctx.tableCtx.footer = append(ctx.tableCtx.footer, res)
|
|
||||||
} else {
|
|
||||||
ctx.tableCtx.body[ctx.tableCtx.tmpRow] = append(ctx.tableCtx.body[ctx.tableCtx.tmpRow], res)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) traverse(node *html.Node) error {
|
|
||||||
switch node.Type {
|
|
||||||
default:
|
|
||||||
return ctx.traverseChildren(node)
|
|
||||||
|
|
||||||
case html.TextNode:
|
|
||||||
var data string
|
|
||||||
if ctx.isPre {
|
|
||||||
data = node.Data
|
|
||||||
} else {
|
|
||||||
data = strings.TrimSpace(spacingRe.ReplaceAllString(node.Data, " "))
|
|
||||||
}
|
|
||||||
return ctx.emit(data)
|
|
||||||
|
|
||||||
case html.ElementNode:
|
|
||||||
return ctx.handleElement(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) traverseChildren(node *html.Node) error {
|
|
||||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
if err := ctx.traverse(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) emit(data string) error {
|
|
||||||
if data == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
lines = ctx.breakLongLines(data)
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for _, line := range lines {
|
|
||||||
runes := []rune(line)
|
|
||||||
startsWithSpace := unicode.IsSpace(runes[0])
|
|
||||||
if !startsWithSpace && !ctx.endsWithSpace && !strings.HasPrefix(data, ".") {
|
|
||||||
if err = ctx.buf.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ctx.lineLength++
|
|
||||||
}
|
|
||||||
ctx.endsWithSpace = unicode.IsSpace(runes[len(runes)-1])
|
|
||||||
for _, c := range line {
|
|
||||||
if _, err = ctx.buf.WriteString(string(c)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ctx.lineLength++
|
|
||||||
if c == '\n' {
|
|
||||||
ctx.lineLength = 0
|
|
||||||
if ctx.prefix != "" {
|
|
||||||
if _, err = ctx.buf.WriteString(ctx.prefix); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxLineLen = 74
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) breakLongLines(data string) []string {
|
|
||||||
// Only break lines when in blockquotes.
|
|
||||||
if ctx.blockquoteLevel == 0 {
|
|
||||||
return []string{data}
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
ret = []string{}
|
|
||||||
runes = []rune(data)
|
|
||||||
l = len(runes)
|
|
||||||
existing = ctx.lineLength
|
|
||||||
)
|
|
||||||
if existing >= maxLineLen {
|
|
||||||
ret = append(ret, "\n")
|
|
||||||
existing = 0
|
|
||||||
}
|
|
||||||
for l+existing > maxLineLen {
|
|
||||||
i := maxLineLen - existing
|
|
||||||
for i >= 0 && !unicode.IsSpace(runes[i]) {
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
if i == -1 {
|
|
||||||
// No spaces, so go the other way.
|
|
||||||
i = maxLineLen - existing
|
|
||||||
for i < l && !unicode.IsSpace(runes[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = append(ret, string(runes[:i])+"\n")
|
|
||||||
for i < l && unicode.IsSpace(runes[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
runes = runes[i:]
|
|
||||||
l = len(runes)
|
|
||||||
existing = 0
|
|
||||||
}
|
|
||||||
if len(runes) > 0 {
|
|
||||||
ret = append(ret, string(runes))
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *textifyTraverseContext) normalizeHrefLink(link string) string {
|
|
||||||
link = strings.TrimSpace(link)
|
|
||||||
link = strings.TrimPrefix(link, "mailto:")
|
|
||||||
return link
|
|
||||||
}
|
|
||||||
|
|
||||||
// renderEachChild visits each direct child of a node and collects the sequence of
|
|
||||||
// textuual representaitons separated by a single newline.
|
|
||||||
func (ctx *textifyTraverseContext) renderEachChild(node *html.Node) (string, error) {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
s, err := FromHTMLNode(c, ctx.options)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err = buf.WriteString(s); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if c.NextSibling != nil {
|
|
||||||
if err = buf.WriteByte('\n'); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAttrVal(node *html.Node, attrName string) string {
|
|
||||||
for _, attr := range node.Attr {
|
|
||||||
if attr.Key == attrName {
|
|
||||||
return attr.Val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
3
vendor/github.com/json-iterator/go/.codecov.yml
generated
vendored
3
vendor/github.com/json-iterator/go/.codecov.yml
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
ignore:
|
|
||||||
- "output_tests/.*"
|
|
||||||
|
|
4
vendor/github.com/json-iterator/go/.gitignore
generated
vendored
4
vendor/github.com/json-iterator/go/.gitignore
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
/vendor
|
|
||||||
/bug_test.go
|
|
||||||
/coverage.txt
|
|
||||||
/.idea
|
|
14
vendor/github.com/json-iterator/go/.travis.yml
generated
vendored
14
vendor/github.com/json-iterator/go/.travis.yml
generated
vendored
|
@ -1,14 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.8.x
|
|
||||||
- 1.x
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./test.sh
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
21
vendor/github.com/json-iterator/go/Gopkg.lock
generated
vendored
21
vendor/github.com/json-iterator/go/Gopkg.lock
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/modern-go/concurrent"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/modern-go/reflect2"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
|
||||||
version = "1.0.1"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
26
vendor/github.com/json-iterator/go/Gopkg.toml
generated
vendored
26
vendor/github.com/json-iterator/go/Gopkg.toml
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
|
|
||||||
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/modern-go/reflect2"
|
|
||||||
version = "1.0.1"
|
|
21
vendor/github.com/json-iterator/go/LICENSE
generated
vendored
21
vendor/github.com/json-iterator/go/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2016 json-iterator
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
85
vendor/github.com/json-iterator/go/README.md
generated
vendored
85
vendor/github.com/json-iterator/go/README.md
generated
vendored
|
@ -1,85 +0,0 @@
|
||||||
[![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge)
|
|
||||||
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go)
|
|
||||||
[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go)
|
|
||||||
[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go)
|
|
||||||
[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go)
|
|
||||||
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE)
|
|
||||||
[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
|
|
||||||
|
|
||||||
A high-performance 100% compatible drop-in replacement of "encoding/json"
|
|
||||||
|
|
||||||
# Benchmark
|
|
||||||
|
|
||||||
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
|
|
||||||
|
|
||||||
Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go
|
|
||||||
|
|
||||||
Raw Result (easyjson requires static code generation)
|
|
||||||
|
|
||||||
| | ns/op | allocation bytes | allocation times |
|
|
||||||
| --------------- | ----------- | ---------------- | ---------------- |
|
|
||||||
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
|
|
||||||
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
|
|
||||||
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
|
|
||||||
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
|
|
||||||
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
|
||||||
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
|
||||||
|
|
||||||
Always benchmark with your own workload.
|
|
||||||
The result depends heavily on the data input.
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
100% compatibility with standard lib
|
|
||||||
|
|
||||||
Replace
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "encoding/json"
|
|
||||||
json.Marshal(&data)
|
|
||||||
```
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
```go
|
|
||||||
import jsoniter "github.com/json-iterator/go"
|
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
json.Marshal(&data)
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "encoding/json"
|
|
||||||
json.Unmarshal(input, &data)
|
|
||||||
```
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
```go
|
|
||||||
import jsoniter "github.com/json-iterator/go"
|
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
json.Unmarshal(input, &data)
|
|
||||||
```
|
|
||||||
|
|
||||||
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
|
|
||||||
|
|
||||||
# How to get
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/json-iterator/go
|
|
||||||
```
|
|
||||||
|
|
||||||
# Contribution Welcomed !
|
|
||||||
|
|
||||||
Contributors
|
|
||||||
|
|
||||||
- [thockin](https://github.com/thockin)
|
|
||||||
- [mattn](https://github.com/mattn)
|
|
||||||
- [cch123](https://github.com/cch123)
|
|
||||||
- [Oleg Shaldybin](https://github.com/olegshaldybin)
|
|
||||||
- [Jason Toffaletti](https://github.com/toffaletti)
|
|
||||||
|
|
||||||
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
|
|
150
vendor/github.com/json-iterator/go/adapter.go
generated
vendored
150
vendor/github.com/json-iterator/go/adapter.go
generated
vendored
|
@ -1,150 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RawMessage to make replace json with jsoniter
|
|
||||||
type RawMessage []byte
|
|
||||||
|
|
||||||
// Unmarshal adapts to json/encoding Unmarshal API
|
|
||||||
//
|
|
||||||
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
|
||||||
// Refer to https://godoc.org/encoding/json#Unmarshal for more information
|
|
||||||
func Unmarshal(data []byte, v interface{}) error {
|
|
||||||
return ConfigDefault.Unmarshal(data, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalFromString is a convenient method to read from string instead of []byte
|
|
||||||
func UnmarshalFromString(str string, v interface{}) error {
|
|
||||||
return ConfigDefault.UnmarshalFromString(str, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get quick method to get value from deeply nested JSON structure
|
|
||||||
func Get(data []byte, path ...interface{}) Any {
|
|
||||||
return ConfigDefault.Get(data, path...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal adapts to json/encoding Marshal API
|
|
||||||
//
|
|
||||||
// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API
|
|
||||||
// Refer to https://godoc.org/encoding/json#Marshal for more information
|
|
||||||
func Marshal(v interface{}) ([]byte, error) {
|
|
||||||
return ConfigDefault.Marshal(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalIndent same as json.MarshalIndent. Prefix is not supported.
|
|
||||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
|
||||||
return ConfigDefault.MarshalIndent(v, prefix, indent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalToString convenient method to write as string instead of []byte
|
|
||||||
func MarshalToString(v interface{}) (string, error) {
|
|
||||||
return ConfigDefault.MarshalToString(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoder adapts to json/stream NewDecoder API.
|
|
||||||
//
|
|
||||||
// NewDecoder returns a new decoder that reads from r.
|
|
||||||
//
|
|
||||||
// Instead of a json/encoding Decoder, an Decoder is returned
|
|
||||||
// Refer to https://godoc.org/encoding/json#NewDecoder for more information
|
|
||||||
func NewDecoder(reader io.Reader) *Decoder {
|
|
||||||
return ConfigDefault.NewDecoder(reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoder reads and decodes JSON values from an input stream.
|
|
||||||
// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress)
|
|
||||||
type Decoder struct {
|
|
||||||
iter *Iterator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode decode JSON into interface{}
|
|
||||||
func (adapter *Decoder) Decode(obj interface{}) error {
|
|
||||||
if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil {
|
|
||||||
if !adapter.iter.loadMore() {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.iter.ReadVal(obj)
|
|
||||||
err := adapter.iter.Error
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return adapter.iter.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// More is there more?
|
|
||||||
func (adapter *Decoder) More() bool {
|
|
||||||
iter := adapter.iter
|
|
||||||
if iter.Error != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return c != ']' && c != '}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffered remaining buffer
|
|
||||||
func (adapter *Decoder) Buffered() io.Reader {
|
|
||||||
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
|
|
||||||
return bytes.NewReader(remaining)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
|
||||||
// Number instead of as a float64.
|
|
||||||
func (adapter *Decoder) UseNumber() {
|
|
||||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
|
||||||
cfg.UseNumber = true
|
|
||||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
|
||||||
// is a struct and the input contains object keys which do not match any
|
|
||||||
// non-ignored, exported fields in the destination.
|
|
||||||
func (adapter *Decoder) DisallowUnknownFields() {
|
|
||||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
|
||||||
cfg.DisallowUnknownFields = true
|
|
||||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder same as json.NewEncoder
|
|
||||||
func NewEncoder(writer io.Writer) *Encoder {
|
|
||||||
return ConfigDefault.NewEncoder(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoder same as json.Encoder
|
|
||||||
type Encoder struct {
|
|
||||||
stream *Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encode interface{} as JSON to io.Writer
|
|
||||||
func (adapter *Encoder) Encode(val interface{}) error {
|
|
||||||
adapter.stream.WriteVal(val)
|
|
||||||
adapter.stream.WriteRaw("\n")
|
|
||||||
adapter.stream.Flush()
|
|
||||||
return adapter.stream.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIndent set the indention. Prefix is not supported
|
|
||||||
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
|
||||||
config := adapter.stream.cfg.configBeforeFrozen
|
|
||||||
config.IndentionStep = len(indent)
|
|
||||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEscapeHTML escape html by default, set to false to disable
|
|
||||||
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
|
||||||
config := adapter.stream.cfg.configBeforeFrozen
|
|
||||||
config.EscapeHTML = escapeHTML
|
|
||||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid reports whether data is a valid JSON encoding.
|
|
||||||
func Valid(data []byte) bool {
|
|
||||||
return ConfigDefault.Valid(data)
|
|
||||||
}
|
|
325
vendor/github.com/json-iterator/go/any.go
generated
vendored
325
vendor/github.com/json-iterator/go/any.go
generated
vendored
|
@ -1,325 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Any generic object representation.
|
|
||||||
// The lazy json implementation holds []byte and parse lazily.
|
|
||||||
type Any interface {
|
|
||||||
LastError() error
|
|
||||||
ValueType() ValueType
|
|
||||||
MustBeValid() Any
|
|
||||||
ToBool() bool
|
|
||||||
ToInt() int
|
|
||||||
ToInt32() int32
|
|
||||||
ToInt64() int64
|
|
||||||
ToUint() uint
|
|
||||||
ToUint32() uint32
|
|
||||||
ToUint64() uint64
|
|
||||||
ToFloat32() float32
|
|
||||||
ToFloat64() float64
|
|
||||||
ToString() string
|
|
||||||
ToVal(val interface{})
|
|
||||||
Get(path ...interface{}) Any
|
|
||||||
Size() int
|
|
||||||
Keys() []string
|
|
||||||
GetInterface() interface{}
|
|
||||||
WriteTo(stream *Stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
type baseAny struct{}
|
|
||||||
|
|
||||||
func (any *baseAny) Get(path ...interface{}) Any {
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *baseAny) Size() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *baseAny) Keys() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *baseAny) ToVal(obj interface{}) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInt32 turn int32 into Any interface
|
|
||||||
func WrapInt32(val int32) Any {
|
|
||||||
return &int32Any{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapInt64 turn int64 into Any interface
|
|
||||||
func WrapInt64(val int64) Any {
|
|
||||||
return &int64Any{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapUint32 turn uint32 into Any interface
|
|
||||||
func WrapUint32(val uint32) Any {
|
|
||||||
return &uint32Any{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapUint64 turn uint64 into Any interface
|
|
||||||
func WrapUint64(val uint64) Any {
|
|
||||||
return &uint64Any{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapFloat64 turn float64 into Any interface
|
|
||||||
func WrapFloat64(val float64) Any {
|
|
||||||
return &floatAny{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapString turn string into Any interface
|
|
||||||
func WrapString(val string) Any {
|
|
||||||
return &stringAny{baseAny{}, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap turn a go object into Any interface
|
|
||||||
func Wrap(val interface{}) Any {
|
|
||||||
if val == nil {
|
|
||||||
return &nilAny{}
|
|
||||||
}
|
|
||||||
asAny, isAny := val.(Any)
|
|
||||||
if isAny {
|
|
||||||
return asAny
|
|
||||||
}
|
|
||||||
typ := reflect2.TypeOf(val)
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
return wrapArray(val)
|
|
||||||
case reflect.Struct:
|
|
||||||
return wrapStruct(val)
|
|
||||||
case reflect.Map:
|
|
||||||
return wrapMap(val)
|
|
||||||
case reflect.String:
|
|
||||||
return WrapString(val.(string))
|
|
||||||
case reflect.Int:
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return WrapInt32(int32(val.(int)))
|
|
||||||
}
|
|
||||||
return WrapInt64(int64(val.(int)))
|
|
||||||
case reflect.Int8:
|
|
||||||
return WrapInt32(int32(val.(int8)))
|
|
||||||
case reflect.Int16:
|
|
||||||
return WrapInt32(int32(val.(int16)))
|
|
||||||
case reflect.Int32:
|
|
||||||
return WrapInt32(val.(int32))
|
|
||||||
case reflect.Int64:
|
|
||||||
return WrapInt64(val.(int64))
|
|
||||||
case reflect.Uint:
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return WrapUint32(uint32(val.(uint)))
|
|
||||||
}
|
|
||||||
return WrapUint64(uint64(val.(uint)))
|
|
||||||
case reflect.Uintptr:
|
|
||||||
if ptrSize == 32 {
|
|
||||||
return WrapUint32(uint32(val.(uintptr)))
|
|
||||||
}
|
|
||||||
return WrapUint64(uint64(val.(uintptr)))
|
|
||||||
case reflect.Uint8:
|
|
||||||
return WrapUint32(uint32(val.(uint8)))
|
|
||||||
case reflect.Uint16:
|
|
||||||
return WrapUint32(uint32(val.(uint16)))
|
|
||||||
case reflect.Uint32:
|
|
||||||
return WrapUint32(uint32(val.(uint32)))
|
|
||||||
case reflect.Uint64:
|
|
||||||
return WrapUint64(val.(uint64))
|
|
||||||
case reflect.Float32:
|
|
||||||
return WrapFloat64(float64(val.(float32)))
|
|
||||||
case reflect.Float64:
|
|
||||||
return WrapFloat64(val.(float64))
|
|
||||||
case reflect.Bool:
|
|
||||||
if val.(bool) == true {
|
|
||||||
return &trueAny{}
|
|
||||||
}
|
|
||||||
return &falseAny{}
|
|
||||||
}
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
|
|
||||||
func (iter *Iterator) ReadAny() Any {
|
|
||||||
return iter.readAny()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readAny() Any {
|
|
||||||
c := iter.nextToken()
|
|
||||||
switch c {
|
|
||||||
case '"':
|
|
||||||
iter.unreadByte()
|
|
||||||
return &stringAny{baseAny{}, iter.ReadString()}
|
|
||||||
case 'n':
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
|
||||||
return &nilAny{}
|
|
||||||
case 't':
|
|
||||||
iter.skipThreeBytes('r', 'u', 'e') // true
|
|
||||||
return &trueAny{}
|
|
||||||
case 'f':
|
|
||||||
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
|
||||||
return &falseAny{}
|
|
||||||
case '{':
|
|
||||||
return iter.readObjectAny()
|
|
||||||
case '[':
|
|
||||||
return iter.readArrayAny()
|
|
||||||
case '-':
|
|
||||||
return iter.readNumberAny(false)
|
|
||||||
case 0:
|
|
||||||
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
|
||||||
default:
|
|
||||||
return iter.readNumberAny(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readNumberAny(positive bool) Any {
|
|
||||||
iter.startCapture(iter.head - 1)
|
|
||||||
iter.skipNumber()
|
|
||||||
lazyBuf := iter.stopCapture()
|
|
||||||
return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readObjectAny() Any {
|
|
||||||
iter.startCapture(iter.head - 1)
|
|
||||||
iter.skipObject()
|
|
||||||
lazyBuf := iter.stopCapture()
|
|
||||||
return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readArrayAny() Any {
|
|
||||||
iter.startCapture(iter.head - 1)
|
|
||||||
iter.skipArray()
|
|
||||||
lazyBuf := iter.stopCapture()
|
|
||||||
return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func locateObjectField(iter *Iterator, target string) []byte {
|
|
||||||
var found []byte
|
|
||||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
|
||||||
if field == target {
|
|
||||||
found = iter.SkipAndReturnBytes()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.Skip()
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func locateArrayElement(iter *Iterator, target int) []byte {
|
|
||||||
var found []byte
|
|
||||||
n := 0
|
|
||||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
||||||
if n == target {
|
|
||||||
found = iter.SkipAndReturnBytes()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.Skip()
|
|
||||||
n++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func locatePath(iter *Iterator, path []interface{}) Any {
|
|
||||||
for i, pathKeyObj := range path {
|
|
||||||
switch pathKey := pathKeyObj.(type) {
|
|
||||||
case string:
|
|
||||||
valueBytes := locateObjectField(iter, pathKey)
|
|
||||||
if valueBytes == nil {
|
|
||||||
return newInvalidAny(path[i:])
|
|
||||||
}
|
|
||||||
iter.ResetBytes(valueBytes)
|
|
||||||
case int:
|
|
||||||
valueBytes := locateArrayElement(iter, pathKey)
|
|
||||||
if valueBytes == nil {
|
|
||||||
return newInvalidAny(path[i:])
|
|
||||||
}
|
|
||||||
iter.ResetBytes(valueBytes)
|
|
||||||
case int32:
|
|
||||||
if '*' == pathKey {
|
|
||||||
return iter.readAny().Get(path[i:]...)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path[i:])
|
|
||||||
default:
|
|
||||||
return newInvalidAny(path[i:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return &invalidAny{baseAny{}, iter.Error}
|
|
||||||
}
|
|
||||||
return iter.readAny()
|
|
||||||
}
|
|
||||||
|
|
||||||
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
|
|
||||||
|
|
||||||
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
if typ == anyType {
|
|
||||||
return &directAnyCodec{}
|
|
||||||
}
|
|
||||||
if typ.Implements(anyType) {
|
|
||||||
return &anyCodec{
|
|
||||||
valType: typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
if typ == anyType {
|
|
||||||
return &directAnyCodec{}
|
|
||||||
}
|
|
||||||
if typ.Implements(anyType) {
|
|
||||||
return &anyCodec{
|
|
||||||
valType: typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type anyCodec struct {
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
obj := codec.valType.UnsafeIndirect(ptr)
|
|
||||||
any := obj.(Any)
|
|
||||||
any.WriteTo(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
obj := codec.valType.UnsafeIndirect(ptr)
|
|
||||||
any := obj.(Any)
|
|
||||||
return any.Size() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type directAnyCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
*(*Any)(ptr) = iter.readAny()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
any := *(*Any)(ptr)
|
|
||||||
if any == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
any.WriteTo(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
any := *(*Any)(ptr)
|
|
||||||
return any.Size() == 0
|
|
||||||
}
|
|
278
vendor/github.com/json-iterator/go/any_array.go
generated
vendored
278
vendor/github.com/json-iterator/go/any_array.go
generated
vendored
|
@ -1,278 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type arrayLazyAny struct {
|
|
||||||
baseAny
|
|
||||||
cfg *frozenConfig
|
|
||||||
buf []byte
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ValueType() ValueType {
|
|
||||||
return ArrayValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToBool() bool {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
return iter.ReadArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToInt() int {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToInt32() int32 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToInt64() int64 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToUint() uint {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToUint32() uint32 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToUint64() uint64 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToFloat32() float32 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToFloat64() float64 {
|
|
||||||
if any.ToBool() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToString() string {
|
|
||||||
return *(*string)(unsafe.Pointer(&any.buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) ToVal(val interface{}) {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadVal(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
switch firstPath := path[0].(type) {
|
|
||||||
case int:
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
valueBytes := locateArrayElement(iter, firstPath)
|
|
||||||
if valueBytes == nil {
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
iter.ResetBytes(valueBytes)
|
|
||||||
return locatePath(iter, path[1:])
|
|
||||||
case int32:
|
|
||||||
if '*' == firstPath {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
arr := make([]Any, 0)
|
|
||||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
||||||
found := iter.readAny().Get(path[1:]...)
|
|
||||||
if found.ValueType() != InvalidValue {
|
|
||||||
arr = append(arr, found)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return wrapArray(arr)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path)
|
|
||||||
default:
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) Size() int {
|
|
||||||
size := 0
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
||||||
size++
|
|
||||||
iter.Skip()
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) WriteTo(stream *Stream) {
|
|
||||||
stream.Write(any.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayLazyAny) GetInterface() interface{} {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
return iter.Read()
|
|
||||||
}
|
|
||||||
|
|
||||||
type arrayAny struct {
|
|
||||||
baseAny
|
|
||||||
val reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapArray(val interface{}) *arrayAny {
|
|
||||||
return &arrayAny{baseAny{}, reflect.ValueOf(val)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ValueType() ValueType {
|
|
||||||
return ArrayValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToBool() bool {
|
|
||||||
return any.val.Len() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToInt() int {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToInt32() int32 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToInt64() int64 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToUint() uint {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToUint32() uint32 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToUint64() uint64 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToFloat32() float32 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToFloat64() float64 {
|
|
||||||
if any.val.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) ToString() string {
|
|
||||||
str, _ := MarshalToString(any.val.Interface())
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
switch firstPath := path[0].(type) {
|
|
||||||
case int:
|
|
||||||
if firstPath < 0 || firstPath >= any.val.Len() {
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
return Wrap(any.val.Index(firstPath).Interface())
|
|
||||||
case int32:
|
|
||||||
if '*' == firstPath {
|
|
||||||
mappedAll := make([]Any, 0)
|
|
||||||
for i := 0; i < any.val.Len(); i++ {
|
|
||||||
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
|
|
||||||
if mapped.ValueType() != InvalidValue {
|
|
||||||
mappedAll = append(mappedAll, mapped)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapArray(mappedAll)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path)
|
|
||||||
default:
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) Size() int {
|
|
||||||
return any.val.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteVal(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *arrayAny) GetInterface() interface{} {
|
|
||||||
return any.val.Interface()
|
|
||||||
}
|
|
137
vendor/github.com/json-iterator/go/any_bool.go
generated
vendored
137
vendor/github.com/json-iterator/go/any_bool.go
generated
vendored
|
@ -1,137 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
type trueAny struct {
|
|
||||||
baseAny
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToBool() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToInt() int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToInt32() int32 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToInt64() int64 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToUint() uint {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToUint32() uint32 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToUint64() uint64 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToFloat32() float32 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToFloat64() float64 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ToString() string {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) GetInterface() interface{} {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) ValueType() ValueType {
|
|
||||||
return BoolValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *trueAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
type falseAny struct {
|
|
||||||
baseAny
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToBool() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ToString() string {
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) GetInterface() interface{} {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) ValueType() ValueType {
|
|
||||||
return BoolValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *falseAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
83
vendor/github.com/json-iterator/go/any_float.go
generated
vendored
83
vendor/github.com/json-iterator/go/any_float.go
generated
vendored
|
@ -1,83 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type floatAny struct {
|
|
||||||
baseAny
|
|
||||||
val float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToBool() bool {
|
|
||||||
return any.ToFloat64() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToInt() int {
|
|
||||||
return int(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToInt32() int32 {
|
|
||||||
return int32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToInt64() int64 {
|
|
||||||
return int64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToUint() uint {
|
|
||||||
if any.val > 0 {
|
|
||||||
return uint(any.val)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToUint32() uint32 {
|
|
||||||
if any.val > 0 {
|
|
||||||
return uint32(any.val)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToUint64() uint64 {
|
|
||||||
if any.val > 0 {
|
|
||||||
return uint64(any.val)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToFloat32() float32 {
|
|
||||||
return float32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToFloat64() float64 {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) ToString() string {
|
|
||||||
return strconv.FormatFloat(any.val, 'E', -1, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteFloat64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *floatAny) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
74
vendor/github.com/json-iterator/go/any_int32.go
generated
vendored
74
vendor/github.com/json-iterator/go/any_int32.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type int32Any struct {
|
|
||||||
baseAny
|
|
||||||
val int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToBool() bool {
|
|
||||||
return any.val != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToInt() int {
|
|
||||||
return int(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToInt32() int32 {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToInt64() int64 {
|
|
||||||
return int64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToUint() uint {
|
|
||||||
return uint(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToUint32() uint32 {
|
|
||||||
return uint32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToUint64() uint64 {
|
|
||||||
return uint64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToFloat32() float32 {
|
|
||||||
return float32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToFloat64() float64 {
|
|
||||||
return float64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) ToString() string {
|
|
||||||
return strconv.FormatInt(int64(any.val), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteInt32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int32Any) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
74
vendor/github.com/json-iterator/go/any_int64.go
generated
vendored
74
vendor/github.com/json-iterator/go/any_int64.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type int64Any struct {
|
|
||||||
baseAny
|
|
||||||
val int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToBool() bool {
|
|
||||||
return any.val != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToInt() int {
|
|
||||||
return int(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToInt32() int32 {
|
|
||||||
return int32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToInt64() int64 {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToUint() uint {
|
|
||||||
return uint(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToUint32() uint32 {
|
|
||||||
return uint32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToUint64() uint64 {
|
|
||||||
return uint64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToFloat32() float32 {
|
|
||||||
return float32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToFloat64() float64 {
|
|
||||||
return float64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) ToString() string {
|
|
||||||
return strconv.FormatInt(any.val, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteInt64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *int64Any) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
82
vendor/github.com/json-iterator/go/any_invalid.go
generated
vendored
82
vendor/github.com/json-iterator/go/any_invalid.go
generated
vendored
|
@ -1,82 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type invalidAny struct {
|
|
||||||
baseAny
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInvalidAny(path []interface{}) *invalidAny {
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ValueType() ValueType {
|
|
||||||
return InvalidValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) MustBeValid() Any {
|
|
||||||
panic(any.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToBool() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) ToString() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) WriteTo(stream *Stream) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) Get(path ...interface{}) Any {
|
|
||||||
if any.err == nil {
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)}
|
|
||||||
}
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *invalidAny) GetInterface() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
69
vendor/github.com/json-iterator/go/any_nil.go
generated
vendored
69
vendor/github.com/json-iterator/go/any_nil.go
generated
vendored
|
@ -1,69 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
type nilAny struct {
|
|
||||||
baseAny
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ValueType() ValueType {
|
|
||||||
return NilValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToBool() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) ToString() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *nilAny) GetInterface() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
123
vendor/github.com/json-iterator/go/any_number.go
generated
vendored
123
vendor/github.com/json-iterator/go/any_number.go
generated
vendored
|
@ -1,123 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type numberLazyAny struct {
|
|
||||||
baseAny
|
|
||||||
cfg *frozenConfig
|
|
||||||
buf []byte
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToBool() bool {
|
|
||||||
return any.ToFloat64() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToInt() int {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadInt()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToInt32() int32 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadInt32()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToInt64() int64 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadInt64()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToUint() uint {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadUint()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToUint32() uint32 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadUint32()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToUint64() uint64 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadUint64()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToFloat32() float32 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadFloat32()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToFloat64() float64 {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
val := iter.ReadFloat64()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
any.err = iter.Error
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) ToString() string {
|
|
||||||
return *(*string)(unsafe.Pointer(&any.buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) WriteTo(stream *Stream) {
|
|
||||||
stream.Write(any.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *numberLazyAny) GetInterface() interface{} {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
return iter.Read()
|
|
||||||
}
|
|
374
vendor/github.com/json-iterator/go/any_object.go
generated
vendored
374
vendor/github.com/json-iterator/go/any_object.go
generated
vendored
|
@ -1,374 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type objectLazyAny struct {
|
|
||||||
baseAny
|
|
||||||
cfg *frozenConfig
|
|
||||||
buf []byte
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ValueType() ValueType {
|
|
||||||
return ObjectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToBool() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToString() string {
|
|
||||||
return *(*string)(unsafe.Pointer(&any.buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) ToVal(obj interface{}) {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadVal(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
switch firstPath := path[0].(type) {
|
|
||||||
case string:
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
valueBytes := locateObjectField(iter, firstPath)
|
|
||||||
if valueBytes == nil {
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
iter.ResetBytes(valueBytes)
|
|
||||||
return locatePath(iter, path[1:])
|
|
||||||
case int32:
|
|
||||||
if '*' == firstPath {
|
|
||||||
mappedAll := map[string]Any{}
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadMapCB(func(iter *Iterator, field string) bool {
|
|
||||||
mapped := locatePath(iter, path[1:])
|
|
||||||
if mapped.ValueType() != InvalidValue {
|
|
||||||
mappedAll[field] = mapped
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return wrapMap(mappedAll)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path)
|
|
||||||
default:
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) Keys() []string {
|
|
||||||
keys := []string{}
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadMapCB(func(iter *Iterator, field string) bool {
|
|
||||||
iter.Skip()
|
|
||||||
keys = append(keys, field)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) Size() int {
|
|
||||||
size := 0
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
|
||||||
iter.Skip()
|
|
||||||
size++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) WriteTo(stream *Stream) {
|
|
||||||
stream.Write(any.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectLazyAny) GetInterface() interface{} {
|
|
||||||
iter := any.cfg.BorrowIterator(any.buf)
|
|
||||||
defer any.cfg.ReturnIterator(iter)
|
|
||||||
return iter.Read()
|
|
||||||
}
|
|
||||||
|
|
||||||
type objectAny struct {
|
|
||||||
baseAny
|
|
||||||
err error
|
|
||||||
val reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapStruct(val interface{}) *objectAny {
|
|
||||||
return &objectAny{baseAny{}, nil, reflect.ValueOf(val)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ValueType() ValueType {
|
|
||||||
return ObjectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToBool() bool {
|
|
||||||
return any.val.NumField() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) ToString() string {
|
|
||||||
str, err := MarshalToString(any.val.Interface())
|
|
||||||
any.err = err
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
switch firstPath := path[0].(type) {
|
|
||||||
case string:
|
|
||||||
field := any.val.FieldByName(firstPath)
|
|
||||||
if !field.IsValid() {
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
return Wrap(field.Interface())
|
|
||||||
case int32:
|
|
||||||
if '*' == firstPath {
|
|
||||||
mappedAll := map[string]Any{}
|
|
||||||
for i := 0; i < any.val.NumField(); i++ {
|
|
||||||
field := any.val.Field(i)
|
|
||||||
if field.CanInterface() {
|
|
||||||
mapped := Wrap(field.Interface()).Get(path[1:]...)
|
|
||||||
if mapped.ValueType() != InvalidValue {
|
|
||||||
mappedAll[any.val.Type().Field(i).Name] = mapped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapMap(mappedAll)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path)
|
|
||||||
default:
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) Keys() []string {
|
|
||||||
keys := make([]string, 0, any.val.NumField())
|
|
||||||
for i := 0; i < any.val.NumField(); i++ {
|
|
||||||
keys = append(keys, any.val.Type().Field(i).Name)
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) Size() int {
|
|
||||||
return any.val.NumField()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteVal(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *objectAny) GetInterface() interface{} {
|
|
||||||
return any.val.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapAny struct {
|
|
||||||
baseAny
|
|
||||||
err error
|
|
||||||
val reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapMap(val interface{}) *mapAny {
|
|
||||||
return &mapAny{baseAny{}, nil, reflect.ValueOf(val)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ValueType() ValueType {
|
|
||||||
return ObjectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) LastError() error {
|
|
||||||
return any.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToBool() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToInt() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToInt32() int32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToInt64() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToUint() uint {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToUint32() uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToUint64() uint64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToFloat32() float32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToFloat64() float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) ToString() string {
|
|
||||||
str, err := MarshalToString(any.val.Interface())
|
|
||||||
any.err = err
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
switch firstPath := path[0].(type) {
|
|
||||||
case int32:
|
|
||||||
if '*' == firstPath {
|
|
||||||
mappedAll := map[string]Any{}
|
|
||||||
for _, key := range any.val.MapKeys() {
|
|
||||||
keyAsStr := key.String()
|
|
||||||
element := Wrap(any.val.MapIndex(key).Interface())
|
|
||||||
mapped := element.Get(path[1:]...)
|
|
||||||
if mapped.ValueType() != InvalidValue {
|
|
||||||
mappedAll[keyAsStr] = mapped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapMap(mappedAll)
|
|
||||||
}
|
|
||||||
return newInvalidAny(path)
|
|
||||||
default:
|
|
||||||
value := any.val.MapIndex(reflect.ValueOf(firstPath))
|
|
||||||
if !value.IsValid() {
|
|
||||||
return newInvalidAny(path)
|
|
||||||
}
|
|
||||||
return Wrap(value.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) Keys() []string {
|
|
||||||
keys := make([]string, 0, any.val.Len())
|
|
||||||
for _, key := range any.val.MapKeys() {
|
|
||||||
keys = append(keys, key.String())
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) Size() int {
|
|
||||||
return any.val.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteVal(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *mapAny) GetInterface() interface{} {
|
|
||||||
return any.val.Interface()
|
|
||||||
}
|
|
166
vendor/github.com/json-iterator/go/any_str.go
generated
vendored
166
vendor/github.com/json-iterator/go/any_str.go
generated
vendored
|
@ -1,166 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stringAny struct {
|
|
||||||
baseAny
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) Get(path ...interface{}) Any {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ValueType() ValueType {
|
|
||||||
return StringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToBool() bool {
|
|
||||||
str := any.ToString()
|
|
||||||
if str == "0" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, c := range str {
|
|
||||||
switch c {
|
|
||||||
case ' ', '\n', '\r', '\t':
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToInt() int {
|
|
||||||
return int(any.ToInt64())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToInt32() int32 {
|
|
||||||
return int32(any.ToInt64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToInt64() int64 {
|
|
||||||
if any.val == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
flag := 1
|
|
||||||
startPos := 0
|
|
||||||
if any.val[0] == '+' || any.val[0] == '-' {
|
|
||||||
startPos = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if any.val[0] == '-' {
|
|
||||||
flag = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
endPos := startPos
|
|
||||||
for i := startPos; i < len(any.val); i++ {
|
|
||||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
|
||||||
endPos = i + 1
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64)
|
|
||||||
return int64(flag) * parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToUint() uint {
|
|
||||||
return uint(any.ToUint64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToUint32() uint32 {
|
|
||||||
return uint32(any.ToUint64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToUint64() uint64 {
|
|
||||||
if any.val == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
startPos := 0
|
|
||||||
|
|
||||||
if any.val[0] == '-' {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if any.val[0] == '+' {
|
|
||||||
startPos = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
endPos := startPos
|
|
||||||
for i := startPos; i < len(any.val); i++ {
|
|
||||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
|
||||||
endPos = i + 1
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64)
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToFloat32() float32 {
|
|
||||||
return float32(any.ToFloat64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToFloat64() float64 {
|
|
||||||
if len(any.val) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// first char invalid
|
|
||||||
if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract valid num expression from string
|
|
||||||
// eg 123true => 123, -12.12xxa => -12.12
|
|
||||||
endPos := 1
|
|
||||||
for i := 1; i < len(any.val); i++ {
|
|
||||||
if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' {
|
|
||||||
endPos = i + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// end position is the first char which is not digit
|
|
||||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
|
||||||
endPos = i + 1
|
|
||||||
} else {
|
|
||||||
endPos = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parsed, _ := strconv.ParseFloat(any.val[:endPos], 64)
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) ToString() string {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteString(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *stringAny) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
74
vendor/github.com/json-iterator/go/any_uint32.go
generated
vendored
74
vendor/github.com/json-iterator/go/any_uint32.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type uint32Any struct {
|
|
||||||
baseAny
|
|
||||||
val uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToBool() bool {
|
|
||||||
return any.val != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToInt() int {
|
|
||||||
return int(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToInt32() int32 {
|
|
||||||
return int32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToInt64() int64 {
|
|
||||||
return int64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToUint() uint {
|
|
||||||
return uint(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToUint32() uint32 {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToUint64() uint64 {
|
|
||||||
return uint64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToFloat32() float32 {
|
|
||||||
return float32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToFloat64() float64 {
|
|
||||||
return float64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) ToString() string {
|
|
||||||
return strconv.FormatInt(int64(any.val), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteUint32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint32Any) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
74
vendor/github.com/json-iterator/go/any_uint64.go
generated
vendored
74
vendor/github.com/json-iterator/go/any_uint64.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type uint64Any struct {
|
|
||||||
baseAny
|
|
||||||
val uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) LastError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ValueType() ValueType {
|
|
||||||
return NumberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) MustBeValid() Any {
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToBool() bool {
|
|
||||||
return any.val != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToInt() int {
|
|
||||||
return int(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToInt32() int32 {
|
|
||||||
return int32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToInt64() int64 {
|
|
||||||
return int64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToUint() uint {
|
|
||||||
return uint(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToUint32() uint32 {
|
|
||||||
return uint32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToUint64() uint64 {
|
|
||||||
return any.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToFloat32() float32 {
|
|
||||||
return float32(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToFloat64() float64 {
|
|
||||||
return float64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) ToString() string {
|
|
||||||
return strconv.FormatUint(any.val, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) WriteTo(stream *Stream) {
|
|
||||||
stream.WriteUint64(any.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) Parse() *Iterator {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (any *uint64Any) GetInterface() interface{} {
|
|
||||||
return any.val
|
|
||||||
}
|
|
12
vendor/github.com/json-iterator/go/build.sh
generated
vendored
12
vendor/github.com/json-iterator/go/build.sh
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
set -x
|
|
||||||
|
|
||||||
if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then
|
|
||||||
mkdir -p /tmp/build-golang/src/github.com/json-iterator
|
|
||||||
ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go
|
|
||||||
fi
|
|
||||||
export GOPATH=/tmp/build-golang
|
|
||||||
go get -u github.com/golang/dep/cmd/dep
|
|
||||||
cd /tmp/build-golang/src/github.com/json-iterator/go
|
|
||||||
exec $GOPATH/bin/dep ensure -update
|
|
375
vendor/github.com/json-iterator/go/config.go
generated
vendored
375
vendor/github.com/json-iterator/go/config.go
generated
vendored
|
@ -1,375 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/modern-go/concurrent"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config customize how the API should behave.
|
|
||||||
// The API is created from Config by Froze.
|
|
||||||
type Config struct {
|
|
||||||
IndentionStep int
|
|
||||||
MarshalFloatWith6Digits bool
|
|
||||||
EscapeHTML bool
|
|
||||||
SortMapKeys bool
|
|
||||||
UseNumber bool
|
|
||||||
DisallowUnknownFields bool
|
|
||||||
TagKey string
|
|
||||||
OnlyTaggedField bool
|
|
||||||
ValidateJsonRawMessage bool
|
|
||||||
ObjectFieldMustBeSimpleString bool
|
|
||||||
CaseSensitive bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// API the public interface of this package.
|
|
||||||
// Primary Marshal and Unmarshal.
|
|
||||||
type API interface {
|
|
||||||
IteratorPool
|
|
||||||
StreamPool
|
|
||||||
MarshalToString(v interface{}) (string, error)
|
|
||||||
Marshal(v interface{}) ([]byte, error)
|
|
||||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
|
||||||
UnmarshalFromString(str string, v interface{}) error
|
|
||||||
Unmarshal(data []byte, v interface{}) error
|
|
||||||
Get(data []byte, path ...interface{}) Any
|
|
||||||
NewEncoder(writer io.Writer) *Encoder
|
|
||||||
NewDecoder(reader io.Reader) *Decoder
|
|
||||||
Valid(data []byte) bool
|
|
||||||
RegisterExtension(extension Extension)
|
|
||||||
DecoderOf(typ reflect2.Type) ValDecoder
|
|
||||||
EncoderOf(typ reflect2.Type) ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigDefault the default API
|
|
||||||
var ConfigDefault = Config{
|
|
||||||
EscapeHTML: true,
|
|
||||||
}.Froze()
|
|
||||||
|
|
||||||
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
|
||||||
var ConfigCompatibleWithStandardLibrary = Config{
|
|
||||||
EscapeHTML: true,
|
|
||||||
SortMapKeys: true,
|
|
||||||
ValidateJsonRawMessage: true,
|
|
||||||
}.Froze()
|
|
||||||
|
|
||||||
// ConfigFastest marshals float with only 6 digits precision
|
|
||||||
var ConfigFastest = Config{
|
|
||||||
EscapeHTML: false,
|
|
||||||
MarshalFloatWith6Digits: true, // will lose precession
|
|
||||||
ObjectFieldMustBeSimpleString: true, // do not unescape object field
|
|
||||||
}.Froze()
|
|
||||||
|
|
||||||
type frozenConfig struct {
|
|
||||||
configBeforeFrozen Config
|
|
||||||
sortMapKeys bool
|
|
||||||
indentionStep int
|
|
||||||
objectFieldMustBeSimpleString bool
|
|
||||||
onlyTaggedField bool
|
|
||||||
disallowUnknownFields bool
|
|
||||||
decoderCache *concurrent.Map
|
|
||||||
encoderCache *concurrent.Map
|
|
||||||
encoderExtension Extension
|
|
||||||
decoderExtension Extension
|
|
||||||
extraExtensions []Extension
|
|
||||||
streamPool *sync.Pool
|
|
||||||
iteratorPool *sync.Pool
|
|
||||||
caseSensitive bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) initCache() {
|
|
||||||
cfg.decoderCache = concurrent.NewMap()
|
|
||||||
cfg.encoderCache = concurrent.NewMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
|
|
||||||
cfg.decoderCache.Store(cacheKey, decoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
|
|
||||||
cfg.encoderCache.Store(cacheKey, encoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
|
|
||||||
decoder, found := cfg.decoderCache.Load(cacheKey)
|
|
||||||
if found {
|
|
||||||
return decoder.(ValDecoder)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
|
|
||||||
encoder, found := cfg.encoderCache.Load(cacheKey)
|
|
||||||
if found {
|
|
||||||
return encoder.(ValEncoder)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfgCache = concurrent.NewMap()
|
|
||||||
|
|
||||||
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
|
|
||||||
obj, found := cfgCache.Load(cfg)
|
|
||||||
if found {
|
|
||||||
return obj.(*frozenConfig)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
|
|
||||||
cfgCache.Store(cfg, frozenConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Froze forge API from config
|
|
||||||
func (cfg Config) Froze() API {
|
|
||||||
api := &frozenConfig{
|
|
||||||
sortMapKeys: cfg.SortMapKeys,
|
|
||||||
indentionStep: cfg.IndentionStep,
|
|
||||||
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
|
||||||
onlyTaggedField: cfg.OnlyTaggedField,
|
|
||||||
disallowUnknownFields: cfg.DisallowUnknownFields,
|
|
||||||
caseSensitive: cfg.CaseSensitive,
|
|
||||||
}
|
|
||||||
api.streamPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return NewStream(api, nil, 512)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
api.iteratorPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return NewIterator(api)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
api.initCache()
|
|
||||||
encoderExtension := EncoderExtension{}
|
|
||||||
decoderExtension := DecoderExtension{}
|
|
||||||
if cfg.MarshalFloatWith6Digits {
|
|
||||||
api.marshalFloatWith6Digits(encoderExtension)
|
|
||||||
}
|
|
||||||
if cfg.EscapeHTML {
|
|
||||||
api.escapeHTML(encoderExtension)
|
|
||||||
}
|
|
||||||
if cfg.UseNumber {
|
|
||||||
api.useNumber(decoderExtension)
|
|
||||||
}
|
|
||||||
if cfg.ValidateJsonRawMessage {
|
|
||||||
api.validateJsonRawMessage(encoderExtension)
|
|
||||||
}
|
|
||||||
api.encoderExtension = encoderExtension
|
|
||||||
api.decoderExtension = decoderExtension
|
|
||||||
api.configBeforeFrozen = cfg
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
|
|
||||||
api := getFrozenConfigFromCache(cfg)
|
|
||||||
if api != nil {
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
api = cfg.Froze().(*frozenConfig)
|
|
||||||
for _, extension := range extraExtensions {
|
|
||||||
api.RegisterExtension(extension)
|
|
||||||
}
|
|
||||||
addFrozenConfigToCache(cfg, api)
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
|
|
||||||
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
rawMessage := *(*json.RawMessage)(ptr)
|
|
||||||
iter := cfg.BorrowIterator([]byte(rawMessage))
|
|
||||||
defer cfg.ReturnIterator(iter)
|
|
||||||
iter.Read()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
stream.WriteRaw("null")
|
|
||||||
} else {
|
|
||||||
stream.WriteRaw(string(rawMessage))
|
|
||||||
}
|
|
||||||
}, func(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*json.RawMessage)(ptr))) == 0
|
|
||||||
}}
|
|
||||||
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
|
||||||
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
|
|
||||||
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
exitingValue := *((*interface{})(ptr))
|
|
||||||
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
|
|
||||||
iter.ReadVal(exitingValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.WhatIsNext() == NumberValue {
|
|
||||||
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
|
|
||||||
} else {
|
|
||||||
*((*interface{})(ptr)) = iter.Read()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
func (cfg *frozenConfig) getTagKey() string {
|
|
||||||
tagKey := cfg.configBeforeFrozen.TagKey
|
|
||||||
if tagKey == "" {
|
|
||||||
return "json"
|
|
||||||
}
|
|
||||||
return tagKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
|
|
||||||
cfg.extraExtensions = append(cfg.extraExtensions, extension)
|
|
||||||
copied := cfg.configBeforeFrozen
|
|
||||||
cfg.configBeforeFrozen = copied
|
|
||||||
}
|
|
||||||
|
|
||||||
type lossyFloat32Encoder struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteFloat32Lossy(*((*float32)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*float32)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type lossyFloat64Encoder struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteFloat64Lossy(*((*float64)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*float64)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableLossyFloatMarshalling keeps 10**(-6) precision
|
|
||||||
// for float variables for better performance.
|
|
||||||
func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
|
|
||||||
// for better performance
|
|
||||||
extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{}
|
|
||||||
extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type htmlEscapedStringEncoder struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
str := *((*string)(ptr))
|
|
||||||
stream.WriteStringWithHTMLEscaped(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*string)(ptr)) == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
|
|
||||||
encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) cleanDecoders() {
|
|
||||||
typeDecoders = map[string]ValDecoder{}
|
|
||||||
fieldDecoders = map[string]ValDecoder{}
|
|
||||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) cleanEncoders() {
|
|
||||||
typeEncoders = map[string]ValEncoder{}
|
|
||||||
fieldEncoders = map[string]ValEncoder{}
|
|
||||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
|
||||||
stream := cfg.BorrowStream(nil)
|
|
||||||
defer cfg.ReturnStream(stream)
|
|
||||||
stream.WriteVal(v)
|
|
||||||
if stream.Error != nil {
|
|
||||||
return "", stream.Error
|
|
||||||
}
|
|
||||||
return string(stream.Buffer()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
|
|
||||||
stream := cfg.BorrowStream(nil)
|
|
||||||
defer cfg.ReturnStream(stream)
|
|
||||||
stream.WriteVal(v)
|
|
||||||
if stream.Error != nil {
|
|
||||||
return nil, stream.Error
|
|
||||||
}
|
|
||||||
result := stream.Buffer()
|
|
||||||
copied := make([]byte, len(result))
|
|
||||||
copy(copied, result)
|
|
||||||
return copied, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
|
||||||
if prefix != "" {
|
|
||||||
panic("prefix is not supported")
|
|
||||||
}
|
|
||||||
for _, r := range indent {
|
|
||||||
if r != ' ' {
|
|
||||||
panic("indent can only be space")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newCfg := cfg.configBeforeFrozen
|
|
||||||
newCfg.IndentionStep = len(indent)
|
|
||||||
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
|
||||||
data := []byte(str)
|
|
||||||
iter := cfg.BorrowIterator(data)
|
|
||||||
defer cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadVal(v)
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 0 {
|
|
||||||
if iter.Error == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return iter.Error
|
|
||||||
}
|
|
||||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
|
||||||
return iter.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
|
|
||||||
iter := cfg.BorrowIterator(data)
|
|
||||||
defer cfg.ReturnIterator(iter)
|
|
||||||
return locatePath(iter, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
|
||||||
iter := cfg.BorrowIterator(data)
|
|
||||||
defer cfg.ReturnIterator(iter)
|
|
||||||
iter.ReadVal(v)
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 0 {
|
|
||||||
if iter.Error == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return iter.Error
|
|
||||||
}
|
|
||||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
|
||||||
return iter.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
|
|
||||||
stream := NewStream(cfg, writer, 512)
|
|
||||||
return &Encoder{stream}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
|
|
||||||
iter := Parse(cfg, reader, 512)
|
|
||||||
return &Decoder{iter}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) Valid(data []byte) bool {
|
|
||||||
iter := cfg.BorrowIterator(data)
|
|
||||||
defer cfg.ReturnIterator(iter)
|
|
||||||
iter.Skip()
|
|
||||||
return iter.Error == nil
|
|
||||||
}
|
|
7
vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md
generated
vendored
7
vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
| json type \ dest type | bool | int | uint | float |string|
|
|
||||||
| --- | --- | --- | --- |--|--|
|
|
||||||
| number | positive => true <br/> negative => true <br/> zero => false| 23.2 => 23 <br/> -32.1 => -32| 12.1 => 12 <br/> -12.1 => 0|as normal|same as origin|
|
|
||||||
| string | empty string => false <br/> string "0" => false <br/> other strings => true | "123.32" => 123 <br/> "-123.4" => -123 <br/> "123.23xxxw" => 123 <br/> "abcde12" => 0 <br/> "-32.1" => -32| 13.2 => 13 <br/> -1.1 => 0 |12.1 => 12.1 <br/> -12.3 => -12.3<br/> 12.4xxa => 12.4 <br/> +1.1e2 =>110 |same as origin|
|
|
||||||
| bool | true => true <br/> false => false| true => 1 <br/> false => 0 | true => 1 <br/> false => 0 |true => 1 <br/>false => 0|true => "true" <br/> false => "false"|
|
|
||||||
| object | true | 0 | 0 |0|originnal json|
|
|
||||||
| array | empty array => false <br/> nonempty array => true| [] => 0 <br/> [1,2] => 1 | [] => 0 <br/> [1,2] => 1 |[] => 0<br/>[1,2] => 1|original json|
|
|
11
vendor/github.com/json-iterator/go/go.mod
generated
vendored
11
vendor/github.com/json-iterator/go/go.mod
generated
vendored
|
@ -1,11 +0,0 @@
|
||||||
module github.com/json-iterator/go
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1
|
|
||||||
github.com/google/gofuzz v1.0.0
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
|
|
||||||
github.com/modern-go/reflect2 v1.0.2
|
|
||||||
github.com/stretchr/testify v1.3.0
|
|
||||||
)
|
|
14
vendor/github.com/json-iterator/go/go.sum
generated
vendored
14
vendor/github.com/json-iterator/go/go.sum
generated
vendored
|
@ -1,14 +0,0 @@
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
349
vendor/github.com/json-iterator/go/iter.go
generated
vendored
349
vendor/github.com/json-iterator/go/iter.go
generated
vendored
|
@ -1,349 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ValueType the type for JSON element
|
|
||||||
type ValueType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// InvalidValue invalid JSON element
|
|
||||||
InvalidValue ValueType = iota
|
|
||||||
// StringValue JSON element "string"
|
|
||||||
StringValue
|
|
||||||
// NumberValue JSON element 100 or 0.10
|
|
||||||
NumberValue
|
|
||||||
// NilValue JSON element null
|
|
||||||
NilValue
|
|
||||||
// BoolValue JSON element true or false
|
|
||||||
BoolValue
|
|
||||||
// ArrayValue JSON element []
|
|
||||||
ArrayValue
|
|
||||||
// ObjectValue JSON element {}
|
|
||||||
ObjectValue
|
|
||||||
)
|
|
||||||
|
|
||||||
var hexDigits []byte
|
|
||||||
var valueTypes []ValueType
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
hexDigits = make([]byte, 256)
|
|
||||||
for i := 0; i < len(hexDigits); i++ {
|
|
||||||
hexDigits[i] = 255
|
|
||||||
}
|
|
||||||
for i := '0'; i <= '9'; i++ {
|
|
||||||
hexDigits[i] = byte(i - '0')
|
|
||||||
}
|
|
||||||
for i := 'a'; i <= 'f'; i++ {
|
|
||||||
hexDigits[i] = byte((i - 'a') + 10)
|
|
||||||
}
|
|
||||||
for i := 'A'; i <= 'F'; i++ {
|
|
||||||
hexDigits[i] = byte((i - 'A') + 10)
|
|
||||||
}
|
|
||||||
valueTypes = make([]ValueType, 256)
|
|
||||||
for i := 0; i < len(valueTypes); i++ {
|
|
||||||
valueTypes[i] = InvalidValue
|
|
||||||
}
|
|
||||||
valueTypes['"'] = StringValue
|
|
||||||
valueTypes['-'] = NumberValue
|
|
||||||
valueTypes['0'] = NumberValue
|
|
||||||
valueTypes['1'] = NumberValue
|
|
||||||
valueTypes['2'] = NumberValue
|
|
||||||
valueTypes['3'] = NumberValue
|
|
||||||
valueTypes['4'] = NumberValue
|
|
||||||
valueTypes['5'] = NumberValue
|
|
||||||
valueTypes['6'] = NumberValue
|
|
||||||
valueTypes['7'] = NumberValue
|
|
||||||
valueTypes['8'] = NumberValue
|
|
||||||
valueTypes['9'] = NumberValue
|
|
||||||
valueTypes['t'] = BoolValue
|
|
||||||
valueTypes['f'] = BoolValue
|
|
||||||
valueTypes['n'] = NilValue
|
|
||||||
valueTypes['['] = ArrayValue
|
|
||||||
valueTypes['{'] = ObjectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterator is a io.Reader like object, with JSON specific read functions.
|
|
||||||
// Error is not returned as return value, but stored as Error member on this iterator instance.
|
|
||||||
type Iterator struct {
|
|
||||||
cfg *frozenConfig
|
|
||||||
reader io.Reader
|
|
||||||
buf []byte
|
|
||||||
head int
|
|
||||||
tail int
|
|
||||||
depth int
|
|
||||||
captureStartedAt int
|
|
||||||
captured []byte
|
|
||||||
Error error
|
|
||||||
Attachment interface{} // open for customized decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIterator creates an empty Iterator instance
|
|
||||||
func NewIterator(cfg API) *Iterator {
|
|
||||||
return &Iterator{
|
|
||||||
cfg: cfg.(*frozenConfig),
|
|
||||||
reader: nil,
|
|
||||||
buf: nil,
|
|
||||||
head: 0,
|
|
||||||
tail: 0,
|
|
||||||
depth: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse creates an Iterator instance from io.Reader
|
|
||||||
func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
|
|
||||||
return &Iterator{
|
|
||||||
cfg: cfg.(*frozenConfig),
|
|
||||||
reader: reader,
|
|
||||||
buf: make([]byte, bufSize),
|
|
||||||
head: 0,
|
|
||||||
tail: 0,
|
|
||||||
depth: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBytes creates an Iterator instance from byte array
|
|
||||||
func ParseBytes(cfg API, input []byte) *Iterator {
|
|
||||||
return &Iterator{
|
|
||||||
cfg: cfg.(*frozenConfig),
|
|
||||||
reader: nil,
|
|
||||||
buf: input,
|
|
||||||
head: 0,
|
|
||||||
tail: len(input),
|
|
||||||
depth: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseString creates an Iterator instance from string
|
|
||||||
func ParseString(cfg API, input string) *Iterator {
|
|
||||||
return ParseBytes(cfg, []byte(input))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pool returns a pool can provide more iterator with same configuration
|
|
||||||
func (iter *Iterator) Pool() IteratorPool {
|
|
||||||
return iter.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset reuse iterator instance by specifying another reader
|
|
||||||
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
|
||||||
iter.reader = reader
|
|
||||||
iter.head = 0
|
|
||||||
iter.tail = 0
|
|
||||||
iter.depth = 0
|
|
||||||
return iter
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetBytes reuse iterator instance by specifying another byte array as input
|
|
||||||
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
|
||||||
iter.reader = nil
|
|
||||||
iter.buf = input
|
|
||||||
iter.head = 0
|
|
||||||
iter.tail = len(input)
|
|
||||||
iter.depth = 0
|
|
||||||
return iter
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhatIsNext gets ValueType of relatively next json element
|
|
||||||
func (iter *Iterator) WhatIsNext() ValueType {
|
|
||||||
valueType := valueTypes[iter.nextToken()]
|
|
||||||
iter.unreadByte()
|
|
||||||
return valueType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case ' ', '\n', '\t', '\r':
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iter.head = i
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) isObjectEnd() bool {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == ',' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c == '}' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) nextToken() byte {
|
|
||||||
// a variation of skip whitespaces, returning the next non-whitespace token
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case ' ', '\n', '\t', '\r':
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iter.head = i + 1
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportError record a error in iterator instance with current position.
|
|
||||||
func (iter *Iterator) ReportError(operation string, msg string) {
|
|
||||||
if iter.Error != nil {
|
|
||||||
if iter.Error != io.EOF {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peekStart := iter.head - 10
|
|
||||||
if peekStart < 0 {
|
|
||||||
peekStart = 0
|
|
||||||
}
|
|
||||||
peekEnd := iter.head + 10
|
|
||||||
if peekEnd > iter.tail {
|
|
||||||
peekEnd = iter.tail
|
|
||||||
}
|
|
||||||
parsing := string(iter.buf[peekStart:peekEnd])
|
|
||||||
contextStart := iter.head - 50
|
|
||||||
if contextStart < 0 {
|
|
||||||
contextStart = 0
|
|
||||||
}
|
|
||||||
contextEnd := iter.head + 50
|
|
||||||
if contextEnd > iter.tail {
|
|
||||||
contextEnd = iter.tail
|
|
||||||
}
|
|
||||||
context := string(iter.buf[contextStart:contextEnd])
|
|
||||||
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...",
|
|
||||||
operation, msg, iter.head-peekStart, parsing, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentBuffer gets current buffer as string for debugging purpose
|
|
||||||
func (iter *Iterator) CurrentBuffer() string {
|
|
||||||
peekStart := iter.head - 10
|
|
||||||
if peekStart < 0 {
|
|
||||||
peekStart = 0
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head,
|
|
||||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readByte() (ret byte) {
|
|
||||||
if iter.head == iter.tail {
|
|
||||||
if iter.loadMore() {
|
|
||||||
ret = iter.buf[iter.head]
|
|
||||||
iter.head++
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
ret = iter.buf[iter.head]
|
|
||||||
iter.head++
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) loadMore() bool {
|
|
||||||
if iter.reader == nil {
|
|
||||||
if iter.Error == nil {
|
|
||||||
iter.head = iter.tail
|
|
||||||
iter.Error = io.EOF
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if iter.captured != nil {
|
|
||||||
iter.captured = append(iter.captured,
|
|
||||||
iter.buf[iter.captureStartedAt:iter.tail]...)
|
|
||||||
iter.captureStartedAt = 0
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
n, err := iter.reader.Read(iter.buf)
|
|
||||||
if n == 0 {
|
|
||||||
if err != nil {
|
|
||||||
if iter.Error == nil {
|
|
||||||
iter.Error = err
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iter.head = 0
|
|
||||||
iter.tail = n
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) unreadByte() {
|
|
||||||
if iter.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.head--
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read read the next JSON element as generic interface{}.
|
|
||||||
func (iter *Iterator) Read() interface{} {
|
|
||||||
valueType := iter.WhatIsNext()
|
|
||||||
switch valueType {
|
|
||||||
case StringValue:
|
|
||||||
return iter.ReadString()
|
|
||||||
case NumberValue:
|
|
||||||
if iter.cfg.configBeforeFrozen.UseNumber {
|
|
||||||
return json.Number(iter.readNumberAsString())
|
|
||||||
}
|
|
||||||
return iter.ReadFloat64()
|
|
||||||
case NilValue:
|
|
||||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
|
||||||
return nil
|
|
||||||
case BoolValue:
|
|
||||||
return iter.ReadBool()
|
|
||||||
case ArrayValue:
|
|
||||||
arr := []interface{}{}
|
|
||||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
||||||
var elem interface{}
|
|
||||||
iter.ReadVal(&elem)
|
|
||||||
arr = append(arr, elem)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return arr
|
|
||||||
case ObjectValue:
|
|
||||||
obj := map[string]interface{}{}
|
|
||||||
iter.ReadMapCB(func(Iter *Iterator, field string) bool {
|
|
||||||
var elem interface{}
|
|
||||||
iter.ReadVal(&elem)
|
|
||||||
obj[field] = elem
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return obj
|
|
||||||
default:
|
|
||||||
iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
|
|
||||||
const maxDepth = 10000
|
|
||||||
|
|
||||||
func (iter *Iterator) incrementDepth() (success bool) {
|
|
||||||
iter.depth++
|
|
||||||
if iter.depth <= maxDepth {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
iter.ReportError("incrementDepth", "exceeded max depth")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) decrementDepth() (success bool) {
|
|
||||||
iter.depth--
|
|
||||||
if iter.depth >= 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
iter.ReportError("decrementDepth", "unexpected negative nesting")
|
|
||||||
return false
|
|
||||||
}
|
|
64
vendor/github.com/json-iterator/go/iter_array.go
generated
vendored
64
vendor/github.com/json-iterator/go/iter_array.go
generated
vendored
|
@ -1,64 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
// ReadArray read array element, tells if the array has more element to read.
|
|
||||||
func (iter *Iterator) ReadArray() (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
switch c {
|
|
||||||
case 'n':
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return false // null
|
|
||||||
case '[':
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ']' {
|
|
||||||
iter.unreadByte()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case ']':
|
|
||||||
return false
|
|
||||||
case ',':
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadArrayCB read array with callback
|
|
||||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '[' {
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ']' {
|
|
||||||
iter.unreadByte()
|
|
||||||
if !callback(iter) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
for c == ',' {
|
|
||||||
if !callback(iter) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
}
|
|
||||||
if c != ']' {
|
|
||||||
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return true // null
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c}))
|
|
||||||
return false
|
|
||||||
}
|
|
342
vendor/github.com/json-iterator/go/iter_float.go
generated
vendored
342
vendor/github.com/json-iterator/go/iter_float.go
generated
vendored
|
@ -1,342 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var floatDigits []int8
|
|
||||||
|
|
||||||
const invalidCharForNumber = int8(-1)
|
|
||||||
const endOfNumber = int8(-2)
|
|
||||||
const dotInNumber = int8(-3)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
floatDigits = make([]int8, 256)
|
|
||||||
for i := 0; i < len(floatDigits); i++ {
|
|
||||||
floatDigits[i] = invalidCharForNumber
|
|
||||||
}
|
|
||||||
for i := int8('0'); i <= int8('9'); i++ {
|
|
||||||
floatDigits[i] = i - int8('0')
|
|
||||||
}
|
|
||||||
floatDigits[','] = endOfNumber
|
|
||||||
floatDigits[']'] = endOfNumber
|
|
||||||
floatDigits['}'] = endOfNumber
|
|
||||||
floatDigits[' '] = endOfNumber
|
|
||||||
floatDigits['\t'] = endOfNumber
|
|
||||||
floatDigits['\n'] = endOfNumber
|
|
||||||
floatDigits['.'] = dotInNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBigFloat read big.Float
|
|
||||||
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
|
||||||
str := iter.readNumberAsString()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
prec := 64
|
|
||||||
if len(str) > prec {
|
|
||||||
prec = len(str)
|
|
||||||
}
|
|
||||||
val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero)
|
|
||||||
if err != nil {
|
|
||||||
iter.Error = err
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBigInt read big.Int
|
|
||||||
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
|
||||||
str := iter.readNumberAsString()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret = big.NewInt(0)
|
|
||||||
var success bool
|
|
||||||
ret, success = ret.SetString(str, 10)
|
|
||||||
if !success {
|
|
||||||
iter.ReportError("ReadBigInt", "invalid big int")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
//ReadFloat32 read float32
|
|
||||||
func (iter *Iterator) ReadFloat32() (ret float32) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
return -iter.readPositiveFloat32()
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return iter.readPositiveFloat32()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
|
||||||
i := iter.head
|
|
||||||
// first char
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
c := iter.buf[i]
|
|
||||||
i++
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case invalidCharForNumber:
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
case endOfNumber:
|
|
||||||
iter.ReportError("readFloat32", "empty number")
|
|
||||||
return
|
|
||||||
case dotInNumber:
|
|
||||||
iter.ReportError("readFloat32", "leading dot is invalid")
|
|
||||||
return
|
|
||||||
case 0:
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
c = iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
iter.ReportError("readFloat32", "leading zero is invalid")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value := uint64(ind)
|
|
||||||
// chars before dot
|
|
||||||
non_decimal_loop:
|
|
||||||
for ; i < iter.tail; i++ {
|
|
||||||
c = iter.buf[i]
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case invalidCharForNumber:
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
case endOfNumber:
|
|
||||||
iter.head = i
|
|
||||||
return float32(value)
|
|
||||||
case dotInNumber:
|
|
||||||
break non_decimal_loop
|
|
||||||
}
|
|
||||||
if value > uint64SafeToMultiple10 {
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
|
|
||||||
}
|
|
||||||
// chars after dot
|
|
||||||
if c == '.' {
|
|
||||||
i++
|
|
||||||
decimalPlaces := 0
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
for ; i < iter.tail; i++ {
|
|
||||||
c = iter.buf[i]
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case endOfNumber:
|
|
||||||
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
|
|
||||||
iter.head = i
|
|
||||||
return float32(float64(value) / float64(pow10[decimalPlaces]))
|
|
||||||
}
|
|
||||||
// too many decimal places
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
case invalidCharForNumber, dotInNumber:
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
decimalPlaces++
|
|
||||||
if value > uint64SafeToMultiple10 {
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint64(ind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iter.readFloat32SlowPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readNumberAsString() (ret string) {
|
|
||||||
strBuf := [16]byte{}
|
|
||||||
str := strBuf[0:0]
|
|
||||||
load_loop:
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
str = append(str, c)
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
iter.head = i
|
|
||||||
break load_loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(str) == 0 {
|
|
||||||
iter.ReportError("readNumberAsString", "invalid number")
|
|
||||||
}
|
|
||||||
return *(*string)(unsafe.Pointer(&str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readFloat32SlowPath() (ret float32) {
|
|
||||||
str := iter.readNumberAsString()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errMsg := validateFloat(str)
|
|
||||||
if errMsg != "" {
|
|
||||||
iter.ReportError("readFloat32SlowPath", errMsg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val, err := strconv.ParseFloat(str, 32)
|
|
||||||
if err != nil {
|
|
||||||
iter.Error = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return float32(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFloat64 read float64
|
|
||||||
func (iter *Iterator) ReadFloat64() (ret float64) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
return -iter.readPositiveFloat64()
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return iter.readPositiveFloat64()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
|
||||||
i := iter.head
|
|
||||||
// first char
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
c := iter.buf[i]
|
|
||||||
i++
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case invalidCharForNumber:
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
case endOfNumber:
|
|
||||||
iter.ReportError("readFloat64", "empty number")
|
|
||||||
return
|
|
||||||
case dotInNumber:
|
|
||||||
iter.ReportError("readFloat64", "leading dot is invalid")
|
|
||||||
return
|
|
||||||
case 0:
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
c = iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
iter.ReportError("readFloat64", "leading zero is invalid")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value := uint64(ind)
|
|
||||||
// chars before dot
|
|
||||||
non_decimal_loop:
|
|
||||||
for ; i < iter.tail; i++ {
|
|
||||||
c = iter.buf[i]
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case invalidCharForNumber:
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
case endOfNumber:
|
|
||||||
iter.head = i
|
|
||||||
return float64(value)
|
|
||||||
case dotInNumber:
|
|
||||||
break non_decimal_loop
|
|
||||||
}
|
|
||||||
if value > uint64SafeToMultiple10 {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
|
|
||||||
}
|
|
||||||
// chars after dot
|
|
||||||
if c == '.' {
|
|
||||||
i++
|
|
||||||
decimalPlaces := 0
|
|
||||||
if i == iter.tail {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
for ; i < iter.tail; i++ {
|
|
||||||
c = iter.buf[i]
|
|
||||||
ind := floatDigits[c]
|
|
||||||
switch ind {
|
|
||||||
case endOfNumber:
|
|
||||||
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
|
|
||||||
iter.head = i
|
|
||||||
return float64(value) / float64(pow10[decimalPlaces])
|
|
||||||
}
|
|
||||||
// too many decimal places
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
case invalidCharForNumber, dotInNumber:
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
decimalPlaces++
|
|
||||||
if value > uint64SafeToMultiple10 {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint64(ind)
|
|
||||||
if value > maxFloat64 {
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iter.readFloat64SlowPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readFloat64SlowPath() (ret float64) {
|
|
||||||
str := iter.readNumberAsString()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errMsg := validateFloat(str)
|
|
||||||
if errMsg != "" {
|
|
||||||
iter.ReportError("readFloat64SlowPath", errMsg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val, err := strconv.ParseFloat(str, 64)
|
|
||||||
if err != nil {
|
|
||||||
iter.Error = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateFloat(str string) string {
|
|
||||||
// strconv.ParseFloat is not validating `1.` or `1.e1`
|
|
||||||
if len(str) == 0 {
|
|
||||||
return "empty number"
|
|
||||||
}
|
|
||||||
if str[0] == '-' {
|
|
||||||
return "-- is not valid"
|
|
||||||
}
|
|
||||||
dotPos := strings.IndexByte(str, '.')
|
|
||||||
if dotPos != -1 {
|
|
||||||
if dotPos == len(str)-1 {
|
|
||||||
return "dot can not be last character"
|
|
||||||
}
|
|
||||||
switch str[dotPos+1] {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
default:
|
|
||||||
return "missing digit after dot"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadNumber read json.Number
|
|
||||||
func (iter *Iterator) ReadNumber() (ret json.Number) {
|
|
||||||
return json.Number(iter.readNumberAsString())
|
|
||||||
}
|
|
346
vendor/github.com/json-iterator/go/iter_int.go
generated
vendored
346
vendor/github.com/json-iterator/go/iter_int.go
generated
vendored
|
@ -1,346 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var intDigits []int8
|
|
||||||
|
|
||||||
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
|
|
||||||
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1
|
|
||||||
const maxFloat64 = 1<<53 - 1
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
intDigits = make([]int8, 256)
|
|
||||||
for i := 0; i < len(intDigits); i++ {
|
|
||||||
intDigits[i] = invalidCharForNumber
|
|
||||||
}
|
|
||||||
for i := int8('0'); i <= int8('9'); i++ {
|
|
||||||
intDigits[i] = i - int8('0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUint read uint
|
|
||||||
func (iter *Iterator) ReadUint() uint {
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return uint(iter.ReadUint32())
|
|
||||||
}
|
|
||||||
return uint(iter.ReadUint64())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInt read int
|
|
||||||
func (iter *Iterator) ReadInt() int {
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return int(iter.ReadInt32())
|
|
||||||
}
|
|
||||||
return int(iter.ReadInt64())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInt8 read int8
|
|
||||||
func (iter *Iterator) ReadInt8() (ret int8) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
val := iter.readUint32(iter.readByte())
|
|
||||||
if val > math.MaxInt8+1 {
|
|
||||||
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return -int8(val)
|
|
||||||
}
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt8 {
|
|
||||||
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int8(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUint8 read uint8
|
|
||||||
func (iter *Iterator) ReadUint8() (ret uint8) {
|
|
||||||
val := iter.readUint32(iter.nextToken())
|
|
||||||
if val > math.MaxUint8 {
|
|
||||||
iter.ReportError("ReadUint8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return uint8(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInt16 read int16
|
|
||||||
func (iter *Iterator) ReadInt16() (ret int16) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
val := iter.readUint32(iter.readByte())
|
|
||||||
if val > math.MaxInt16+1 {
|
|
||||||
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return -int16(val)
|
|
||||||
}
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt16 {
|
|
||||||
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int16(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUint16 read uint16
|
|
||||||
func (iter *Iterator) ReadUint16() (ret uint16) {
|
|
||||||
val := iter.readUint32(iter.nextToken())
|
|
||||||
if val > math.MaxUint16 {
|
|
||||||
iter.ReportError("ReadUint16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return uint16(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInt32 read int32
|
|
||||||
func (iter *Iterator) ReadInt32() (ret int32) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
val := iter.readUint32(iter.readByte())
|
|
||||||
if val > math.MaxInt32+1 {
|
|
||||||
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return -int32(val)
|
|
||||||
}
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt32 {
|
|
||||||
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int32(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUint32 read uint32
|
|
||||||
func (iter *Iterator) ReadUint32() (ret uint32) {
|
|
||||||
return iter.readUint32(iter.nextToken())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
|
||||||
ind := intDigits[c]
|
|
||||||
if ind == 0 {
|
|
||||||
iter.assertInteger()
|
|
||||||
return 0 // single zero
|
|
||||||
}
|
|
||||||
if ind == invalidCharForNumber {
|
|
||||||
iter.ReportError("readUint32", "unexpected character: "+string([]byte{byte(ind)}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value := uint32(ind)
|
|
||||||
if iter.tail-iter.head > 10 {
|
|
||||||
i := iter.head
|
|
||||||
ind2 := intDigits[iter.buf[i]]
|
|
||||||
if ind2 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind3 := intDigits[iter.buf[i]]
|
|
||||||
if ind3 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*10 + uint32(ind2)
|
|
||||||
}
|
|
||||||
//iter.head = i + 1
|
|
||||||
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
|
|
||||||
i++
|
|
||||||
ind4 := intDigits[iter.buf[i]]
|
|
||||||
if ind4 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*100 + uint32(ind2)*10 + uint32(ind3)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind5 := intDigits[iter.buf[i]]
|
|
||||||
if ind5 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind6 := intDigits[iter.buf[i]]
|
|
||||||
if ind6 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind7 := intDigits[iter.buf[i]]
|
|
||||||
if ind7 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind8 := intDigits[iter.buf[i]]
|
|
||||||
if ind8 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind9 := intDigits[iter.buf[i]]
|
|
||||||
value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8)
|
|
||||||
iter.head = i
|
|
||||||
if ind9 == invalidCharForNumber {
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
ind = intDigits[iter.buf[i]]
|
|
||||||
if ind == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
if value > uint32SafeToMultiply10 {
|
|
||||||
value2 := (value << 3) + (value << 1) + uint32(ind)
|
|
||||||
if value2 < value {
|
|
||||||
iter.ReportError("readUint32", "overflow")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = value2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint32(ind)
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInt64 read int64
|
|
||||||
func (iter *Iterator) ReadInt64() (ret int64) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '-' {
|
|
||||||
val := iter.readUint64(iter.readByte())
|
|
||||||
if val > math.MaxInt64+1 {
|
|
||||||
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return -int64(val)
|
|
||||||
}
|
|
||||||
val := iter.readUint64(c)
|
|
||||||
if val > math.MaxInt64 {
|
|
||||||
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int64(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUint64 read uint64
|
|
||||||
func (iter *Iterator) ReadUint64() uint64 {
|
|
||||||
return iter.readUint64(iter.nextToken())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
|
||||||
ind := intDigits[c]
|
|
||||||
if ind == 0 {
|
|
||||||
iter.assertInteger()
|
|
||||||
return 0 // single zero
|
|
||||||
}
|
|
||||||
if ind == invalidCharForNumber {
|
|
||||||
iter.ReportError("readUint64", "unexpected character: "+string([]byte{byte(ind)}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value := uint64(ind)
|
|
||||||
if iter.tail-iter.head > 10 {
|
|
||||||
i := iter.head
|
|
||||||
ind2 := intDigits[iter.buf[i]]
|
|
||||||
if ind2 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind3 := intDigits[iter.buf[i]]
|
|
||||||
if ind3 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*10 + uint64(ind2)
|
|
||||||
}
|
|
||||||
//iter.head = i + 1
|
|
||||||
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
|
|
||||||
i++
|
|
||||||
ind4 := intDigits[iter.buf[i]]
|
|
||||||
if ind4 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*100 + uint64(ind2)*10 + uint64(ind3)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind5 := intDigits[iter.buf[i]]
|
|
||||||
if ind5 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind6 := intDigits[iter.buf[i]]
|
|
||||||
if ind6 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind7 := intDigits[iter.buf[i]]
|
|
||||||
if ind7 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind8 := intDigits[iter.buf[i]]
|
|
||||||
if ind8 == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
ind9 := intDigits[iter.buf[i]]
|
|
||||||
value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8)
|
|
||||||
iter.head = i
|
|
||||||
if ind9 == invalidCharForNumber {
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
ind = intDigits[iter.buf[i]]
|
|
||||||
if ind == invalidCharForNumber {
|
|
||||||
iter.head = i
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
if value > uint64SafeToMultiple10 {
|
|
||||||
value2 := (value << 3) + (value << 1) + uint64(ind)
|
|
||||||
if value2 < value {
|
|
||||||
iter.ReportError("readUint64", "overflow")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = value2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value = (value << 3) + (value << 1) + uint64(ind)
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.assertInteger()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) assertInteger() {
|
|
||||||
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
|
|
||||||
iter.ReportError("assertInteger", "can not decode float as int")
|
|
||||||
}
|
|
||||||
}
|
|
267
vendor/github.com/json-iterator/go/iter_object.go
generated
vendored
267
vendor/github.com/json-iterator/go/iter_object.go
generated
vendored
|
@ -1,267 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadObject read one field from object.
|
|
||||||
// If object ended, returns empty string.
|
|
||||||
// Otherwise, returns the field name.
|
|
||||||
func (iter *Iterator) ReadObject() (ret string) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
switch c {
|
|
||||||
case 'n':
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return "" // null
|
|
||||||
case '{':
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == '"' {
|
|
||||||
iter.unreadByte()
|
|
||||||
field := iter.ReadString()
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
}
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
if c == '}' {
|
|
||||||
return "" // end of object
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
case ',':
|
|
||||||
field := iter.ReadString()
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
}
|
|
||||||
return field
|
|
||||||
case '}':
|
|
||||||
return "" // end of object
|
|
||||||
default:
|
|
||||||
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaseInsensitive
|
|
||||||
func (iter *Iterator) readFieldHash() int64 {
|
|
||||||
hash := int64(0x811c9dc5)
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c != '"' {
|
|
||||||
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
// require ascii string and no escape
|
|
||||||
b := iter.buf[i]
|
|
||||||
if b == '\\' {
|
|
||||||
iter.head = i
|
|
||||||
for _, b := range iter.readStringSlowPath() {
|
|
||||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
|
||||||
b += 'a' - 'A'
|
|
||||||
}
|
|
||||||
hash ^= int64(b)
|
|
||||||
hash *= 0x1000193
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
if b == '"' {
|
|
||||||
iter.head = i + 1
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
|
||||||
b += 'a' - 'A'
|
|
||||||
}
|
|
||||||
hash ^= int64(b)
|
|
||||||
hash *= 0x1000193
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.ReportError("readFieldHash", `incomplete field name`)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func calcHash(str string, caseSensitive bool) int64 {
|
|
||||||
if !caseSensitive {
|
|
||||||
str = strings.ToLower(str)
|
|
||||||
}
|
|
||||||
hash := int64(0x811c9dc5)
|
|
||||||
for _, b := range []byte(str) {
|
|
||||||
hash ^= int64(b)
|
|
||||||
hash *= 0x1000193
|
|
||||||
}
|
|
||||||
return int64(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
|
||||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|
||||||
c := iter.nextToken()
|
|
||||||
var field string
|
|
||||||
if c == '{' {
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == '"' {
|
|
||||||
iter.unreadByte()
|
|
||||||
field = iter.ReadString()
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
}
|
|
||||||
if !callback(iter, field) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
for c == ',' {
|
|
||||||
field = iter.ReadString()
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
}
|
|
||||||
if !callback(iter, field) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
}
|
|
||||||
if c != '}' {
|
|
||||||
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
if c == '}' {
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c}))
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return true // null
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMapCB read map with callback, the key can be any string
|
|
||||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '{' {
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == '"' {
|
|
||||||
iter.unreadByte()
|
|
||||||
field := iter.ReadString()
|
|
||||||
if iter.nextToken() != ':' {
|
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !callback(iter, field) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
for c == ',' {
|
|
||||||
field = iter.ReadString()
|
|
||||||
if iter.nextToken() != ':' {
|
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !callback(iter, field) {
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
}
|
|
||||||
if c != '}' {
|
|
||||||
iter.ReportError("ReadMapCB", `object not ended with }`)
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
if c == '}' {
|
|
||||||
return iter.decrementDepth()
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c}))
|
|
||||||
iter.decrementDepth()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return true // null
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readObjectStart() bool {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '{' {
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == '}' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return true
|
|
||||||
} else if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
|
|
||||||
str := iter.ReadStringAsSlice()
|
|
||||||
if iter.skipWhitespacesWithoutLoadMore() {
|
|
||||||
if ret == nil {
|
|
||||||
ret = make([]byte, len(str))
|
|
||||||
copy(ret, str)
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if iter.buf[iter.head] != ':' {
|
|
||||||
iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.head++
|
|
||||||
if iter.skipWhitespacesWithoutLoadMore() {
|
|
||||||
if ret == nil {
|
|
||||||
ret = make([]byte, len(str))
|
|
||||||
copy(ret, str)
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ret == nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
130
vendor/github.com/json-iterator/go/iter_skip.go
generated
vendored
130
vendor/github.com/json-iterator/go/iter_skip.go
generated
vendored
|
@ -1,130 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// ReadNil reads a json object as nil and
|
|
||||||
// returns whether it's a nil or not
|
|
||||||
func (iter *Iterator) ReadNil() (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBool reads a json object as BoolValue
|
|
||||||
func (iter *Iterator) ReadBool() (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 't' {
|
|
||||||
iter.skipThreeBytes('r', 'u', 'e')
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c == 'f' {
|
|
||||||
iter.skipFourBytes('a', 'l', 's', 'e')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipAndReturnBytes skip next JSON element, and return its content as []byte.
|
|
||||||
// The []byte can be kept, it is a copy of data.
|
|
||||||
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
|
||||||
iter.startCapture(iter.head)
|
|
||||||
iter.Skip()
|
|
||||||
return iter.stopCapture()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipAndAppendBytes skips next JSON element and appends its content to
|
|
||||||
// buffer, returning the result.
|
|
||||||
func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte {
|
|
||||||
iter.startCaptureTo(buf, iter.head)
|
|
||||||
iter.Skip()
|
|
||||||
return iter.stopCapture()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) {
|
|
||||||
if iter.captured != nil {
|
|
||||||
panic("already in capture mode")
|
|
||||||
}
|
|
||||||
iter.captureStartedAt = captureStartedAt
|
|
||||||
iter.captured = buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) startCapture(captureStartedAt int) {
|
|
||||||
iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) stopCapture() []byte {
|
|
||||||
if iter.captured == nil {
|
|
||||||
panic("not in capture mode")
|
|
||||||
}
|
|
||||||
captured := iter.captured
|
|
||||||
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
|
||||||
iter.captureStartedAt = -1
|
|
||||||
iter.captured = nil
|
|
||||||
return append(captured, remaining...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip skips a json object and positions to relatively the next json object
|
|
||||||
func (iter *Iterator) Skip() {
|
|
||||||
c := iter.nextToken()
|
|
||||||
switch c {
|
|
||||||
case '"':
|
|
||||||
iter.skipString()
|
|
||||||
case 'n':
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
|
||||||
case 't':
|
|
||||||
iter.skipThreeBytes('r', 'u', 'e') // true
|
|
||||||
case 'f':
|
|
||||||
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
|
||||||
case '0':
|
|
||||||
iter.unreadByte()
|
|
||||||
iter.ReadFloat32()
|
|
||||||
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
iter.skipNumber()
|
|
||||||
case '[':
|
|
||||||
iter.skipArray()
|
|
||||||
case '{':
|
|
||||||
iter.skipObject()
|
|
||||||
default:
|
|
||||||
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) {
|
|
||||||
if iter.readByte() != b1 {
|
|
||||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.readByte() != b2 {
|
|
||||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.readByte() != b3 {
|
|
||||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.readByte() != b4 {
|
|
||||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) {
|
|
||||||
if iter.readByte() != b1 {
|
|
||||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.readByte() != b2 {
|
|
||||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if iter.readByte() != b3 {
|
|
||||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
163
vendor/github.com/json-iterator/go/iter_skip_sloppy.go
generated
vendored
163
vendor/github.com/json-iterator/go/iter_skip_sloppy.go
generated
vendored
|
@ -1,163 +0,0 @@
|
||||||
//+build jsoniter_sloppy
|
|
||||||
|
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
// sloppy but faster implementation, do not validate the input json
|
|
||||||
|
|
||||||
func (iter *Iterator) skipNumber() {
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
|
||||||
iter.head = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipArray() {
|
|
||||||
level := 1
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
switch iter.buf[i] {
|
|
||||||
case '"': // If inside string, skip it
|
|
||||||
iter.head = i + 1
|
|
||||||
iter.skipString()
|
|
||||||
i = iter.head - 1 // it will be i++ soon
|
|
||||||
case '[': // If open symbol, increase level
|
|
||||||
level++
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case ']': // If close symbol, increase level
|
|
||||||
level--
|
|
||||||
if !iter.decrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
|
||||||
if level == 0 {
|
|
||||||
iter.head = i + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.ReportError("skipObject", "incomplete array")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipObject() {
|
|
||||||
level := 1
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
switch iter.buf[i] {
|
|
||||||
case '"': // If inside string, skip it
|
|
||||||
iter.head = i + 1
|
|
||||||
iter.skipString()
|
|
||||||
i = iter.head - 1 // it will be i++ soon
|
|
||||||
case '{': // If open symbol, increase level
|
|
||||||
level++
|
|
||||||
if !iter.incrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case '}': // If close symbol, increase level
|
|
||||||
level--
|
|
||||||
if !iter.decrementDepth() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
|
||||||
if level == 0 {
|
|
||||||
iter.head = i + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.ReportError("skipObject", "incomplete object")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipString() {
|
|
||||||
for {
|
|
||||||
end, escaped := iter.findStringEnd()
|
|
||||||
if end == -1 {
|
|
||||||
if !iter.loadMore() {
|
|
||||||
iter.ReportError("skipString", "incomplete string")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if escaped {
|
|
||||||
iter.head = 1 // skip the first char as last char read is \
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iter.head = end
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
|
|
||||||
// Tries to find the end of string
|
|
||||||
// Support if string contains escaped quote symbols.
|
|
||||||
func (iter *Iterator) findStringEnd() (int, bool) {
|
|
||||||
escaped := false
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
if c == '"' {
|
|
||||||
if !escaped {
|
|
||||||
return i + 1, false
|
|
||||||
}
|
|
||||||
j := i - 1
|
|
||||||
for {
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// even number of backslashes
|
|
||||||
// either end of buffer, or " found
|
|
||||||
return i + 1, true
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// odd number of backslashes
|
|
||||||
// it is \" or \\\"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
} else if c == '\\' {
|
|
||||||
escaped = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j := iter.tail - 1
|
|
||||||
for {
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// even number of backslashes
|
|
||||||
// either end of buffer, or " found
|
|
||||||
return -1, false // do not end with \
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// odd number of backslashes
|
|
||||||
// it is \" or \\\"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
|
|
||||||
}
|
|
||||||
return -1, true // end with \
|
|
||||||
}
|
|
99
vendor/github.com/json-iterator/go/iter_skip_strict.go
generated
vendored
99
vendor/github.com/json-iterator/go/iter_skip_strict.go
generated
vendored
|
@ -1,99 +0,0 @@
|
||||||
//+build !jsoniter_sloppy
|
|
||||||
|
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (iter *Iterator) skipNumber() {
|
|
||||||
if !iter.trySkipNumber() {
|
|
||||||
iter.unreadByte()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.ReadFloat64()
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
iter.Error = nil
|
|
||||||
iter.ReadBigFloat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) trySkipNumber() bool {
|
|
||||||
dotFound := false
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
case '.':
|
|
||||||
if dotFound {
|
|
||||||
iter.ReportError("validateNumber", `more than one dot found in number`)
|
|
||||||
return true // already failed
|
|
||||||
}
|
|
||||||
if i+1 == iter.tail {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c = iter.buf[i+1]
|
|
||||||
switch c {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
default:
|
|
||||||
iter.ReportError("validateNumber", `missing digit after dot`)
|
|
||||||
return true // already failed
|
|
||||||
}
|
|
||||||
dotFound = true
|
|
||||||
default:
|
|
||||||
switch c {
|
|
||||||
case ',', ']', '}', ' ', '\t', '\n', '\r':
|
|
||||||
if iter.head == i {
|
|
||||||
return false // if - without following digits
|
|
||||||
}
|
|
||||||
iter.head = i
|
|
||||||
return true // must be valid
|
|
||||||
}
|
|
||||||
return false // may be invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipString() {
|
|
||||||
if !iter.trySkipString() {
|
|
||||||
iter.unreadByte()
|
|
||||||
iter.ReadString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) trySkipString() bool {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
if c == '"' {
|
|
||||||
iter.head = i + 1
|
|
||||||
return true // valid
|
|
||||||
} else if c == '\\' {
|
|
||||||
return false
|
|
||||||
} else if c < ' ' {
|
|
||||||
iter.ReportError("trySkipString",
|
|
||||||
fmt.Sprintf(`invalid control character found: %d`, c))
|
|
||||||
return true // already failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipObject() {
|
|
||||||
iter.unreadByte()
|
|
||||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
|
||||||
iter.Skip()
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipArray() {
|
|
||||||
iter.unreadByte()
|
|
||||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
||||||
iter.Skip()
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
215
vendor/github.com/json-iterator/go/iter_str.go
generated
vendored
215
vendor/github.com/json-iterator/go/iter_str.go
generated
vendored
|
@ -1,215 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"unicode/utf16"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadString read string from iterator
|
|
||||||
func (iter *Iterator) ReadString() (ret string) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '"' {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
if c == '"' {
|
|
||||||
ret = string(iter.buf[iter.head:i])
|
|
||||||
iter.head = i + 1
|
|
||||||
return ret
|
|
||||||
} else if c == '\\' {
|
|
||||||
break
|
|
||||||
} else if c < ' ' {
|
|
||||||
iter.ReportError("ReadString",
|
|
||||||
fmt.Sprintf(`invalid control character found: %d`, c))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iter.readStringSlowPath()
|
|
||||||
} else if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readStringSlowPath() (ret string) {
|
|
||||||
var str []byte
|
|
||||||
var c byte
|
|
||||||
for iter.Error == nil {
|
|
||||||
c = iter.readByte()
|
|
||||||
if c == '"' {
|
|
||||||
return string(str)
|
|
||||||
}
|
|
||||||
if c == '\\' {
|
|
||||||
c = iter.readByte()
|
|
||||||
str = iter.readEscapedChar(c, str)
|
|
||||||
} else {
|
|
||||||
str = append(str, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iter.ReportError("readStringSlowPath", "unexpected end of input")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
|
|
||||||
switch c {
|
|
||||||
case 'u':
|
|
||||||
r := iter.readU4()
|
|
||||||
if utf16.IsSurrogate(r) {
|
|
||||||
c = iter.readByte()
|
|
||||||
if iter.Error != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if c != '\\' {
|
|
||||||
iter.unreadByte()
|
|
||||||
str = appendRune(str, r)
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
c = iter.readByte()
|
|
||||||
if iter.Error != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if c != 'u' {
|
|
||||||
str = appendRune(str, r)
|
|
||||||
return iter.readEscapedChar(c, str)
|
|
||||||
}
|
|
||||||
r2 := iter.readU4()
|
|
||||||
if iter.Error != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
combined := utf16.DecodeRune(r, r2)
|
|
||||||
if combined == '\uFFFD' {
|
|
||||||
str = appendRune(str, r)
|
|
||||||
str = appendRune(str, r2)
|
|
||||||
} else {
|
|
||||||
str = appendRune(str, combined)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str = appendRune(str, r)
|
|
||||||
}
|
|
||||||
case '"':
|
|
||||||
str = append(str, '"')
|
|
||||||
case '\\':
|
|
||||||
str = append(str, '\\')
|
|
||||||
case '/':
|
|
||||||
str = append(str, '/')
|
|
||||||
case 'b':
|
|
||||||
str = append(str, '\b')
|
|
||||||
case 'f':
|
|
||||||
str = append(str, '\f')
|
|
||||||
case 'n':
|
|
||||||
str = append(str, '\n')
|
|
||||||
case 'r':
|
|
||||||
str = append(str, '\r')
|
|
||||||
case 't':
|
|
||||||
str = append(str, '\t')
|
|
||||||
default:
|
|
||||||
iter.ReportError("readEscapedChar",
|
|
||||||
`invalid escape char after \`)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadStringAsSlice read string from iterator without copying into string form.
|
|
||||||
// The []byte can not be kept, as it will change after next iterator call.
|
|
||||||
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == '"' {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
// require ascii string and no escape
|
|
||||||
// for: field name, base64, number
|
|
||||||
if iter.buf[i] == '"' {
|
|
||||||
// fast path: reuse the underlying buffer
|
|
||||||
ret = iter.buf[iter.head:i]
|
|
||||||
iter.head = i + 1
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readLen := iter.tail - iter.head
|
|
||||||
copied := make([]byte, readLen, readLen*2)
|
|
||||||
copy(copied, iter.buf[iter.head:iter.tail])
|
|
||||||
iter.head = iter.tail
|
|
||||||
for iter.Error == nil {
|
|
||||||
c := iter.readByte()
|
|
||||||
if c == '"' {
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
copied = append(copied, c)
|
|
||||||
}
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) readU4() (ret rune) {
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
c := iter.readByte()
|
|
||||||
if iter.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c >= '0' && c <= '9' {
|
|
||||||
ret = ret*16 + rune(c-'0')
|
|
||||||
} else if c >= 'a' && c <= 'f' {
|
|
||||||
ret = ret*16 + rune(c-'a'+10)
|
|
||||||
} else if c >= 'A' && c <= 'F' {
|
|
||||||
ret = ret*16 + rune(c-'A'+10)
|
|
||||||
} else {
|
|
||||||
iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
t1 = 0x00 // 0000 0000
|
|
||||||
tx = 0x80 // 1000 0000
|
|
||||||
t2 = 0xC0 // 1100 0000
|
|
||||||
t3 = 0xE0 // 1110 0000
|
|
||||||
t4 = 0xF0 // 1111 0000
|
|
||||||
t5 = 0xF8 // 1111 1000
|
|
||||||
|
|
||||||
maskx = 0x3F // 0011 1111
|
|
||||||
mask2 = 0x1F // 0001 1111
|
|
||||||
mask3 = 0x0F // 0000 1111
|
|
||||||
mask4 = 0x07 // 0000 0111
|
|
||||||
|
|
||||||
rune1Max = 1<<7 - 1
|
|
||||||
rune2Max = 1<<11 - 1
|
|
||||||
rune3Max = 1<<16 - 1
|
|
||||||
|
|
||||||
surrogateMin = 0xD800
|
|
||||||
surrogateMax = 0xDFFF
|
|
||||||
|
|
||||||
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
|
|
||||||
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
|
|
||||||
)
|
|
||||||
|
|
||||||
func appendRune(p []byte, r rune) []byte {
|
|
||||||
// Negative values are erroneous. Making it unsigned addresses the problem.
|
|
||||||
switch i := uint32(r); {
|
|
||||||
case i <= rune1Max:
|
|
||||||
p = append(p, byte(r))
|
|
||||||
return p
|
|
||||||
case i <= rune2Max:
|
|
||||||
p = append(p, t2|byte(r>>6))
|
|
||||||
p = append(p, tx|byte(r)&maskx)
|
|
||||||
return p
|
|
||||||
case i > maxRune, surrogateMin <= i && i <= surrogateMax:
|
|
||||||
r = runeError
|
|
||||||
fallthrough
|
|
||||||
case i <= rune3Max:
|
|
||||||
p = append(p, t3|byte(r>>12))
|
|
||||||
p = append(p, tx|byte(r>>6)&maskx)
|
|
||||||
p = append(p, tx|byte(r)&maskx)
|
|
||||||
return p
|
|
||||||
default:
|
|
||||||
p = append(p, t4|byte(r>>18))
|
|
||||||
p = append(p, tx|byte(r>>12)&maskx)
|
|
||||||
p = append(p, tx|byte(r>>6)&maskx)
|
|
||||||
p = append(p, tx|byte(r)&maskx)
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
}
|
|
18
vendor/github.com/json-iterator/go/jsoniter.go
generated
vendored
18
vendor/github.com/json-iterator/go/jsoniter.go
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
// Package jsoniter implements encoding and decoding of JSON as defined in
|
|
||||||
// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json.
|
|
||||||
// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter
|
|
||||||
// and variable type declarations (if any).
|
|
||||||
// jsoniter interfaces gives 100% compatibility with code using standard lib.
|
|
||||||
//
|
|
||||||
// "JSON and Go"
|
|
||||||
// (https://golang.org/doc/articles/json_and_go.html)
|
|
||||||
// gives a description of how Marshal/Unmarshal operate
|
|
||||||
// between arbitrary or predefined json objects and bytes,
|
|
||||||
// and it applies to jsoniter.Marshal/Unmarshal as well.
|
|
||||||
//
|
|
||||||
// Besides, jsoniter.Iterator provides a different set of interfaces
|
|
||||||
// iterating given bytes/string/reader
|
|
||||||
// and yielding parsed elements one by one.
|
|
||||||
// This set of interfaces reads input as required and gives
|
|
||||||
// better performance.
|
|
||||||
package jsoniter
|
|
42
vendor/github.com/json-iterator/go/pool.go
generated
vendored
42
vendor/github.com/json-iterator/go/pool.go
generated
vendored
|
@ -1,42 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IteratorPool a thread safe pool of iterators with same configuration
|
|
||||||
type IteratorPool interface {
|
|
||||||
BorrowIterator(data []byte) *Iterator
|
|
||||||
ReturnIterator(iter *Iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StreamPool a thread safe pool of streams with same configuration
|
|
||||||
type StreamPool interface {
|
|
||||||
BorrowStream(writer io.Writer) *Stream
|
|
||||||
ReturnStream(stream *Stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
|
|
||||||
stream := cfg.streamPool.Get().(*Stream)
|
|
||||||
stream.Reset(writer)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
|
|
||||||
stream.out = nil
|
|
||||||
stream.Error = nil
|
|
||||||
stream.Attachment = nil
|
|
||||||
cfg.streamPool.Put(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator {
|
|
||||||
iter := cfg.iteratorPool.Get().(*Iterator)
|
|
||||||
iter.ResetBytes(data)
|
|
||||||
return iter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
|
|
||||||
iter.Error = nil
|
|
||||||
iter.Attachment = nil
|
|
||||||
cfg.iteratorPool.Put(iter)
|
|
||||||
}
|
|
337
vendor/github.com/json-iterator/go/reflect.go
generated
vendored
337
vendor/github.com/json-iterator/go/reflect.go
generated
vendored
|
@ -1,337 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ValDecoder is an internal type registered to cache as needed.
|
|
||||||
// Don't confuse jsoniter.ValDecoder with json.Decoder.
|
|
||||||
// For json.Decoder's adapter, refer to jsoniter.AdapterDecoder(todo link).
|
|
||||||
//
|
|
||||||
// Reflection on type to create decoders, which is then cached
|
|
||||||
// Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions
|
|
||||||
// 1. create instance of new value, for example *int will need a int to be allocated
|
|
||||||
// 2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New
|
|
||||||
// 3. assignment to map, both key and value will be reflect.Value
|
|
||||||
// For a simple struct binding, it will be reflect.Value free and allocation free
|
|
||||||
type ValDecoder interface {
|
|
||||||
Decode(ptr unsafe.Pointer, iter *Iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValEncoder is an internal type registered to cache as needed.
|
|
||||||
// Don't confuse jsoniter.ValEncoder with json.Encoder.
|
|
||||||
// For json.Encoder's adapter, refer to jsoniter.AdapterEncoder(todo godoc link).
|
|
||||||
type ValEncoder interface {
|
|
||||||
IsEmpty(ptr unsafe.Pointer) bool
|
|
||||||
Encode(ptr unsafe.Pointer, stream *Stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
type checkIsEmpty interface {
|
|
||||||
IsEmpty(ptr unsafe.Pointer) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ctx struct {
|
|
||||||
*frozenConfig
|
|
||||||
prefix string
|
|
||||||
encoders map[reflect2.Type]ValEncoder
|
|
||||||
decoders map[reflect2.Type]ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *ctx) caseSensitive() bool {
|
|
||||||
if b.frozenConfig == nil {
|
|
||||||
// default is case-insensitive
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return b.frozenConfig.caseSensitive
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *ctx) append(prefix string) *ctx {
|
|
||||||
return &ctx{
|
|
||||||
frozenConfig: b.frozenConfig,
|
|
||||||
prefix: b.prefix + " " + prefix,
|
|
||||||
encoders: b.encoders,
|
|
||||||
decoders: b.decoders,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
|
||||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
|
||||||
depth := iter.depth
|
|
||||||
cacheKey := reflect2.RTypeOf(obj)
|
|
||||||
decoder := iter.cfg.getDecoderFromCache(cacheKey)
|
|
||||||
if decoder == nil {
|
|
||||||
typ := reflect2.TypeOf(obj)
|
|
||||||
if typ == nil || typ.Kind() != reflect.Ptr {
|
|
||||||
iter.ReportError("ReadVal", "can only unmarshal into pointer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decoder = iter.cfg.DecoderOf(typ)
|
|
||||||
}
|
|
||||||
ptr := reflect2.PtrOf(obj)
|
|
||||||
if ptr == nil {
|
|
||||||
iter.ReportError("ReadVal", "can not read into nil pointer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decoder.Decode(ptr, iter)
|
|
||||||
if iter.depth != depth {
|
|
||||||
iter.ReportError("ReadVal", "unexpected mismatched nesting")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
|
||||||
func (stream *Stream) WriteVal(val interface{}) {
|
|
||||||
if nil == val {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cacheKey := reflect2.RTypeOf(val)
|
|
||||||
encoder := stream.cfg.getEncoderFromCache(cacheKey)
|
|
||||||
if encoder == nil {
|
|
||||||
typ := reflect2.TypeOf(val)
|
|
||||||
encoder = stream.cfg.EncoderOf(typ)
|
|
||||||
}
|
|
||||||
encoder.Encode(reflect2.PtrOf(val), stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) DecoderOf(typ reflect2.Type) ValDecoder {
|
|
||||||
cacheKey := typ.RType()
|
|
||||||
decoder := cfg.getDecoderFromCache(cacheKey)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
ctx := &ctx{
|
|
||||||
frozenConfig: cfg,
|
|
||||||
prefix: "",
|
|
||||||
decoders: map[reflect2.Type]ValDecoder{},
|
|
||||||
encoders: map[reflect2.Type]ValEncoder{},
|
|
||||||
}
|
|
||||||
ptrType := typ.(*reflect2.UnsafePtrType)
|
|
||||||
decoder = decoderOfType(ctx, ptrType.Elem())
|
|
||||||
cfg.addDecoderToCache(cacheKey, decoder)
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func decoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
decoder := getTypeDecoderFromExtension(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
decoder = createDecoderOfType(ctx, typ)
|
|
||||||
for _, extension := range extensions {
|
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
|
||||||
}
|
|
||||||
decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder)
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
|
||||||
}
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
decoder := ctx.decoders[typ]
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
placeholder := &placeholderDecoder{}
|
|
||||||
ctx.decoders[typ] = placeholder
|
|
||||||
decoder = _createDecoderOfType(ctx, typ)
|
|
||||||
placeholder.decoder = decoder
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func _createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
decoder := createDecoderOfJsonRawMessage(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
decoder = createDecoderOfJsonNumber(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
decoder = createDecoderOfMarshaler(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
decoder = createDecoderOfAny(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
decoder = createDecoderOfNative(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Interface:
|
|
||||||
ifaceType, isIFace := typ.(*reflect2.UnsafeIFaceType)
|
|
||||||
if isIFace {
|
|
||||||
return &ifaceDecoder{valType: ifaceType}
|
|
||||||
}
|
|
||||||
return &efaceDecoder{}
|
|
||||||
case reflect.Struct:
|
|
||||||
return decoderOfStruct(ctx, typ)
|
|
||||||
case reflect.Array:
|
|
||||||
return decoderOfArray(ctx, typ)
|
|
||||||
case reflect.Slice:
|
|
||||||
return decoderOfSlice(ctx, typ)
|
|
||||||
case reflect.Map:
|
|
||||||
return decoderOfMap(ctx, typ)
|
|
||||||
case reflect.Ptr:
|
|
||||||
return decoderOfOptional(ctx, typ)
|
|
||||||
default:
|
|
||||||
return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *frozenConfig) EncoderOf(typ reflect2.Type) ValEncoder {
|
|
||||||
cacheKey := typ.RType()
|
|
||||||
encoder := cfg.getEncoderFromCache(cacheKey)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
ctx := &ctx{
|
|
||||||
frozenConfig: cfg,
|
|
||||||
prefix: "",
|
|
||||||
decoders: map[reflect2.Type]ValDecoder{},
|
|
||||||
encoders: map[reflect2.Type]ValEncoder{},
|
|
||||||
}
|
|
||||||
encoder = encoderOfType(ctx, typ)
|
|
||||||
if typ.LikePtr() {
|
|
||||||
encoder = &onePtrEncoder{encoder}
|
|
||||||
}
|
|
||||||
cfg.addEncoderToCache(cacheKey, encoder)
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
type onePtrEncoder struct {
|
|
||||||
encoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *onePtrEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *onePtrEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
encoder.encoder.Encode(unsafe.Pointer(&ptr), stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
encoder := getTypeEncoderFromExtension(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
encoder = createEncoderOfType(ctx, typ)
|
|
||||||
for _, extension := range extensions {
|
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
|
||||||
}
|
|
||||||
encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder)
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
encoder := ctx.encoders[typ]
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
placeholder := &placeholderEncoder{}
|
|
||||||
ctx.encoders[typ] = placeholder
|
|
||||||
encoder = _createEncoderOfType(ctx, typ)
|
|
||||||
placeholder.encoder = encoder
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
func _createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
encoder := createEncoderOfJsonRawMessage(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
encoder = createEncoderOfJsonNumber(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
encoder = createEncoderOfMarshaler(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
encoder = createEncoderOfAny(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
encoder = createEncoderOfNative(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
kind := typ.Kind()
|
|
||||||
switch kind {
|
|
||||||
case reflect.Interface:
|
|
||||||
return &dynamicEncoder{typ}
|
|
||||||
case reflect.Struct:
|
|
||||||
return encoderOfStruct(ctx, typ)
|
|
||||||
case reflect.Array:
|
|
||||||
return encoderOfArray(ctx, typ)
|
|
||||||
case reflect.Slice:
|
|
||||||
return encoderOfSlice(ctx, typ)
|
|
||||||
case reflect.Map:
|
|
||||||
return encoderOfMap(ctx, typ)
|
|
||||||
case reflect.Ptr:
|
|
||||||
return encoderOfOptional(ctx, typ)
|
|
||||||
default:
|
|
||||||
return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lazyErrorDecoder struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *lazyErrorDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.WhatIsNext() != NilValue {
|
|
||||||
if iter.Error == nil {
|
|
||||||
iter.Error = decoder.err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iter.Skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lazyErrorEncoder struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if ptr == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
} else if stream.Error == nil {
|
|
||||||
stream.Error = encoder.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type placeholderDecoder struct {
|
|
||||||
decoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
decoder.decoder.Decode(ptr, iter)
|
|
||||||
}
|
|
||||||
|
|
||||||
type placeholderEncoder struct {
|
|
||||||
encoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
encoder.encoder.Encode(ptr, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.encoder.IsEmpty(ptr)
|
|
||||||
}
|
|
104
vendor/github.com/json-iterator/go/reflect_array.go
generated
vendored
104
vendor/github.com/json-iterator/go/reflect_array.go
generated
vendored
|
@ -1,104 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
arrayType := typ.(*reflect2.UnsafeArrayType)
|
|
||||||
decoder := decoderOfType(ctx.append("[arrayElem]"), arrayType.Elem())
|
|
||||||
return &arrayDecoder{arrayType, decoder}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfArray(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
arrayType := typ.(*reflect2.UnsafeArrayType)
|
|
||||||
if arrayType.Len() == 0 {
|
|
||||||
return emptyArrayEncoder{}
|
|
||||||
}
|
|
||||||
encoder := encoderOfType(ctx.append("[arrayElem]"), arrayType.Elem())
|
|
||||||
return &arrayEncoder{arrayType, encoder}
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyArrayEncoder struct{}
|
|
||||||
|
|
||||||
func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteEmptyArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type arrayEncoder struct {
|
|
||||||
arrayType *reflect2.UnsafeArrayType
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteArrayStart()
|
|
||||||
elemPtr := unsafe.Pointer(ptr)
|
|
||||||
encoder.elemEncoder.Encode(elemPtr, stream)
|
|
||||||
for i := 1; i < encoder.arrayType.Len(); i++ {
|
|
||||||
stream.WriteMore()
|
|
||||||
elemPtr = encoder.arrayType.UnsafeGetIndex(ptr, i)
|
|
||||||
encoder.elemEncoder.Encode(elemPtr, stream)
|
|
||||||
}
|
|
||||||
stream.WriteArrayEnd()
|
|
||||||
if stream.Error != nil && stream.Error != io.EOF {
|
|
||||||
stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type arrayDecoder struct {
|
|
||||||
arrayType *reflect2.UnsafeArrayType
|
|
||||||
elemDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
decoder.doDecode(ptr, iter)
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
arrayType := decoder.arrayType
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c != '[' {
|
|
||||||
iter.ReportError("decode array", "expect [ or n, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == ']' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
elemPtr := arrayType.UnsafeGetIndex(ptr, 0)
|
|
||||||
decoder.elemDecoder.Decode(elemPtr, iter)
|
|
||||||
length := 1
|
|
||||||
for c = iter.nextToken(); c == ','; c = iter.nextToken() {
|
|
||||||
if length >= arrayType.Len() {
|
|
||||||
iter.Skip()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
idx := length
|
|
||||||
length += 1
|
|
||||||
elemPtr = arrayType.UnsafeGetIndex(ptr, idx)
|
|
||||||
decoder.elemDecoder.Decode(elemPtr, iter)
|
|
||||||
}
|
|
||||||
if c != ']' {
|
|
||||||
iter.ReportError("decode array", "expect ], but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
70
vendor/github.com/json-iterator/go/reflect_dynamic.go
generated
vendored
70
vendor/github.com/json-iterator/go/reflect_dynamic.go
generated
vendored
|
@ -1,70 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dynamicEncoder struct {
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
obj := encoder.valType.UnsafeIndirect(ptr)
|
|
||||||
stream.WriteVal(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.valType.UnsafeIndirect(ptr) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type efaceDecoder struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
pObj := (*interface{})(ptr)
|
|
||||||
obj := *pObj
|
|
||||||
if obj == nil {
|
|
||||||
*pObj = iter.Read()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
typ := reflect2.TypeOf(obj)
|
|
||||||
if typ.Kind() != reflect.Ptr {
|
|
||||||
*pObj = iter.Read()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ptrType := typ.(*reflect2.UnsafePtrType)
|
|
||||||
ptrElemType := ptrType.Elem()
|
|
||||||
if iter.WhatIsNext() == NilValue {
|
|
||||||
if ptrElemType.Kind() != reflect.Ptr {
|
|
||||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
|
||||||
*pObj = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if reflect2.IsNil(obj) {
|
|
||||||
obj := ptrElemType.New()
|
|
||||||
iter.ReadVal(obj)
|
|
||||||
*pObj = obj
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.ReadVal(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ifaceDecoder struct {
|
|
||||||
valType *reflect2.UnsafeIFaceType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.ReadNil() {
|
|
||||||
decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
obj := decoder.valType.UnsafeIndirect(ptr)
|
|
||||||
if reflect2.IsNil(obj) {
|
|
||||||
iter.ReportError("decode non empty interface", "can not unmarshal into nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.ReadVal(obj)
|
|
||||||
}
|
|
483
vendor/github.com/json-iterator/go/reflect_extension.go
generated
vendored
483
vendor/github.com/json-iterator/go/reflect_extension.go
generated
vendored
|
@ -1,483 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var typeDecoders = map[string]ValDecoder{}
|
|
||||||
var fieldDecoders = map[string]ValDecoder{}
|
|
||||||
var typeEncoders = map[string]ValEncoder{}
|
|
||||||
var fieldEncoders = map[string]ValEncoder{}
|
|
||||||
var extensions = []Extension{}
|
|
||||||
|
|
||||||
// StructDescriptor describe how should we encode/decode the struct
|
|
||||||
type StructDescriptor struct {
|
|
||||||
Type reflect2.Type
|
|
||||||
Fields []*Binding
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetField get one field from the descriptor by its name.
|
|
||||||
// Can not use map here to keep field orders.
|
|
||||||
func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding {
|
|
||||||
for _, binding := range structDescriptor.Fields {
|
|
||||||
if binding.Field.Name() == fieldName {
|
|
||||||
return binding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binding describe how should we encode/decode the struct field
|
|
||||||
type Binding struct {
|
|
||||||
levels []int
|
|
||||||
Field reflect2.StructField
|
|
||||||
FromNames []string
|
|
||||||
ToNames []string
|
|
||||||
Encoder ValEncoder
|
|
||||||
Decoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension the one for all SPI. Customize encoding/decoding by specifying alternate encoder/decoder.
|
|
||||||
// Can also rename fields by UpdateStructDescriptor.
|
|
||||||
type Extension interface {
|
|
||||||
UpdateStructDescriptor(structDescriptor *StructDescriptor)
|
|
||||||
CreateMapKeyDecoder(typ reflect2.Type) ValDecoder
|
|
||||||
CreateMapKeyEncoder(typ reflect2.Type) ValEncoder
|
|
||||||
CreateDecoder(typ reflect2.Type) ValDecoder
|
|
||||||
CreateEncoder(typ reflect2.Type) ValEncoder
|
|
||||||
DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder
|
|
||||||
DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// DummyExtension embed this type get dummy implementation for all methods of Extension
|
|
||||||
type DummyExtension struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateStructDescriptor No-op
|
|
||||||
func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyDecoder No-op
|
|
||||||
func (extension *DummyExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyEncoder No-op
|
|
||||||
func (extension *DummyExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDecoder No-op
|
|
||||||
func (extension *DummyExtension) CreateDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateEncoder No-op
|
|
||||||
func (extension *DummyExtension) CreateEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateDecoder No-op
|
|
||||||
func (extension *DummyExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateEncoder No-op
|
|
||||||
func (extension *DummyExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
type EncoderExtension map[reflect2.Type]ValEncoder
|
|
||||||
|
|
||||||
// UpdateStructDescriptor No-op
|
|
||||||
func (extension EncoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDecoder No-op
|
|
||||||
func (extension EncoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateEncoder get encoder from map
|
|
||||||
func (extension EncoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return extension[typ]
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyDecoder No-op
|
|
||||||
func (extension EncoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyEncoder No-op
|
|
||||||
func (extension EncoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateDecoder No-op
|
|
||||||
func (extension EncoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateEncoder No-op
|
|
||||||
func (extension EncoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
type DecoderExtension map[reflect2.Type]ValDecoder
|
|
||||||
|
|
||||||
// UpdateStructDescriptor No-op
|
|
||||||
func (extension DecoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyDecoder No-op
|
|
||||||
func (extension DecoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMapKeyEncoder No-op
|
|
||||||
func (extension DecoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDecoder get decoder from map
|
|
||||||
func (extension DecoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder {
|
|
||||||
return extension[typ]
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateEncoder No-op
|
|
||||||
func (extension DecoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateDecoder No-op
|
|
||||||
func (extension DecoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecorateEncoder No-op
|
|
||||||
func (extension DecoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
type funcDecoder struct {
|
|
||||||
fun DecoderFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
decoder.fun(ptr, iter)
|
|
||||||
}
|
|
||||||
|
|
||||||
type funcEncoder struct {
|
|
||||||
fun EncoderFunc
|
|
||||||
isEmptyFunc func(ptr unsafe.Pointer) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
encoder.fun(ptr, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
if encoder.isEmptyFunc == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return encoder.isEmptyFunc(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecoderFunc the function form of TypeDecoder
|
|
||||||
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
|
|
||||||
|
|
||||||
// EncoderFunc the function form of TypeEncoder
|
|
||||||
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
|
|
||||||
|
|
||||||
// RegisterTypeDecoderFunc register TypeDecoder for a type with function
|
|
||||||
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
|
|
||||||
typeDecoders[typ] = &funcDecoder{fun}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterTypeDecoder register TypeDecoder for a typ
|
|
||||||
func RegisterTypeDecoder(typ string, decoder ValDecoder) {
|
|
||||||
typeDecoders[typ] = decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterFieldDecoderFunc register TypeDecoder for a struct field with function
|
|
||||||
func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) {
|
|
||||||
RegisterFieldDecoder(typ, field, &funcDecoder{fun})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterFieldDecoder register TypeDecoder for a struct field
|
|
||||||
func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) {
|
|
||||||
fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterTypeEncoderFunc register TypeEncoder for a type with encode/isEmpty function
|
|
||||||
func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
|
|
||||||
typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterTypeEncoder register TypeEncoder for a type
|
|
||||||
func RegisterTypeEncoder(typ string, encoder ValEncoder) {
|
|
||||||
typeEncoders[typ] = encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterFieldEncoderFunc register TypeEncoder for a struct field with encode/isEmpty function
|
|
||||||
func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
|
|
||||||
RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterFieldEncoder register TypeEncoder for a struct field
|
|
||||||
func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
|
|
||||||
fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterExtension register extension
|
|
||||||
func RegisterExtension(extension Extension) {
|
|
||||||
extensions = append(extensions, extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
decoder := _getTypeDecoderFromExtension(ctx, typ)
|
|
||||||
if decoder != nil {
|
|
||||||
for _, extension := range extensions {
|
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
|
||||||
}
|
|
||||||
decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder)
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
func _getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
for _, extension := range extensions {
|
|
||||||
decoder := extension.CreateDecoder(typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decoder := ctx.decoderExtension.CreateDecoder(typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
decoder := extension.CreateDecoder(typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typeName := typ.String()
|
|
||||||
decoder = typeDecoders[typeName]
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
ptrType := typ.(*reflect2.UnsafePtrType)
|
|
||||||
decoder := typeDecoders[ptrType.Elem().String()]
|
|
||||||
if decoder != nil {
|
|
||||||
return &OptionalDecoder{ptrType.Elem(), decoder}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
encoder := _getTypeEncoderFromExtension(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
for _, extension := range extensions {
|
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
|
||||||
}
|
|
||||||
encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder)
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func _getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
for _, extension := range extensions {
|
|
||||||
encoder := extension.CreateEncoder(typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encoder := ctx.encoderExtension.CreateEncoder(typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
encoder := extension.CreateEncoder(typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typeName := typ.String()
|
|
||||||
encoder = typeEncoders[typeName]
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
typePtr := typ.(*reflect2.UnsafePtrType)
|
|
||||||
encoder := typeEncoders[typePtr.Elem().String()]
|
|
||||||
if encoder != nil {
|
|
||||||
return &OptionalEncoder{encoder}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
|
|
||||||
structType := typ.(*reflect2.UnsafeStructType)
|
|
||||||
embeddedBindings := []*Binding{}
|
|
||||||
bindings := []*Binding{}
|
|
||||||
for i := 0; i < structType.NumField(); i++ {
|
|
||||||
field := structType.Field(i)
|
|
||||||
tag, hastag := field.Tag().Lookup(ctx.getTagKey())
|
|
||||||
if ctx.onlyTaggedField && !hastag && !field.Anonymous() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if tag == "-" || field.Name() == "_" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tagParts := strings.Split(tag, ",")
|
|
||||||
if field.Anonymous() && (tag == "" || tagParts[0] == "") {
|
|
||||||
if field.Type().Kind() == reflect.Struct {
|
|
||||||
structDescriptor := describeStruct(ctx, field.Type())
|
|
||||||
for _, binding := range structDescriptor.Fields {
|
|
||||||
binding.levels = append([]int{i}, binding.levels...)
|
|
||||||
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
|
||||||
binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty}
|
|
||||||
binding.Decoder = &structFieldDecoder{field, binding.Decoder}
|
|
||||||
embeddedBindings = append(embeddedBindings, binding)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else if field.Type().Kind() == reflect.Ptr {
|
|
||||||
ptrType := field.Type().(*reflect2.UnsafePtrType)
|
|
||||||
if ptrType.Elem().Kind() == reflect.Struct {
|
|
||||||
structDescriptor := describeStruct(ctx, ptrType.Elem())
|
|
||||||
for _, binding := range structDescriptor.Fields {
|
|
||||||
binding.levels = append([]int{i}, binding.levels...)
|
|
||||||
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
|
||||||
binding.Encoder = &dereferenceEncoder{binding.Encoder}
|
|
||||||
binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty}
|
|
||||||
binding.Decoder = &dereferenceDecoder{ptrType.Elem(), binding.Decoder}
|
|
||||||
binding.Decoder = &structFieldDecoder{field, binding.Decoder}
|
|
||||||
embeddedBindings = append(embeddedBindings, binding)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNames := calcFieldNames(field.Name(), tagParts[0], tag)
|
|
||||||
fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name())
|
|
||||||
decoder := fieldDecoders[fieldCacheKey]
|
|
||||||
if decoder == nil {
|
|
||||||
decoder = decoderOfType(ctx.append(field.Name()), field.Type())
|
|
||||||
}
|
|
||||||
encoder := fieldEncoders[fieldCacheKey]
|
|
||||||
if encoder == nil {
|
|
||||||
encoder = encoderOfType(ctx.append(field.Name()), field.Type())
|
|
||||||
}
|
|
||||||
binding := &Binding{
|
|
||||||
Field: field,
|
|
||||||
FromNames: fieldNames,
|
|
||||||
ToNames: fieldNames,
|
|
||||||
Decoder: decoder,
|
|
||||||
Encoder: encoder,
|
|
||||||
}
|
|
||||||
binding.levels = []int{i}
|
|
||||||
bindings = append(bindings, binding)
|
|
||||||
}
|
|
||||||
return createStructDescriptor(ctx, typ, bindings, embeddedBindings)
|
|
||||||
}
|
|
||||||
func createStructDescriptor(ctx *ctx, typ reflect2.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
|
|
||||||
structDescriptor := &StructDescriptor{
|
|
||||||
Type: typ,
|
|
||||||
Fields: bindings,
|
|
||||||
}
|
|
||||||
for _, extension := range extensions {
|
|
||||||
extension.UpdateStructDescriptor(structDescriptor)
|
|
||||||
}
|
|
||||||
ctx.encoderExtension.UpdateStructDescriptor(structDescriptor)
|
|
||||||
ctx.decoderExtension.UpdateStructDescriptor(structDescriptor)
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
extension.UpdateStructDescriptor(structDescriptor)
|
|
||||||
}
|
|
||||||
processTags(structDescriptor, ctx.frozenConfig)
|
|
||||||
// merge normal & embedded bindings & sort with original order
|
|
||||||
allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
|
|
||||||
sort.Sort(allBindings)
|
|
||||||
structDescriptor.Fields = allBindings
|
|
||||||
return structDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortableBindings []*Binding
|
|
||||||
|
|
||||||
func (bindings sortableBindings) Len() int {
|
|
||||||
return len(bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bindings sortableBindings) Less(i, j int) bool {
|
|
||||||
left := bindings[i].levels
|
|
||||||
right := bindings[j].levels
|
|
||||||
k := 0
|
|
||||||
for {
|
|
||||||
if left[k] < right[k] {
|
|
||||||
return true
|
|
||||||
} else if left[k] > right[k] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bindings sortableBindings) Swap(i, j int) {
|
|
||||||
bindings[i], bindings[j] = bindings[j], bindings[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
|
|
||||||
for _, binding := range structDescriptor.Fields {
|
|
||||||
shouldOmitEmpty := false
|
|
||||||
tagParts := strings.Split(binding.Field.Tag().Get(cfg.getTagKey()), ",")
|
|
||||||
for _, tagPart := range tagParts[1:] {
|
|
||||||
if tagPart == "omitempty" {
|
|
||||||
shouldOmitEmpty = true
|
|
||||||
} else if tagPart == "string" {
|
|
||||||
if binding.Field.Type().Kind() == reflect.String {
|
|
||||||
binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg}
|
|
||||||
binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg}
|
|
||||||
} else {
|
|
||||||
binding.Decoder = &stringModeNumberDecoder{binding.Decoder}
|
|
||||||
binding.Encoder = &stringModeNumberEncoder{binding.Encoder}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder}
|
|
||||||
binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
|
|
||||||
// ignore?
|
|
||||||
if wholeTag == "-" {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
// rename?
|
|
||||||
var fieldNames []string
|
|
||||||
if tagProvidedFieldName == "" {
|
|
||||||
fieldNames = []string{originalFieldName}
|
|
||||||
} else {
|
|
||||||
fieldNames = []string{tagProvidedFieldName}
|
|
||||||
}
|
|
||||||
// private?
|
|
||||||
isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_'
|
|
||||||
if isNotExported {
|
|
||||||
fieldNames = []string{}
|
|
||||||
}
|
|
||||||
return fieldNames
|
|
||||||
}
|
|
112
vendor/github.com/json-iterator/go/reflect_json_number.go
generated
vendored
112
vendor/github.com/json-iterator/go/reflect_json_number.go
generated
vendored
|
@ -1,112 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"strconv"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Number string
|
|
||||||
|
|
||||||
// String returns the literal text of the number.
|
|
||||||
func (n Number) String() string { return string(n) }
|
|
||||||
|
|
||||||
// Float64 returns the number as a float64.
|
|
||||||
func (n Number) Float64() (float64, error) {
|
|
||||||
return strconv.ParseFloat(string(n), 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 returns the number as an int64.
|
|
||||||
func (n Number) Int64() (int64, error) {
|
|
||||||
return strconv.ParseInt(string(n), 10, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CastJsonNumber(val interface{}) (string, bool) {
|
|
||||||
switch typedVal := val.(type) {
|
|
||||||
case json.Number:
|
|
||||||
return string(typedVal), true
|
|
||||||
case Number:
|
|
||||||
return string(typedVal), true
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsonNumberType = reflect2.TypeOfPtr((*json.Number)(nil)).Elem()
|
|
||||||
var jsoniterNumberType = reflect2.TypeOfPtr((*Number)(nil)).Elem()
|
|
||||||
|
|
||||||
func createDecoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
if typ.AssignableTo(jsonNumberType) {
|
|
||||||
return &jsonNumberCodec{}
|
|
||||||
}
|
|
||||||
if typ.AssignableTo(jsoniterNumberType) {
|
|
||||||
return &jsoniterNumberCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEncoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
if typ.AssignableTo(jsonNumberType) {
|
|
||||||
return &jsonNumberCodec{}
|
|
||||||
}
|
|
||||||
if typ.AssignableTo(jsoniterNumberType) {
|
|
||||||
return &jsoniterNumberCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsonNumberCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
switch iter.WhatIsNext() {
|
|
||||||
case StringValue:
|
|
||||||
*((*json.Number)(ptr)) = json.Number(iter.ReadString())
|
|
||||||
case NilValue:
|
|
||||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
|
||||||
*((*json.Number)(ptr)) = ""
|
|
||||||
default:
|
|
||||||
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
number := *((*json.Number)(ptr))
|
|
||||||
if len(number) == 0 {
|
|
||||||
stream.writeByte('0')
|
|
||||||
} else {
|
|
||||||
stream.WriteRaw(string(number))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*json.Number)(ptr))) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsoniterNumberCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
switch iter.WhatIsNext() {
|
|
||||||
case StringValue:
|
|
||||||
*((*Number)(ptr)) = Number(iter.ReadString())
|
|
||||||
case NilValue:
|
|
||||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
|
||||||
*((*Number)(ptr)) = ""
|
|
||||||
default:
|
|
||||||
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
number := *((*Number)(ptr))
|
|
||||||
if len(number) == 0 {
|
|
||||||
stream.writeByte('0')
|
|
||||||
} else {
|
|
||||||
stream.WriteRaw(string(number))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*Number)(ptr))) == 0
|
|
||||||
}
|
|
76
vendor/github.com/json-iterator/go/reflect_json_raw_message.go
generated
vendored
76
vendor/github.com/json-iterator/go/reflect_json_raw_message.go
generated
vendored
|
@ -1,76 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var jsonRawMessageType = reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()
|
|
||||||
var jsoniterRawMessageType = reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()
|
|
||||||
|
|
||||||
func createEncoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
if typ == jsonRawMessageType {
|
|
||||||
return &jsonRawMessageCodec{}
|
|
||||||
}
|
|
||||||
if typ == jsoniterRawMessageType {
|
|
||||||
return &jsoniterRawMessageCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDecoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
if typ == jsonRawMessageType {
|
|
||||||
return &jsonRawMessageCodec{}
|
|
||||||
}
|
|
||||||
if typ == jsoniterRawMessageType {
|
|
||||||
return &jsoniterRawMessageCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsonRawMessageCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.ReadNil() {
|
|
||||||
*((*json.RawMessage)(ptr)) = nil
|
|
||||||
} else {
|
|
||||||
*((*json.RawMessage)(ptr)) = iter.SkipAndReturnBytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *((*json.RawMessage)(ptr)) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
} else {
|
|
||||||
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*json.RawMessage)(ptr))) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsoniterRawMessageCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.ReadNil() {
|
|
||||||
*((*RawMessage)(ptr)) = nil
|
|
||||||
} else {
|
|
||||||
*((*RawMessage)(ptr)) = iter.SkipAndReturnBytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *((*RawMessage)(ptr)) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
} else {
|
|
||||||
stream.WriteRaw(string(*((*RawMessage)(ptr))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*RawMessage)(ptr))) == 0
|
|
||||||
}
|
|
346
vendor/github.com/json-iterator/go/reflect_map.go
generated
vendored
346
vendor/github.com/json-iterator/go/reflect_map.go
generated
vendored
|
@ -1,346 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func decoderOfMap(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
mapType := typ.(*reflect2.UnsafeMapType)
|
|
||||||
keyDecoder := decoderOfMapKey(ctx.append("[mapKey]"), mapType.Key())
|
|
||||||
elemDecoder := decoderOfType(ctx.append("[mapElem]"), mapType.Elem())
|
|
||||||
return &mapDecoder{
|
|
||||||
mapType: mapType,
|
|
||||||
keyType: mapType.Key(),
|
|
||||||
elemType: mapType.Elem(),
|
|
||||||
keyDecoder: keyDecoder,
|
|
||||||
elemDecoder: elemDecoder,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
mapType := typ.(*reflect2.UnsafeMapType)
|
|
||||||
if ctx.sortMapKeys {
|
|
||||||
return &sortKeysMapEncoder{
|
|
||||||
mapType: mapType,
|
|
||||||
keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()),
|
|
||||||
elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &mapEncoder{
|
|
||||||
mapType: mapType,
|
|
||||||
keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()),
|
|
||||||
elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
decoder := ctx.decoderExtension.CreateMapKeyDecoder(typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
decoder := extension.CreateMapKeyDecoder(typ)
|
|
||||||
if decoder != nil {
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ptrType := reflect2.PtrTo(typ)
|
|
||||||
if ptrType.Implements(unmarshalerType) {
|
|
||||||
return &referenceDecoder{
|
|
||||||
&unmarshalerDecoder{
|
|
||||||
valType: ptrType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if typ.Implements(unmarshalerType) {
|
|
||||||
return &unmarshalerDecoder{
|
|
||||||
valType: typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ptrType.Implements(textUnmarshalerType) {
|
|
||||||
return &referenceDecoder{
|
|
||||||
&textUnmarshalerDecoder{
|
|
||||||
valType: ptrType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if typ.Implements(textUnmarshalerType) {
|
|
||||||
return &textUnmarshalerDecoder{
|
|
||||||
valType: typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String))
|
|
||||||
case reflect.Bool,
|
|
||||||
reflect.Uint8, reflect.Int8,
|
|
||||||
reflect.Uint16, reflect.Int16,
|
|
||||||
reflect.Uint32, reflect.Int32,
|
|
||||||
reflect.Uint64, reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Int,
|
|
||||||
reflect.Float32, reflect.Float64,
|
|
||||||
reflect.Uintptr:
|
|
||||||
typ = reflect2.DefaultTypeOfKind(typ.Kind())
|
|
||||||
return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
|
|
||||||
default:
|
|
||||||
return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
encoder := ctx.encoderExtension.CreateMapKeyEncoder(typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
for _, extension := range ctx.extraExtensions {
|
|
||||||
encoder := extension.CreateMapKeyEncoder(typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ == textMarshalerType {
|
|
||||||
return &directTextMarshalerEncoder{
|
|
||||||
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if typ.Implements(textMarshalerType) {
|
|
||||||
return &textMarshalerEncoder{
|
|
||||||
valType: typ,
|
|
||||||
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String))
|
|
||||||
case reflect.Bool,
|
|
||||||
reflect.Uint8, reflect.Int8,
|
|
||||||
reflect.Uint16, reflect.Int16,
|
|
||||||
reflect.Uint32, reflect.Int32,
|
|
||||||
reflect.Uint64, reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Int,
|
|
||||||
reflect.Float32, reflect.Float64,
|
|
||||||
reflect.Uintptr:
|
|
||||||
typ = reflect2.DefaultTypeOfKind(typ.Kind())
|
|
||||||
return &numericMapKeyEncoder{encoderOfType(ctx, typ)}
|
|
||||||
default:
|
|
||||||
if typ.Kind() == reflect.Interface {
|
|
||||||
return &dynamicMapKeyEncoder{ctx, typ}
|
|
||||||
}
|
|
||||||
return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapDecoder struct {
|
|
||||||
mapType *reflect2.UnsafeMapType
|
|
||||||
keyType reflect2.Type
|
|
||||||
elemType reflect2.Type
|
|
||||||
keyDecoder ValDecoder
|
|
||||||
elemDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
mapType := decoder.mapType
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
*(*unsafe.Pointer)(ptr) = nil
|
|
||||||
mapType.UnsafeSet(ptr, mapType.UnsafeNew())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mapType.UnsafeIsNil(ptr) {
|
|
||||||
mapType.UnsafeSet(ptr, mapType.UnsafeMakeMap(0))
|
|
||||||
}
|
|
||||||
if c != '{' {
|
|
||||||
iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == '}' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
key := decoder.keyType.UnsafeNew()
|
|
||||||
decoder.keyDecoder.Decode(key, iter)
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
elem := decoder.elemType.UnsafeNew()
|
|
||||||
decoder.elemDecoder.Decode(elem, iter)
|
|
||||||
decoder.mapType.UnsafeSetIndex(ptr, key, elem)
|
|
||||||
for c = iter.nextToken(); c == ','; c = iter.nextToken() {
|
|
||||||
key := decoder.keyType.UnsafeNew()
|
|
||||||
decoder.keyDecoder.Decode(key, iter)
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != ':' {
|
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
elem := decoder.elemType.UnsafeNew()
|
|
||||||
decoder.elemDecoder.Decode(elem, iter)
|
|
||||||
decoder.mapType.UnsafeSetIndex(ptr, key, elem)
|
|
||||||
}
|
|
||||||
if c != '}' {
|
|
||||||
iter.ReportError("ReadMapCB", `expect }, but found `+string([]byte{c}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type numericMapKeyDecoder struct {
|
|
||||||
decoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *numericMapKeyDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c != '"' {
|
|
||||||
iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decoder.decoder.Decode(ptr, iter)
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c != '"' {
|
|
||||||
iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type numericMapKeyEncoder struct {
|
|
||||||
encoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *numericMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.writeByte('"')
|
|
||||||
encoder.encoder.Encode(ptr, stream)
|
|
||||||
stream.writeByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *numericMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type dynamicMapKeyEncoder struct {
|
|
||||||
ctx *ctx
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dynamicMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
obj := encoder.valType.UnsafeIndirect(ptr)
|
|
||||||
encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).Encode(reflect2.PtrOf(obj), stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
obj := encoder.valType.UnsafeIndirect(ptr)
|
|
||||||
return encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).IsEmpty(reflect2.PtrOf(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapEncoder struct {
|
|
||||||
mapType *reflect2.UnsafeMapType
|
|
||||||
keyEncoder ValEncoder
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *(*unsafe.Pointer)(ptr) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.WriteObjectStart()
|
|
||||||
iter := encoder.mapType.UnsafeIterate(ptr)
|
|
||||||
for i := 0; iter.HasNext(); i++ {
|
|
||||||
if i != 0 {
|
|
||||||
stream.WriteMore()
|
|
||||||
}
|
|
||||||
key, elem := iter.UnsafeNext()
|
|
||||||
encoder.keyEncoder.Encode(key, stream)
|
|
||||||
if stream.indention > 0 {
|
|
||||||
stream.writeTwoBytes(byte(':'), byte(' '))
|
|
||||||
} else {
|
|
||||||
stream.writeByte(':')
|
|
||||||
}
|
|
||||||
encoder.elemEncoder.Encode(elem, stream)
|
|
||||||
}
|
|
||||||
stream.WriteObjectEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
iter := encoder.mapType.UnsafeIterate(ptr)
|
|
||||||
return !iter.HasNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortKeysMapEncoder struct {
|
|
||||||
mapType *reflect2.UnsafeMapType
|
|
||||||
keyEncoder ValEncoder
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *(*unsafe.Pointer)(ptr) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.WriteObjectStart()
|
|
||||||
mapIter := encoder.mapType.UnsafeIterate(ptr)
|
|
||||||
subStream := stream.cfg.BorrowStream(nil)
|
|
||||||
subStream.Attachment = stream.Attachment
|
|
||||||
subIter := stream.cfg.BorrowIterator(nil)
|
|
||||||
keyValues := encodedKeyValues{}
|
|
||||||
for mapIter.HasNext() {
|
|
||||||
key, elem := mapIter.UnsafeNext()
|
|
||||||
subStreamIndex := subStream.Buffered()
|
|
||||||
encoder.keyEncoder.Encode(key, subStream)
|
|
||||||
if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil {
|
|
||||||
stream.Error = subStream.Error
|
|
||||||
}
|
|
||||||
encodedKey := subStream.Buffer()[subStreamIndex:]
|
|
||||||
subIter.ResetBytes(encodedKey)
|
|
||||||
decodedKey := subIter.ReadString()
|
|
||||||
if stream.indention > 0 {
|
|
||||||
subStream.writeTwoBytes(byte(':'), byte(' '))
|
|
||||||
} else {
|
|
||||||
subStream.writeByte(':')
|
|
||||||
}
|
|
||||||
encoder.elemEncoder.Encode(elem, subStream)
|
|
||||||
keyValues = append(keyValues, encodedKV{
|
|
||||||
key: decodedKey,
|
|
||||||
keyValue: subStream.Buffer()[subStreamIndex:],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sort.Sort(keyValues)
|
|
||||||
for i, keyValue := range keyValues {
|
|
||||||
if i != 0 {
|
|
||||||
stream.WriteMore()
|
|
||||||
}
|
|
||||||
stream.Write(keyValue.keyValue)
|
|
||||||
}
|
|
||||||
if subStream.Error != nil && stream.Error == nil {
|
|
||||||
stream.Error = subStream.Error
|
|
||||||
}
|
|
||||||
stream.WriteObjectEnd()
|
|
||||||
stream.cfg.ReturnStream(subStream)
|
|
||||||
stream.cfg.ReturnIterator(subIter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *sortKeysMapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
iter := encoder.mapType.UnsafeIterate(ptr)
|
|
||||||
return !iter.HasNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
type encodedKeyValues []encodedKV
|
|
||||||
|
|
||||||
type encodedKV struct {
|
|
||||||
key string
|
|
||||||
keyValue []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sv encodedKeyValues) Len() int { return len(sv) }
|
|
||||||
func (sv encodedKeyValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
|
||||||
func (sv encodedKeyValues) Less(i, j int) bool { return sv[i].key < sv[j].key }
|
|
225
vendor/github.com/json-iterator/go/reflect_marshaler.go
generated
vendored
225
vendor/github.com/json-iterator/go/reflect_marshaler.go
generated
vendored
|
@ -1,225 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"encoding/json"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem()
|
|
||||||
var unmarshalerType = reflect2.TypeOfPtr((*json.Unmarshaler)(nil)).Elem()
|
|
||||||
var textMarshalerType = reflect2.TypeOfPtr((*encoding.TextMarshaler)(nil)).Elem()
|
|
||||||
var textUnmarshalerType = reflect2.TypeOfPtr((*encoding.TextUnmarshaler)(nil)).Elem()
|
|
||||||
|
|
||||||
func createDecoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
ptrType := reflect2.PtrTo(typ)
|
|
||||||
if ptrType.Implements(unmarshalerType) {
|
|
||||||
return &referenceDecoder{
|
|
||||||
&unmarshalerDecoder{ptrType},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ptrType.Implements(textUnmarshalerType) {
|
|
||||||
return &referenceDecoder{
|
|
||||||
&textUnmarshalerDecoder{ptrType},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEncoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
if typ == marshalerType {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, typ)
|
|
||||||
var encoder ValEncoder = &directMarshalerEncoder{
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
if typ.Implements(marshalerType) {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, typ)
|
|
||||||
var encoder ValEncoder = &marshalerEncoder{
|
|
||||||
valType: typ,
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
ptrType := reflect2.PtrTo(typ)
|
|
||||||
if ctx.prefix != "" && ptrType.Implements(marshalerType) {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
|
|
||||||
var encoder ValEncoder = &marshalerEncoder{
|
|
||||||
valType: ptrType,
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
}
|
|
||||||
return &referenceEncoder{encoder}
|
|
||||||
}
|
|
||||||
if typ == textMarshalerType {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, typ)
|
|
||||||
var encoder ValEncoder = &directTextMarshalerEncoder{
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
if typ.Implements(textMarshalerType) {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, typ)
|
|
||||||
var encoder ValEncoder = &textMarshalerEncoder{
|
|
||||||
valType: typ,
|
|
||||||
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
// if prefix is empty, the type is the root type
|
|
||||||
if ctx.prefix != "" && ptrType.Implements(textMarshalerType) {
|
|
||||||
checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
|
|
||||||
var encoder ValEncoder = &textMarshalerEncoder{
|
|
||||||
valType: ptrType,
|
|
||||||
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
|
|
||||||
checkIsEmpty: checkIsEmpty,
|
|
||||||
}
|
|
||||||
return &referenceEncoder{encoder}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type marshalerEncoder struct {
|
|
||||||
checkIsEmpty checkIsEmpty
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
obj := encoder.valType.UnsafeIndirect(ptr)
|
|
||||||
if encoder.valType.IsNullable() && reflect2.IsNil(obj) {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
marshaler := obj.(json.Marshaler)
|
|
||||||
bytes, err := marshaler.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
} else {
|
|
||||||
// html escape was already done by jsoniter
|
|
||||||
// but the extra '\n' should be trimed
|
|
||||||
l := len(bytes)
|
|
||||||
if l > 0 && bytes[l-1] == '\n' {
|
|
||||||
bytes = bytes[:l-1]
|
|
||||||
}
|
|
||||||
stream.Write(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type directMarshalerEncoder struct {
|
|
||||||
checkIsEmpty checkIsEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *directMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
marshaler := *(*json.Marshaler)(ptr)
|
|
||||||
if marshaler == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bytes, err := marshaler.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
} else {
|
|
||||||
stream.Write(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *directMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type textMarshalerEncoder struct {
|
|
||||||
valType reflect2.Type
|
|
||||||
stringEncoder ValEncoder
|
|
||||||
checkIsEmpty checkIsEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
obj := encoder.valType.UnsafeIndirect(ptr)
|
|
||||||
if encoder.valType.IsNullable() && reflect2.IsNil(obj) {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
marshaler := (obj).(encoding.TextMarshaler)
|
|
||||||
bytes, err := marshaler.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
} else {
|
|
||||||
str := string(bytes)
|
|
||||||
encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type directTextMarshalerEncoder struct {
|
|
||||||
stringEncoder ValEncoder
|
|
||||||
checkIsEmpty checkIsEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *directTextMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
marshaler := *(*encoding.TextMarshaler)(ptr)
|
|
||||||
if marshaler == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bytes, err := marshaler.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
} else {
|
|
||||||
str := string(bytes)
|
|
||||||
encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *directTextMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type unmarshalerDecoder struct {
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
valType := decoder.valType
|
|
||||||
obj := valType.UnsafeIndirect(ptr)
|
|
||||||
unmarshaler := obj.(json.Unmarshaler)
|
|
||||||
iter.nextToken()
|
|
||||||
iter.unreadByte() // skip spaces
|
|
||||||
bytes := iter.SkipAndReturnBytes()
|
|
||||||
err := unmarshaler.UnmarshalJSON(bytes)
|
|
||||||
if err != nil {
|
|
||||||
iter.ReportError("unmarshalerDecoder", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type textUnmarshalerDecoder struct {
|
|
||||||
valType reflect2.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
valType := decoder.valType
|
|
||||||
obj := valType.UnsafeIndirect(ptr)
|
|
||||||
if reflect2.IsNil(obj) {
|
|
||||||
ptrType := valType.(*reflect2.UnsafePtrType)
|
|
||||||
elemType := ptrType.Elem()
|
|
||||||
elem := elemType.UnsafeNew()
|
|
||||||
ptrType.UnsafeSet(ptr, unsafe.Pointer(&elem))
|
|
||||||
obj = valType.UnsafeIndirect(ptr)
|
|
||||||
}
|
|
||||||
unmarshaler := (obj).(encoding.TextUnmarshaler)
|
|
||||||
str := iter.ReadString()
|
|
||||||
err := unmarshaler.UnmarshalText([]byte(str))
|
|
||||||
if err != nil {
|
|
||||||
iter.ReportError("textUnmarshalerDecoder", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
453
vendor/github.com/json-iterator/go/reflect_native.go
generated
vendored
453
vendor/github.com/json-iterator/go/reflect_native.go
generated
vendored
|
@ -1,453 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ptrSize = 32 << uintptr(^uintptr(0)>>63)
|
|
||||||
|
|
||||||
func createEncoderOfNative(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 {
|
|
||||||
sliceDecoder := decoderOfSlice(ctx, typ)
|
|
||||||
return &base64Codec{sliceDecoder: sliceDecoder}
|
|
||||||
}
|
|
||||||
typeName := typ.String()
|
|
||||||
kind := typ.Kind()
|
|
||||||
switch kind {
|
|
||||||
case reflect.String:
|
|
||||||
if typeName != "string" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &stringCodec{}
|
|
||||||
case reflect.Int:
|
|
||||||
if typeName != "int" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return &int32Codec{}
|
|
||||||
}
|
|
||||||
return &int64Codec{}
|
|
||||||
case reflect.Int8:
|
|
||||||
if typeName != "int8" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int8Codec{}
|
|
||||||
case reflect.Int16:
|
|
||||||
if typeName != "int16" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int16Codec{}
|
|
||||||
case reflect.Int32:
|
|
||||||
if typeName != "int32" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int32Codec{}
|
|
||||||
case reflect.Int64:
|
|
||||||
if typeName != "int64" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int64Codec{}
|
|
||||||
case reflect.Uint:
|
|
||||||
if typeName != "uint" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return &uint32Codec{}
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Uint8:
|
|
||||||
if typeName != "uint8" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint8Codec{}
|
|
||||||
case reflect.Uint16:
|
|
||||||
if typeName != "uint16" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint16Codec{}
|
|
||||||
case reflect.Uint32:
|
|
||||||
if typeName != "uint32" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint32Codec{}
|
|
||||||
case reflect.Uintptr:
|
|
||||||
if typeName != "uintptr" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if ptrSize == 32 {
|
|
||||||
return &uint32Codec{}
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Uint64:
|
|
||||||
if typeName != "uint64" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Float32:
|
|
||||||
if typeName != "float32" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &float32Codec{}
|
|
||||||
case reflect.Float64:
|
|
||||||
if typeName != "float64" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &float64Codec{}
|
|
||||||
case reflect.Bool:
|
|
||||||
if typeName != "bool" {
|
|
||||||
return encoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &boolCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDecoderOfNative(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 {
|
|
||||||
sliceDecoder := decoderOfSlice(ctx, typ)
|
|
||||||
return &base64Codec{sliceDecoder: sliceDecoder}
|
|
||||||
}
|
|
||||||
typeName := typ.String()
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
if typeName != "string" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &stringCodec{}
|
|
||||||
case reflect.Int:
|
|
||||||
if typeName != "int" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return &int32Codec{}
|
|
||||||
}
|
|
||||||
return &int64Codec{}
|
|
||||||
case reflect.Int8:
|
|
||||||
if typeName != "int8" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int8Codec{}
|
|
||||||
case reflect.Int16:
|
|
||||||
if typeName != "int16" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int16Codec{}
|
|
||||||
case reflect.Int32:
|
|
||||||
if typeName != "int32" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int32Codec{}
|
|
||||||
case reflect.Int64:
|
|
||||||
if typeName != "int64" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &int64Codec{}
|
|
||||||
case reflect.Uint:
|
|
||||||
if typeName != "uint" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if strconv.IntSize == 32 {
|
|
||||||
return &uint32Codec{}
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Uint8:
|
|
||||||
if typeName != "uint8" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint8Codec{}
|
|
||||||
case reflect.Uint16:
|
|
||||||
if typeName != "uint16" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint16Codec{}
|
|
||||||
case reflect.Uint32:
|
|
||||||
if typeName != "uint32" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint32Codec{}
|
|
||||||
case reflect.Uintptr:
|
|
||||||
if typeName != "uintptr" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem())
|
|
||||||
}
|
|
||||||
if ptrSize == 32 {
|
|
||||||
return &uint32Codec{}
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Uint64:
|
|
||||||
if typeName != "uint64" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &uint64Codec{}
|
|
||||||
case reflect.Float32:
|
|
||||||
if typeName != "float32" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &float32Codec{}
|
|
||||||
case reflect.Float64:
|
|
||||||
if typeName != "float64" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &float64Codec{}
|
|
||||||
case reflect.Bool:
|
|
||||||
if typeName != "bool" {
|
|
||||||
return decoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem())
|
|
||||||
}
|
|
||||||
return &boolCodec{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type stringCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
*((*string)(ptr)) = iter.ReadString()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
str := *((*string)(ptr))
|
|
||||||
stream.WriteString(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *stringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*string)(ptr)) == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type int8Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*int8)(ptr)) = iter.ReadInt8()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteInt8(*((*int8)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int8Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*int8)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type int16Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*int16)(ptr)) = iter.ReadInt16()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteInt16(*((*int16)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int16Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*int16)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type int32Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*int32)(ptr)) = iter.ReadInt32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteInt32(*((*int32)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*int32)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type int64Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*int64)(ptr)) = iter.ReadInt64()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteInt64(*((*int64)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *int64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*int64)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type uint8Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*uint8)(ptr)) = iter.ReadUint8()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteUint8(*((*uint8)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint8Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*uint8)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type uint16Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*uint16)(ptr)) = iter.ReadUint16()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteUint16(*((*uint16)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint16Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*uint16)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type uint32Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*uint32)(ptr)) = iter.ReadUint32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteUint32(*((*uint32)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*uint32)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type uint64Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*uint64)(ptr)) = iter.ReadUint64()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteUint64(*((*uint64)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *uint64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*uint64)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type float32Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteFloat32(*((*float32)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*float32)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type float64Codec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteFloat64(*((*float64)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *float64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*float64)(ptr)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type boolCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if !iter.ReadNil() {
|
|
||||||
*((*bool)(ptr)) = iter.ReadBool()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteBool(*((*bool)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return !(*((*bool)(ptr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type base64Codec struct {
|
|
||||||
sliceType *reflect2.UnsafeSliceType
|
|
||||||
sliceDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.ReadNil() {
|
|
||||||
codec.sliceType.UnsafeSetNil(ptr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch iter.WhatIsNext() {
|
|
||||||
case StringValue:
|
|
||||||
src := iter.ReadString()
|
|
||||||
dst, err := base64.StdEncoding.DecodeString(src)
|
|
||||||
if err != nil {
|
|
||||||
iter.ReportError("decode base64", err.Error())
|
|
||||||
} else {
|
|
||||||
codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst))
|
|
||||||
}
|
|
||||||
case ArrayValue:
|
|
||||||
codec.sliceDecoder.Decode(ptr, iter)
|
|
||||||
default:
|
|
||||||
iter.ReportError("base64Codec", "invalid input")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if codec.sliceType.UnsafeIsNil(ptr) {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
src := *((*[]byte)(ptr))
|
|
||||||
encoding := base64.StdEncoding
|
|
||||||
stream.writeByte('"')
|
|
||||||
if len(src) != 0 {
|
|
||||||
size := encoding.EncodedLen(len(src))
|
|
||||||
buf := make([]byte, size)
|
|
||||||
encoding.Encode(buf, src)
|
|
||||||
stream.buf = append(stream.buf, buf...)
|
|
||||||
}
|
|
||||||
stream.writeByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec *base64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return len(*((*[]byte)(ptr))) == 0
|
|
||||||
}
|
|
129
vendor/github.com/json-iterator/go/reflect_optional.go
generated
vendored
129
vendor/github.com/json-iterator/go/reflect_optional.go
generated
vendored
|
@ -1,129 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
ptrType := typ.(*reflect2.UnsafePtrType)
|
|
||||||
elemType := ptrType.Elem()
|
|
||||||
decoder := decoderOfType(ctx, elemType)
|
|
||||||
return &OptionalDecoder{elemType, decoder}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfOptional(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
ptrType := typ.(*reflect2.UnsafePtrType)
|
|
||||||
elemType := ptrType.Elem()
|
|
||||||
elemEncoder := encoderOfType(ctx, elemType)
|
|
||||||
encoder := &OptionalEncoder{elemEncoder}
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionalDecoder struct {
|
|
||||||
ValueType reflect2.Type
|
|
||||||
ValueDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if iter.ReadNil() {
|
|
||||||
*((*unsafe.Pointer)(ptr)) = nil
|
|
||||||
} else {
|
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
|
||||||
//pointer to null, we have to allocate memory to hold the value
|
|
||||||
newPtr := decoder.ValueType.UnsafeNew()
|
|
||||||
decoder.ValueDecoder.Decode(newPtr, iter)
|
|
||||||
*((*unsafe.Pointer)(ptr)) = newPtr
|
|
||||||
} else {
|
|
||||||
//reuse existing instance
|
|
||||||
decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type dereferenceDecoder struct {
|
|
||||||
// only to deference a pointer
|
|
||||||
valueType reflect2.Type
|
|
||||||
valueDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
|
||||||
//pointer to null, we have to allocate memory to hold the value
|
|
||||||
newPtr := decoder.valueType.UnsafeNew()
|
|
||||||
decoder.valueDecoder.Decode(newPtr, iter)
|
|
||||||
*((*unsafe.Pointer)(ptr)) = newPtr
|
|
||||||
} else {
|
|
||||||
//reuse existing instance
|
|
||||||
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionalEncoder struct {
|
|
||||||
ValueEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *OptionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
} else {
|
|
||||||
encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *OptionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return *((*unsafe.Pointer)(ptr)) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type dereferenceEncoder struct {
|
|
||||||
ValueEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
|
||||||
stream.WriteNil()
|
|
||||||
} else {
|
|
||||||
encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
dePtr := *((*unsafe.Pointer)(ptr))
|
|
||||||
if dePtr == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return encoder.ValueEncoder.IsEmpty(dePtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
|
||||||
deReferenced := *((*unsafe.Pointer)(ptr))
|
|
||||||
if deReferenced == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil)
|
|
||||||
if !converted {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fieldPtr := unsafe.Pointer(deReferenced)
|
|
||||||
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type referenceEncoder struct {
|
|
||||||
encoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *referenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
encoder.encoder.Encode(unsafe.Pointer(&ptr), stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *referenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
type referenceDecoder struct {
|
|
||||||
decoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
decoder.decoder.Decode(unsafe.Pointer(&ptr), iter)
|
|
||||||
}
|
|
99
vendor/github.com/json-iterator/go/reflect_slice.go
generated
vendored
99
vendor/github.com/json-iterator/go/reflect_slice.go
generated
vendored
|
@ -1,99 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
||||||
sliceType := typ.(*reflect2.UnsafeSliceType)
|
|
||||||
decoder := decoderOfType(ctx.append("[sliceElem]"), sliceType.Elem())
|
|
||||||
return &sliceDecoder{sliceType, decoder}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
sliceType := typ.(*reflect2.UnsafeSliceType)
|
|
||||||
encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem())
|
|
||||||
return &sliceEncoder{sliceType, encoder}
|
|
||||||
}
|
|
||||||
|
|
||||||
type sliceEncoder struct {
|
|
||||||
sliceType *reflect2.UnsafeSliceType
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
if encoder.sliceType.UnsafeIsNil(ptr) {
|
|
||||||
stream.WriteNil()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length := encoder.sliceType.UnsafeLengthOf(ptr)
|
|
||||||
if length == 0 {
|
|
||||||
stream.WriteEmptyArray()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.WriteArrayStart()
|
|
||||||
encoder.elemEncoder.Encode(encoder.sliceType.UnsafeGetIndex(ptr, 0), stream)
|
|
||||||
for i := 1; i < length; i++ {
|
|
||||||
stream.WriteMore()
|
|
||||||
elemPtr := encoder.sliceType.UnsafeGetIndex(ptr, i)
|
|
||||||
encoder.elemEncoder.Encode(elemPtr, stream)
|
|
||||||
}
|
|
||||||
stream.WriteArrayEnd()
|
|
||||||
if stream.Error != nil && stream.Error != io.EOF {
|
|
||||||
stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.sliceType.UnsafeLengthOf(ptr) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type sliceDecoder struct {
|
|
||||||
sliceType *reflect2.UnsafeSliceType
|
|
||||||
elemDecoder ValDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
decoder.doDecode(ptr, iter)
|
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
|
||||||
iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
sliceType := decoder.sliceType
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
|
||||||
sliceType.UnsafeSetNil(ptr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c != '[' {
|
|
||||||
iter.ReportError("decode slice", "expect [ or n, but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = iter.nextToken()
|
|
||||||
if c == ']' {
|
|
||||||
sliceType.UnsafeSet(ptr, sliceType.UnsafeMakeSlice(0, 0))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
sliceType.UnsafeGrow(ptr, 1)
|
|
||||||
elemPtr := sliceType.UnsafeGetIndex(ptr, 0)
|
|
||||||
decoder.elemDecoder.Decode(elemPtr, iter)
|
|
||||||
length := 1
|
|
||||||
for c = iter.nextToken(); c == ','; c = iter.nextToken() {
|
|
||||||
idx := length
|
|
||||||
length += 1
|
|
||||||
sliceType.UnsafeGrow(ptr, length)
|
|
||||||
elemPtr = sliceType.UnsafeGetIndex(ptr, idx)
|
|
||||||
decoder.elemDecoder.Decode(elemPtr, iter)
|
|
||||||
}
|
|
||||||
if c != ']' {
|
|
||||||
iter.ReportError("decode slice", "expect ], but found "+string([]byte{c}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
1097
vendor/github.com/json-iterator/go/reflect_struct_decoder.go
generated
vendored
1097
vendor/github.com/json-iterator/go/reflect_struct_decoder.go
generated
vendored
File diff suppressed because it is too large
Load diff
211
vendor/github.com/json-iterator/go/reflect_struct_encoder.go
generated
vendored
211
vendor/github.com/json-iterator/go/reflect_struct_encoder.go
generated
vendored
|
@ -1,211 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
||||||
type bindingTo struct {
|
|
||||||
binding *Binding
|
|
||||||
toName string
|
|
||||||
ignored bool
|
|
||||||
}
|
|
||||||
orderedBindings := []*bindingTo{}
|
|
||||||
structDescriptor := describeStruct(ctx, typ)
|
|
||||||
for _, binding := range structDescriptor.Fields {
|
|
||||||
for _, toName := range binding.ToNames {
|
|
||||||
new := &bindingTo{
|
|
||||||
binding: binding,
|
|
||||||
toName: toName,
|
|
||||||
}
|
|
||||||
for _, old := range orderedBindings {
|
|
||||||
if old.toName != toName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding)
|
|
||||||
}
|
|
||||||
orderedBindings = append(orderedBindings, new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(orderedBindings) == 0 {
|
|
||||||
return &emptyStructEncoder{}
|
|
||||||
}
|
|
||||||
finalOrderedFields := []structFieldTo{}
|
|
||||||
for _, bindingTo := range orderedBindings {
|
|
||||||
if !bindingTo.ignored {
|
|
||||||
finalOrderedFields = append(finalOrderedFields, structFieldTo{
|
|
||||||
encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
|
|
||||||
toName: bindingTo.toName,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &structEncoder{typ, finalOrderedFields}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty {
|
|
||||||
encoder := createEncoderOfNative(ctx, typ)
|
|
||||||
if encoder != nil {
|
|
||||||
return encoder
|
|
||||||
}
|
|
||||||
kind := typ.Kind()
|
|
||||||
switch kind {
|
|
||||||
case reflect.Interface:
|
|
||||||
return &dynamicEncoder{typ}
|
|
||||||
case reflect.Struct:
|
|
||||||
return &structEncoder{typ: typ}
|
|
||||||
case reflect.Array:
|
|
||||||
return &arrayEncoder{}
|
|
||||||
case reflect.Slice:
|
|
||||||
return &sliceEncoder{}
|
|
||||||
case reflect.Map:
|
|
||||||
return encoderOfMap(ctx, typ)
|
|
||||||
case reflect.Ptr:
|
|
||||||
return &OptionalEncoder{}
|
|
||||||
default:
|
|
||||||
return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
|
|
||||||
newTagged := new.Field.Tag().Get(cfg.getTagKey()) != ""
|
|
||||||
oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != ""
|
|
||||||
if newTagged {
|
|
||||||
if oldTagged {
|
|
||||||
if len(old.levels) > len(new.levels) {
|
|
||||||
return true, false
|
|
||||||
} else if len(new.levels) > len(old.levels) {
|
|
||||||
return false, true
|
|
||||||
} else {
|
|
||||||
return true, true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true, false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if oldTagged {
|
|
||||||
return true, false
|
|
||||||
}
|
|
||||||
if len(old.levels) > len(new.levels) {
|
|
||||||
return true, false
|
|
||||||
} else if len(new.levels) > len(old.levels) {
|
|
||||||
return false, true
|
|
||||||
} else {
|
|
||||||
return true, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type structFieldEncoder struct {
|
|
||||||
field reflect2.StructField
|
|
||||||
fieldEncoder ValEncoder
|
|
||||||
omitempty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
||||||
encoder.fieldEncoder.Encode(fieldPtr, stream)
|
|
||||||
if stream.Error != nil && stream.Error != io.EOF {
|
|
||||||
stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
||||||
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
|
||||||
isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
|
|
||||||
if !converted {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
||||||
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type IsEmbeddedPtrNil interface {
|
|
||||||
IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type structEncoder struct {
|
|
||||||
typ reflect2.Type
|
|
||||||
fields []structFieldTo
|
|
||||||
}
|
|
||||||
|
|
||||||
type structFieldTo struct {
|
|
||||||
encoder *structFieldEncoder
|
|
||||||
toName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteObjectStart()
|
|
||||||
isNotFirst := false
|
|
||||||
for _, field := range encoder.fields {
|
|
||||||
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if field.encoder.IsEmbeddedPtrNil(ptr) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if isNotFirst {
|
|
||||||
stream.WriteMore()
|
|
||||||
}
|
|
||||||
stream.WriteObjectField(field.toName)
|
|
||||||
field.encoder.Encode(ptr, stream)
|
|
||||||
isNotFirst = true
|
|
||||||
}
|
|
||||||
stream.WriteObjectEnd()
|
|
||||||
if stream.Error != nil && stream.Error != io.EOF {
|
|
||||||
stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyStructEncoder struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.WriteEmptyObject()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type stringModeNumberEncoder struct {
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
stream.writeByte('"')
|
|
||||||
encoder.elemEncoder.Encode(ptr, stream)
|
|
||||||
stream.writeByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.elemEncoder.IsEmpty(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type stringModeStringEncoder struct {
|
|
||||||
elemEncoder ValEncoder
|
|
||||||
cfg *frozenConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
||||||
tempStream := encoder.cfg.BorrowStream(nil)
|
|
||||||
tempStream.Attachment = stream.Attachment
|
|
||||||
defer encoder.cfg.ReturnStream(tempStream)
|
|
||||||
encoder.elemEncoder.Encode(ptr, tempStream)
|
|
||||||
stream.WriteString(string(tempStream.Buffer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
||||||
return encoder.elemEncoder.IsEmpty(ptr)
|
|
||||||
}
|
|
210
vendor/github.com/json-iterator/go/stream.go
generated
vendored
210
vendor/github.com/json-iterator/go/stream.go
generated
vendored
|
@ -1,210 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// stream is a io.Writer like object, with JSON specific write functions.
|
|
||||||
// Error is not returned as return value, but stored as Error member on this stream instance.
|
|
||||||
type Stream struct {
|
|
||||||
cfg *frozenConfig
|
|
||||||
out io.Writer
|
|
||||||
buf []byte
|
|
||||||
Error error
|
|
||||||
indention int
|
|
||||||
Attachment interface{} // open for customized encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStream create new stream instance.
|
|
||||||
// cfg can be jsoniter.ConfigDefault.
|
|
||||||
// out can be nil if write to internal buffer.
|
|
||||||
// bufSize is the initial size for the internal buffer in bytes.
|
|
||||||
func NewStream(cfg API, out io.Writer, bufSize int) *Stream {
|
|
||||||
return &Stream{
|
|
||||||
cfg: cfg.(*frozenConfig),
|
|
||||||
out: out,
|
|
||||||
buf: make([]byte, 0, bufSize),
|
|
||||||
Error: nil,
|
|
||||||
indention: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pool returns a pool can provide more stream with same configuration
|
|
||||||
func (stream *Stream) Pool() StreamPool {
|
|
||||||
return stream.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset reuse this stream instance by assign a new writer
|
|
||||||
func (stream *Stream) Reset(out io.Writer) {
|
|
||||||
stream.out = out
|
|
||||||
stream.buf = stream.buf[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Available returns how many bytes are unused in the buffer.
|
|
||||||
func (stream *Stream) Available() int {
|
|
||||||
return cap(stream.buf) - len(stream.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffered returns the number of bytes that have been written into the current buffer.
|
|
||||||
func (stream *Stream) Buffered() int {
|
|
||||||
return len(stream.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer if writer is nil, use this method to take the result
|
|
||||||
func (stream *Stream) Buffer() []byte {
|
|
||||||
return stream.buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBuffer allows to append to the internal buffer directly
|
|
||||||
func (stream *Stream) SetBuffer(buf []byte) {
|
|
||||||
stream.buf = buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes the contents of p into the buffer.
|
|
||||||
// It returns the number of bytes written.
|
|
||||||
// If nn < len(p), it also returns an error explaining
|
|
||||||
// why the write is short.
|
|
||||||
func (stream *Stream) Write(p []byte) (nn int, err error) {
|
|
||||||
stream.buf = append(stream.buf, p...)
|
|
||||||
if stream.out != nil {
|
|
||||||
nn, err = stream.out.Write(stream.buf)
|
|
||||||
stream.buf = stream.buf[nn:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteByte writes a single byte.
|
|
||||||
func (stream *Stream) writeByte(c byte) {
|
|
||||||
stream.buf = append(stream.buf, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) {
|
|
||||||
stream.buf = append(stream.buf, c1, c2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) {
|
|
||||||
stream.buf = append(stream.buf, c1, c2, c3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) {
|
|
||||||
stream.buf = append(stream.buf, c1, c2, c3, c4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) {
|
|
||||||
stream.buf = append(stream.buf, c1, c2, c3, c4, c5)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush writes any buffered data to the underlying io.Writer.
|
|
||||||
func (stream *Stream) Flush() error {
|
|
||||||
if stream.out == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if stream.Error != nil {
|
|
||||||
return stream.Error
|
|
||||||
}
|
|
||||||
_, err := stream.out.Write(stream.buf)
|
|
||||||
if err != nil {
|
|
||||||
if stream.Error == nil {
|
|
||||||
stream.Error = err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
stream.buf = stream.buf[:0]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteRaw write string out without quotes, just like []byte
|
|
||||||
func (stream *Stream) WriteRaw(s string) {
|
|
||||||
stream.buf = append(stream.buf, s...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteNil write null to stream
|
|
||||||
func (stream *Stream) WriteNil() {
|
|
||||||
stream.writeFourBytes('n', 'u', 'l', 'l')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteTrue write true to stream
|
|
||||||
func (stream *Stream) WriteTrue() {
|
|
||||||
stream.writeFourBytes('t', 'r', 'u', 'e')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFalse write false to stream
|
|
||||||
func (stream *Stream) WriteFalse() {
|
|
||||||
stream.writeFiveBytes('f', 'a', 'l', 's', 'e')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteBool write true or false into stream
|
|
||||||
func (stream *Stream) WriteBool(val bool) {
|
|
||||||
if val {
|
|
||||||
stream.WriteTrue()
|
|
||||||
} else {
|
|
||||||
stream.WriteFalse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteObjectStart write { with possible indention
|
|
||||||
func (stream *Stream) WriteObjectStart() {
|
|
||||||
stream.indention += stream.cfg.indentionStep
|
|
||||||
stream.writeByte('{')
|
|
||||||
stream.writeIndention(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteObjectField write "field": with possible indention
|
|
||||||
func (stream *Stream) WriteObjectField(field string) {
|
|
||||||
stream.WriteString(field)
|
|
||||||
if stream.indention > 0 {
|
|
||||||
stream.writeTwoBytes(':', ' ')
|
|
||||||
} else {
|
|
||||||
stream.writeByte(':')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteObjectEnd write } with possible indention
|
|
||||||
func (stream *Stream) WriteObjectEnd() {
|
|
||||||
stream.writeIndention(stream.cfg.indentionStep)
|
|
||||||
stream.indention -= stream.cfg.indentionStep
|
|
||||||
stream.writeByte('}')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteEmptyObject write {}
|
|
||||||
func (stream *Stream) WriteEmptyObject() {
|
|
||||||
stream.writeByte('{')
|
|
||||||
stream.writeByte('}')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMore write , with possible indention
|
|
||||||
func (stream *Stream) WriteMore() {
|
|
||||||
stream.writeByte(',')
|
|
||||||
stream.writeIndention(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArrayStart write [ with possible indention
|
|
||||||
func (stream *Stream) WriteArrayStart() {
|
|
||||||
stream.indention += stream.cfg.indentionStep
|
|
||||||
stream.writeByte('[')
|
|
||||||
stream.writeIndention(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteEmptyArray write []
|
|
||||||
func (stream *Stream) WriteEmptyArray() {
|
|
||||||
stream.writeTwoBytes('[', ']')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArrayEnd write ] with possible indention
|
|
||||||
func (stream *Stream) WriteArrayEnd() {
|
|
||||||
stream.writeIndention(stream.cfg.indentionStep)
|
|
||||||
stream.indention -= stream.cfg.indentionStep
|
|
||||||
stream.writeByte(']')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stream *Stream) writeIndention(delta int) {
|
|
||||||
if stream.indention == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.writeByte('\n')
|
|
||||||
toWrite := stream.indention - delta
|
|
||||||
for i := 0; i < toWrite; i++ {
|
|
||||||
stream.buf = append(stream.buf, ' ')
|
|
||||||
}
|
|
||||||
}
|
|
111
vendor/github.com/json-iterator/go/stream_float.go
generated
vendored
111
vendor/github.com/json-iterator/go/stream_float.go
generated
vendored
|
@ -1,111 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var pow10 []uint64
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFloat32 write float32 to stream
|
|
||||||
func (stream *Stream) WriteFloat32(val float32) {
|
|
||||||
if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) {
|
|
||||||
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
abs := math.Abs(float64(val))
|
|
||||||
fmt := byte('f')
|
|
||||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
|
||||||
if abs != 0 {
|
|
||||||
if float32(abs) < 1e-6 || float32(abs) >= 1e21 {
|
|
||||||
fmt = 'e'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster
|
|
||||||
func (stream *Stream) WriteFloat32Lossy(val float32) {
|
|
||||||
if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) {
|
|
||||||
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if val < 0 {
|
|
||||||
stream.writeByte('-')
|
|
||||||
val = -val
|
|
||||||
}
|
|
||||||
if val > 0x4ffffff {
|
|
||||||
stream.WriteFloat32(val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
precision := 6
|
|
||||||
exp := uint64(1000000) // 6
|
|
||||||
lval := uint64(float64(val)*float64(exp) + 0.5)
|
|
||||||
stream.WriteUint64(lval / exp)
|
|
||||||
fval := lval % exp
|
|
||||||
if fval == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.writeByte('.')
|
|
||||||
for p := precision - 1; p > 0 && fval < pow10[p]; p-- {
|
|
||||||
stream.writeByte('0')
|
|
||||||
}
|
|
||||||
stream.WriteUint64(fval)
|
|
||||||
for stream.buf[len(stream.buf)-1] == '0' {
|
|
||||||
stream.buf = stream.buf[:len(stream.buf)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFloat64 write float64 to stream
|
|
||||||
func (stream *Stream) WriteFloat64(val float64) {
|
|
||||||
if math.IsInf(val, 0) || math.IsNaN(val) {
|
|
||||||
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
abs := math.Abs(val)
|
|
||||||
fmt := byte('f')
|
|
||||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
|
||||||
if abs != 0 {
|
|
||||||
if abs < 1e-6 || abs >= 1e21 {
|
|
||||||
fmt = 'e'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster
|
|
||||||
func (stream *Stream) WriteFloat64Lossy(val float64) {
|
|
||||||
if math.IsInf(val, 0) || math.IsNaN(val) {
|
|
||||||
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if val < 0 {
|
|
||||||
stream.writeByte('-')
|
|
||||||
val = -val
|
|
||||||
}
|
|
||||||
if val > 0x4ffffff {
|
|
||||||
stream.WriteFloat64(val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
precision := 6
|
|
||||||
exp := uint64(1000000) // 6
|
|
||||||
lval := uint64(val*float64(exp) + 0.5)
|
|
||||||
stream.WriteUint64(lval / exp)
|
|
||||||
fval := lval % exp
|
|
||||||
if fval == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.writeByte('.')
|
|
||||||
for p := precision - 1; p > 0 && fval < pow10[p]; p-- {
|
|
||||||
stream.writeByte('0')
|
|
||||||
}
|
|
||||||
stream.WriteUint64(fval)
|
|
||||||
for stream.buf[len(stream.buf)-1] == '0' {
|
|
||||||
stream.buf = stream.buf[:len(stream.buf)-1]
|
|
||||||
}
|
|
||||||
}
|
|
190
vendor/github.com/json-iterator/go/stream_int.go
generated
vendored
190
vendor/github.com/json-iterator/go/stream_int.go
generated
vendored
|
@ -1,190 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
var digits []uint32
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
digits = make([]uint32, 1000)
|
|
||||||
for i := uint32(0); i < 1000; i++ {
|
|
||||||
digits[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i%10 + '0'
|
|
||||||
if i < 10 {
|
|
||||||
digits[i] += 2 << 24
|
|
||||||
} else if i < 100 {
|
|
||||||
digits[i] += 1 << 24
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFirstBuf(space []byte, v uint32) []byte {
|
|
||||||
start := v >> 24
|
|
||||||
if start == 0 {
|
|
||||||
space = append(space, byte(v>>16), byte(v>>8))
|
|
||||||
} else if start == 1 {
|
|
||||||
space = append(space, byte(v>>8))
|
|
||||||
}
|
|
||||||
space = append(space, byte(v))
|
|
||||||
return space
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeBuf(buf []byte, v uint32) []byte {
|
|
||||||
return append(buf, byte(v>>16), byte(v>>8), byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteUint8 write uint8 to stream
|
|
||||||
func (stream *Stream) WriteUint8(val uint8) {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[val])
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteInt8 write int8 to stream
|
|
||||||
func (stream *Stream) WriteInt8(nval int8) {
|
|
||||||
var val uint8
|
|
||||||
if nval < 0 {
|
|
||||||
val = uint8(-nval)
|
|
||||||
stream.buf = append(stream.buf, '-')
|
|
||||||
} else {
|
|
||||||
val = uint8(nval)
|
|
||||||
}
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[val])
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteUint16 write uint16 to stream
|
|
||||||
func (stream *Stream) WriteUint16(val uint16) {
|
|
||||||
q1 := val / 1000
|
|
||||||
if q1 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[val])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r1 := val - q1*1000
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q1])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteInt16 write int16 to stream
|
|
||||||
func (stream *Stream) WriteInt16(nval int16) {
|
|
||||||
var val uint16
|
|
||||||
if nval < 0 {
|
|
||||||
val = uint16(-nval)
|
|
||||||
stream.buf = append(stream.buf, '-')
|
|
||||||
} else {
|
|
||||||
val = uint16(nval)
|
|
||||||
}
|
|
||||||
stream.WriteUint16(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteUint32 write uint32 to stream
|
|
||||||
func (stream *Stream) WriteUint32(val uint32) {
|
|
||||||
q1 := val / 1000
|
|
||||||
if q1 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[val])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r1 := val - q1*1000
|
|
||||||
q2 := q1 / 1000
|
|
||||||
if q2 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q1])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r2 := q1 - q2*1000
|
|
||||||
q3 := q2 / 1000
|
|
||||||
if q3 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q2])
|
|
||||||
} else {
|
|
||||||
r3 := q2 - q3*1000
|
|
||||||
stream.buf = append(stream.buf, byte(q3+'0'))
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r3])
|
|
||||||
}
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteInt32 write int32 to stream
|
|
||||||
func (stream *Stream) WriteInt32(nval int32) {
|
|
||||||
var val uint32
|
|
||||||
if nval < 0 {
|
|
||||||
val = uint32(-nval)
|
|
||||||
stream.buf = append(stream.buf, '-')
|
|
||||||
} else {
|
|
||||||
val = uint32(nval)
|
|
||||||
}
|
|
||||||
stream.WriteUint32(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteUint64 write uint64 to stream
|
|
||||||
func (stream *Stream) WriteUint64(val uint64) {
|
|
||||||
q1 := val / 1000
|
|
||||||
if q1 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[val])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r1 := val - q1*1000
|
|
||||||
q2 := q1 / 1000
|
|
||||||
if q2 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q1])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r2 := q1 - q2*1000
|
|
||||||
q3 := q2 / 1000
|
|
||||||
if q3 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r3 := q2 - q3*1000
|
|
||||||
q4 := q3 / 1000
|
|
||||||
if q4 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q3])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r3])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r4 := q3 - q4*1000
|
|
||||||
q5 := q4 / 1000
|
|
||||||
if q5 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q4])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r4])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r3])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r5 := q4 - q5*1000
|
|
||||||
q6 := q5 / 1000
|
|
||||||
if q6 == 0 {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q5])
|
|
||||||
} else {
|
|
||||||
stream.buf = writeFirstBuf(stream.buf, digits[q6])
|
|
||||||
r6 := q5 - q6*1000
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r6])
|
|
||||||
}
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r5])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r4])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r3])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r2])
|
|
||||||
stream.buf = writeBuf(stream.buf, digits[r1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteInt64 write int64 to stream
|
|
||||||
func (stream *Stream) WriteInt64(nval int64) {
|
|
||||||
var val uint64
|
|
||||||
if nval < 0 {
|
|
||||||
val = uint64(-nval)
|
|
||||||
stream.buf = append(stream.buf, '-')
|
|
||||||
} else {
|
|
||||||
val = uint64(nval)
|
|
||||||
}
|
|
||||||
stream.WriteUint64(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteInt write int to stream
|
|
||||||
func (stream *Stream) WriteInt(val int) {
|
|
||||||
stream.WriteInt64(int64(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteUint write uint to stream
|
|
||||||
func (stream *Stream) WriteUint(val uint) {
|
|
||||||
stream.WriteUint64(uint64(val))
|
|
||||||
}
|
|
372
vendor/github.com/json-iterator/go/stream_str.go
generated
vendored
372
vendor/github.com/json-iterator/go/stream_str.go
generated
vendored
|
@ -1,372 +0,0 @@
|
||||||
package jsoniter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// htmlSafeSet holds the value true if the ASCII character with the given
|
|
||||||
// array position can be safely represented inside a JSON string, embedded
|
|
||||||
// inside of HTML <script> tags, without any additional escaping.
|
|
||||||
//
|
|
||||||
// All values are true except for the ASCII control characters (0-31), the
|
|
||||||
// double quote ("), the backslash character ("\"), HTML opening and closing
|
|
||||||
// tags ("<" and ">"), and the ampersand ("&").
|
|
||||||
var htmlSafeSet = [utf8.RuneSelf]bool{
|
|
||||||
' ': true,
|
|
||||||
'!': true,
|
|
||||||
'"': false,
|
|
||||||
'#': true,
|
|
||||||
'$': true,
|
|
||||||
'%': true,
|
|
||||||
'&': false,
|
|
||||||
'\'': true,
|
|
||||||
'(': true,
|
|
||||||
')': true,
|
|
||||||
'*': true,
|
|
||||||
'+': true,
|
|
||||||
',': true,
|
|
||||||
'-': true,
|
|
||||||
'.': true,
|
|
||||||
'/': true,
|
|
||||||
'0': true,
|
|
||||||
'1': true,
|
|
||||||
'2': true,
|
|
||||||
'3': true,
|
|
||||||
'4': true,
|
|
||||||
'5': true,
|
|
||||||
'6': true,
|
|
||||||
'7': true,
|
|
||||||
'8': true,
|
|
||||||
'9': true,
|
|
||||||
':': true,
|
|
||||||
';': true,
|
|
||||||
'<': false,
|
|
||||||
'=': true,
|
|
||||||
'>': false,
|
|
||||||
'?': true,
|
|
||||||
'@': true,
|
|
||||||
'A': true,
|
|
||||||
'B': true,
|
|
||||||
'C': true,
|
|
||||||
'D': true,
|
|
||||||
'E': true,
|
|
||||||
'F': true,
|
|
||||||
'G': true,
|
|
||||||
'H': true,
|
|
||||||
'I': true,
|
|
||||||
'J': true,
|
|
||||||
'K': true,
|
|
||||||
'L': true,
|
|
||||||
'M': true,
|
|
||||||
'N': true,
|
|
||||||
'O': true,
|
|
||||||
'P': true,
|
|
||||||
'Q': true,
|
|
||||||
'R': true,
|
|
||||||
'S': true,
|
|
||||||
'T': true,
|
|
||||||
'U': true,
|
|
||||||
'V': true,
|
|
||||||
'W': true,
|
|
||||||
'X': true,
|
|
||||||
'Y': true,
|
|
||||||
'Z': true,
|
|
||||||
'[': true,
|
|
||||||
'\\': false,
|
|
||||||
']': true,
|
|
||||||
'^': true,
|
|
||||||
'_': true,
|
|
||||||
'`': true,
|
|
||||||
'a': true,
|
|
||||||
'b': true,
|
|
||||||
'c': true,
|
|
||||||
'd': true,
|
|
||||||
'e': true,
|
|
||||||
'f': true,
|
|
||||||
'g': true,
|
|
||||||
'h': true,
|
|
||||||
'i': true,
|
|
||||||
'j': true,
|
|
||||||
'k': true,
|
|
||||||
'l': true,
|
|
||||||
'm': true,
|
|
||||||
'n': true,
|
|
||||||
'o': true,
|
|
||||||
'p': true,
|
|
||||||
'q': true,
|
|
||||||
'r': true,
|
|
||||||
's': true,
|
|
||||||
't': true,
|
|
||||||
'u': true,
|
|
||||||
'v': true,
|
|
||||||
'w': true,
|
|
||||||
'x': true,
|
|
||||||
'y': true,
|
|
||||||
'z': true,
|
|
||||||
'{': true,
|
|
||||||
'|': true,
|
|
||||||
'}': true,
|
|
||||||
'~': true,
|
|
||||||
'\u007f': true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// safeSet holds the value true if the ASCII character with the given array
|
|
||||||
// position can be represented inside a JSON string without any further
|
|
||||||
// escaping.
|
|
||||||
//
|
|
||||||
// All values are true except for the ASCII control characters (0-31), the
|
|
||||||
// double quote ("), and the backslash character ("\").
|
|
||||||
var safeSet = [utf8.RuneSelf]bool{
|
|
||||||
' ': true,
|
|
||||||
'!': true,
|
|
||||||
'"': false,
|
|
||||||
'#': true,
|
|
||||||
'$': true,
|
|
||||||
'%': true,
|
|
||||||
'&': true,
|
|
||||||
'\'': true,
|
|
||||||
'(': true,
|
|
||||||
')': true,
|
|
||||||
'*': true,
|
|
||||||
'+': true,
|
|
||||||
',': true,
|
|
||||||
'-': true,
|
|
||||||
'.': true,
|
|
||||||
'/': true,
|
|
||||||
'0': true,
|
|
||||||
'1': true,
|
|
||||||
'2': true,
|
|
||||||
'3': true,
|
|
||||||
'4': true,
|
|
||||||
'5': true,
|
|
||||||
'6': true,
|
|
||||||
'7': true,
|
|
||||||
'8': true,
|
|
||||||
'9': true,
|
|
||||||
':': true,
|
|
||||||
';': true,
|
|
||||||
'<': true,
|
|
||||||
'=': true,
|
|
||||||
'>': true,
|
|
||||||
'?': true,
|
|
||||||
'@': true,
|
|
||||||
'A': true,
|
|
||||||
'B': true,
|
|
||||||
'C': true,
|
|
||||||
'D': true,
|
|
||||||
'E': true,
|
|
||||||
'F': true,
|
|
||||||
'G': true,
|
|
||||||
'H': true,
|
|
||||||
'I': true,
|
|
||||||
'J': true,
|
|
||||||
'K': true,
|
|
||||||
'L': true,
|
|
||||||
'M': true,
|
|
||||||
'N': true,
|
|
||||||
'O': true,
|
|
||||||
'P': true,
|
|
||||||
'Q': true,
|
|
||||||
'R': true,
|
|
||||||
'S': true,
|
|
||||||
'T': true,
|
|
||||||
'U': true,
|
|
||||||
'V': true,
|
|
||||||
'W': true,
|
|
||||||
'X': true,
|
|
||||||
'Y': true,
|
|
||||||
'Z': true,
|
|
||||||
'[': true,
|
|
||||||
'\\': false,
|
|
||||||
']': true,
|
|
||||||
'^': true,
|
|
||||||
'_': true,
|
|
||||||
'`': true,
|
|
||||||
'a': true,
|
|
||||||
'b': true,
|
|
||||||
'c': true,
|
|
||||||
'd': true,
|
|
||||||
'e': true,
|
|
||||||
'f': true,
|
|
||||||
'g': true,
|
|
||||||
'h': true,
|
|
||||||
'i': true,
|
|
||||||
'j': true,
|
|
||||||
'k': true,
|
|
||||||
'l': true,
|
|
||||||
'm': true,
|
|
||||||
'n': true,
|
|
||||||
'o': true,
|
|
||||||
'p': true,
|
|
||||||
'q': true,
|
|
||||||
'r': true,
|
|
||||||
's': true,
|
|
||||||
't': true,
|
|
||||||
'u': true,
|
|
||||||
'v': true,
|
|
||||||
'w': true,
|
|
||||||
'x': true,
|
|
||||||
'y': true,
|
|
||||||
'z': true,
|
|
||||||
'{': true,
|
|
||||||
'|': true,
|
|
||||||
'}': true,
|
|
||||||
'~': true,
|
|
||||||
'\u007f': true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var hex = "0123456789abcdef"
|
|
||||||
|
|
||||||
// WriteStringWithHTMLEscaped write string to stream with html special characters escaped
|
|
||||||
func (stream *Stream) WriteStringWithHTMLEscaped(s string) {
|
|
||||||
valLen := len(s)
|
|
||||||
stream.buf = append(stream.buf, '"')
|
|
||||||
// write string, the fast path, without utf8 and escape support
|
|
||||||
i := 0
|
|
||||||
for ; i < valLen; i++ {
|
|
||||||
c := s[i]
|
|
||||||
if c < utf8.RuneSelf && htmlSafeSet[c] {
|
|
||||||
stream.buf = append(stream.buf, c)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i == valLen {
|
|
||||||
stream.buf = append(stream.buf, '"')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen int) {
|
|
||||||
start := i
|
|
||||||
// for the remaining parts, we process them char by char
|
|
||||||
for i < valLen {
|
|
||||||
if b := s[i]; b < utf8.RuneSelf {
|
|
||||||
if htmlSafeSet[b] {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if start < i {
|
|
||||||
stream.WriteRaw(s[start:i])
|
|
||||||
}
|
|
||||||
switch b {
|
|
||||||
case '\\', '"':
|
|
||||||
stream.writeTwoBytes('\\', b)
|
|
||||||
case '\n':
|
|
||||||
stream.writeTwoBytes('\\', 'n')
|
|
||||||
case '\r':
|
|
||||||
stream.writeTwoBytes('\\', 'r')
|
|
||||||
case '\t':
|
|
||||||
stream.writeTwoBytes('\\', 't')
|
|
||||||
default:
|
|
||||||
// This encodes bytes < 0x20 except for \t, \n and \r.
|
|
||||||
// If escapeHTML is set, it also escapes <, >, and &
|
|
||||||
// because they can lead to security holes when
|
|
||||||
// user-controlled strings are rendered into JSON
|
|
||||||
// and served to some browsers.
|
|
||||||
stream.WriteRaw(`\u00`)
|
|
||||||
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
start = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c, size := utf8.DecodeRuneInString(s[i:])
|
|
||||||
if c == utf8.RuneError && size == 1 {
|
|
||||||
if start < i {
|
|
||||||
stream.WriteRaw(s[start:i])
|
|
||||||
}
|
|
||||||
stream.WriteRaw(`\ufffd`)
|
|
||||||
i++
|
|
||||||
start = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// 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 c == '\u2028' || c == '\u2029' {
|
|
||||||
if start < i {
|
|
||||||
stream.WriteRaw(s[start:i])
|
|
||||||
}
|
|
||||||
stream.WriteRaw(`\u202`)
|
|
||||||
stream.writeByte(hex[c&0xF])
|
|
||||||
i += size
|
|
||||||
start = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i += size
|
|
||||||
}
|
|
||||||
if start < len(s) {
|
|
||||||
stream.WriteRaw(s[start:])
|
|
||||||
}
|
|
||||||
stream.writeByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteString write string to stream without html escape
|
|
||||||
func (stream *Stream) WriteString(s string) {
|
|
||||||
valLen := len(s)
|
|
||||||
stream.buf = append(stream.buf, '"')
|
|
||||||
// write string, the fast path, without utf8 and escape support
|
|
||||||
i := 0
|
|
||||||
for ; i < valLen; i++ {
|
|
||||||
c := s[i]
|
|
||||||
if c > 31 && c != '"' && c != '\\' {
|
|
||||||
stream.buf = append(stream.buf, c)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i == valLen {
|
|
||||||
stream.buf = append(stream.buf, '"')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeStringSlowPath(stream, i, s, valLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStringSlowPath(stream *Stream, i int, s string, valLen int) {
|
|
||||||
start := i
|
|
||||||
// for the remaining parts, we process them char by char
|
|
||||||
for i < valLen {
|
|
||||||
if b := s[i]; b < utf8.RuneSelf {
|
|
||||||
if safeSet[b] {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if start < i {
|
|
||||||
stream.WriteRaw(s[start:i])
|
|
||||||
}
|
|
||||||
switch b {
|
|
||||||
case '\\', '"':
|
|
||||||
stream.writeTwoBytes('\\', b)
|
|
||||||
case '\n':
|
|
||||||
stream.writeTwoBytes('\\', 'n')
|
|
||||||
case '\r':
|
|
||||||
stream.writeTwoBytes('\\', 'r')
|
|
||||||
case '\t':
|
|
||||||
stream.writeTwoBytes('\\', 't')
|
|
||||||
default:
|
|
||||||
// This encodes bytes < 0x20 except for \t, \n and \r.
|
|
||||||
// If escapeHTML is set, it also escapes <, >, and &
|
|
||||||
// because they can lead to security holes when
|
|
||||||
// user-controlled strings are rendered into JSON
|
|
||||||
// and served to some browsers.
|
|
||||||
stream.WriteRaw(`\u00`)
|
|
||||||
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
start = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if start < len(s) {
|
|
||||||
stream.WriteRaw(s[start:])
|
|
||||||
}
|
|
||||||
stream.writeByte('"')
|
|
||||||
}
|
|
12
vendor/github.com/json-iterator/go/test.sh
generated
vendored
12
vendor/github.com/json-iterator/go/test.sh
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
echo "" > coverage.txt
|
|
||||||
|
|
||||||
for d in $(go list ./... | grep -v vendor); do
|
|
||||||
go test -coverprofile=profile.out -coverpkg=github.com/json-iterator/go $d
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out >> coverage.txt
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
done
|
|
16
vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
16
vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.13.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go generate
|
|
||||||
- git diff --cached --exit-code
|
|
||||||
- ./go.test.sh
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
21
vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
21
vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
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.
|
|
27
vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
27
vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
go-runewidth
|
|
||||||
============
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
|
|
||||||
[![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
|
|
||||||
|
|
||||||
Provides functions to get fixed width of the character or string.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
```go
|
|
||||||
runewidth.StringWidth("つのだ☆HIRO") == 12
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Author
|
|
||||||
------
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
under the MIT License: http://mattn.mit-license.org/2013
|
|
5
vendor/github.com/mattn/go-runewidth/go.mod
generated
vendored
5
vendor/github.com/mattn/go-runewidth/go.mod
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
module github.com/mattn/go-runewidth
|
|
||||||
|
|
||||||
go 1.9
|
|
||||||
|
|
||||||
require github.com/rivo/uniseg v0.2.0
|
|
2
vendor/github.com/mattn/go-runewidth/go.sum
generated
vendored
2
vendor/github.com/mattn/go-runewidth/go.sum
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
12
vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
12
vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
echo "" > coverage.txt
|
|
||||||
|
|
||||||
for d in $(go list ./... | grep -v vendor); do
|
|
||||||
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out >> coverage.txt
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
done
|
|
273
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
273
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
|
@ -1,273 +0,0 @@
|
||||||
package runewidth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate go run script/generate.go
|
|
||||||
|
|
||||||
var (
|
|
||||||
// EastAsianWidth will be set true if the current locale is CJK
|
|
||||||
EastAsianWidth bool
|
|
||||||
|
|
||||||
// StrictEmojiNeutral should be set false if handle broken fonts
|
|
||||||
StrictEmojiNeutral bool = true
|
|
||||||
|
|
||||||
// DefaultCondition is a condition in current locale
|
|
||||||
DefaultCondition = &Condition{
|
|
||||||
EastAsianWidth: false,
|
|
||||||
StrictEmojiNeutral: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
handleEnv()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleEnv() {
|
|
||||||
env := os.Getenv("RUNEWIDTH_EASTASIAN")
|
|
||||||
if env == "" {
|
|
||||||
EastAsianWidth = IsEastAsian()
|
|
||||||
} else {
|
|
||||||
EastAsianWidth = env == "1"
|
|
||||||
}
|
|
||||||
// update DefaultCondition
|
|
||||||
DefaultCondition.EastAsianWidth = EastAsianWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
type interval struct {
|
|
||||||
first rune
|
|
||||||
last rune
|
|
||||||
}
|
|
||||||
|
|
||||||
type table []interval
|
|
||||||
|
|
||||||
func inTables(r rune, ts ...table) bool {
|
|
||||||
for _, t := range ts {
|
|
||||||
if inTable(r, t) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func inTable(r rune, t table) bool {
|
|
||||||
if r < t[0].first {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
bot := 0
|
|
||||||
top := len(t) - 1
|
|
||||||
for top >= bot {
|
|
||||||
mid := (bot + top) >> 1
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case t[mid].last < r:
|
|
||||||
bot = mid + 1
|
|
||||||
case t[mid].first > r:
|
|
||||||
top = mid - 1
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var private = table{
|
|
||||||
{0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
|
|
||||||
}
|
|
||||||
|
|
||||||
var nonprint = table{
|
|
||||||
{0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
|
|
||||||
{0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
|
|
||||||
{0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
|
|
||||||
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
|
|
||||||
type Condition struct {
|
|
||||||
EastAsianWidth bool
|
|
||||||
StrictEmojiNeutral bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCondition return new instance of Condition which is current locale.
|
|
||||||
func NewCondition() *Condition {
|
|
||||||
return &Condition{
|
|
||||||
EastAsianWidth: EastAsianWidth,
|
|
||||||
StrictEmojiNeutral: StrictEmojiNeutral,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RuneWidth returns the number of cells in r.
|
|
||||||
// See http://www.unicode.org/reports/tr11/
|
|
||||||
func (c *Condition) RuneWidth(r rune) int {
|
|
||||||
// optimized version, verified by TestRuneWidthChecksums()
|
|
||||||
if !c.EastAsianWidth {
|
|
||||||
switch {
|
|
||||||
case r < 0x20 || r > 0x10FFFF:
|
|
||||||
return 0
|
|
||||||
case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint
|
|
||||||
return 0
|
|
||||||
case r < 0x300:
|
|
||||||
return 1
|
|
||||||
case inTable(r, narrow):
|
|
||||||
return 1
|
|
||||||
case inTables(r, nonprint, combining):
|
|
||||||
return 0
|
|
||||||
case inTable(r, doublewidth):
|
|
||||||
return 2
|
|
||||||
default:
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch {
|
|
||||||
case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining):
|
|
||||||
return 0
|
|
||||||
case inTable(r, narrow):
|
|
||||||
return 1
|
|
||||||
case inTables(r, ambiguous, doublewidth):
|
|
||||||
return 2
|
|
||||||
case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
|
|
||||||
return 2
|
|
||||||
default:
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringWidth return width as you can see
|
|
||||||
func (c *Condition) StringWidth(s string) (width int) {
|
|
||||||
g := uniseg.NewGraphemes(s)
|
|
||||||
for g.Next() {
|
|
||||||
var chWidth int
|
|
||||||
for _, r := range g.Runes() {
|
|
||||||
chWidth = c.RuneWidth(r)
|
|
||||||
if chWidth > 0 {
|
|
||||||
break // Our best guess at this point is to use the width of the first non-zero-width rune.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
width += chWidth
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate return string truncated with w cells
|
|
||||||
func (c *Condition) Truncate(s string, w int, tail string) string {
|
|
||||||
if c.StringWidth(s) <= w {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
w -= c.StringWidth(tail)
|
|
||||||
var width int
|
|
||||||
pos := len(s)
|
|
||||||
g := uniseg.NewGraphemes(s)
|
|
||||||
for g.Next() {
|
|
||||||
var chWidth int
|
|
||||||
for _, r := range g.Runes() {
|
|
||||||
chWidth = c.RuneWidth(r)
|
|
||||||
if chWidth > 0 {
|
|
||||||
break // See StringWidth() for details.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if width+chWidth > w {
|
|
||||||
pos, _ = g.Positions()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
width += chWidth
|
|
||||||
}
|
|
||||||
return s[:pos] + tail
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap return string wrapped with w cells
|
|
||||||
func (c *Condition) Wrap(s string, w int) string {
|
|
||||||
width := 0
|
|
||||||
out := ""
|
|
||||||
for _, r := range []rune(s) {
|
|
||||||
cw := c.RuneWidth(r)
|
|
||||||
if r == '\n' {
|
|
||||||
out += string(r)
|
|
||||||
width = 0
|
|
||||||
continue
|
|
||||||
} else if width+cw > w {
|
|
||||||
out += "\n"
|
|
||||||
width = 0
|
|
||||||
out += string(r)
|
|
||||||
width += cw
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out += string(r)
|
|
||||||
width += cw
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillLeft return string filled in left by spaces in w cells
|
|
||||||
func (c *Condition) FillLeft(s string, w int) string {
|
|
||||||
width := c.StringWidth(s)
|
|
||||||
count := w - width
|
|
||||||
if count > 0 {
|
|
||||||
b := make([]byte, count)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = ' '
|
|
||||||
}
|
|
||||||
return string(b) + s
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillRight return string filled in left by spaces in w cells
|
|
||||||
func (c *Condition) FillRight(s string, w int) string {
|
|
||||||
width := c.StringWidth(s)
|
|
||||||
count := w - width
|
|
||||||
if count > 0 {
|
|
||||||
b := make([]byte, count)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = ' '
|
|
||||||
}
|
|
||||||
return s + string(b)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// RuneWidth returns the number of cells in r.
|
|
||||||
// See http://www.unicode.org/reports/tr11/
|
|
||||||
func RuneWidth(r rune) int {
|
|
||||||
return DefaultCondition.RuneWidth(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAmbiguousWidth returns whether is ambiguous width or not.
|
|
||||||
func IsAmbiguousWidth(r rune) bool {
|
|
||||||
return inTables(r, private, ambiguous)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNeutralWidth returns whether is neutral width or not.
|
|
||||||
func IsNeutralWidth(r rune) bool {
|
|
||||||
return inTable(r, neutral)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringWidth return width as you can see
|
|
||||||
func StringWidth(s string) (width int) {
|
|
||||||
return DefaultCondition.StringWidth(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate return string truncated with w cells
|
|
||||||
func Truncate(s string, w int, tail string) string {
|
|
||||||
return DefaultCondition.Truncate(s, w, tail)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap return string wrapped with w cells
|
|
||||||
func Wrap(s string, w int) string {
|
|
||||||
return DefaultCondition.Wrap(s, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillLeft return string filled in left by spaces in w cells
|
|
||||||
func FillLeft(s string, w int) string {
|
|
||||||
return DefaultCondition.FillLeft(s, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillRight return string filled in left by spaces in w cells
|
|
||||||
func FillRight(s string, w int) string {
|
|
||||||
return DefaultCondition.FillRight(s, w)
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue