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 }