
The big change is that the GeoJSON package has been completely rewritten to fix a few of geometry calculation bugs, increase performance, and to better follow the GeoJSON spec RFC 7946. GeoJSON updates - A LineString now requires at least two points. - All json members, even foreign, now persist with the object. - The bbox member persists too but is no longer used for geometry calculations. This is change in behavior. Previously Tile38 would treat the bbox as the object's physical rectangle. - Corrections to geometry intersects and within calculations. Faster spatial queries - The performance of Point-in-polygon and object intersect operations are greatly improved for complex polygons and line strings. It went from O(n) to roughly O(log n). - The same for all collection types with many children, including FeatureCollection, GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon. Codebase changes - The pkg directory has been renamed to internal - The GeoJSON internal package has been moved to a seperate repo at https://github.com/tidwall/geojson. It's now vendored. Please look out for higher memory usage for datasets using complex shapes. A complex shape is one that has 64 or more points. For these shapes it's expected that there will be increase of least 54 bytes per point.
214 lines
4.8 KiB
Go
214 lines
4.8 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package glob
|
|
|
|
import (
|
|
"errors"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// ErrBadPattern indicates a globbing pattern was malformed.
|
|
var ErrBadPattern = errors.New("syntax error in pattern")
|
|
|
|
// Match reports whether name matches the shell file name pattern.
|
|
// The pattern syntax is:
|
|
//
|
|
// pattern:
|
|
// { term }
|
|
// term:
|
|
// '*' matches any sequence of non-Separator characters
|
|
// '?' matches any single non-Separator character
|
|
// '[' [ '^' ] { character-range } ']'
|
|
// character class (must be non-empty)
|
|
// c matches character c (c != '*', '?', '\\', '[')
|
|
// '\\' c matches character c
|
|
//
|
|
// character-range:
|
|
// c matches character c (c != '\\', '-', ']')
|
|
// '\\' c matches character c
|
|
// lo '-' hi matches character c for lo <= c <= hi
|
|
//
|
|
// Match requires pattern to match all of name, not just a substring.
|
|
// The only possible returned error is ErrBadPattern, when pattern
|
|
// is malformed.
|
|
//
|
|
// On Windows, escaping is disabled. Instead, '\\' is treated as
|
|
// path separator.
|
|
//
|
|
func wildcardMatch(pattern, name string) (matched bool, err error) {
|
|
Pattern:
|
|
for len(pattern) > 0 {
|
|
var star bool
|
|
var chunk string
|
|
star, chunk, pattern = scanChunk(pattern)
|
|
if star && chunk == "" {
|
|
// Trailing * matches rest of string unless it has a /.
|
|
return true, nil
|
|
}
|
|
// Look for match at current position.
|
|
t, ok, err := matchChunk(chunk, name)
|
|
// if we're the last chunk, make sure we've exhausted the name
|
|
// otherwise we'll give a false result even if we could still match
|
|
// using the star
|
|
if ok && (len(t) == 0 || len(pattern) > 0) {
|
|
name = t
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if star {
|
|
// Look for match skipping i+1 bytes.
|
|
// Cannot skip /.
|
|
for i := 0; i < len(name); i++ {
|
|
t, ok, err := matchChunk(chunk, name[i+1:])
|
|
if ok {
|
|
// if we're the last chunk, make sure we exhausted the name
|
|
if len(pattern) == 0 && len(t) > 0 {
|
|
continue
|
|
}
|
|
name = t
|
|
continue Pattern
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
return len(name) == 0, nil
|
|
}
|
|
|
|
// scanChunk gets the next segment of pattern, which is a non-star string
|
|
// possibly preceded by a star.
|
|
func scanChunk(pattern string) (star bool, chunk, rest string) {
|
|
for len(pattern) > 0 && pattern[0] == '*' {
|
|
pattern = pattern[1:]
|
|
star = true
|
|
}
|
|
inrange := false
|
|
var i int
|
|
Scan:
|
|
for i = 0; i < len(pattern); i++ {
|
|
switch pattern[i] {
|
|
case '\\':
|
|
// error check handled in matchChunk: bad pattern.
|
|
if i+1 < len(pattern) {
|
|
i++
|
|
}
|
|
case '[':
|
|
inrange = true
|
|
case ']':
|
|
inrange = false
|
|
case '*':
|
|
if !inrange {
|
|
break Scan
|
|
}
|
|
}
|
|
}
|
|
return star, pattern[0:i], pattern[i:]
|
|
}
|
|
|
|
// matchChunk checks whether chunk matches the beginning of s.
|
|
// If so, it returns the remainder of s (after the match).
|
|
// Chunk is all single-character operators: literals, char classes, and ?.
|
|
func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
|
for len(chunk) > 0 {
|
|
if len(s) == 0 {
|
|
return
|
|
}
|
|
switch chunk[0] {
|
|
case '[':
|
|
// character class
|
|
r, n := utf8.DecodeRuneInString(s)
|
|
s = s[n:]
|
|
chunk = chunk[1:]
|
|
// We can't end right after '[', we're expecting at least
|
|
// a closing bracket and possibly a caret.
|
|
if len(chunk) == 0 {
|
|
err = ErrBadPattern
|
|
return
|
|
}
|
|
// possibly negated
|
|
negated := chunk[0] == '^'
|
|
if negated {
|
|
chunk = chunk[1:]
|
|
}
|
|
// parse all ranges
|
|
match := false
|
|
nrange := 0
|
|
for {
|
|
if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
|
|
chunk = chunk[1:]
|
|
break
|
|
}
|
|
var lo, hi rune
|
|
if lo, chunk, err = getEsc(chunk); err != nil {
|
|
return
|
|
}
|
|
hi = lo
|
|
if chunk[0] == '-' {
|
|
if hi, chunk, err = getEsc(chunk[1:]); err != nil {
|
|
return
|
|
}
|
|
}
|
|
if lo <= r && r <= hi {
|
|
match = true
|
|
}
|
|
nrange++
|
|
}
|
|
if match == negated {
|
|
return
|
|
}
|
|
|
|
case '?':
|
|
_, n := utf8.DecodeRuneInString(s)
|
|
s = s[n:]
|
|
chunk = chunk[1:]
|
|
|
|
case '\\':
|
|
chunk = chunk[1:]
|
|
if len(chunk) == 0 {
|
|
err = ErrBadPattern
|
|
return
|
|
}
|
|
fallthrough
|
|
|
|
default:
|
|
if chunk[0] != s[0] {
|
|
return
|
|
}
|
|
s = s[1:]
|
|
chunk = chunk[1:]
|
|
}
|
|
}
|
|
return s, true, nil
|
|
}
|
|
|
|
// getEsc gets a possibly-escaped character from chunk, for a character class.
|
|
func getEsc(chunk string) (r rune, nchunk string, err error) {
|
|
if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
|
|
err = ErrBadPattern
|
|
return
|
|
}
|
|
if chunk[0] == '\\' {
|
|
chunk = chunk[1:]
|
|
if len(chunk) == 0 {
|
|
err = ErrBadPattern
|
|
return
|
|
}
|
|
}
|
|
r, n := utf8.DecodeRuneInString(chunk)
|
|
if r == utf8.RuneError && n == 1 {
|
|
err = ErrBadPattern
|
|
}
|
|
nchunk = chunk[n:]
|
|
if len(nchunk) == 0 {
|
|
err = ErrBadPattern
|
|
}
|
|
return
|
|
}
|