github.com/matrixorigin/matrixone@v0.7.0/pkg/container/bytejson/path.go (about)

     1  // Copyright 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bytejson
    16  
    17  import (
    18  	"encoding/json"
    19  	"math"
    20  	"strconv"
    21  	"strings"
    22  	"unicode"
    23  )
    24  
    25  func (p *Path) init(subs []subPath) {
    26  	p.paths = subs
    27  	for _, sub := range subs {
    28  		if sub.tp == subPathDoubleStar {
    29  			p.flag |= pathFlagDoubleStar
    30  		}
    31  		if sub.tp == subPathKey && sub.key == "*" {
    32  			p.flag |= pathFlagSingleStar
    33  		}
    34  		if sub.tp == subPathIdx && sub.idx.num == subPathIdxALL && sub.idx.tp == numberIndices {
    35  			p.flag |= pathFlagSingleStar
    36  		}
    37  	}
    38  }
    39  
    40  func (p *Path) empty() bool {
    41  	return len(p.paths) == 0
    42  }
    43  
    44  func (p *Path) step() (sub subPath, newP Path) {
    45  	sub = p.paths[0]
    46  	newP.init(p.paths[1:])
    47  	return
    48  }
    49  
    50  func (p *Path) String() string {
    51  	var s strings.Builder
    52  
    53  	s.WriteString("$")
    54  	for _, sub := range p.paths {
    55  		switch sub.tp {
    56  		case subPathIdx:
    57  			s.WriteString("[")
    58  			sub.idx.toString(&s)
    59  			s.WriteString("]")
    60  		case subPathRange:
    61  			s.WriteString("[")
    62  			sub.iRange.toString(&s)
    63  			s.WriteString("]")
    64  		case subPathKey:
    65  			s.WriteString(".")
    66  			//TODO check here is ok
    67  			s.WriteString(strconv.Quote(sub.key))
    68  		case subPathDoubleStar:
    69  			s.WriteString("**")
    70  		}
    71  	}
    72  	return s.String()
    73  }
    74  
    75  func (pi subPathIndices) toString(s *strings.Builder) {
    76  	switch pi.tp {
    77  	case numberIndices:
    78  		if pi.num == subPathIdxALL {
    79  			s.WriteString("*")
    80  		} else {
    81  			s.WriteString(strconv.Itoa(pi.num))
    82  		}
    83  	case lastIndices:
    84  		s.WriteString(lastKey)
    85  		if pi.num > 0 {
    86  			s.WriteString(" - ")
    87  			s.WriteString(strconv.Itoa(pi.num))
    88  		}
    89  	default:
    90  		panic("invalid index type")
    91  	}
    92  }
    93  func (pe subPathRangeExpr) toString(s *strings.Builder) {
    94  	pe.start.toString(s)
    95  	s.WriteString(" to ")
    96  	pe.end.toString(s)
    97  }
    98  
    99  func newPathGenerator(path string) *pathGenerator {
   100  	return &pathGenerator{
   101  		pathStr: path,
   102  		pos:     0,
   103  	}
   104  }
   105  
   106  func (pg *pathGenerator) trimSpace() {
   107  	for ; pg.pos < len(pg.pathStr); pg.pos++ {
   108  		if !unicode.IsSpace(rune(pg.pathStr[pg.pos])) {
   109  			break
   110  		}
   111  	}
   112  }
   113  
   114  func (pg *pathGenerator) hasNext() bool {
   115  	return pg.pos < len(pg.pathStr)
   116  }
   117  
   118  func (pg *pathGenerator) next() byte {
   119  	ret := pg.pathStr[pg.pos]
   120  	pg.pos++
   121  	return ret
   122  }
   123  func (pg *pathGenerator) front() byte {
   124  	return pg.pathStr[pg.pos]
   125  }
   126  func (pg *pathGenerator) tryNext(inc int) string {
   127  	if pg.pos+inc > len(pg.pathStr) {
   128  		return ""
   129  	}
   130  	return pg.pathStr[pg.pos : pg.pos+inc]
   131  }
   132  func (pg *pathGenerator) skip(inc int) {
   133  	pg.pos += inc
   134  }
   135  
   136  func (pg *pathGenerator) nextUtil(f func(byte) bool) (string, bool) {
   137  	start := pg.pos
   138  	isEnd := true
   139  	for ; pg.hasNext(); pg.next() {
   140  		if !f(pg.front()) {
   141  			isEnd = false
   142  			break
   143  		}
   144  	}
   145  	return pg.pathStr[start:pg.pos], isEnd
   146  }
   147  
   148  func (pg *pathGenerator) generateDoubleStar(legs []subPath) ([]subPath, bool) {
   149  	pg.next()
   150  	if !pg.hasNext() || pg.next() != '*' {
   151  		return nil, false
   152  	}
   153  	if !pg.hasNext() || pg.front() == '*' { //check if it is ***
   154  		return nil, false
   155  	}
   156  
   157  	legs = append(legs, subPath{
   158  		tp: subPathDoubleStar,
   159  	})
   160  	return legs, true
   161  }
   162  
   163  func (pg *pathGenerator) tryIndices(rs *subPathIndices) bool {
   164  	rs.num = 0
   165  	if pg.tryNext(lastKeyLen) == lastKey {
   166  		rs.tp = lastIndices
   167  		pg.skip(lastKeyLen)
   168  		pg.trimSpace()
   169  		if !pg.hasNext() {
   170  			return false
   171  		}
   172  		if pg.front() == '-' {
   173  			pg.next()
   174  			pg.trimSpace()
   175  			if !pg.hasNext() {
   176  				return false
   177  			}
   178  			if idx, ok := pg.tryNumberIndex(); ok {
   179  				rs.num = idx
   180  				return true
   181  			}
   182  			return false
   183  		}
   184  		return true
   185  	}
   186  	if idx, ok := pg.tryNumberIndex(); ok {
   187  		rs.tp = numberIndices
   188  		rs.num = idx
   189  		return true
   190  	}
   191  	return false
   192  }
   193  
   194  func (pg *pathGenerator) tryNumberIndex() (int, bool) {
   195  	str, isEnd := pg.nextUtil(func(b byte) bool { // now only support non-negative integer
   196  		return b >= '0' && b <= '9'
   197  	})
   198  	if isEnd {
   199  		return 0, false
   200  	}
   201  	index, err := strconv.Atoi(str)
   202  	if err != nil || index > math.MaxUint32 {
   203  		return 0, false
   204  	}
   205  	return index, true
   206  }
   207  
   208  func (pg *pathGenerator) generateIndex(legs []subPath) ([]subPath, bool) {
   209  	pg.next()
   210  	pg.trimSpace()
   211  	if !pg.hasNext() {
   212  		return nil, false
   213  	}
   214  	if pg.front() == '*' {
   215  		pg.next()
   216  		legs = append(legs, subPath{
   217  			tp: subPathIdx,
   218  			idx: &subPathIndices{
   219  				tp:  numberIndices,
   220  				num: subPathIdxALL,
   221  			},
   222  		})
   223  		pg.trimSpace()
   224  		if !pg.hasNext() || pg.next() != ']' {
   225  			return nil, false
   226  		}
   227  		return legs, true
   228  	}
   229  	i1 := &subPathIndices{}
   230  	ok := pg.tryIndices(i1)
   231  	if !ok {
   232  		return nil, false
   233  	}
   234  	if !pg.hasNext() {
   235  		return nil, false
   236  	}
   237  	pg.trimSpace()
   238  	if pg.tryNext(toKeyLen) == toKey {
   239  		if pg.pathStr[pg.pos-1] != ' ' {
   240  			return nil, false
   241  		}
   242  		pg.skip(toKeyLen)
   243  		if !pg.hasNext() {
   244  			return nil, false
   245  		}
   246  		if pg.front() != ' ' {
   247  			return nil, false
   248  		}
   249  		pg.trimSpace()
   250  		i2 := &subPathIndices{}
   251  		ok = pg.tryIndices(i2)
   252  		if !ok {
   253  			return nil, false
   254  		}
   255  		if i1.tp == lastIndices && i2.tp == lastIndices && i1.num < i2.num {
   256  			return nil, false
   257  		}
   258  		legs = append(legs, subPath{
   259  			tp: subPathRange,
   260  			iRange: &subPathRangeExpr{
   261  				start: i1,
   262  				end:   i2,
   263  			},
   264  		})
   265  	} else {
   266  		legs = append(legs, subPath{
   267  			tp:  subPathIdx,
   268  			idx: i1,
   269  		})
   270  	}
   271  
   272  	pg.trimSpace()
   273  	if !pg.hasNext() || pg.next() != ']' {
   274  		return nil, false
   275  	}
   276  	return legs, true
   277  }
   278  
   279  func (pg *pathGenerator) generateKey(legs []subPath) ([]subPath, bool) {
   280  	pg.next()
   281  	pg.trimSpace()
   282  	if !pg.hasNext() {
   283  		return nil, false
   284  	}
   285  	if pg.front() == '*' {
   286  		pg.next()
   287  		legs = append(legs, subPath{
   288  			tp:  subPathKey,
   289  			key: "*",
   290  		})
   291  	} else {
   292  		var quoted bool
   293  		var key string
   294  		if pg.front() == '"' {
   295  			pg.next()
   296  			str, isEnd := pg.nextUtil(func(b byte) bool {
   297  				if b == '\\' {
   298  					pg.next()
   299  					return true
   300  				}
   301  				return b != '"'
   302  			})
   303  			if isEnd {
   304  				return nil, false
   305  			}
   306  			pg.next()
   307  			key = str
   308  			quoted = true
   309  		} else {
   310  			key, _ = pg.nextUtil(func(b byte) bool {
   311  				return !(unicode.IsSpace(rune(b)) || b == '.' || b == '[' || b == '*')
   312  			})
   313  		}
   314  		key = "\"" + key + "\""
   315  		if !json.Valid(string2Slice(key)) {
   316  			return nil, false
   317  		}
   318  		key, err := strconv.Unquote(key)
   319  		if err != nil {
   320  			return nil, false
   321  		}
   322  		if !quoted && !isIdentifier(key) {
   323  			return nil, false
   324  		}
   325  		legs = append(legs, subPath{
   326  			tp:  subPathKey,
   327  			key: key,
   328  		})
   329  	}
   330  	return legs, true
   331  }
   332  
   333  // genIndex returns originVal,modifiedVal,ok
   334  func (pi subPathIndices) genIndex(cnt int) (int, int, bool) {
   335  	switch pi.tp {
   336  	case numberIndices:
   337  		if pi.num >= cnt {
   338  			return pi.num, cnt - 1, false
   339  		}
   340  		return pi.num, pi.num, false
   341  	case lastIndices:
   342  		idx := cnt - pi.num - 1
   343  		if idx < 0 {
   344  			return idx, 0, true
   345  		}
   346  		return idx, idx, true
   347  	}
   348  	return subPathIdxErr, subPathIdxErr, false
   349  }
   350  
   351  func (pe subPathRangeExpr) genRange(cnt int) (ret [2]int) {
   352  	orig1, mdf1, _ := pe.start.genIndex(cnt)
   353  	orig2, mdf2, _ := pe.end.genIndex(cnt)
   354  	if orig1 > orig2 {
   355  		ret[0], ret[1] = subPathIdxErr, subPathIdxErr
   356  		return
   357  	}
   358  	ret[0], ret[1] = mdf1, mdf2
   359  	return
   360  }