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