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 }