From 0964f1397e904d6e95ab4fc5244f47d25a3a8559 Mon Sep 17 00:00:00 2001 From: Vladimir Hodakov Date: Tue, 26 Feb 2019 13:05:15 +0400 Subject: [PATCH] Initial commit --- LICENSE | 21 ++ README.md | 118 +++++++++++ constants.go | 14 ++ eval.go | 335 +++++++++++++++++++++++++++++ eval_states.go | 193 +++++++++++++++++ eval_test.go | 101 +++++++++ expression.go | 429 ++++++++++++++++++++++++++++++++++++++ expression_states.go | 287 +++++++++++++++++++++++++ expression_states_test.go | 40 ++++ expression_test.go | 226 ++++++++++++++++++++ json_states.go | 266 +++++++++++++++++++++++ json_states_test.go | 172 +++++++++++++++ lexer.go | 86 ++++++++ lexer_reader.go | 161 ++++++++++++++ lexer_slice.go | 131 ++++++++++++ lexer_test.go | 309 +++++++++++++++++++++++++++ misc.go | 181 ++++++++++++++++ path.go | 208 ++++++++++++++++++ path_states.go | 213 +++++++++++++++++++ path_states_test.go | 30 +++ path_test.go | 40 ++++ queue.go | 55 +++++ result.go | 57 +++++ run.go | 27 +++ stack.go | 148 +++++++++++++ stack_test.go | 56 +++++ 26 files changed, 3904 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 constants.go create mode 100644 eval.go create mode 100644 eval_states.go create mode 100644 eval_test.go create mode 100644 expression.go create mode 100644 expression_states.go create mode 100644 expression_states_test.go create mode 100644 expression_test.go create mode 100644 json_states.go create mode 100644 json_states_test.go create mode 100644 lexer.go create mode 100644 lexer_reader.go create mode 100644 lexer_slice.go create mode 100644 lexer_test.go create mode 100644 misc.go create mode 100644 path.go create mode 100644 path_states.go create mode 100644 path_states_test.go create mode 100644 path_test.go create mode 100644 queue.go create mode 100644 result.go create mode 100644 run.go create mode 100644 stack.go create mode 100644 stack_test.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9125b53 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Jumbo Interactive Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..073ae64 --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# The jsonpath library + +jsonpath is used to pull values out of a JSON document without unmarshalling the string into an object. At the loss of post-parse random access and conversion to primitive types, you gain faster return speeds and lower memory utilization. If the value you want is located near the start of the json, the evaluator will terminate after reaching and recording its destination. + +The evaluator can be initialized with several paths, so you can retrieve multiple sections of the document with just one scan. Naturally, when all paths have been reached, the evaluator will early terminate. + +For each value returned by a path, you'll also get the keys & indexes needed to reach that value. Use the `keys` flag to view this in the CLI. The Go package will return an `[]interface{}` of length `n` with indexes `0 - (n-2)` being the keys and the value at index `n-1`. + +## The history of a library + +This fork is owned and currently maintained by [WTFTeam](https://lab.wtfteam.pro). It's based on [that one](https://github.com/JumboInteractiveLimited/jsonpath) from Jumbo Interactive Limited, which sequentally based/copied from NodePrime/jsonpath Github repository, currently unavailable. The MIT license on this code is inherited from Jumbo Interactive Limited fork, so we can support and maintain this library freely. If there is by any chance someone's proprietary code, you can reach us by ``abuse wtfteam pro`` with details. Unless proved against all code here is licensed under MIT. + +### Go Package +go get lab.wtfteam.pro/wtfteam/jsonpath + +```go +paths, err := jsonpath.ParsePaths(pathStrings ...string) { +``` + +```go +eval, err := jsonpath.EvalPathsInBytes(json []byte, paths) +// OR +eval, err := jsonpath.EvalPathsInReader(r io.Reader, paths) +``` + +then + +```go +for { + if result, ok := eval.Next(); ok { + fmt.Println(result.Pretty(true)) // true -> show keys in pretty string + } else { + break + } +} +if eval.Error != nil { + return eval.Error +} +``` + +`eval.Next()` will traverse JSON until another value is found. This has the potential of traversing the entire JSON document in an attempt to find one. If you prefer to have more control over traversing, use the `eval.Iterate()` method. It will return after every scanned JSON token and return `([]*Result, bool)`. This array will usually be empty, but occasionally contain results. + +### Path Syntax +All paths start from the root node `$`. Similar to getting properties in a JavaScript object, a period `.title` or brackets `["title"]` are used. + +Syntax|Meaning|Examples +------|-------|------- +`$`|root of doc| +`.`|property selector |`$.Items` +`["abc"]`|quoted property selector|`$["Items"]` +`*`|wildcard property name|`$.*` +`[n]`|Nth index of array|`[0]` `[1]` +`[n:m]`|Nth index to m-1 index (same as Go slicing)|`[0:1]` `[2:5]` +`[n:]`|Nth index to end of array|`[1:]` `[2:]` +`[*]`|wildcard index of array|`[*]` +`+`|get value at end of path|`$.title+` +`?(expression)`|where clause (expression can reference current json node with @)|`?(@.title == "ABC")` + + +Expressions + +- paths (that start from current node `@`) +- numbers (integers, floats, scientific notation) +- mathematical operators (+ - / * ^) +- numerical comparisos (< <= > >=) +- logic operators (&& || == !=) +- parentheses `(2 < (3 * 5))` +- static values like (`true`, `false`) +- `@.value > 0.5` + +Example: this will only return tags of all items that match this expression. +`$.Items[*]?(@.title == "A Tale of Two Cities").tags` + +Example: + +```javascript +{ + "Items": + [ + { + "title": "A Midsummer Night's Dream", + "tags":[ + "comedy", + "shakespeare", + "play" + ] + },{ + "title": "A Tale of Two Cities", + "tags":[ + "french", + "revolution", + "london" + ] + } + ] +} +``` + +Example Paths: + +*Paths* +`$.Items[*].title+` +... "A Midsummer Night's Dream" +... "A Tale of Two Cities" + +`$.Items[*].tags+` +... ["comedy","shakespeare","play"] +... ["french","revolution","london"] + +`$.Items[*].tags[*]+` +... "comedy" +... "shakespeare" +... "play" +... "french" +... "revolution" +... "london" + +... = keys/indexes of path diff --git a/constants.go b/constants.go new file mode 100644 index 0000000..4bd54b4 --- /dev/null +++ b/constants.go @@ -0,0 +1,14 @@ +package jsonpath + +const ( + BadStructure = "Bad Structure" + NoMoreResults = "No more results" + UnexpectedToken = "Unexpected token in evaluation" + AbruptTokenStreamEnd = "Token reader is not sending anymore tokens" +) + +var ( + bytesTrue = []byte{'t', 'r', 'u', 'e'} + bytesFalse = []byte{'f', 'a', 'l', 's', 'e'} + bytesNull = []byte{'n', 'u', 'l', 'l'} +) diff --git a/eval.go b/eval.go new file mode 100644 index 0000000..563b998 --- /dev/null +++ b/eval.go @@ -0,0 +1,335 @@ +package jsonpath + +import ( + "bytes" + "fmt" +) + +type queryStateFn func(*query, *Eval, *Item) queryStateFn + +type query struct { + Path + state queryStateFn + start int + pos int + firstType int // first json token type in buffer + buffer bytes.Buffer + resultQueue *Results + valLoc stack // capture the current location stack at capture + errors []error + buckets stack // stack of exprBucket +} + +type exprBucket struct { + operatorLoc int + expression []Item + queries []*query + results *Results +} + +type evalStateFn func(*Eval, *Item) evalStateFn + +type Eval struct { + tr tokenReader + levelStack intStack + location stack + queries map[string]*query + state evalStateFn + prevIndex int + nextKey []byte + copyValues bool + + resultQueue *Results + Error error +} + +func newEvaluation(tr tokenReader, paths ...*Path) *Eval { + e := &Eval{ + tr: tr, + location: *newStack(), + levelStack: *newIntStack(), + state: evalRoot, + queries: make(map[string]*query, 0), + prevIndex: -1, + nextKey: nil, + copyValues: true, // depends on which lexer is used + resultQueue: newResults(), + } + + for _, p := range paths { + e.queries[p.stringValue] = newQuery(p) + } + // Determine whether to copy emitted item values ([]byte) from lexer + switch tr.(type) { + case *readerLexer: + e.copyValues = true + default: + e.copyValues = false + } + + return e +} + +func newQuery(p *Path) *query { + return &query{ + Path: *p, + state: pathMatchOp, + start: -1, + pos: -1, + buffer: *bytes.NewBuffer(make([]byte, 0, 50)), + valLoc: *newStack(), + errors: make([]error, 0), + resultQueue: newResults(), + buckets: *newStack(), + } +} + +func (e *Eval) Iterate() (*Results, bool) { + e.resultQueue.clear() + + t, ok := e.tr.next() + if !ok || e.state == nil { + return nil, false + } + + // run evaluator function + e.state = e.state(e, t) + + anyRunning := false + // run path function for each path + for str, query := range e.queries { + anyRunning = true + query.state = query.state(query, e, t) + if query.state == nil { + delete(e.queries, str) + } + + if query.resultQueue.len() > 0 { + e.resultQueue.push(query.resultQueue.Pop()) + } + + for _, b := range query.buckets.values { + bucket := b.(exprBucket) + for _, dq := range bucket.queries { + dq.state = dq.state(dq, e, t) + + if query.resultQueue.len() > 0 { + e.resultQueue.push(query.resultQueue.Pop()) + } + } + } + } + + if !anyRunning { + return nil, false + } + + if e.Error != nil { + return nil, false + } + + return e.resultQueue, true +} + +func (e *Eval) Next() (*Result, bool) { + if e.resultQueue.len() > 0 { + return e.resultQueue.Pop(), true + } + + for { + if _, ok := e.Iterate(); ok { + if e.resultQueue.len() > 0 { + return e.resultQueue.Pop(), true + } + } else { + break + } + + } + return nil, false +} + +func (q *query) loc() int { + return abs(q.pos-q.start) + q.start +} + +func (q *query) trySpillOver() { + if b, ok := q.buckets.peek(); ok { + bucket := b.(exprBucket) + if q.loc() < bucket.operatorLoc { + q.buckets.pop() + + exprRes, err := bucket.evaluate() + if err != nil { + q.errors = append(q.errors, err) + } + if exprRes { + next, ok := q.buckets.peek() + var spillover *Results + if !ok { + // fmt.Println("Spilling over into end queue") + spillover = q.resultQueue + } else { + // fmt.Println("Spilling over into lower bucket") + nextBucket := next.(exprBucket) + spillover = nextBucket.results + } + for { + v := bucket.results.Pop() + if v != nil { + spillover.push(v) + } else { + break + } + } + } + } + } +} + +func pathMatchOp(q *query, e *Eval, i *Item) queryStateFn { + curLocation := e.location.len() - 1 + + if q.loc() > curLocation { + q.pos -= 1 + q.trySpillOver() + } else if q.loc() <= curLocation { + if q.loc() == curLocation-1 { + if len(q.operators)+q.start >= curLocation { + current, _ := e.location.peek() + nextOp := q.operators[abs(q.loc()-q.start)] + if itemMatchOperator(current, i, nextOp) { + q.pos += 1 + + if nextOp.whereClauseBytes != nil && len(nextOp.whereClause) > 0 { + bucket := exprBucket{ + operatorLoc: q.loc(), + expression: nextOp.whereClause, + queries: make([]*query, len(nextOp.dependentPaths)), + results: newResults(), + } + + for i, p := range nextOp.dependentPaths { + bucket.queries[i] = newQuery(p) + bucket.queries[i].pos = q.loc() + bucket.queries[i].start = q.loc() + bucket.queries[i].captureEndValue = true + } + q.buckets.push(bucket) + } + } + + } + } + } + + if q.loc() == len(q.operators)+q.start && q.loc() <= curLocation { + if q.captureEndValue { + q.firstType = i.typ + q.buffer.Write(i.val) + } + q.valLoc = *e.location.clone() + return pathEndValue + } + + if q.loc() < -1 { + return nil + } else { + return pathMatchOp + } +} + +func pathEndValue(q *query, e *Eval, i *Item) queryStateFn { + if e.location.len()-1 >= q.loc() { + if q.captureEndValue { + q.buffer.Write(i.val) + } + } else { + r := &Result{Keys: q.valLoc.toArray()} + if q.buffer.Len() > 0 { + val := make([]byte, q.buffer.Len()) + copy(val, q.buffer.Bytes()) + r.Value = val + + switch q.firstType { + case jsonBraceLeft: + r.Type = JsonObject + case jsonString: + r.Type = JsonString + case jsonBracketLeft: + r.Type = JsonArray + case jsonNull: + r.Type = JsonNull + case jsonBool: + r.Type = JsonBool + case jsonNumber: + r.Type = JsonNumber + default: + r.Type = -1 + } + } + + if q.buckets.len() == 0 { + q.resultQueue.push(r) + } else { + b, _ := q.buckets.peek() + b.(exprBucket).results.push(r) + } + + q.valLoc = *newStack() + q.buffer.Truncate(0) + q.pos -= 1 + return pathMatchOp + } + return pathEndValue +} + +func (b *exprBucket) evaluate() (bool, error) { + values := make(map[string]Item) + for _, q := range b.queries { + result := q.resultQueue.Pop() + if result != nil { + t, err := getJsonTokenType(result.Value) + if err != nil { + return false, err + } + i := Item{ + typ: t, + val: result.Value, + } + values[q.Path.stringValue] = i + } + } + + res, err := evaluatePostFix(b.expression, values) + if err != nil { + return false, err + } + res_bool, ok := res.(bool) + if !ok { + return false, fmt.Errorf(exprErrorFinalValueNotBool, res) + } + return res_bool, nil +} + +func itemMatchOperator(loc interface{}, i *Item, op *operator) bool { + topBytes, isKey := loc.([]byte) + topInt, isIndex := loc.(int) + if isKey { + switch op.typ { + case opTypeNameWild: + return true + case opTypeName, opTypeNameList: + _, found := op.keyStrings[string(topBytes)] + return found + } + } else if isIndex { + switch op.typ { + case opTypeIndexWild: + return true + case opTypeIndex, opTypeIndexRange: + return topInt >= op.indexStart && (!op.hasIndexEnd || topInt <= op.indexEnd) + } + } + return false +} diff --git a/eval_states.go b/eval_states.go new file mode 100644 index 0000000..16db87e --- /dev/null +++ b/eval_states.go @@ -0,0 +1,193 @@ +package jsonpath + +import ( + "errors" + "fmt" +) + +func evalRoot(e *Eval, i *Item) evalStateFn { + switch i.typ { + case jsonBraceLeft: + e.levelStack.push(i.typ) + return evalObjectAfterOpen + case jsonBracketLeft: + e.levelStack.push(i.typ) + return evalArrayAfterOpen + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func evalObjectAfterOpen(e *Eval, i *Item) evalStateFn { + switch i.typ { + case jsonKey: + c := i.val[1 : len(i.val)-1] + if e.copyValues { + d := make([]byte, len(c)) + copy(d, c) + c = d + } + e.nextKey = c + return evalObjectColon + case jsonBraceRight: + return rightBraceOrBracket(e) + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func evalObjectColon(e *Eval, i *Item) evalStateFn { + switch i.typ { + case jsonColon: + return evalObjectValue + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + + return nil +} + +func evalObjectValue(e *Eval, i *Item) evalStateFn { + e.location.push(e.nextKey) + + switch i.typ { + case jsonNull, jsonNumber, jsonString, jsonBool: + return evalObjectAfterValue + case jsonBraceLeft: + e.levelStack.push(i.typ) + return evalObjectAfterOpen + case jsonBracketLeft: + e.levelStack.push(i.typ) + return evalArrayAfterOpen + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func evalObjectAfterValue(e *Eval, i *Item) evalStateFn { + e.location.pop() + switch i.typ { + case jsonComma: + return evalObjectAfterOpen + case jsonBraceRight: + return rightBraceOrBracket(e) + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func rightBraceOrBracket(e *Eval) evalStateFn { + e.levelStack.pop() + + lowerTyp, ok := e.levelStack.peek() + if !ok { + return evalRootEnd + } else { + switch lowerTyp { + case jsonBraceLeft: + return evalObjectAfterValue + case jsonBracketLeft: + return evalArrayAfterValue + } + } + return nil +} + +func evalArrayAfterOpen(e *Eval, i *Item) evalStateFn { + e.prevIndex = -1 + + switch i.typ { + case jsonNull, jsonNumber, jsonString, jsonBool, jsonBraceLeft, jsonBracketLeft: + return evalArrayValue(e, i) + case jsonBracketRight: + setPrevIndex(e) + return rightBraceOrBracket(e) + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func evalArrayValue(e *Eval, i *Item) evalStateFn { + e.prevIndex++ + e.location.push(e.prevIndex) + + switch i.typ { + case jsonNull, jsonNumber, jsonString, jsonBool: + return evalArrayAfterValue + case jsonBraceLeft: + e.levelStack.push(i.typ) + return evalObjectAfterOpen + case jsonBracketLeft: + e.levelStack.push(i.typ) + return evalArrayAfterOpen + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func evalArrayAfterValue(e *Eval, i *Item) evalStateFn { + switch i.typ { + case jsonComma: + if val, ok := e.location.pop(); ok { + if valIndex, ok := val.(int); ok { + e.prevIndex = valIndex + } + } + return evalArrayValue + case jsonBracketRight: + e.location.pop() + setPrevIndex(e) + return rightBraceOrBracket(e) + case jsonError: + return evalError(e, i) + default: + e.Error = errors.New(UnexpectedToken) + } + return nil +} + +func setPrevIndex(e *Eval) { + e.prevIndex = -1 + peeked, ok := e.location.peek() + if ok { + if peekedIndex, intOk := peeked.(int); intOk { + e.prevIndex = peekedIndex + } + } +} + +func evalRootEnd(e *Eval, i *Item) evalStateFn { + if i.typ != jsonEOF { + if i.typ == jsonError { + evalError(e, i) + } else { + e.Error = errors.New(BadStructure) + } + } + return nil +} + +func evalError(e *Eval, i *Item) evalStateFn { + e.Error = fmt.Errorf("%s at byte index %d", string(i.val), i.pos) + return nil +} diff --git a/eval_test.go b/eval_test.go new file mode 100644 index 0000000..bc391af --- /dev/null +++ b/eval_test.go @@ -0,0 +1,101 @@ +package jsonpath + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +type test struct { + name string + json string + path string + expected []Result +} + +var tests = []test{ + test{`key selection`, `{"aKey":32}`, `$.aKey+`, []Result{newResult(`32`, JsonNumber, `aKey`)}}, + test{`nested key selection`, `{"aKey":{"bKey":32}}`, `$.aKey+`, []Result{newResult(`{"bKey":32}`, JsonObject, `aKey`)}}, + test{`empty array`, `{"aKey":[]}`, `$.aKey+`, []Result{newResult(`[]`, JsonArray, `aKey`)}}, + test{`multiple same-level keys, weird spacing`, `{ "aKey" : true , "bKey": [ 1 , 2 ], "cKey" : true } `, `$.bKey+`, []Result{newResult(`[1,2]`, JsonArray, `bKey`)}}, + + test{`array index selection`, `{"aKey":[123,456]}`, `$.aKey[1]+`, []Result{newResult(`456`, JsonNumber, `aKey`, 1)}}, + test{`array wild index selection`, `{"aKey":[123,456]}`, `$.aKey[*]+`, []Result{newResult(`123`, JsonNumber, `aKey`, 0), newResult(`456`, JsonNumber, `aKey`, 1)}}, + test{`array range index selection`, `{"aKey":[11,22,33,44]}`, `$.aKey[1:3]+`, []Result{newResult(`22`, JsonNumber, `aKey`, 1), newResult(`33`, JsonNumber, `aKey`, 2)}}, + test{`array range (no index) selection`, `{"aKey":[11,22,33,44]}`, `$.aKey[1:1]+`, []Result{}}, + test{`array range (no upper bound) selection`, `{"aKey":[11,22,33]}`, `$.aKey[1:]+`, []Result{newResult(`22`, JsonNumber, `aKey`, 1), newResult(`33`, JsonNumber, `aKey`, 2)}}, + + test{`empty array - try selection`, `{"aKey":[]}`, `$.aKey[1]+`, []Result{}}, + test{`null selection`, `{"aKey":[null]}`, `$.aKey[0]+`, []Result{newResult(`null`, JsonNull, `aKey`, 0)}}, + test{`empty object`, `{"aKey":{}}`, `$.aKey+`, []Result{newResult(`{}`, JsonObject, `aKey`)}}, + test{`object w/ height=2`, `{"aKey":{"bKey":32}}`, `$.aKey.bKey+`, []Result{newResult(`32`, JsonNumber, `aKey`, `bKey`)}}, + test{`array of multiple types`, `{"aKey":[1,{"s":true},"asdf"]}`, `$.aKey[1]+`, []Result{newResult(`{"s":true}`, JsonObject, `aKey`, 1)}}, + test{`nested array selection`, `{"aKey":{"bKey":[123,456]}}`, `$.aKey.bKey+`, []Result{newResult(`[123,456]`, JsonArray, `aKey`, `bKey`)}}, + test{`nested array`, `[[[[[]], [true, false, []]]]]`, `$[0][0][1][2]+`, []Result{newResult(`[]`, JsonArray, 0, 0, 1, 2)}}, + test{`index of array selection`, `{"aKey":{"bKey":[123, 456, 789]}}`, `$.aKey.bKey[1]+`, []Result{newResult(`456`, JsonNumber, `aKey`, `bKey`, 1)}}, + test{`index of array selection (more than one)`, `{"aKey":{"bKey":[123,456]}}`, `$.aKey.bKey[1]+`, []Result{newResult(`456`, JsonNumber, `aKey`, `bKey`, 1)}}, + test{`multi-level object/array`, `{"1Key":{"aKey": null, "bKey":{"trash":[1,2]}, "cKey":[123,456] }, "2Key":false}`, `$.1Key.bKey.trash[0]+`, []Result{newResult(`1`, JsonNumber, `1Key`, `bKey`, `trash`, 0)}}, + test{`multi-level array`, `{"aKey":[true,false,null,{"michael":[5,6,7]}, ["s", "3"] ]}`, `$.*[*].michael[1]+`, []Result{newResult(`6`, JsonNumber, `aKey`, 3, `michael`, 1)}}, + test{`multi-level array 2`, `{"aKey":[true,false,null,{"michael":[5,6,7]}, ["s", "3"] ]}`, `$.*[*][1]+`, []Result{newResult(`"3"`, JsonString, `aKey`, 4, 1)}}, + + test{`evaluation literal equality`, `{"items":[ {"name":"alpha", "value":11}]}`, `$.items[*]?("bravo" == "bravo").value+`, []Result{newResult(`11`, JsonNumber, `items`, 0, `value`)}}, + test{`evaluation based on string equal to path value`, `{"items":[ {"name":"alpha", "value":11}, {"name":"bravo", "value":22}, {"name":"charlie", "value":33} ]}`, `$.items[*]?(@.name == "bravo").value+`, []Result{newResult(`22`, JsonNumber, `items`, 1, `value`)}}, +} + +func TestPathQuery(t *testing.T) { + as := assert.New(t) + + for _, t := range tests { + paths, err := ParsePaths(t.path) + if as.NoError(err) { + eval, err := EvalPathsInBytes([]byte(t.json), paths) + if as.NoError(err, "Testing: %s", t.name) { + res := toResultArray(eval) + if as.NoError(eval.Error) { + as.EqualValues(t.expected, res, "Testing of %q", t.name) + } + } + + eval_reader, err := EvalPathsInReader(strings.NewReader(t.json), paths) + if as.NoError(err, "Testing: %s", t.name) { + res := toResultArray(eval_reader) + if as.NoError(eval.Error) { + as.EqualValues(t.expected, res, "Testing of %q", t.name) + } + } + } + } +} + +func newResult(value string, typ int, keys ...interface{}) Result { + keysChanged := make([]interface{}, len(keys)) + for i, k := range keys { + switch v := k.(type) { + case string: + keysChanged[i] = []byte(v) + default: + keysChanged[i] = v + } + } + + return Result{ + Value: []byte(value), + Keys: keysChanged, + Type: typ, + } +} + +func toResultArray(e *Eval) []Result { + vals := make([]Result, 0) + for { + if r, ok := e.Next(); ok { + if r != nil { + vals = append(vals, *r) + } + } else { + break + } + } + return vals +} diff --git a/expression.go b/expression.go new file mode 100644 index 0000000..fd6d192 --- /dev/null +++ b/expression.go @@ -0,0 +1,429 @@ +package jsonpath + +import ( + "errors" + "fmt" + "math" + "reflect" + "strconv" +) + +const ( + exprErrorMismatchedParens = "Mismatched parentheses" + exprErrorBadExpression = "Bad Expression" + exprErrorFinalValueNotBool = "Expression evaluated to a non-bool: %v" + exprErrorNotEnoughOperands = "Not enough operands for operation %q" + exprErrorValueNotFound = "Value for %q not found" + exprErrorBadValue = "Bad value %q for type %q" + exprErrorPathValueNotScalar = "Path value must be scalar value" + exprErrorBadOperandType = "Operand type expected to be %q for operation %q" +) + +type exprErrorBadTypeComparison struct { + valueType string + expectedType string +} + +func (e exprErrorBadTypeComparison) Error() string { + return fmt.Sprintf("Type %s cannot be compared to type %s", e.valueType, e.expectedType) +} + +// Lowest priority = lowest # +var opa = map[int]struct { + prec int + rAssoc bool +}{ + exprOpAnd: {1, false}, + exprOpOr: {1, false}, + exprOpEq: {2, false}, + exprOpNeq: {2, false}, + exprOpLt: {3, false}, + exprOpLe: {3, false}, + exprOpGt: {3, false}, + exprOpGe: {3, false}, + exprOpPlus: {4, false}, + exprOpMinus: {4, false}, + exprOpSlash: {5, false}, + exprOpStar: {5, false}, + exprOpPercent: {5, false}, + exprOpHat: {6, false}, + exprOpNot: {7, true}, + exprOpPlusUn: {7, true}, + exprOpMinusUn: {7, true}, +} + +// Shunting-yard Algorithm (infix -> postfix) +// http://rosettacode.org/wiki/Parsing/Shunting-yard_algorithm#Go +func infixToPostFix(items []Item) (out []Item, err error) { + stack := newStack() + + for _, i := range items { + switch i.typ { + case exprParenLeft: + stack.push(i) // push "(" to stack + case exprParenRight: + found := false + for { + // pop item ("(" or operator) from stack + op_interface, ok := stack.pop() + if !ok { + return nil, errors.New(exprErrorMismatchedParens) + } + op := op_interface.(Item) + if op.typ == exprParenLeft { + found = true + break // discard "(" + } + out = append(out, op) // add operator to result + } + if !found { + return nil, errors.New(exprErrorMismatchedParens) + } + default: + if o1, isOp := opa[i.typ]; isOp { + // token is an operator + for stack.len() > 0 { + // consider top item on stack + op_int, _ := stack.peek() + op := op_int.(Item) + if o2, isOp := opa[op.typ]; !isOp || o1.prec > o2.prec || + o1.prec == o2.prec && o1.rAssoc { + break + } + // top item is an operator that needs to come off + stack.pop() // pop it + out = append(out, op) // add it to result + } + // push operator (the new one) to stack + stack.push(i) + } else { // token is an operand + out = append(out, i) // add operand to result + } + } + } + // drain stack to result + for stack.len() > 0 { + op_int, _ := stack.pop() + op := op_int.(Item) + if op.typ == exprParenLeft { + return nil, errors.New(exprErrorMismatchedParens) + } + out = append(out, op) + } + return +} + +func evaluatePostFix(postFixItems []Item, pathValues map[string]Item) (interface{}, error) { + s := newStack() + + if len(postFixItems) == 0 { + return false, errors.New(exprErrorBadExpression) + } + + for _, item := range postFixItems { + switch item.typ { + + // VALUES + case exprBool: + val, err := strconv.ParseBool(string(item.val)) + if err != nil { + return false, fmt.Errorf(exprErrorBadValue, string(item.val), exprTokenNames[exprBool]) + } + s.push(val) + case exprNumber: + val, err := strconv.ParseFloat(string(item.val), 64) + if err != nil { + return false, fmt.Errorf(exprErrorBadValue, string(item.val), exprTokenNames[exprNumber]) + } + s.push(val) + case exprPath: + // TODO: Handle datatypes of JSON + i, ok := pathValues[string(item.val)] + if !ok { + return false, fmt.Errorf(exprErrorValueNotFound, string(item.val)) + } + switch i.typ { + case jsonNull: + s.push(nil) + case jsonNumber: + val_float, err := strconv.ParseFloat(string(i.val), 64) + if err != nil { + return false, fmt.Errorf(exprErrorBadValue, string(item.val), jsonTokenNames[jsonNumber]) + } + s.push(val_float) + case jsonKey, jsonString: + s.push(i.val) + default: + return false, fmt.Errorf(exprErrorPathValueNotScalar) + } + case exprString: + s.push(item.val) + case exprNull: + s.push(nil) + + // OPERATORS + case exprOpAnd: + a, b, err := take2Bool(s, item.typ) + if err != nil { + return false, err + } + + s.push(a && b) + case exprOpEq: + p, ok := s.peek() + if !ok { + return false, fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[item.typ]) + } + switch p.(type) { + case nil: + err := take2Null(s, item.typ) + if err != nil { + return false, err + } else { + s.push(true) + } + case bool: + a, b, err := take2Bool(s, item.typ) + if err != nil { + return false, err + } + s.push(a == b) + case float64: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + s.push(a == b) + case []byte: + a, b, err := take2ByteSlice(s, item.typ) + if err != nil { + return false, err + } + s.push(byteSlicesEqual(a, b)) + } + case exprOpNeq: + p, ok := s.peek() + if !ok { + return false, fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[item.typ]) + } + switch p.(type) { + case nil: + err := take2Null(s, item.typ) + if err != nil { + return true, err + } else { + s.push(false) + } + case bool: + a, b, err := take2Bool(s, item.typ) + if err != nil { + return false, err + } + s.push(a != b) + case float64: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + s.push(a != b) + case []byte: + a, b, err := take2ByteSlice(s, item.typ) + if err != nil { + return false, err + } + s.push(!byteSlicesEqual(a, b)) + } + case exprOpNot: + a, err := take1Bool(s, item.typ) + if err != nil { + return false, err + } + + s.push(!a) + case exprOpOr: + a, b, err := take2Bool(s, item.typ) + if err != nil { + return false, err + } + + s.push(a || b) + case exprOpGt: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(b > a) + case exprOpGe: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(b >= a) + case exprOpLt: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(b < a) + case exprOpLe: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(b <= a) + case exprOpPlus: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + s.push(b + a) + case exprOpPlusUn: + a, err := take1Float(s, item.typ) + if err != nil { + return false, err + } + s.push(a) + case exprOpMinus: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + s.push(b - a) + case exprOpMinusUn: + a, err := take1Float(s, item.typ) + if err != nil { + return false, err + } + s.push(0 - a) + case exprOpSlash: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + if a == 0.0 { + return false, errors.New("Cannot divide by zero") + } + s.push(b / a) + case exprOpStar: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(b * a) + case exprOpPercent: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(math.Mod(b, a)) + case exprOpHat: + a, b, err := take2Float(s, item.typ) + if err != nil { + return false, err + } + + s.push(math.Pow(b, a)) + case exprOpExclam: + a, err := take1Bool(s, item.typ) + if err != nil { + return false, err + } + s.push(!a) + // Other + default: + return false, fmt.Errorf("Token not supported in evaluator: %v", exprTokenNames[item.typ]) + } + } + + if s.len() != 1 { + return false, fmt.Errorf(exprErrorBadExpression) + } + end_int, _ := s.pop() + return end_int, nil +} + +func take1Bool(s *stack, op int) (bool, error) { + t := exprBool + val, ok := s.pop() + if !ok { + return false, fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[op]) + } + + b, ok := val.(bool) + if !ok { + return false, exprErrorBadTypeComparison{exprTokenNames[t], (reflect.TypeOf(val)).String()} + } + return b, nil +} + +func take2Bool(s *stack, op int) (bool, bool, error) { + a, a_err := take1Bool(s, op) + b, b_err := take1Bool(s, op) + return a, b, firstError(a_err, b_err) +} + +func take1Float(s *stack, op int) (float64, error) { + t := exprNumber + val, ok := s.pop() + if !ok { + return 0.0, fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[op]) + } + + b, ok := val.(float64) + if !ok { + return 0.0, exprErrorBadTypeComparison{exprTokenNames[t], (reflect.TypeOf(val)).String()} + } + return b, nil +} + +func take2Float(s *stack, op int) (float64, float64, error) { + a, a_err := take1Float(s, op) + b, b_err := take1Float(s, op) + return a, b, firstError(a_err, b_err) +} + +func take1ByteSlice(s *stack, op int) ([]byte, error) { + t := exprNumber + val, ok := s.pop() + if !ok { + return nil, fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[op]) + } + + b, ok := val.([]byte) + if !ok { + return nil, exprErrorBadTypeComparison{exprTokenNames[t], (reflect.TypeOf(val)).String()} + } + return b, nil +} + +func take2ByteSlice(s *stack, op int) ([]byte, []byte, error) { + a, a_err := take1ByteSlice(s, op) + b, b_err := take1ByteSlice(s, op) + return a, b, firstError(a_err, b_err) +} + +func take1Null(s *stack, op int) error { + t := exprNull + val, ok := s.pop() + if !ok { + return fmt.Errorf(exprErrorNotEnoughOperands, exprTokenNames[op]) + } + + if v := reflect.TypeOf(val); v != nil { + return exprErrorBadTypeComparison{exprTokenNames[t], v.String()} + } + return nil +} + +func take2Null(s *stack, op int) error { + a_err := take1Null(s, op) + b_err := take1Null(s, op) + return firstError(a_err, b_err) +} diff --git a/expression_states.go b/expression_states.go new file mode 100644 index 0000000..81ab06d --- /dev/null +++ b/expression_states.go @@ -0,0 +1,287 @@ +package jsonpath + +const ( + exprError = iota + exprEOF + exprParenLeft + exprParenRight + exprNumber + exprPath + exprBool + exprNull + exprString + + exprOperators + exprOpEq + exprOpNeq + exprOpNot + exprOpLt + exprOpLe + exprOpGt + exprOpGe + exprOpAnd + exprOpOr + exprOpPlus + exprOpPlusUn + exprOpMinus + exprOpMinusUn + exprOpSlash + exprOpStar + exprOpHat + exprOpPercent + exprOpExclam +) + +var exprTokenNames = map[int]string{ + exprError: "error", + exprEOF: "EOF", + + exprParenLeft: "(", + exprParenRight: ")", + exprNumber: "number", + exprPath: "path", + exprBool: "bool", + exprNull: "null", + exprString: "string", + exprOpEq: "==", + exprOpNeq: "!=", + exprOpNot: "!", + exprOpLt: "<", + exprOpLe: "<=", + exprOpGt: ">", + exprOpGe: ">=", + exprOpAnd: "&&", + exprOpOr: "||", + exprOpPlus: "+", + exprOpPlusUn: "(+)", + exprOpMinus: "-", + exprOpMinusUn: "(-)", + exprOpSlash: "/", + exprOpStar: "*", + exprOpHat: "^", + exprOpPercent: "%", + exprOpExclam: "!", +} + +var EXPRESSION = lexExprText + +func lexExprText(l lexer, state *intStack) stateFn { + ignoreSpaceRun(l) + cur := l.peek() + var next stateFn + switch cur { + case '(': + l.take() + state.push(exprParenLeft) + l.emit(exprParenLeft) + next = lexExprText + case ')': + if top, ok := state.peek(); ok && top != exprParenLeft { + next = l.errorf("Received %#U but has no matching (", cur) + break + } + state.pop() + l.take() + l.emit(exprParenRight) + + next = lexOneValue + case '!': + l.take() + l.emit(exprOpNot) + next = lexExprText + case '+': + l.take() + l.emit(exprOpPlusUn) + next = lexExprText + case '-': + l.take() + l.emit(exprOpMinusUn) + next = lexExprText + case '@': //, '$': // Only support current key location + l.take() + takePath(l) + l.emit(exprPath) + next = lexOneValue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + takeNumeric(l) + l.emit(exprNumber) + next = lexOneValue + case 't': + takeExactSequence(l, bytesTrue) + l.emit(exprBool) + next = lexOneValue + case 'f': + takeExactSequence(l, bytesFalse) + l.emit(exprBool) + next = lexOneValue + case 'n': + takeExactSequence(l, bytesNull) + l.emit(exprNull) + next = lexOneValue + case '"': + err := l.takeString() + if err != nil { + return l.errorf("Could not take string because %q", err) + } + l.emit(exprString) + next = lexOneValue + case eof: + l.emit(exprEOF) + // next = nil + default: + return l.errorf("Unrecognized sequence in expression: %#U", cur) + } + return next +} + +func lexOneValue(l lexer, state *intStack) stateFn { + var next stateFn + cur := l.peek() + switch cur { + case '+': + l.take() + l.emit(exprOpPlus) + next = lexExprText + case '-': + l.take() + l.emit(exprOpMinus) + next = lexExprText + case '*': + l.take() + l.emit(exprOpStar) + next = lexExprText + case '/': + l.take() + l.emit(exprOpSlash) + next = lexExprText + case '%': + l.take() + l.emit(exprOpPercent) + next = lexExprText + case '^': + l.take() + l.emit(exprOpHat) + next = lexExprText + case '<': + l.take() + cur = l.peek() + if cur == '=' { + l.take() + l.emit(exprOpLe) + } else { + l.emit(exprOpLt) + } + next = lexExprText + case '>': + l.take() + cur = l.peek() + if cur == '=' { + l.take() + l.emit(exprOpGe) + } else { + l.emit(exprOpGt) + } + next = lexExprText + case '&': + l.take() + cur = l.take() + if cur != '&' { + return l.errorf("Expected double & instead of %#U", cur) + } + l.emit(exprOpAnd) + next = lexExprText + case '|': + l.take() + cur = l.take() + if cur != '|' { + return l.errorf("Expected double | instead of %#U", cur) + } + l.emit(exprOpOr) + next = lexExprText + case '=': + l.take() + cur = l.take() + if cur != '=' { + return l.errorf("Expected double = instead of %#U", cur) + } + l.emit(exprOpEq) + next = lexExprText + case '!': + l.take() + cur = l.take() + if cur != '=' { + return l.errorf("Expected = for != instead of %#U", cur) + } + l.emit(exprOpNeq) + next = lexExprText + case ')': + if top, ok := state.peek(); ok && top != exprParenLeft { + next = l.errorf("Received %#U but has no matching (", cur) + break + } + state.pop() + l.take() + l.emit(exprParenRight) + + next = lexOneValue + case eof: + l.emit(exprEOF) + default: + return l.errorf("Unrecognized sequence in expression: %#U", cur) + } + return next +} + +func takeNumeric(l lexer) { + takeDigits(l) + if l.peek() == '.' { + l.take() + takeDigits(l) + } + if l.peek() == 'e' || l.peek() == 'E' { + l.take() + if l.peek() == '+' || l.peek() == '-' { + l.take() + takeDigits(l) + } else { + takeDigits(l) + } + } +} + +func takePath(l lexer) { + inQuotes := false + var prev int = 0 + // capture until end of path - ugly +takeLoop: + for { + cur := l.peek() + switch cur { + case '"': + if prev != '\\' { + inQuotes = !inQuotes + } + l.take() + case ' ': + if !inQuotes { + break takeLoop + } + l.take() + case eof: + break takeLoop + default: + l.take() + } + + prev = cur + } +} + +func lexExprEnd(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != eof { + return l.errorf("Expected EOF but received %#U", cur) + } + l.emit(exprEOF) + return nil +} diff --git a/expression_states_test.go b/expression_states_test.go new file mode 100644 index 0000000..2861c30 --- /dev/null +++ b/expression_states_test.go @@ -0,0 +1,40 @@ +package jsonpath + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +var expressionTests = []lexTest{ + {"empty", "", []int{exprEOF}}, + {"spaces", " \t\r\n", []int{exprEOF}}, + {"numbers", " 1.3e10 ", []int{exprNumber, exprEOF}}, + // {"numbers with signs", "+1 -2.23", []int{exprNumber, exprOpPlus, exprNumber, exprEOF}}, + {"paths", " @.aKey[2].bKey ", []int{exprPath, exprEOF}}, + {"addition with mixed sign", "4+-19", []int{exprNumber, exprOpPlus, exprOpMinusUn, exprNumber, exprEOF}}, + {"addition", "4+19", []int{exprNumber, exprOpPlus, exprNumber, exprEOF}}, + {"subtraction", "4-19", []int{exprNumber, exprOpMinus, exprNumber, exprEOF}}, + + {"parens", "( () + () )", []int{exprParenLeft, exprParenLeft, exprParenRight, exprOpPlus, exprParenLeft, exprParenRight, exprParenRight, exprEOF}}, + {"equals", "true ==", []int{exprBool, exprOpEq, exprEOF}}, + {"numerical comparisons", "3.4 <", []int{exprNumber, exprOpLt, exprEOF}}, +} + +func TestExpressionTokens(t *testing.T) { + as := assert.New(t) + for _, test := range expressionTests { + lexer := NewSliceLexer([]byte(test.input), EXPRESSION) + items := readerToArray(lexer) + types := itemsToTypes(items) + + for _, i := range items { + if i.typ == exprError { + fmt.Println(string(i.val)) + } + } + + as.EqualValues(types, test.tokenTypes, "Testing of %s: \nactual\n\t%+v\nexpected\n\t%v", test.name, typesDescription(types, exprTokenNames), typesDescription(test.tokenTypes, exprTokenNames)) + } +} diff --git a/expression_test.go b/expression_test.go new file mode 100644 index 0000000..fca4903 --- /dev/null +++ b/expression_test.go @@ -0,0 +1,226 @@ +package jsonpath + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +var exprTests = []struct { + input string + fields map[string]Item + expectedValue interface{} +}{ + // && + {"true && true", nil, true}, + {"false && true", nil, false}, + {"false && false", nil, false}, + + // || + {"true || true", nil, true}, + {"true || false", nil, true}, + {"false || false", nil, false}, + + // LT + {"10 < 20", nil, true}, + {"10 < 10", nil, false}, + {"100 < 20", nil, false}, + {"@a < 50", map[string]Item{"@a": genValue(`49`, jsonNumber)}, true}, + {"@a < 50", map[string]Item{"@a": genValue(`50`, jsonNumber)}, false}, + {"@a < 50", map[string]Item{"@a": genValue(`51`, jsonNumber)}, false}, + + // LE + {"10 <= 20", nil, true}, + {"10 <= 10", nil, true}, + {"100 <= 20", nil, false}, + {"@a <= 54", map[string]Item{"@a": genValue(`53`, jsonNumber)}, true}, + {"@a <= 54", map[string]Item{"@a": genValue(`54`, jsonNumber)}, true}, + {"@a <= 54", map[string]Item{"@a": genValue(`55`, jsonNumber)}, false}, + + // GT + {"30 > 20", nil, true}, + {"20 > 20", nil, false}, + {"10 > 20", nil, false}, + {"@a > 50", map[string]Item{"@a": genValue(`49`, jsonNumber)}, false}, + {"@a > 50", map[string]Item{"@a": genValue(`50`, jsonNumber)}, false}, + {"@a > 50", map[string]Item{"@a": genValue(`51`, jsonNumber)}, true}, + + // GE + {"30 >= 20", nil, true}, + {"20 >= 20", nil, true}, + {"10 >= 20", nil, false}, + {"@a >= 50", map[string]Item{"@a": genValue(`49`, jsonNumber)}, false}, + {"@a >= 50", map[string]Item{"@a": genValue(`50`, jsonNumber)}, true}, + {"@a >= 50", map[string]Item{"@a": genValue(`51`, jsonNumber)}, true}, + + // EQ + {"20 == 20", nil, true}, + {"20 == 21", nil, false}, + {"true == true", nil, true}, + {"true == false", nil, false}, + {"@a == @b", map[string]Item{"@a": genValue(`"one"`, jsonString), "@b": genValue(`"one"`, jsonString)}, true}, + {"@a == @b", map[string]Item{"@a": genValue(`"one"`, jsonString), "@b": genValue(`"two"`, jsonString)}, false}, + {`"fire" == "fire"`, nil, true}, + {`"fire" == "water"`, nil, false}, + {`@a == "toronto"`, map[string]Item{"@a": genValue(`"toronto"`, jsonString)}, true}, + {`@a == "toronto"`, map[string]Item{"@a": genValue(`"los angeles"`, jsonString)}, false}, + {`@a == 3.4`, map[string]Item{"@a": genValue(`3.4`, jsonNumber)}, true}, + {`@a == 3.4`, map[string]Item{"@a": genValue(`3.41`, jsonNumber)}, false}, + {`@a == null`, map[string]Item{"@a": genValue(`null`, jsonNull)}, true}, + + // NEQ + {"20 != 20", nil, false}, + {"20 != 21", nil, true}, + {"true != true", nil, false}, + {"true != false", nil, true}, + {"@a != @b", map[string]Item{"@a": genValue(`"one"`, jsonString), "@b": genValue(`"one"`, jsonString)}, false}, + {"@a != @b", map[string]Item{"@a": genValue(`"one"`, jsonString), "@b": genValue(`"two"`, jsonString)}, true}, + {`"fire" != "fire"`, nil, false}, + {`"fire" != "water"`, nil, true}, + {`@a != "toronto"`, map[string]Item{"@a": genValue(`"toronto"`, jsonString)}, false}, + {`@a != "toronto"`, map[string]Item{"@a": genValue(`"los angeles"`, jsonString)}, true}, + {`@a != 3.4`, map[string]Item{"@a": genValue(`3.4`, jsonNumber)}, false}, + {`@a != 3.4`, map[string]Item{"@a": genValue(`3.41`, jsonNumber)}, true}, + {`@a != null`, map[string]Item{"@a": genValue(`null`, jsonNull)}, false}, + + // Plus + {"20 + 7", nil, 27}, + {"20 + 6.999999", nil, 26.999999}, + + // Minus + {"20 - 7", nil, 13}, + {"20 - 7.11111", nil, 12.88889}, + + // Minus Unary + {"-27", nil, -27}, + {"30 - -3", nil, 33}, + {"30 + -3", nil, 27}, + + {"2 +++++ 3", nil, 5}, + {"2+--3", nil, 5}, + // Star + {"20 * 7", nil, 140}, + {"20 * 6.999999", nil, 139.99998}, + {"20 * -7", nil, -140}, + {"-20 * -7", nil, 140}, + + // Slash + {"20 / 5", nil, 4}, + {"20 / 6.999999 - 2.85714326531 <= 0.00000001", nil, true}, + + // Hat + {"7 ^ 4", nil, 2401}, + {"2 ^ -2", nil, 0.25}, + {"((7 ^ -4) - 0.00041649312) <= 0.0001", nil, true}, + + // Mod + {"7.5 % 4", nil, 3.5}, + {"2 % -2", nil, 0}, + {"11 % 22", nil, 11}, + + // Negate + {"!true", nil, false}, + {"!false", nil, true}, + + // Mix + {"20 >= 20 || 2 == 2", nil, true}, + {"20 > @.test && @.test < 13 && @.test > 1.99994", map[string]Item{"@.test": genValue(`10.23423`, jsonNumber)}, true}, + {"20 > @.test && @.test < 13 && @.test > 1.99994", map[string]Item{"@.test": genValue(`15.3423`, jsonNumber)}, false}, +} + +func genValue(val string, typ int) Item { + return Item{ + val: []byte(val), + typ: typ, + } +} + +func TestExpressions(t *testing.T) { + as := assert.New(t) + emptyFields := map[string]Item{} + + for _, test := range exprTests { + if test.fields == nil { + test.fields = emptyFields + } + + lexer := NewSliceLexer([]byte(test.input), EXPRESSION) + items := readerToArray(lexer) + // trim EOF + items = items[0 : len(items)-1] + items_post, err := infixToPostFix(items) + if as.NoError(err, "Could not transform to postfix\nTest: %q", test.input) { + val, err := evaluatePostFix(items_post, test.fields) + if as.NoError(err, "Could not evaluate postfix\nTest Input: %q\nTest Values:%q\nError:%q", test.input, test.fields, err) { + as.EqualValues(test.expectedValue, val, "\nTest: %q\nActual: %v \nExpected %v\n", test.input, val, test.expectedValue) + } + } + } +} + +var exprErrorTests = []struct { + input string + fields map[string]Item + expectedErrorSubstring string +}{ + {"@a == @b", map[string]Item{"@a": genValue(`"one"`, jsonString), "@b": genValue("3.4", jsonNumber)}, "cannot be compared"}, + {")(", nil, "Mismatched parentheses"}, + {")123", nil, "Mismatched parentheses"}, + {"20 == null", nil, "cannot be compared"}, + {`"toronto" == null`, nil, "cannot be compared"}, + {`false == 20`, nil, "cannot be compared"}, + {`"nick" == 20`, nil, "cannot be compared"}, + {"20 != null", nil, "cannot be compared"}, + {`"toronto" != null`, nil, "cannot be compared"}, + {`false != 20`, nil, "cannot be compared"}, + {`"nick" != 20`, nil, "cannot be compared"}, + {``, nil, "Bad Expression"}, + {`==`, nil, "Bad Expression"}, + {`!=`, nil, "Not enough operands"}, + + {`!23`, nil, "cannot be compared"}, + {`"nick" || true`, nil, "cannot be compared"}, + {`"nick" >= 3.2`, nil, "cannot be compared"}, + {`"nick" >3.2`, nil, "cannot be compared"}, + {`"nick" <= 3.2`, nil, "cannot be compared"}, + {`"nick" < 3.2`, nil, "cannot be compared"}, + {`"nick" + 3.2`, nil, "cannot be compared"}, + {`"nick" - 3.2`, nil, "cannot be compared"}, + {`"nick" / 3.2`, nil, "cannot be compared"}, + {`"nick" * 3.2`, nil, "cannot be compared"}, + {`"nick" % 3.2`, nil, "cannot be compared"}, + {`"nick"+`, nil, "cannot be compared"}, + {`"nick"-`, nil, "cannot be compared"}, + {`"nick"^3.2`, nil, "cannot be compared"}, + + {`@a == null`, map[string]Item{"@a": genValue(`3.41`, jsonNumber)}, "cannot be compared"}, +} + +func TestBadExpressions(t *testing.T) { + as := assert.New(t) + emptyFields := map[string]Item{} + + for _, test := range exprErrorTests { + if test.fields == nil { + test.fields = emptyFields + } + + lexer := NewSliceLexer([]byte(test.input), EXPRESSION) + items := readerToArray(lexer) + // trim EOF + items = items[0 : len(items)-1] + items_post, err := infixToPostFix(items) + if err != nil { + as.True(strings.Contains(err.Error(), test.expectedErrorSubstring), "Test Input: %q\nError %q does not contain %q", test.input, err.Error(), test.expectedErrorSubstring) + continue + } + if as.NoError(err, "Could not transform to postfix\nTest: %q", test.input) { + _, err := evaluatePostFix(items_post, test.fields) + if as.Error(err, "Could not evaluate postfix\nTest Input: %q\nTest Values:%q\nError:%s", test.input, test.fields, err) { + as.True(strings.Contains(err.Error(), test.expectedErrorSubstring), "Test Input: %q\nError %s does not contain %q", test.input, err.Error(), test.expectedErrorSubstring) + } + + } + } +} diff --git a/json_states.go b/json_states.go new file mode 100644 index 0000000..2c28d7c --- /dev/null +++ b/json_states.go @@ -0,0 +1,266 @@ +package jsonpath + +const ( + jsonError = iota + jsonEOF + + jsonBraceLeft + jsonBraceRight + jsonBracketLeft + jsonBracketRight + jsonColon + jsonComma + jsonNumber + jsonString + jsonNull + jsonKey + jsonBool +) + +var trueBytes = []byte{'t', 'r', 'u', 'e'} +var falseBytes = []byte{'f', 'a', 'l', 's', 'e'} +var nullBytes = []byte{'n', 'u', 'l', 'l'} + +var jsonTokenNames = map[int]string{ + jsonEOF: "EOF", + jsonError: "ERROR", + + jsonBraceLeft: "{", + jsonBraceRight: "}", + jsonBracketLeft: "[", + jsonBracketRight: "]", + jsonColon: ":", + jsonComma: ",", + jsonNumber: "NUMBER", + jsonString: "STRING", + jsonNull: "NULL", + jsonKey: "KEY", + jsonBool: "BOOL", +} + +var JSON = lexJsonRoot + +func lexJsonRoot(l lexer, state *intStack) stateFn { + ignoreSpaceRun(l) + cur := l.peek() + var next stateFn + switch cur { + case '{': + next = stateJsonObjectOpen + case '[': + next = stateJsonArrayOpen + default: + next = l.errorf("Expected '{' or '[' at root of JSON instead of %#U", cur) + } + return next +} + +func stateJsonObjectOpen(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != '{' { + return l.errorf("Expected '{' as start of object instead of %#U", cur) + } + l.emit(jsonBraceLeft) + state.push(jsonBraceLeft) + + return stateJsonObject +} + +func stateJsonArrayOpen(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != '[' { + return l.errorf("Expected '[' as start of array instead of %#U", cur) + } + l.emit(jsonBracketLeft) + state.push(jsonBracketLeft) + + return stateJsonArray +} + +func stateJsonObject(l lexer, state *intStack) stateFn { + var next stateFn + cur := l.peek() + switch cur { + case '}': + if top, ok := state.peek(); ok && top != jsonBraceLeft { + next = l.errorf("Received %#U but has no matching '{'", cur) + break + } + l.take() + l.emit(jsonBraceRight) + state.pop() + next = stateJsonAfterValue + case '"': + next = stateJsonKey + default: + next = l.errorf("Expected '}' or \" within an object instead of %#U", cur) + } + return next +} + +func stateJsonArray(l lexer, state *intStack) stateFn { + var next stateFn + cur := l.peek() + switch cur { + case ']': + if top, ok := state.peek(); ok && top != jsonBracketLeft { + next = l.errorf("Received %#U but has no matching '['", cur) + break + } + l.take() + l.emit(jsonBracketRight) + state.pop() + next = stateJsonAfterValue + default: + next = stateJsonValue + } + return next +} + +func stateJsonAfterValue(l lexer, state *intStack) stateFn { + cur := l.take() + top, ok := state.peek() + topVal := noValue + if ok { + topVal = top + } + + switch cur { + case ',': + l.emit(jsonComma) + switch topVal { + case jsonBraceLeft: + return stateJsonKey + case jsonBracketLeft: + return stateJsonValue + case noValue: + return l.errorf("Found %#U outside of array or object", cur) + default: + return l.errorf("Unexpected character in lexer stack: %#U", cur) + } + case '}': + l.emit(jsonBraceRight) + state.pop() + switch topVal { + case jsonBraceLeft: + return stateJsonAfterValue + case jsonBracketLeft: + return l.errorf("Unexpected %#U in array", cur) + case noValue: + return stateJsonAfterRoot + } + case ']': + l.emit(jsonBracketRight) + state.pop() + switch topVal { + case jsonBraceLeft: + return l.errorf("Unexpected %#U in object", cur) + case jsonBracketLeft: + return stateJsonAfterValue + case noValue: + return stateJsonAfterRoot + } + case eof: + if state.len() == 0 { + l.emit(jsonEOF) + return nil + } else { + return l.errorf("Unexpected EOF instead of value") + } + default: + return l.errorf("Unexpected character after json value token: %#U", cur) + } + return nil +} + +func stateJsonKey(l lexer, state *intStack) stateFn { + if err := l.takeString(); err != nil { + return l.errorf(err.Error()) + } + l.emit(jsonKey) + + return stateJsonColon +} + +func stateJsonColon(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != ':' { + return l.errorf("Expected ':' after key string instead of %#U", cur) + } + l.emit(jsonColon) + + return stateJsonValue +} + +func stateJsonValue(l lexer, state *intStack) stateFn { + cur := l.peek() + + switch cur { + case eof: + return l.errorf("Unexpected EOF instead of value") + case '"': + return stateJsonString + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return stateJsonNumber + case 't', 'f': + return stateJsonBool + case 'n': + return stateJsonNull + case '{': + return stateJsonObjectOpen + case '[': + return stateJsonArrayOpen + default: + return l.errorf("Unexpected character as start of value: %#U", cur) + } +} + +func stateJsonString(l lexer, state *intStack) stateFn { + if err := l.takeString(); err != nil { + return l.errorf(err.Error()) + } + l.emit(jsonString) + return stateJsonAfterValue +} + +func stateJsonNumber(l lexer, state *intStack) stateFn { + if err := takeJSONNumeric(l); err != nil { + return l.errorf(err.Error()) + } + l.emit(jsonNumber) + return stateJsonAfterValue +} + +func stateJsonBool(l lexer, state *intStack) stateFn { + cur := l.peek() + var match []byte + switch cur { + case 't': + match = trueBytes + case 'f': + match = falseBytes + } + + if !takeExactSequence(l, match) { + return l.errorf("Could not parse value as 'true' or 'false'") + } + l.emit(jsonBool) + return stateJsonAfterValue +} + +func stateJsonNull(l lexer, state *intStack) stateFn { + if !takeExactSequence(l, nullBytes) { + return l.errorf("Could not parse value as 'null'") + } + l.emit(jsonNull) + return stateJsonAfterValue +} + +func stateJsonAfterRoot(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != eof { + return l.errorf("Expected EOF instead of %#U", cur) + } + l.emit(jsonEOF) + return nil +} diff --git a/json_states_test.go b/json_states_test.go new file mode 100644 index 0000000..4ba22c1 --- /dev/null +++ b/json_states_test.go @@ -0,0 +1,172 @@ +package jsonpath + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var jsonTests = []lexTest{ + {"empty object", `{}`, []int{jsonBraceLeft, jsonBraceRight, jsonEOF}}, + {"empty array", `[]`, []int{jsonBracketLeft, jsonBracketRight, jsonEOF}}, + {"key string", `{"key" :"value"}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonString, jsonBraceRight, jsonEOF}}, + {"multiple pairs", `{"key" :"value","key2" :"value"}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonString, jsonComma, jsonKey, jsonColon, jsonString, jsonBraceRight, jsonEOF}}, + {"key number", `{"key" : 12.34e+56}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonNumber, jsonBraceRight, jsonEOF}}, + {"key true", `{"key" :true}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBool, jsonBraceRight, jsonEOF}}, + {"key false", `{"key" :false}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBool, jsonBraceRight, jsonEOF}}, + {"key null", `{"key" :null}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonNull, jsonBraceRight, jsonEOF}}, + {"key arrayOf number", `{"key" :[23]}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonNumber, jsonBracketRight, jsonBraceRight, jsonEOF}}, + {"key array", `{"key" :[23,"45",67]}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonNumber, jsonComma, jsonString, jsonComma, jsonNumber, jsonBracketRight, jsonBraceRight, jsonEOF}}, + {"key array", `{"key" :["45",{}]}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonString, jsonComma, jsonBraceLeft, jsonBraceRight, jsonBracketRight, jsonBraceRight, jsonEOF}}, + {"key nestedObject", `{"key" :{"innerkey":"value"}}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBraceLeft, jsonKey, jsonColon, jsonString, jsonBraceRight, jsonBraceRight, jsonEOF}}, + {"key nestedArray", `[1,["a","b"]]`, []int{jsonBracketLeft, jsonNumber, jsonComma, jsonBracketLeft, jsonString, jsonComma, jsonString, jsonBracketRight, jsonBracketRight, jsonEOF}}, +} + +func TestValidJson(t *testing.T) { + as := assert.New(t) + + for _, test := range jsonTests { + lexer := NewSliceLexer([]byte(test.input), JSON) + types := itemsToTypes(readerToArray(lexer)) + + as.EqualValues(types, test.tokenTypes, "Testing of %q: \nactual\n\t%+v\nexpected\n\t%v", test.name, typesDescription(types, jsonTokenNames), typesDescription(test.tokenTypes, jsonTokenNames)) + } +} + +var errorJsonTests = []lexTest{ + {"Missing end brace", `{`, []int{jsonBraceLeft, jsonError}}, + {"Missing start brace", `}`, []int{jsonError}}, + {"Missing key start quote", `{key":true}`, []int{jsonBraceLeft, jsonError}}, + {"Missing key end quote", `{"key:true}`, []int{jsonBraceLeft, jsonError}}, + {"Missing colon", `{"key"true}`, []int{jsonBraceLeft, jsonKey, jsonError}}, + {"Missing value", `{"key":}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonError}}, + {"Missing string start quote", `{"key":test"}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonError}}, + {"Missing embedded array bracket", `{"key":[}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonError}}, + {"Missing values in array", `{"key":[,]`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonError}}, + {"Missing value after comma", `{"key":[343,]}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonNumber, jsonComma, jsonError}}, + {"Missing comma in array", `{"key":[234 424]}`, []int{jsonBraceLeft, jsonKey, jsonColon, jsonBracketLeft, jsonNumber, jsonError}}, +} + +func TestMalformedJson(t *testing.T) { + as := assert.New(t) + + for _, test := range errorJsonTests { + lexer := NewSliceLexer([]byte(test.input), JSON) + types := itemsToTypes(readerToArray(lexer)) + + as.EqualValues(types, test.tokenTypes, "Testing of %q: \nactual\n\t%+v\nexpected\n\t%v", test.name, typesDescription(types, jsonTokenNames), typesDescription(test.tokenTypes, jsonTokenNames)) + } +} + +func itemsToTypes(items []Item) []int { + types := make([]int, len(items)) + for i, item := range items { + types[i] = item.typ + } + return types +} + +// func TestEarlyTerminationForJSON(t *testing.T) { +// as := assert.New(t) +// wg := sync.WaitGroup{} + +// lexer := NewSliceLexer(`{"key":"value", "key2":{"ikey":3}, "key3":[1,2,3,4]}`) +// wg.Add(1) +// go func() { +// lexer.Run(JSON) +// wg.Done() +// }() + +// // Pop a few items +// <-lexer.items +// <-lexer.items +// // Kill command +// close(lexer.kill) + +// wg.Wait() +// remainingItems := readerToArray(lexer.items) +// // TODO: Occasionally fails - rethink this +// _ = as +// _ = remainingItems +// // as.True(len(remainingItems) <= bufferSize, "Count of remaining items should be less than buffer size: %d", len(remainingItems)) +// } + +var examples = []string{ + `{"items":[ + { + "name": "example document for wicked fast parsing of huge json docs", + "integer": 123, + "totally sweet scientific notation": -123.123e-2, + "unicode? you betcha!": "ú™£¢∞§\u2665", + "zero character": "0", + "null is boring": null + }, + { + "name": "another object", + "cooler than first object?": true, + "nested object": { + "nested object?": true, + "is nested array the same combination i have on my luggage?": true, + "nested array": [1,2,3,4,5] + }, + "false": false + } +]}`, + `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product set", + "type": "array", + "items": { + "title": "Product", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "number" + }, + "name": { + "type": "string" + }, + "price": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "dimensions": { + "type": "object", + "properties": { + "length": {"type": "number"}, + "width": {"type": "number"}, + "height": {"type": "number"} + }, + "required": ["length", "width", "height"] + }, + "warehouseLocation": { + "description": "Coordinates of the warehouse with the product", + "$ref": "http://json-schema.org/geo" + } + }, + "required": ["id", "name", "price"] + } + }`, +} + +func TestMixedCaseJson(t *testing.T) { + as := assert.New(t) + for _, json := range examples { + lexer := NewSliceLexer([]byte(json), JSON) + items := readerToArray(lexer) + + for _, i := range items { + as.False(i.typ == jsonError, "Found error while parsing: %q", i.val) + } + } +} diff --git a/lexer.go b/lexer.go new file mode 100644 index 0000000..bc7aebf --- /dev/null +++ b/lexer.go @@ -0,0 +1,86 @@ +package jsonpath + +type Pos int +type stateFn func(lexer, *intStack) stateFn + +const ( + lexError = 0 // must match jsonError and pathError + lexEOF = 1 + eof = -1 + noValue = -2 +) + +type Item struct { + typ int + pos Pos // The starting position, in bytes, of this Item in the input string. + val []byte +} + +// Used by evaluator +type tokenReader interface { + next() (*Item, bool) +} + +// Used by state functions +type lexer interface { + tokenReader + take() int + takeString() error + peek() int + emit(int) + ignore() + errorf(string, ...interface{}) stateFn + reset() +} + +type lex struct { + initialState stateFn + currentStateFn stateFn + item Item + hasItem bool + stack intStack +} + +func newLex(initial stateFn) lex { + return lex{ + initialState: initial, + currentStateFn: initial, + item: Item{}, + stack: *newIntStack(), + } +} + +func (i *Item) clone() *Item { + ic := Item{ + typ: i.typ, + pos: i.pos, + val: make([]byte, len(i.val)), + } + copy(ic.val, i.val) + return &ic +} + +func itemsDescription(items []Item, nameMap map[int]string) []string { + vals := make([]string, len(items)) + for i, item := range items { + vals[i] = itemDescription(&item, nameMap) + } + return vals +} + +func itemDescription(item *Item, nameMap map[int]string) string { + var found bool + val, found := nameMap[item.typ] + if !found { + return string(item.val) + } + return val +} + +func typesDescription(types []int, nameMap map[int]string) []string { + vals := make([]string, len(types)) + for i, val := range types { + vals[i] = nameMap[val] + } + return vals +} diff --git a/lexer_reader.go b/lexer_reader.go new file mode 100644 index 0000000..066d8dc --- /dev/null +++ b/lexer_reader.go @@ -0,0 +1,161 @@ +package jsonpath + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" +) + +type readerLexer struct { + lex + bufInput *bufio.Reader + input io.Reader + pos Pos + nextByte int + lexeme *bytes.Buffer +} + +func NewReaderLexer(rr io.Reader, initial stateFn) *readerLexer { + l := readerLexer{ + input: rr, + bufInput: bufio.NewReader(rr), + nextByte: noValue, + lex: newLex(initial), + lexeme: bytes.NewBuffer(make([]byte, 0, 100)), + } + return &l +} + +func (l *readerLexer) take() int { + if l.nextByte == noValue { + l.peek() + } + + nr := l.nextByte + l.nextByte = noValue + l.lexeme.WriteByte(byte(nr)) + return nr +} + +func (l *readerLexer) takeString() error { + cur := l.take() + if cur != '"' { + return fmt.Errorf("Expected \" as start of string instead of %#U", cur) + } + + var previous byte +looper: + for { + curByte, err := l.bufInput.ReadByte() + if err == io.EOF { + return errors.New("Unexpected EOF in string") + } + l.lexeme.WriteByte(curByte) + + if curByte == '"' { + if previous != '\\' { + break looper + } else { + curByte, err = l.bufInput.ReadByte() + if err == io.EOF { + return errors.New("Unexpected EOF in string") + } + l.lexeme.WriteByte(curByte) + } + } + + previous = curByte + } + return nil +} + +func (l *readerLexer) peek() int { + if l.nextByte != noValue { + return l.nextByte + } + + r, err := l.bufInput.ReadByte() + if err == io.EOF { + l.nextByte = eof + return eof + } + + l.nextByte = int(r) + return l.nextByte +} + +func (l *readerLexer) emit(t int) { + l.setItem(t, l.pos, l.lexeme.Bytes()) + l.pos += Pos(l.lexeme.Len()) + l.hasItem = true + + if t == lexEOF { + // Do not capture eof character to match slice_lexer + l.item.val = []byte{} + } + + // Ignore whitespace after this token + if l.nextByte == noValue { + l.peek() + } + + // ignore white space + for l.nextByte != eof { + if l.nextByte == ' ' || l.nextByte == '\t' || l.nextByte == '\r' || l.nextByte == '\n' { + l.pos++ + r, err := l.bufInput.ReadByte() + if err == io.EOF { + l.nextByte = eof + } else { + l.nextByte = int(r) + } + } else { + break + } + } +} + +func (l *readerLexer) setItem(typ int, pos Pos, val []byte) { + l.item.typ = typ + l.item.pos = pos + l.item.val = val +} + +func (l *readerLexer) ignore() { + l.pos += Pos(l.lexeme.Len()) + l.lexeme.Reset() +} + +func (l *readerLexer) next() (*Item, bool) { + l.lexeme.Reset() + for { + if l.currentStateFn == nil { + break + } + + l.currentStateFn = l.currentStateFn(l, &l.stack) + + if l.hasItem { + l.hasItem = false + return &l.item, true + } + } + return &l.item, false +} + +func (l *readerLexer) errorf(format string, args ...interface{}) stateFn { + l.setItem(lexError, l.pos, []byte(fmt.Sprintf(format, args...))) + l.lexeme.Truncate(0) + l.hasItem = true + return nil +} + +func (l *readerLexer) reset() { + l.bufInput.Reset(l.input) + l.lexeme.Reset() + l.nextByte = noValue + l.pos = 0 + l.lex = newLex(l.initialState) +} diff --git a/lexer_slice.go b/lexer_slice.go new file mode 100644 index 0000000..0d5aa66 --- /dev/null +++ b/lexer_slice.go @@ -0,0 +1,131 @@ +package jsonpath + +import ( + "errors" + "fmt" +) + +type sliceLexer struct { + lex + input []byte // the []byte being scanned. + start Pos // start position of this Item. + pos Pos // current position in the input +} + +func NewSliceLexer(input []byte, initial stateFn) *sliceLexer { + l := &sliceLexer{ + lex: newLex(initial), + input: input, + } + return l +} + +func (l *sliceLexer) take() int { + if int(l.pos) >= len(l.input) { + return eof + } + r := int(l.input[l.pos]) + l.pos += 1 + return r +} + +func (l *sliceLexer) takeString() error { + curPos := l.pos + inputLen := len(l.input) + + if int(curPos) >= inputLen { + return errors.New("End of file where string expected") + } + + cur := int(l.input[curPos]) + curPos++ + if cur != '"' { + l.pos = curPos + return fmt.Errorf("Expected \" as start of string instead of %#U", cur) + } + + var previous int +looper: + for { + if int(curPos) >= inputLen { + l.pos = curPos + return errors.New("End of file where string expected") + } + cur := int(l.input[curPos]) + curPos++ + if cur == '"' { + if previous == noValue || previous != '\\' { + break looper + } else { + l.take() + } + } + + previous = cur + } + l.pos = curPos + return nil +} + +func (l *sliceLexer) peek() int { + if int(l.pos) >= len(l.input) { + return eof + } + return int(l.input[l.pos]) +} + +func (l *sliceLexer) emit(t int) { + l.setItem(t, l.start, l.input[l.start:l.pos]) + l.hasItem = true + + // Ignore whitespace after this token + for int(l.pos) < len(l.input) { + r := l.input[l.pos] + if r == ' ' || r == '\t' || r == '\r' || r == '\n' { + l.pos++ + } else { + break + } + } + + l.start = l.pos +} + +func (l *sliceLexer) setItem(typ int, pos Pos, val []byte) { + l.item.typ = typ + l.item.pos = pos + l.item.val = val +} + +func (l *sliceLexer) ignore() { + l.start = l.pos +} + +func (l *sliceLexer) next() (*Item, bool) { + for { + if l.currentStateFn == nil { + break + } + + l.currentStateFn = l.currentStateFn(l, &l.stack) + + if l.hasItem { + l.hasItem = false + return &l.item, true + } + } + return &l.item, false +} + +func (l *sliceLexer) errorf(format string, args ...interface{}) stateFn { + l.setItem(lexError, l.start, []byte(fmt.Sprintf(format, args...))) + l.start = l.pos + l.hasItem = true + return nil +} + +func (l *sliceLexer) reset() { + l.start = 0 + l.pos = 0 + l.lex = newLex(l.initialState) +} diff --git a/lexer_test.go b/lexer_test.go new file mode 100644 index 0000000..6627f76 --- /dev/null +++ b/lexer_test.go @@ -0,0 +1,309 @@ +package jsonpath + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "math" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testLexerMethods(l lexer, as *assert.Assertions) { + s := l.peek() + as.EqualValues('{', s, "First rune should match") + r := l.take() + as.EqualValues('{', r, "First rune should match") + r = l.take() + as.EqualValues('"', r, "Second rune should match") + r = l.take() + as.EqualValues('k', r, "Third rune should match") + // Try peeking + r = l.peek() + as.EqualValues('e', r, "Peek fifth rune should match") + // Second peek should yield same result + r = l.peek() + as.EqualValues('e', r, "Peek fifth rune should match") + r = l.take() + // Taking should yield peeked result + as.EqualValues('e', r, "Rune should match") + // Taking should yield next result + r = l.take() + as.EqualValues('y', r, "Rune should match") + r = l.take() + as.EqualValues('"', r, "Rune should match") + r = l.peek() + as.EqualValues(' ', r, "Rune should match") + + l.take() + l.ignore() + + r = l.peek() + as.EqualValues(':', r, "Rune should match") +} + +func TestLexerMethods(t *testing.T) { + as := assert.New(t) + input := `{"key" :"value"}` + + sl := NewSliceLexer([]byte(input), JSON) + testLexerMethods(sl, as) + + r := strings.NewReader(input) + rl := NewReaderLexer(r, JSON) + testLexerMethods(rl, as) +} + +const ( + sampleMix = `{"Type":"inventory.all","Id":1, "Digest": "f0e8ff11922e2e988ad8a68e99dbd055be7445ee", "NodeID": "588be7c36150f57babf564e1a25ea1c1", "Content": {"sysinfo.package": {"Entries":[{"Category":"RPM_Packages","Details":[{"Tag":"Name","Value":"initscripts"},{"Tag":"Summary","Value":"The inittab file and the /etc/init.d scripts"},{"Tag":"Version","Value":"9.03.40"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"5720353"},{"Tag":"InstallTime","Value":"1412965846"},{"Tag":"Name","Value":"setup"},{"Tag":"Summary","Value":"A set of system configuration and setup files"},{"Tag":"Version","Value":"2.8.14"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"666477"},{"Tag":"InstallTime","Value":"1412965633"},{"Tag":"Name","Value":"dracut"},{"Tag":"Summary","Value":"Initramfs generator using udev"},{"Tag":"Version","Value":"004"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"257710"},{"Tag":"InstallTime","Value":"1412965847"},{"Tag":"Name","Value":"basesystem"},{"Tag":"Summary","Value":"The skeleton package which defines a simple Red Hat Enterprise Linux system"},{"Tag":"Version","Value":"10.0"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"0"},{"Tag":"InstallTime","Value":"1412965634"},{"Tag":"Name","Value":"kernel"},{"Tag":"Summary","Value":"The Linux kernel"},{"Tag":"Version","Value":"2.6.32"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"126773875"},{"Tag":"InstallTime","Value":"1412965853"},{"Tag":"Name","Value":"e2fsprogs"},{"Tag":"Summary","Value":"Utilities for managing ext2, ext3, and ext4 filesystems"},{"Tag":"Version","Value":"1.41.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2020232"},{"Tag":"InstallTime","Value":"1412965856"},{"Tag":"Name","Value":"curl"},{"Tag":"Summary","Value":"A utility for getting files from remote servers (FTP, HTTP, and others)"},{"Tag":"Version","Value":"7.19.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"355395"},{"Tag":"InstallTime","Value":"1412965888"},{"Tag":"Name","Value":"ncurses-libs"},{"Tag":"Summary","Value":"Ncurses libraries"},{"Tag":"Version","Value":"5.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"760448"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"audit"},{"Tag":"Summary","Value":"User space tools for 2.6 kernel auditing"},{"Tag":"Version","Value":"2.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"979444"},{"Tag":"InstallTime","Value":"1412965889"},{"Tag":"Name","Value":"libattr"},{"Tag":"Summary","Value":"Dynamic library for extended attribute support"},{"Tag":"Version","Value":"2.4.44"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"18712"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"ql2400-firmware"},{"Tag":"Summary","Value":"Firmware for qlogic 2400 devices"},{"Tag":"Version","Value":"7.03.00"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"262499"},{"Tag":"InstallTime","Value":"1412965890"},{"Tag":"Name","Value":"zlib"},{"Tag":"Summary","Value":"The zlib compression and decompression library"},{"Tag":"Version","Value":"1.2.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"152305"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"libedit"},{"Tag":"Summary","Value":"The NetBSD Editline library"},{"Tag":"Version","Value":"2.11"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"185648"},{"Tag":"InstallTime","Value":"1412986023"},{"Tag":"Name","Value":"ntpdate"},{"Tag":"Summary","Value":"Utility to set the date and time via NTP"},{"Tag":"Version","Value":"4.2.6p5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"121391"},{"Tag":"InstallTime","Value":"1412987706"},{"Tag":"Name","Value":"chkconfig"},{"Tag":"Summary","Value":"A system tool for maintaining the /etc/rc*.d hierarchy"},{"Tag":"Version","Value":"1.3.49.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"670132"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"ipmitool"},{"Tag":"Summary","Value":"Utility for IPMI control"},{"Tag":"Version","Value":"1.8.11"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1044037"},{"Tag":"InstallTime","Value":"1412989003"},{"Tag":"Name","Value":"bzip2-libs"},{"Tag":"Summary","Value":"Libraries for applications using bzip2"},{"Tag":"Version","Value":"1.0.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"67592"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"libselinux"},{"Tag":"Summary","Value":"SELinux library and simple utilities"},{"Tag":"Version","Value":"2.0.94"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"130088"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"shadow-utils"},{"Tag":"Summary","Value":"Utilities for managing accounts and shadow password files"},{"Tag":"Version","Value":"4.1.4.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2777471"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"nss-softokn-freebl"},{"Tag":"Summary","Value":"Freebl library for the Network Security Services"},{"Tag":"Version","Value":"3.14.3"},{"Tag":"Arch","Value":"i686"},{"Tag":"Size","Value":"371211"},{"Tag":"InstallTime","Value":"1412989021"},{"Tag":"Name","Value":"readline"},{"Tag":"Summary","Value":"A library for editing typed command lines"},{"Tag":"Version","Value":"6.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"433957"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"dmidecode"},{"Tag":"Summary","Value":"Tool to analyse BIOS DMI data"},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"195002"},{"Tag":"InstallTime","Value":"1412989023"},{"Tag":"Name","Value":"dbus-libs"},{"Tag":"Summary","Value":"Libraries for accessing D-BUS"},{"Tag":"Version","Value":"1.2.24"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"265728"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"gpg-pubkey"},{"Tag":"Summary","Value":"gpg(EPEL (6) \u003cepel@fedoraproject.org\u003e)"},{"Tag":"Version","Value":"0608b895"},{"Tag":"Arch","Value":"(none)"},{"Tag":"Size","Value":"0"},{"Tag":"InstallTime","Value":"1413304599"},{"Tag":"Name","Value":"pcre"},{"Tag":"Summary","Value":"Perl-compatible regular expression library"},{"Tag":"Version","Value":"7.8"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"526268"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"libxml2-python"},{"Tag":"Summary","Value":"Python bindings for the libxml2 library"},{"Tag":"Version","Value":"2.7.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1976700"},{"Tag":"InstallTime","Value":"1413304602"},{"Tag":"Name","Value":"lua"},{"Tag":"Summary","Value":"Powerful light-weight programming language"},{"Tag":"Version","Value":"5.1.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"617799"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"usbutils"},{"Tag":"Summary","Value":"Linux USB utilities"},{"Tag":"Version","Value":"003"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"179745"},{"Tag":"InstallTime","Value":"1413304603"},{"Tag":"Name","Value":"cyrus-sasl-lib"},{"Tag":"Summary","Value":"Shared libraries needed by applications which use Cyrus SASL"},{"Tag":"Version","Value":"2.1.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"357710"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"pciutils"},{"Tag":"Summary","Value":"PCI bus related utilities"},{"Tag":"Version","Value":"3.1.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"183931"},{"Tag":"InstallTime","Value":"1413304604"},{"Tag":"Name","Value":"apr"},{"Tag":"Summary","Value":"Apache Portable Runtime library"},{"Tag":"Version","Value":"1.3.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"303205"},{"Tag":"InstallTime","Value":"1418766182"},{"Tag":"Name","Value":"httpd-tools"},{"Tag":"Summary","Value":"Tools for use with the Apache HTTP Server"},{"Tag":"Version","Value":"2.2.15"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"140449"},{"Tag":"InstallTime","Value":"1418766184"},{"Tag":"Name","Value":"libgpg-error"},{"Tag":"Summary","Value":"Library for error values used by GnuPG components"},{"Tag":"Version","Value":"1.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"214321"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"mailcap"},{"Tag":"Summary","Value":"Helper application and MIME type associations for file types"},{"Tag":"Version","Value":"2.1.31"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"52877"},{"Tag":"InstallTime","Value":"1418766185"},{"Tag":"Name","Value":"libselinux-utils"},{"Tag":"Summary","Value":"SELinux libselinux utilies"},{"Tag":"Version","Value":"2.0.94"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"62593"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"hdparm"},{"Tag":"Summary","Value":"A utility for displaying and/or setting hard disk parameters"},{"Tag":"Version","Value":"9.43"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"153536"},{"Tag":"InstallTime","Value":"1418777989"},{"Tag":"Name","Value":"bzip2"},{"Tag":"Summary","Value":"A file compression utility"},{"Tag":"Version","Value":"1.0.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"79087"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"glibc"},{"Tag":"Summary","Value":"The GNU libc libraries"},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"12959134"},{"Tag":"InstallTime","Value":"1419897862"},{"Tag":"Name","Value":"perl-version"},{"Tag":"Summary","Value":"Perl extension for Version Objects"},{"Tag":"Version","Value":"0.77"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"51960"},{"Tag":"InstallTime","Value":"1419897868"},{"Tag":"Name","Value":"sysvinit-tools"},{"Tag":"Summary","Value":"Tools used for process and utmp management."},{"Tag":"Version","Value":"2.87"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"112727"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"perl-libs"},{"Tag":"Summary","Value":"The libraries for the perl runtime"},{"Tag":"Version","Value":"5.10.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1485896"},{"Tag":"InstallTime","Value":"1419897869"},{"Tag":"Name","Value":"pth"},{"Tag":"Summary","Value":"The GNU Portable Threads library"},{"Tag":"Version","Value":"2.0.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"261931"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"perl"},{"Tag":"Summary","Value":"Practical Extraction and Report Language"},{"Tag":"Version","Value":"5.10.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"35282080"},{"Tag":"InstallTime","Value":"1419897872"},{"Tag":"Name","Value":"xz"},{"Tag":"Summary","Value":"LZMA compression utilities"},{"Tag":"Version","Value":"4.999.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"488160"},{"Tag":"InstallTime","Value":"1419897875"},{"Tag":"Name","Value":"man"},{"Tag":"Summary","Value":"A set of documentation tools: man, apropos and whatis"},{"Tag":"Version","Value":"1.6f"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"398388"},{"Tag":"InstallTime","Value":"1419897877"},{"Tag":"Name","Value":"cvs"},{"Tag":"Summary","Value":"A version control system"},{"Tag":"Version","Value":"1.11.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1590583"},{"Tag":"InstallTime","Value":"1419897878"},{"Tag":"Name","Value":"libusb"},{"Tag":"Summary","Value":"A library which allows userspace access to USB devices"},{"Tag":"Version","Value":"0.1.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"54440"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"pax"},{"Tag":"Summary","Value":"POSIX File System Archiver"},{"Tag":"Version","Value":"3.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"137868"},{"Tag":"InstallTime","Value":"1419897879"},{"Tag":"Name","Value":"libgomp"},{"Tag":"Summary","Value":"GCC OpenMP v3.0 shared support library"},{"Tag":"Version","Value":"4.4.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"127982"},{"Tag":"InstallTime","Value":"1419897880"},{"Tag":"Name","Value":"libutempter"},{"Tag":"Summary","Value":"A privileged helper for utmp/wtmp updates"},{"Tag":"Version","Value":"1.1.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"40785"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"time"},{"Tag":"Summary","Value":"A GNU utility for monitoring a program's use of system resources"},{"Tag":"Version","Value":"1.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"39759"},{"Tag":"InstallTime","Value":"1419897883"},{"Tag":"Name","Value":"vim-minimal"},{"Tag":"Summary","Value":"A minimal version of the VIM editor"},{"Tag":"Version","Value":"7.2.411"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"766050"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"db4-devel"},{"Tag":"Summary","Value":"C development files for the Berkeley DB (version 4) library"},{"Tag":"Version","Value":"4.7.25"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"25205070"},{"Tag":"InstallTime","Value":"1419897885"},{"Tag":"Name","Value":"net-tools"},{"Tag":"Summary","Value":"Basic networking tools"},{"Tag":"Version","Value":"1.60"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"778085"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"at"},{"Tag":"Summary","Value":"Job spooling tools"},{"Tag":"Version","Value":"3.1.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"94036"},{"Tag":"InstallTime","Value":"1419897887"},{"Tag":"Name","Value":"tar"},{"Tag":"Summary","Value":"A GNU file archiving program"},{"Tag":"Version","Value":"1.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2616001"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"gdbm-devel"},{"Tag":"Summary","Value":"Development libraries and header files for the gdbm library"},{"Tag":"Version","Value":"1.8.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"22134"},{"Tag":"InstallTime","Value":"1419897888"},{"Tag":"Name","Value":"glibc-headers"},{"Tag":"Summary","Value":"Header files for development using standard C libraries."},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2144199"},{"Tag":"InstallTime","Value":"1419897891"},{"Tag":"Name","Value":"pinentry"},{"Tag":"Summary","Value":"Collection of simple PIN or passphrase entry dialogs"},{"Tag":"Version","Value":"0.7.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"143708"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"perl-Test-Harness"},{"Tag":"Summary","Value":"Run Perl standard test scripts with statistics"},{"Tag":"Version","Value":"3.17"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"408793"},{"Tag":"InstallTime","Value":"1419897892"},{"Tag":"Name","Value":"binutils"},{"Tag":"Summary","Value":"A GNU collection of binary utilities"},{"Tag":"Version","Value":"2.20.51.0.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"9814963"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"perl-ExtUtils-MakeMaker"},{"Tag":"Summary","Value":"Create a module Makefile"},{"Tag":"Version","Value":"6.55"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"622454"},{"Tag":"InstallTime","Value":"1419897893"},{"Tag":"Name","Value":"m4"},{"Tag":"Summary","Value":"The GNU macro processor"},{"Tag":"Version","Value":"1.4.13"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"560949"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"perl-Test-Simple"},{"Tag":"Summary","Value":"Basic utilities for writing tests"},{"Tag":"Version","Value":"0.92"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"188830"},{"Tag":"InstallTime","Value":"1419897894"},{"Tag":"Name","Value":"dash"},{"Tag":"Summary","Value":"Small and fast POSIX-compliant shell"},{"Tag":"Version","Value":"0.5.5.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"127277"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"glibc"},{"Tag":"Summary","Value":"The GNU libc libraries"},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"i686"},{"Tag":"Size","Value":"13791310"},{"Tag":"InstallTime","Value":"1419897896"},{"Tag":"Name","Value":"groff"},{"Tag":"Summary","Value":"A document formatting system"},{"Tag":"Version","Value":"1.18.1.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"5318766"},{"Tag":"InstallTime","Value":"1412965652"},{"Tag":"Name","Value":"wget"},{"Tag":"Summary","Value":"A utility for retrieving files using the HTTP or FTP protocols"},{"Tag":"Version","Value":"1.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1881673"},{"Tag":"InstallTime","Value":"1422408926"},{"Tag":"Name","Value":"MegaCli"},{"Tag":"Summary","Value":"MegaCli SAS RAID Management Utility."},{"Tag":"Version","Value":"8.07.14"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"5730524"},{"Tag":"InstallTime","Value":"1422409051"},{"Tag":"Name","Value":"cracklib"},{"Tag":"Summary","Value":"A password-checking library"},{"Tag":"Version","Value":"2.8.16"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"187265"},{"Tag":"InstallTime","Value":"1412965652"},{"Tag":"Name","Value":"module-init-tools"},{"Tag":"Summary","Value":"Kernel module management utilities."},{"Tag":"Version","Value":"3.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1207417"},{"Tag":"InstallTime","Value":"1412965654"},{"Tag":"Name","Value":"redhat-logos"},{"Tag":"Summary","Value":"CentOS-related icons and pictures"},{"Tag":"Version","Value":"60.0.14"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"15816517"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"libpciaccess"},{"Tag":"Summary","Value":"PCI access library"},{"Tag":"Version","Value":"0.13.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"37951"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"libcap-ng"},{"Tag":"Summary","Value":"An alternate posix capabilities library"},{"Tag":"Version","Value":"0.6.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"45214"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"keyutils-libs"},{"Tag":"Summary","Value":"Key utilities library"},{"Tag":"Version","Value":"1.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"36624"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"rpm-libs"},{"Tag":"Summary","Value":"Libraries for manipulating RPM packages"},{"Tag":"Version","Value":"4.8.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"772920"},{"Tag":"InstallTime","Value":"1412965657"},{"Tag":"Name","Value":"gnupg2"},{"Tag":"Summary","Value":"Utility for secure communication and data storage"},{"Tag":"Version","Value":"2.0.14"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"6086838"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"fipscheck"},{"Tag":"Summary","Value":"A library for integrity verification of FIPS validated modules"},{"Tag":"Version","Value":"1.2.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"28163"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"libsemanage"},{"Tag":"Summary","Value":"SELinux binary policy manipulation library"},{"Tag":"Version","Value":"2.0.43"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"204095"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"newt"},{"Tag":"Summary","Value":"A library for text mode user interfaces"},{"Tag":"Version","Value":"0.52.11"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"174037"},{"Tag":"InstallTime","Value":"1412965659"},{"Tag":"Name","Value":"libffi"},{"Tag":"Summary","Value":"A portable foreign function interface library"},{"Tag":"Version","Value":"3.0.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"42881"},{"Tag":"InstallTime","Value":"1412965659"},{"Tag":"Name","Value":"pygpgme"},{"Tag":"Summary","Value":"Python module for working with OpenPGP messages"},{"Tag":"Version","Value":"0.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"251432"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"python-urlgrabber"},{"Tag":"Summary","Value":"A high-level cross-protocol url-grabber"},{"Tag":"Version","Value":"3.9.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"322181"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"python-iniparse"},{"Tag":"Summary","Value":"Python Module for Accessing and Modifying Configuration Data in INI files"},{"Tag":"Version","Value":"0.3.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"109284"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"gamin"},{"Tag":"Summary","Value":"Library providing the FAM File Alteration Monitor API"},{"Tag":"Version","Value":"0.1.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"416440"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"shared-mime-info"},{"Tag":"Summary","Value":"Shared MIME information database"},{"Tag":"Version","Value":"0.70"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1411492"},{"Tag":"InstallTime","Value":"1412965663"},{"Tag":"Name","Value":"libuser"},{"Tag":"Summary","Value":"A user and group account administration library"},{"Tag":"Version","Value":"0.56.13"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1879447"},{"Tag":"InstallTime","Value":"1412965663"},{"Tag":"Name","Value":"yum-metadata-parser"},{"Tag":"Summary","Value":"A fast metadata parser for yum"},{"Tag":"Version","Value":"1.1.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"58327"},{"Tag":"InstallTime","Value":"1412965663"},{"Tag":"Name","Value":"kbd-misc"},{"Tag":"Summary","Value":"Data for kbd package"},{"Tag":"Version","Value":"1.15"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"1443451"},{"Tag":"InstallTime","Value":"1412965664"},{"Tag":"Name","Value":"policycoreutils"},{"Tag":"Summary","Value":"SELinux policy core utilities"},{"Tag":"Version","Value":"2.0.83"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"3637446"},{"Tag":"InstallTime","Value":"1412965664"},{"Tag":"Name","Value":"udev"},{"Tag":"Summary","Value":"A userspace implementation of devfs"},{"Tag":"Version","Value":"147"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1235003"},{"Tag":"InstallTime","Value":"1412965666"},{"Tag":"Name","Value":"rsyslog"},{"Tag":"Summary","Value":"Enhanced system logging and kernel message trapping daemons"},{"Tag":"Version","Value":"5.8.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2183410"},{"Tag":"InstallTime","Value":"1412965667"},{"Tag":"Name","Value":"cyrus-sasl"},{"Tag":"Summary","Value":"The Cyrus SASL library"},{"Tag":"Version","Value":"2.1.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"132742"},{"Tag":"InstallTime","Value":"1412965667"},{"Tag":"Name","Value":"cronie-anacron"},{"Tag":"Summary","Value":"Utility for running regular jobs"},{"Tag":"Version","Value":"1.4.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"44006"},{"Tag":"InstallTime","Value":"1412965669"},{"Tag":"Name","Value":"crontabs"},{"Tag":"Summary","Value":"Root crontab files used to schedule the execution of programs"},{"Tag":"Version","Value":"1.10"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"2495"},{"Tag":"InstallTime","Value":"1412965669"},{"Tag":"Name","Value":"dhclient"},{"Tag":"Summary","Value":"Provides the dhclient ISC DHCP client daemon and dhclient-script"},{"Tag":"Version","Value":"4.1.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"625041"},{"Tag":"InstallTime","Value":"1412965678"},{"Tag":"Name","Value":"system-config-firewall-base"},{"Tag":"Summary","Value":"system-config-firewall base components and command line tool"},{"Tag":"Version","Value":"1.2.27"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"2364002"},{"Tag":"InstallTime","Value":"1412965708"},{"Tag":"Name","Value":"bfa-firmware"},{"Tag":"Summary","Value":"Brocade Fibre Channel HBA Firmware"},{"Tag":"Version","Value":"3.2.21.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"7266082"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl100-firmware"},{"Tag":"Summary","Value":"Firmware for Intel(R) Wireless WiFi Link 100 Series Adapters"},{"Tag":"Version","Value":"39.31.5.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"344388"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"b43-openfwwf"},{"Tag":"Summary","Value":"Open firmware for some Broadcom 43xx series WLAN chips"},{"Tag":"Version","Value":"5.2"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"31549"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"aic94xx-firmware"},{"Tag":"Summary","Value":"Adaptec SAS 44300, 48300, 58300 Sequencer Firmware for AIC94xx driver"},{"Tag":"Version","Value":"30"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"30752"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl1000-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 1000 B/G/N network adaptors"},{"Tag":"Version","Value":"39.31.5.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"679399"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"authconfig"},{"Tag":"Summary","Value":"Command line tool for setting up authentication from network services"},{"Tag":"Version","Value":"6.1.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1939089"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"efibootmgr"},{"Tag":"Summary","Value":"EFI Boot Manager"},{"Tag":"Version","Value":"0.5.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"72694"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"acl"},{"Tag":"Summary","Value":"Access control list utilities"},{"Tag":"Version","Value":"2.2.49"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"193226"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"ql2100-firmware"},{"Tag":"Summary","Value":"Firmware for qlogic 2100 devices"},{"Tag":"Version","Value":"1.19.38"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"78639"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"libertas-usb8388-firmware"},{"Tag":"Summary","Value":"Firmware for Marvell Libertas USB 8388 Network Adapter"},{"Tag":"Version","Value":"5.110.22.p23"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"129601"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"zd1211-firmware"},{"Tag":"Summary","Value":"Firmware for wireless devices based on zd1211 chipset"},{"Tag":"Version","Value":"1.4"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"65337"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"rt61pci-firmware"},{"Tag":"Summary","Value":"Firmware for Ralink® RT2561/RT2661 A/B/G network adaptors"},{"Tag":"Version","Value":"1.2"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"26679"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"ql2200-firmware"},{"Tag":"Summary","Value":"Firmware for qlogic 2200 devices"},{"Tag":"Version","Value":"2.02.08"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"86403"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"ipw2100-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 2100 network adaptors"},{"Tag":"Version","Value":"1.3"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"618666"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"ipw2200-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 2200 network adaptors"},{"Tag":"Version","Value":"3.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"576425"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"epel-release"},{"Tag":"Summary","Value":"Extra Packages for Enterprise Linux repository configuration"},{"Tag":"Version","Value":"6"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"22169"},{"Tag":"InstallTime","Value":"1412965772"},{"Tag":"Name","Value":"kernel-firmware"},{"Tag":"Summary","Value":"Firmware files used by the Linux kernel"},{"Tag":"Version","Value":"2.6.32"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"22415632"},{"Tag":"InstallTime","Value":"1412965805"},{"Tag":"Name","Value":"nss-softokn-freebl"},{"Tag":"Summary","Value":"Freebl library for the Network Security Services"},{"Tag":"Version","Value":"3.14.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"470427"},{"Tag":"InstallTime","Value":"1412965807"},{"Tag":"Name","Value":"nspr"},{"Tag":"Summary","Value":"Netscape Portable Runtime"},{"Tag":"Version","Value":"4.10.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"277936"},{"Tag":"InstallTime","Value":"1412965818"},{"Tag":"Name","Value":"libcom_err"},{"Tag":"Summary","Value":"Common error description library"},{"Tag":"Version","Value":"1.41.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"59233"},{"Tag":"InstallTime","Value":"1412965819"},{"Tag":"Name","Value":"coreutils-libs"},{"Tag":"Summary","Value":"Libraries for coreutils"},{"Tag":"Version","Value":"8.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"5576"},{"Tag":"InstallTime","Value":"1412965820"},{"Tag":"Name","Value":"krb5-libs"},{"Tag":"Summary","Value":"The shared libraries used by Kerberos 5"},{"Tag":"Version","Value":"1.10.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2126628"},{"Tag":"InstallTime","Value":"1412965822"},{"Tag":"Name","Value":"libuuid"},{"Tag":"Summary","Value":"Universally unique ID library"},{"Tag":"Version","Value":"2.17.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"16304"},{"Tag":"InstallTime","Value":"1412965823"},{"Tag":"Name","Value":"util-linux-ng"},{"Tag":"Summary","Value":"A collection of basic system utilities"},{"Tag":"Version","Value":"2.17.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"6002027"},{"Tag":"InstallTime","Value":"1412965825"},{"Tag":"Name","Value":"nss-softokn"},{"Tag":"Summary","Value":"Network Security Services Softoken Module"},{"Tag":"Version","Value":"3.14.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1127638"},{"Tag":"InstallTime","Value":"1412965826"},{"Tag":"Name","Value":"nss-sysinit"},{"Tag":"Summary","Value":"System NSS Initialization"},{"Tag":"Version","Value":"3.16.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"32822"},{"Tag":"InstallTime","Value":"1412965827"},{"Tag":"Name","Value":"libtasn1"},{"Tag":"Summary","Value":"The ASN.1 library used in GNUTLS"},{"Tag":"Version","Value":"2.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"443140"},{"Tag":"InstallTime","Value":"1412965828"},{"Tag":"Name","Value":"p11-kit-trust"},{"Tag":"Summary","Value":"System trust module from p11-kit"},{"Tag":"Version","Value":"0.18.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"178775"},{"Tag":"InstallTime","Value":"1412965829"},{"Tag":"Name","Value":"openssl"},{"Tag":"Summary","Value":"A general purpose cryptography library with TLS implementation"},{"Tag":"Version","Value":"1.0.1e"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"4209650"},{"Tag":"InstallTime","Value":"1412965831"},{"Tag":"Name","Value":"python-libs"},{"Tag":"Summary","Value":"Runtime libraries for Python"},{"Tag":"Version","Value":"2.6.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"22961066"},{"Tag":"InstallTime","Value":"1412965835"},{"Tag":"Name","Value":"yum"},{"Tag":"Summary","Value":"RPM package installer/updater/manager"},{"Tag":"Version","Value":"3.2.29"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"4736145"},{"Tag":"InstallTime","Value":"1412965836"},{"Tag":"Name","Value":"nss-tools"},{"Tag":"Summary","Value":"Tools for the Network Security Services"},{"Tag":"Version","Value":"3.16.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1787152"},{"Tag":"InstallTime","Value":"1412965838"},{"Tag":"Name","Value":"libcurl"},{"Tag":"Summary","Value":"A library for getting files from web servers"},{"Tag":"Version","Value":"7.19.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"343088"},{"Tag":"InstallTime","Value":"1412965839"},{"Tag":"Name","Value":"selinux-policy"},{"Tag":"Summary","Value":"SELinux policy configuration"},{"Tag":"Version","Value":"3.7.19"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"8929936"},{"Tag":"InstallTime","Value":"1412965841"},{"Tag":"Name","Value":"e2fsprogs-libs"},{"Tag":"Summary","Value":"Ext2/3/4 filesystem-specific shared libraries"},{"Tag":"Version","Value":"1.41.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"274473"},{"Tag":"InstallTime","Value":"1412965841"},{"Tag":"Name","Value":"iproute"},{"Tag":"Summary","Value":"Advanced IP routing and network device configuration tools"},{"Tag":"Version","Value":"2.6.32"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"950255"},{"Tag":"InstallTime","Value":"1412965843"},{"Tag":"Name","Value":"plymouth-core-libs"},{"Tag":"Summary","Value":"Plymouth libraries"},{"Tag":"Version","Value":"0.8.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"177752"},{"Tag":"InstallTime","Value":"1412965844"},{"Tag":"Name","Value":"centos-release"},{"Tag":"Summary","Value":"CentOS release file"},{"Tag":"Version","Value":"6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"33534"},{"Tag":"InstallTime","Value":"1412965845"},{"Tag":"Name","Value":"libgcc"},{"Tag":"Summary","Value":"GCC version 4.4 shared support library"},{"Tag":"Version","Value":"4.4.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"117320"},{"Tag":"InstallTime","Value":"1412965633"},{"Tag":"Name","Value":"plymouth"},{"Tag":"Summary","Value":"Graphical Boot Animation and Logger"},{"Tag":"Version","Value":"0.8.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"193924"},{"Tag":"InstallTime","Value":"1412965846"},{"Tag":"Name","Value":"filesystem"},{"Tag":"Summary","Value":"The basic directory layout for a Linux system"},{"Tag":"Version","Value":"2.4.30"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"0"},{"Tag":"InstallTime","Value":"1412965634"},{"Tag":"Name","Value":"dracut-kernel"},{"Tag":"Summary","Value":"Metapackage to build generic initramfs with dracut with only kernel modules"},{"Tag":"Version","Value":"004"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"202"},{"Tag":"InstallTime","Value":"1412965848"},{"Tag":"Name","Value":"ncurses-base"},{"Tag":"Summary","Value":"Descriptions of common terminals"},{"Tag":"Version","Value":"5.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"193090"},{"Tag":"InstallTime","Value":"1412965634"},{"Tag":"Name","Value":"postfix"},{"Tag":"Summary","Value":"Postfix Mail Transport Agent"},{"Tag":"Version","Value":"2.6.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"10136868"},{"Tag":"InstallTime","Value":"1412965855"},{"Tag":"Name","Value":"selinux-policy-targeted"},{"Tag":"Summary","Value":"SELinux targeted base policy"},{"Tag":"Version","Value":"3.7.19"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"3398852"},{"Tag":"InstallTime","Value":"1412965857"},{"Tag":"Name","Value":"grub"},{"Tag":"Summary","Value":"Grand Unified Boot Loader."},{"Tag":"Version","Value":"0.97"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2490457"},{"Tag":"InstallTime","Value":"1412965888"},{"Tag":"Name","Value":"libxml2"},{"Tag":"Summary","Value":"Library providing XML and HTML support"},{"Tag":"Version","Value":"2.7.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1775963"},{"Tag":"InstallTime","Value":"1412965890"},{"Tag":"Name","Value":"libcap"},{"Tag":"Summary","Value":"Library for getting and setting POSIX.1e capabilities"},{"Tag":"Version","Value":"2.16"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"64437"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"ql2500-firmware"},{"Tag":"Summary","Value":"Firmware for qlogic 2500 devices"},{"Tag":"Version","Value":"7.03.00"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"268755"},{"Tag":"InstallTime","Value":"1412965890"},{"Tag":"Name","Value":"info"},{"Tag":"Summary","Value":"A stand-alone TTY-based reader for GNU texinfo documentation"},{"Tag":"Version","Value":"4.13a"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"329482"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"openssh-clients"},{"Tag":"Summary","Value":"An open source SSH client applications"},{"Tag":"Version","Value":"5.3p1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1216865"},{"Tag":"InstallTime","Value":"1412986024"},{"Tag":"Name","Value":"popt"},{"Tag":"Summary","Value":"C library for parsing command line parameters"},{"Tag":"Version","Value":"1.13"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"83420"},{"Tag":"InstallTime","Value":"1412965644"},{"Tag":"Name","Value":"ntp"},{"Tag":"Summary","Value":"The NTP daemon and utilities"},{"Tag":"Version","Value":"4.2.6p5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1706943"},{"Tag":"InstallTime","Value":"1412987706"},{"Tag":"Name","Value":"libacl"},{"Tag":"Summary","Value":"Dynamic library for access control list support"},{"Tag":"Version","Value":"2.2.49"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"31280"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"freeipmi"},{"Tag":"Summary","Value":"IPMI remote console and system management software"},{"Tag":"Version","Value":"1.2.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"10082894"},{"Tag":"InstallTime","Value":"1412989004"},{"Tag":"Name","Value":"db4"},{"Tag":"Summary","Value":"The Berkeley DB database library (version 4) for C"},{"Tag":"Version","Value":"4.7.25"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1530343"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"libsepol"},{"Tag":"Summary","Value":"SELinux binary policy manipulation library"},{"Tag":"Version","Value":"2.0.41"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"248680"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"sed"},{"Tag":"Summary","Value":"A GNU stream text editor"},{"Tag":"Version","Value":"4.2.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"542324"},{"Tag":"InstallTime","Value":"1412965645"},{"Tag":"Name","Value":"dell-pec-bmc-tool"},{"Tag":"Summary","Value":"BMC system management tool for Dell PowerEdge C"},{"Tag":"Version","Value":"2014.09.26"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"1138668"},{"Tag":"InstallTime","Value":"1412989017"},{"Tag":"Name","Value":"gawk"},{"Tag":"Summary","Value":"The GNU version of the awk text processing utility"},{"Tag":"Version","Value":"3.1.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2036440"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"file-libs"},{"Tag":"Summary","Value":"Libraries for applications using libmagic"},{"Tag":"Version","Value":"5.04"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2508853"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"dell-pec-setupbios"},{"Tag":"Summary","Value":"Simple BIOS setup and management tool for Dell PowerEdge C"},{"Tag":"Version","Value":"2013.10.03"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"690270"},{"Tag":"InstallTime","Value":"1412989024"},{"Tag":"Name","Value":"libstdc++"},{"Tag":"Summary","Value":"GNU Standard C++ Library"},{"Tag":"Version","Value":"4.4.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"987096"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"libusb1"},{"Tag":"Summary","Value":"A library which allows userspace access to USB devices"},{"Tag":"Version","Value":"1.0.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"206072"},{"Tag":"InstallTime","Value":"1413304601"},{"Tag":"Name","Value":"python-dmidecode"},{"Tag":"Summary","Value":"Python module to access DMI data"},{"Tag":"Version","Value":"3.10.13"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"264954"},{"Tag":"InstallTime","Value":"1413304603"},{"Tag":"Name","Value":"sqlite"},{"Tag":"Summary","Value":"Library that implements an embeddable SQL database engine"},{"Tag":"Version","Value":"3.6.20"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"641020"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"python-psutil"},{"Tag":"Summary","Value":"A process and system utilities module for Python"},{"Tag":"Version","Value":"0.6.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"362814"},{"Tag":"InstallTime","Value":"1413304604"},{"Tag":"Name","Value":"libidn"},{"Tag":"Summary","Value":"Internationalized Domain Name support library"},{"Tag":"Version","Value":"1.18"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"567612"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"sysstat"},{"Tag":"Summary","Value":"The sar and iostat system monitoring commands"},{"Tag":"Version","Value":"9.0.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"842883"},{"Tag":"InstallTime","Value":"1413304605"},{"Tag":"Name","Value":"apr-util"},{"Tag":"Summary","Value":"Apache Portable Runtime Utility library"},{"Tag":"Version","Value":"1.3.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"202360"},{"Tag":"InstallTime","Value":"1418766183"},{"Tag":"Name","Value":"elfutils-libelf"},{"Tag":"Summary","Value":"Library to read and write ELF files"},{"Tag":"Version","Value":"0.152"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"920293"},{"Tag":"InstallTime","Value":"1412965646"},{"Tag":"Name","Value":"apr-util-ldap"},{"Tag":"Summary","Value":"APR utility library LDAP support"},{"Tag":"Version","Value":"1.3.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"9488"},{"Tag":"InstallTime","Value":"1418766184"},{"Tag":"Name","Value":"findutils"},{"Tag":"Summary","Value":"The GNU versions of find utilities (find and xargs)"},{"Tag":"Version","Value":"4.4.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1446945"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"checkpolicy"},{"Tag":"Summary","Value":"SELinux policy compiler"},{"Tag":"Version","Value":"2.0.22"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"870239"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"glibc-common"},{"Tag":"Summary","Value":"Common binaries and locale data for glibc"},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"112401140"},{"Tag":"InstallTime","Value":"1419897857"},{"Tag":"Name","Value":"perl-Pod-Escapes"},{"Tag":"Summary","Value":"Perl module for resolving POD escape sequences"},{"Tag":"Version","Value":"1.04"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"21092"},{"Tag":"InstallTime","Value":"1419897866"},{"Tag":"Name","Value":"tcp_wrappers-libs"},{"Tag":"Summary","Value":"Libraries for tcp_wrappers"},{"Tag":"Version","Value":"7.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"131475"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"perl-Module-Pluggable"},{"Tag":"Summary","Value":"Automatically give your module the ability to have plugins"},{"Tag":"Version","Value":"3.90"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"31031"},{"Tag":"InstallTime","Value":"1419897868"},{"Tag":"Name","Value":"expat"},{"Tag":"Summary","Value":"An XML parser library"},{"Tag":"Version","Value":"2.0.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"198018"},{"Tag":"InstallTime","Value":"1412965647"},{"Tag":"Name","Value":"perl-Pod-Simple"},{"Tag":"Summary","Value":"Framework for parsing POD documentation"},{"Tag":"Version","Value":"3.13"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"476739"},{"Tag":"InstallTime","Value":"1419897870"},{"Tag":"Name","Value":"xz-libs"},{"Tag":"Summary","Value":"Libraries for decoding LZMA compression"},{"Tag":"Version","Value":"4.999.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"214490"},{"Tag":"InstallTime","Value":"1419897874"},{"Tag":"Name","Value":"xz-lzma-compat"},{"Tag":"Summary","Value":"Older LZMA format compatibility binaries"},{"Tag":"Version","Value":"4.999.9"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"21146"},{"Tag":"InstallTime","Value":"1419897876"},{"Tag":"Name","Value":"libgcrypt"},{"Tag":"Summary","Value":"A general-purpose cryptography library"},{"Tag":"Version","Value":"1.4.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"536622"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"perl-CGI"},{"Tag":"Summary","Value":"Handle Common Gateway Interface requests and responses"},{"Tag":"Version","Value":"3.51"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"444445"},{"Tag":"InstallTime","Value":"1419897877"},{"Tag":"Name","Value":"gmp"},{"Tag":"Summary","Value":"A GNU arbitrary precision library"},{"Tag":"Version","Value":"4.3.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"657883"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"patch"},{"Tag":"Summary","Value":"Utility for modifying/upgrading files"},{"Tag":"Version","Value":"2.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"175662"},{"Tag":"InstallTime","Value":"1419897879"},{"Tag":"Name","Value":"libnih"},{"Tag":"Summary","Value":"Lightweight application development library"},{"Tag":"Version","Value":"1.0.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"486830"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"mailx"},{"Tag":"Summary","Value":"Enhanced implementation of the mailx command"},{"Tag":"Version","Value":"12.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"462578"},{"Tag":"InstallTime","Value":"1419897880"},{"Tag":"Name","Value":"file"},{"Tag":"Summary","Value":"A utility for determining file types"},{"Tag":"Version","Value":"5.04"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"56567"},{"Tag":"InstallTime","Value":"1412965649"},{"Tag":"Name","Value":"gettext"},{"Tag":"Summary","Value":"GNU libraries and utilities for producing multi-lingual messages"},{"Tag":"Version","Value":"0.17"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"6369345"},{"Tag":"InstallTime","Value":"1419897882"},{"Tag":"Name","Value":"MAKEDEV"},{"Tag":"Summary","Value":"A program used for creating device files in /dev"},{"Tag":"Version","Value":"3.24"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"227290"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"db4-cxx"},{"Tag":"Summary","Value":"The Berkeley DB database library (version 4) for C++"},{"Tag":"Version","Value":"4.7.25"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1645008"},{"Tag":"InstallTime","Value":"1419897883"},{"Tag":"Name","Value":"procps"},{"Tag":"Summary","Value":"System and process monitoring utilities"},{"Tag":"Version","Value":"3.2.8"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"465911"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"ed"},{"Tag":"Summary","Value":"The GNU line editor"},{"Tag":"Version","Value":"1.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"119832"},{"Tag":"InstallTime","Value":"1419897887"},{"Tag":"Name","Value":"bc"},{"Tag":"Summary","Value":"GNU's bc (a numeric processing language) and dc (a calculator)"},{"Tag":"Version","Value":"1.06.95"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"217289"},{"Tag":"InstallTime","Value":"1419897888"},{"Tag":"Name","Value":"db4-utils"},{"Tag":"Summary","Value":"Command line tools for managing Berkeley DB (version 4) databases"},{"Tag":"Version","Value":"4.7.25"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"415046"},{"Tag":"InstallTime","Value":"1412965650"},{"Tag":"Name","Value":"kernel-headers"},{"Tag":"Summary","Value":"Header files for the Linux kernel for use by glibc"},{"Tag":"Version","Value":"2.6.32"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2685224"},{"Tag":"InstallTime","Value":"1419897889"},{"Tag":"Name","Value":"glibc-devel"},{"Tag":"Summary","Value":"Object files for development using standard C libraries."},{"Tag":"Version","Value":"2.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"990368"},{"Tag":"InstallTime","Value":"1419897891"},{"Tag":"Name","Value":"diffutils"},{"Tag":"Summary","Value":"A GNU collection of diff utilities"},{"Tag":"Version","Value":"2.8.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"588813"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"perl-ExtUtils-ParseXS"},{"Tag":"Summary","Value":"Module and a script for converting Perl XS code into C code"},{"Tag":"Version","Value":"2.2003.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"62207"},{"Tag":"InstallTime","Value":"1419897893"},{"Tag":"Name","Value":"make"},{"Tag":"Summary","Value":"A GNU tool which simplifies the build process for users"},{"Tag":"Version","Value":"3.81"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1079569"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"perl-devel"},{"Tag":"Summary","Value":"Header files for use in perl development"},{"Tag":"Version","Value":"5.10.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1869952"},{"Tag":"InstallTime","Value":"1419897894"},{"Tag":"Name","Value":"which"},{"Tag":"Summary","Value":"Displays where a particular program in your path is located"},{"Tag":"Version","Value":"2.19"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"73004"},{"Tag":"InstallTime","Value":"1412965651"},{"Tag":"Name","Value":"redhat-lsb-core"},{"Tag":"Summary","Value":"LSB base libraries support for CentOS"},{"Tag":"Version","Value":"4.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"22825"},{"Tag":"InstallTime","Value":"1419897895"},{"Tag":"Name","Value":"ncurses"},{"Tag":"Summary","Value":"Ncurses support utilities"},{"Tag":"Version","Value":"5.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"386379"},{"Tag":"InstallTime","Value":"1412965652"},{"Tag":"Name","Value":"telnet"},{"Tag":"Summary","Value":"The client program for the Telnet remote login protocol"},{"Tag":"Version","Value":"0.17"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"112112"},{"Tag":"InstallTime","Value":"1422408914"},{"Tag":"Name","Value":"less"},{"Tag":"Summary","Value":"A text file browser similar to more, but better"},{"Tag":"Version","Value":"436"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"200427"},{"Tag":"InstallTime","Value":"1412965652"},{"Tag":"Name","Value":"unzip"},{"Tag":"Summary","Value":"A utility for unpacking zip files"},{"Tag":"Version","Value":"6.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"320358"},{"Tag":"InstallTime","Value":"1422408997"},{"Tag":"Name","Value":"gzip"},{"Tag":"Summary","Value":"The GNU data compression program"},{"Tag":"Version","Value":"1.3.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"225849"},{"Tag":"InstallTime","Value":"1412965652"},{"Tag":"Name","Value":"cracklib-dicts"},{"Tag":"Summary","Value":"The standard CrackLib dictionaries"},{"Tag":"Version","Value":"2.8.16"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"9327207"},{"Tag":"InstallTime","Value":"1412965653"},{"Tag":"Name","Value":"pam"},{"Tag":"Summary","Value":"An extensible library which provides authentication for applications"},{"Tag":"Version","Value":"1.1.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2409602"},{"Tag":"InstallTime","Value":"1412965654"},{"Tag":"Name","Value":"hwdata"},{"Tag":"Summary","Value":"Hardware identification and configuration data"},{"Tag":"Version","Value":"0.233"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"4936719"},{"Tag":"InstallTime","Value":"1412965655"},{"Tag":"Name","Value":"logrotate"},{"Tag":"Summary","Value":"Rotates, compresses, removes and mails system log files"},{"Tag":"Version","Value":"3.7.8"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"84835"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"pciutils-libs"},{"Tag":"Summary","Value":"Linux PCI library"},{"Tag":"Version","Value":"3.1.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"48992"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"mingetty"},{"Tag":"Summary","Value":"A compact getty program for virtual consoles only"},{"Tag":"Version","Value":"1.08"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"34586"},{"Tag":"InstallTime","Value":"1412965656"},{"Tag":"Name","Value":"libssh2"},{"Tag":"Summary","Value":"A library implementing the SSH2 protocol"},{"Tag":"Version","Value":"1.4.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"324941"},{"Tag":"InstallTime","Value":"1412965657"},{"Tag":"Name","Value":"rpm"},{"Tag":"Summary","Value":"The RPM package management system"},{"Tag":"Version","Value":"4.8.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2031240"},{"Tag":"InstallTime","Value":"1412965657"},{"Tag":"Name","Value":"gpgme"},{"Tag":"Summary","Value":"GnuPG Made Easy - high level crypto API"},{"Tag":"Version","Value":"1.1.8"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"729658"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"fipscheck-lib"},{"Tag":"Summary","Value":"Library files for fipscheck"},{"Tag":"Version","Value":"1.2.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"10353"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"ustr"},{"Tag":"Summary","Value":"String library, very low memory overhead, simple to import"},{"Tag":"Version","Value":"1.0.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"273983"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"slang"},{"Tag":"Summary","Value":"The shared library for the S-Lang extension language"},{"Tag":"Version","Value":"2.2.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"1800725"},{"Tag":"InstallTime","Value":"1412965658"},{"Tag":"Name","Value":"gdbm"},{"Tag":"Summary","Value":"A GNU set of database routines which use extensible hashing"},{"Tag":"Version","Value":"1.8.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"48586"},{"Tag":"InstallTime","Value":"1412965659"},{"Tag":"Name","Value":"rpm-python"},{"Tag":"Summary","Value":"Python bindings for apps which will manipulate RPM packages"},{"Tag":"Version","Value":"4.8.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"120906"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"python-pycurl"},{"Tag":"Summary","Value":"A Python interface to libcurl"},{"Tag":"Version","Value":"7.19.0"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"236939"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"newt-python"},{"Tag":"Summary","Value":"Python bindings for newt"},{"Tag":"Version","Value":"0.52.11"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"111017"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"pkgconfig"},{"Tag":"Summary","Value":"A tool for determining compilation options"},{"Tag":"Version","Value":"0.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"140091"},{"Tag":"InstallTime","Value":"1412965662"},{"Tag":"Name","Value":"grubby"},{"Tag":"Summary","Value":"Command line tool for updating bootloader configs"},{"Tag":"Version","Value":"7.0.15"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"84546"},{"Tag":"InstallTime","Value":"1412965663"},{"Tag":"Name","Value":"dbus-glib"},{"Tag":"Summary","Value":"GLib bindings for D-Bus"},{"Tag":"Version","Value":"0.86"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"579611"},{"Tag":"InstallTime","Value":"1412965663"},{"Tag":"Name","Value":"iptables"},{"Tag":"Summary","Value":"Tools for managing Linux kernel packet filtering capabilities"},{"Tag":"Version","Value":"1.4.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"855952"},{"Tag":"InstallTime","Value":"1412965664"},{"Tag":"Name","Value":"iputils"},{"Tag":"Summary","Value":"Network monitoring tools including ping"},{"Tag":"Version","Value":"20071127"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"299474"},{"Tag":"InstallTime","Value":"1412965665"},{"Tag":"Name","Value":"libdrm"},{"Tag":"Summary","Value":"Direct Rendering Manager runtime library"},{"Tag":"Version","Value":"2.4.45"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"271667"},{"Tag":"InstallTime","Value":"1412965666"},{"Tag":"Name","Value":"kbd"},{"Tag":"Summary","Value":"Tools for configuring the console (keyboard, virtual terminals, etc.)"},{"Tag":"Version","Value":"1.15"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"991792"},{"Tag":"InstallTime","Value":"1412965666"},{"Tag":"Name","Value":"openssh"},{"Tag":"Summary","Value":"An open source implementation of SSH protocol versions 1 and 2"},{"Tag":"Version","Value":"5.3p1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"738990"},{"Tag":"InstallTime","Value":"1412965667"},{"Tag":"Name","Value":"cronie"},{"Tag":"Summary","Value":"Cron daemon for executing programs at set times"},{"Tag":"Version","Value":"1.4.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"178643"},{"Tag":"InstallTime","Value":"1412965669"},{"Tag":"Name","Value":"iptables-ipv6"},{"Tag":"Summary","Value":"IPv6 support for iptables"},{"Tag":"Version","Value":"1.4.7"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"190287"},{"Tag":"InstallTime","Value":"1412965669"},{"Tag":"Name","Value":"dhcp-common"},{"Tag":"Summary","Value":"Common files used by ISC dhcp client and server"},{"Tag":"Version","Value":"4.1.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"198000"},{"Tag":"InstallTime","Value":"1412965670"},{"Tag":"Name","Value":"kernel"},{"Tag":"Summary","Value":"The Linux kernel"},{"Tag":"Version","Value":"2.6.32"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"126734131"},{"Tag":"InstallTime","Value":"1412965678"},{"Tag":"Name","Value":"openssh-server"},{"Tag":"Summary","Value":"An open source SSH server daemon"},{"Tag":"Version","Value":"5.3p1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"689757"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl5150-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® Wireless 5150 A/G/N network adaptors"},{"Tag":"Version","Value":"8.24.2.2"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"344430"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl6050-firmware"},{"Tag":"Summary","Value":"Firmware for Intel(R) Wireless WiFi Link 6050 Series Adapters"},{"Tag":"Version","Value":"41.28.5.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"940307"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl6000g2a-firmware"},{"Tag":"Summary","Value":"Firmware for Intel(R) Wireless WiFi Link 6005 Series Adapters"},{"Tag":"Version","Value":"17.168.5.3"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"450973"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"iwl6000-firmware"},{"Tag":"Summary","Value":"Firmware for Intel(R) Wireless WiFi Link 6000 Series AGN Adapter"},{"Tag":"Version","Value":"9.221.4.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"461443"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"passwd"},{"Tag":"Summary","Value":"An utility for setting or changing passwords using PAM"},{"Tag":"Version","Value":"0.77"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"357699"},{"Tag":"InstallTime","Value":"1412965709"},{"Tag":"Name","Value":"sudo"},{"Tag":"Summary","Value":"Allows restricted root access for specified users"},{"Tag":"Version","Value":"1.8.6p3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2465716"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"attr"},{"Tag":"Summary","Value":"Utilities for managing filesystem extended attributes"},{"Tag":"Version","Value":"2.4.44"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"142313"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"iwl5000-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 5000 A/G/N network adaptors"},{"Tag":"Version","Value":"8.83.5.1_1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"1045904"},{"Tag":"InstallTime","Value":"1412965710"},{"Tag":"Name","Value":"ivtv-firmware"},{"Tag":"Summary","Value":"Firmware for the Hauppauge PVR 250/350/150/500/USB2 model series"},{"Tag":"Version","Value":"20080701"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"857256"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"xorg-x11-drv-ati-firmware"},{"Tag":"Summary","Value":"ATI firmware for R600/700/Evergreen/NI/PALM"},{"Tag":"Version","Value":"7.1.0"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"589551"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"atmel-firmware"},{"Tag":"Summary","Value":"Firmware for Atmel at76c50x wireless network chips"},{"Tag":"Version","Value":"1.3"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"728154"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"iwl4965-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 4965 A/G/N network adaptors"},{"Tag":"Version","Value":"228.61.2.24"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"382618"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"iwl3945-firmware"},{"Tag":"Summary","Value":"Firmware for Intel® PRO/Wireless 3945 A/B/G network adaptors"},{"Tag":"Version","Value":"15.32.2.9"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"457396"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"rt73usb-firmware"},{"Tag":"Summary","Value":"Firmware for Ralink® RT2571W/RT2671 A/B/G network adaptors"},{"Tag":"Version","Value":"1.8"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"4151"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"ql23xx-firmware"},{"Tag":"Summary","Value":"Firmware for qlogic 23xx devices"},{"Tag":"Version","Value":"3.03.27"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"262821"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"rootfiles"},{"Tag":"Summary","Value":"The basic required files for the root user's directory"},{"Tag":"Version","Value":"8.1"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"599"},{"Tag":"InstallTime","Value":"1412965711"},{"Tag":"Name","Value":"gpg-pubkey"},{"Tag":"Summary","Value":"gpg(CentOS-6 Key (CentOS 6 Official Signing Key) \u003ccentos-6-key@centos.org\u003e)"},{"Tag":"Version","Value":"c105b9de"},{"Tag":"Arch","Value":"(none)"},{"Tag":"Size","Value":"0"},{"Tag":"InstallTime","Value":"1412965782"},{"Tag":"Name","Value":"tzdata"},{"Tag":"Summary","Value":"Timezone data"},{"Tag":"Version","Value":"2014h"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"1846718"},{"Tag":"InstallTime","Value":"1412965807"},{"Tag":"Name","Value":"bash"},{"Tag":"Summary","Value":"The GNU Bourne Again shell"},{"Tag":"Version","Value":"4.1.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"3139803"},{"Tag":"InstallTime","Value":"1412965818"},{"Tag":"Name","Value":"nss-util"},{"Tag":"Summary","Value":"Network Security Services Utilities Library"},{"Tag":"Version","Value":"3.16.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"179416"},{"Tag":"InstallTime","Value":"1412965819"},{"Tag":"Name","Value":"grep"},{"Tag":"Summary","Value":"Pattern matching utilities"},{"Tag":"Version","Value":"2.6.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"803774"},{"Tag":"InstallTime","Value":"1412965820"},{"Tag":"Name","Value":"coreutils"},{"Tag":"Summary","Value":"A set of basic GNU tools commonly used in shell scripts"},{"Tag":"Version","Value":"8.4"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"12866759"},{"Tag":"InstallTime","Value":"1412965821"},{"Tag":"Name","Value":"audit-libs"},{"Tag":"Summary","Value":"Dynamic library for libaudit"},{"Tag":"Version","Value":"2.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"173762"},{"Tag":"InstallTime","Value":"1412965823"},{"Tag":"Name","Value":"libblkid"},{"Tag":"Summary","Value":"Block device ID library"},{"Tag":"Version","Value":"2.17.2"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"136136"},{"Tag":"InstallTime","Value":"1412965824"},{"Tag":"Name","Value":"plymouth-scripts"},{"Tag":"Summary","Value":"Plymouth related scripts"},{"Tag":"Version","Value":"0.8.3"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"11006"},{"Tag":"InstallTime","Value":"1412965825"},{"Tag":"Name","Value":"nss"},{"Tag":"Summary","Value":"Network Security Services"},{"Tag":"Version","Value":"3.16.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"2631784"},{"Tag":"InstallTime","Value":"1412965826"},{"Tag":"Name","Value":"cpio"},{"Tag":"Summary","Value":"A GNU archiving program"},{"Tag":"Version","Value":"2.10"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"650433"},{"Tag":"InstallTime","Value":"1412965827"},{"Tag":"Name","Value":"p11-kit"},{"Tag":"Summary","Value":"Library for loading and sharing PKCS#11 modules"},{"Tag":"Version","Value":"0.18.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"262669"},{"Tag":"InstallTime","Value":"1412965828"},{"Tag":"Name","Value":"ca-certificates"},{"Tag":"Summary","Value":"The Mozilla CA root certificate bundle"},{"Tag":"Version","Value":"2014.1.98"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"3006388"},{"Tag":"InstallTime","Value":"1412965830"},{"Tag":"Name","Value":"python"},{"Tag":"Summary","Value":"An interpreted, interactive, object-oriented programming language"},{"Tag":"Version","Value":"2.6.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"79603"},{"Tag":"InstallTime","Value":"1412965832"},{"Tag":"Name","Value":"yum-plugin-fastestmirror"},{"Tag":"Summary","Value":"Yum plugin which chooses fastest repository from a mirrorlist"},{"Tag":"Version","Value":"1.1.30"},{"Tag":"Arch","Value":"noarch"},{"Tag":"Size","Value":"53961"},{"Tag":"InstallTime","Value":"1412965836"},{"Tag":"Name","Value":"mysql-libs"},{"Tag":"Summary","Value":"The shared libraries required for MySQL clients"},{"Tag":"Version","Value":"5.1.73"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"4244886"},{"Tag":"InstallTime","Value":"1412965837"},{"Tag":"Name","Value":"openldap"},{"Tag":"Summary","Value":"LDAP support libraries"},{"Tag":"Version","Value":"2.4.23"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"792868"},{"Tag":"InstallTime","Value":"1412965838"},{"Tag":"Name","Value":"upstart"},{"Tag":"Summary","Value":"An event-driven init system"},{"Tag":"Version","Value":"0.6.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"563633"},{"Tag":"InstallTime","Value":"1412965839"},{"Tag":"Name","Value":"libss"},{"Tag":"Summary","Value":"Command line interface parsing library"},{"Tag":"Version","Value":"1.41.12"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"71689"},{"Tag":"InstallTime","Value":"1412965841"},{"Tag":"Name","Value":"glib2"},{"Tag":"Summary","Value":"A library of handy utility functions"},{"Tag":"Version","Value":"2.26.1"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"7166786"},{"Tag":"InstallTime","Value":"1412965842"},{"Tag":"Name","Value":"ethtool"},{"Tag":"Summary","Value":"Ethernet settings tool for PCI ethernet cards"},{"Tag":"Version","Value":"3.5"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"279483"},{"Tag":"InstallTime","Value":"1412965844"},{"Tag":"Name","Value":"psmisc"},{"Tag":"Summary","Value":"Utilities for managing processes on your system"},{"Tag":"Version","Value":"22.6"},{"Tag":"Arch","Value":"x86_64"},{"Tag":"Size","Value":"222294"},{"Tag":"InstallTime","Value":"1412965844"},{"Tag":"Name","Value":""}]}]},"sysinfo.disk": "collection failed","sysinfo.pci": {"Entries":[{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:00.0"},{"Tag":"Class","Value":"Hostbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 5500 I/O Hub to ESI Port (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:01.0"},{"Tag":"Class","Value":"PCIbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 5520/5500/X58 I/O Hub PCI Express Root Port 1 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:03.0"},{"Tag":"Class","Value":"PCIbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 5520/5500/X58 I/O Hub PCI Express Root Port 3 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:07.0"},{"Tag":"Class","Value":"PCIbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 5520/5500/X58 I/O Hub PCI Express Root Port 7 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:10.0"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 Physical and Link Layer Registers Port 0 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:10.1"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 Routing and Protocol Layer Registers Port 0 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:11.0"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500 Physical and Link Layer Registers Port 1 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:11.1"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500 Routing \u0026 Protocol Layer Register Port 1 (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:14.0"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 I/O Hub System Management Registers (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:14.1"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 I/O Hub GPIO and Scratch Pad Registers (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:14.2"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 I/O Hub Control Status and RAS Registers (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:14.3"},{"Tag":"Class","Value":"PIC"},{"Tag":"Description","Value":"Intel Corporation 7500/5520/5500/X58 I/O Hub Throttle Registers (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.0"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.1"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.2"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.3"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.4"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.5"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.6"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:16.7"},{"Tag":"Class","Value":"Systemperipheral"},{"Tag":"Description","Value":"peripheral: Intel Corporation 5520/5500/X58 Chipset QuickData Technology Device (rev 13)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1d.0"},{"Tag":"Class","Value":"USBcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82801JI (ICH10 Family) USB UHCI Controller #1"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1d.1"},{"Tag":"Class","Value":"USBcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82801JI (ICH10 Family) USB UHCI Controller #2"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1d.2"},{"Tag":"Class","Value":"USBcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82801JI (ICH10 Family) USB UHCI Controller #3"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1d.7"},{"Tag":"Class","Value":"USBcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82801JI (ICH10 Family) USB2 EHCI Controller #1"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1e.0"},{"Tag":"Class","Value":"PCIbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 82801 PCI Bridge (rev 90)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1f.0"},{"Tag":"Class","Value":"ISAbridge"},{"Tag":"Description","Value":"bridge: Intel Corporation 82801JIR (ICH10R) LPC Interface Controller"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1f.2"},{"Tag":"Class","Value":"SATAcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82801JI (ICH10 Family) SATA AHCI Controller"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"00:1f.3"},{"Tag":"Class","Value":"SMBus"},{"Tag":"Description","Value":"Intel Corporation 82801JI (ICH10 Family) SMBus Controller"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"01:00.0"},{"Tag":"Class","Value":"Ethernetcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"01:00.1"},{"Tag":"Class","Value":"Ethernetcontroller"},{"Tag":"Description","Value":"controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)"}]},{"Category":"PCIInfo","Details":[{"Tag":"Slot","Value":"04:04.0"},{"Tag":"Class","Value":"VGAcompatiblecontroller"},{"Tag":"Description","Value":"compatible controller: ASPEED Technology, Inc. ASPEED Graphics Family (rev 10)"}]}]},"sysinfo.usb": {"Entries":[{"Category":"","Details":[{"Tag":"Device","Value":"Descriptor:"},{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"2.00"},{"Tag":"bDeviceClass","Value":"9"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x1d6b"},{"Tag":"idProduct","Value":"0x0002"},{"Tag":"bcdDevice","Value":"2.06"},{"Tag":"iManufacturer","Value":"3"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"1"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"25"},{"Tag":"bNumInterfaces","Value":"1"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"9"},{"Tag":"bInterfaceSubClass","Value":"0"},{"Tag":"bInterfaceProtocol","Value":"0"},{"Tag":"iInterface","Value":"0"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0004"},{"Tag":"bInterval","Value":"12"},{"Tag":"Hub","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"41"},{"Tag":"nNbrPorts","Value":"6"},{"Tag":"wHubCharacteristic","Value":"0x000a"},{"Tag":"No","Value":"power"},{"Tag":"Per-port","Value":"overcurrent"},{"Tag":"bPwrOn2PwrGood","Value":"10"},{"Tag":"bHubContrCurrent","Value":"0"},{"Tag":"DeviceRemovable","Value":"0x00"},{"Tag":"PortPwrCtrlMask","Value":"0xff"},{"Tag":"Hub","Value":"Port"},{"Tag":"Port","Value":"1:"},{"Tag":"Port","Value":"2:"},{"Tag":"Port","Value":"3:"},{"Tag":"Port","Value":"4:"},{"Tag":"Port","Value":"5:"},{"Tag":"Port","Value":"6:"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]},{"Category":"Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub","Details":[{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"1.10"},{"Tag":"bDeviceClass","Value":"9"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x1d6b"},{"Tag":"idProduct","Value":"0x0001"},{"Tag":"bcdDevice","Value":"2.06"},{"Tag":"iManufacturer","Value":"3"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"1"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"25"},{"Tag":"bNumInterfaces","Value":"1"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"9"},{"Tag":"bInterfaceSubClass","Value":"0"},{"Tag":"bInterfaceProtocol","Value":"0"},{"Tag":"iInterface","Value":"0"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0002"},{"Tag":"bInterval","Value":"255"},{"Tag":"Hub","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"41"},{"Tag":"nNbrPorts","Value":"2"},{"Tag":"wHubCharacteristic","Value":"0x000a"},{"Tag":"No","Value":"power"},{"Tag":"Per-port","Value":"overcurrent"},{"Tag":"bPwrOn2PwrGood","Value":"1"},{"Tag":"bHubContrCurrent","Value":"0"},{"Tag":"DeviceRemovable","Value":"0x00"},{"Tag":"PortPwrCtrlMask","Value":"0xff"},{"Tag":"Hub","Value":"Port"},{"Tag":"Port","Value":"1:"},{"Tag":"Port","Value":"2:"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]},{"Category":"Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub","Details":[{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"1.10"},{"Tag":"bDeviceClass","Value":"9"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x1d6b"},{"Tag":"idProduct","Value":"0x0001"},{"Tag":"bcdDevice","Value":"2.06"},{"Tag":"iManufacturer","Value":"3"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"1"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"25"},{"Tag":"bNumInterfaces","Value":"1"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"9"},{"Tag":"bInterfaceSubClass","Value":"0"},{"Tag":"bInterfaceProtocol","Value":"0"},{"Tag":"iInterface","Value":"0"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0002"},{"Tag":"bInterval","Value":"255"},{"Tag":"Hub","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"41"},{"Tag":"nNbrPorts","Value":"2"},{"Tag":"wHubCharacteristic","Value":"0x000a"},{"Tag":"No","Value":"power"},{"Tag":"Per-port","Value":"overcurrent"},{"Tag":"bPwrOn2PwrGood","Value":"1"},{"Tag":"bHubContrCurrent","Value":"0"},{"Tag":"DeviceRemovable","Value":"0x00"},{"Tag":"PortPwrCtrlMask","Value":"0xff"},{"Tag":"Hub","Value":"Port"},{"Tag":"Port","Value":"1:"},{"Tag":"Port","Value":"2:"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]},{"Category":"Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub","Details":[{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"1.10"},{"Tag":"bDeviceClass","Value":"9"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x1d6b"},{"Tag":"idProduct","Value":"0x0001"},{"Tag":"bcdDevice","Value":"2.06"},{"Tag":"iManufacturer","Value":"3"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"1"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"25"},{"Tag":"bNumInterfaces","Value":"1"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"9"},{"Tag":"bInterfaceSubClass","Value":"0"},{"Tag":"bInterfaceProtocol","Value":"0"},{"Tag":"iInterface","Value":"0"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0002"},{"Tag":"bInterval","Value":"255"},{"Tag":"Hub","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"41"},{"Tag":"nNbrPorts","Value":"2"},{"Tag":"wHubCharacteristic","Value":"0x000a"},{"Tag":"No","Value":"power"},{"Tag":"Per-port","Value":"overcurrent"},{"Tag":"bPwrOn2PwrGood","Value":"1"},{"Tag":"bHubContrCurrent","Value":"0"},{"Tag":"DeviceRemovable","Value":"0x00"},{"Tag":"PortPwrCtrlMask","Value":"0xff"},{"Tag":"Hub","Value":"Port"},{"Tag":"Port","Value":"1:"},{"Tag":"Port","Value":"2:"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]},{"Category":"Bus 001 Device 002: ID 046b:ff01 American Megatrends, Inc. ","Details":[{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"2.00"},{"Tag":"bDeviceClass","Value":"9"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"1"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x046b"},{"Tag":"idProduct","Value":"0xff01"},{"Tag":"bcdDevice","Value":"1.00"},{"Tag":"iManufacturer","Value":"1"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"3"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"25"},{"Tag":"bNumInterfaces","Value":"1"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"9"},{"Tag":"bInterfaceSubClass","Value":"0"},{"Tag":"bInterfaceProtocol","Value":"0"},{"Tag":"iInterface","Value":"4"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0001"},{"Tag":"bInterval","Value":"12"},{"Tag":"Hub","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"41"},{"Tag":"nNbrPorts","Value":"3"},{"Tag":"wHubCharacteristic","Value":"0x0069"},{"Tag":"Per-port","Value":"power"},{"Tag":"Per-port","Value":"overcurrent"},{"Tag":"TT","Value":"think"},{"Tag":"bPwrOn2PwrGood","Value":"50"},{"Tag":"bHubContrCurrent","Value":"100"},{"Tag":"DeviceRemovable","Value":"0x00"},{"Tag":"PortPwrCtrlMask","Value":"0xff"},{"Tag":"Hub","Value":"Port"},{"Tag":"Port","Value":"1:"},{"Tag":"Port","Value":"2:"},{"Tag":"Port","Value":"3:"},{"Tag":"Device","Value":"Qualifier"},{"Tag":"bLength","Value":"10"},{"Tag":"bDescriptorType","Value":"6"},{"Tag":"bcdUSB","Value":"2.00"},{"Tag":"bDeviceClass","Value":"0"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]},{"Category":"Bus 001 Device 003: ID 046b:ff10 American Megatrends, Inc. Virtual Keyboard and Mouse","Details":[{"Tag":"bLength","Value":"18"},{"Tag":"bDescriptorType","Value":"1"},{"Tag":"bcdUSB","Value":"2.00"},{"Tag":"bDeviceClass","Value":"0"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"idVendor","Value":"0x046b"},{"Tag":"idProduct","Value":"0xff10"},{"Tag":"bcdDevice","Value":"1.00"},{"Tag":"iManufacturer","Value":"1"},{"Tag":"iProduct","Value":"2"},{"Tag":"iSerial","Value":"3"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Configuration","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"2"},{"Tag":"wTotalLength","Value":"59"},{"Tag":"bNumInterfaces","Value":"2"},{"Tag":"bConfigurationValue","Value":"1"},{"Tag":"iConfiguration","Value":"0"},{"Tag":"bmAttributes","Value":"0xe0"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"},{"Tag":"MaxPower","Value":"0mA"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"0"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"3"},{"Tag":"bInterfaceSubClass","Value":"1"},{"Tag":"bInterfaceProtocol","Value":"1"},{"Tag":"iInterface","Value":"4"},{"Tag":"HID","Value":"Device"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"33"},{"Tag":"bcdHID","Value":"1.10"},{"Tag":"bCountryCode","Value":"0"},{"Tag":"bNumDescriptors","Value":"1"},{"Tag":"bDescriptorType","Value":"34"},{"Tag":"wDescriptorLength","Value":"63"},{"Tag":"Report","Value":"Descriptors:"},{"Tag":"**","Value":"UNAVAILABLE"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x81"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0008"},{"Tag":"bInterval","Value":"7"},{"Tag":"Interface","Value":"Descriptor:"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"4"},{"Tag":"bInterfaceNumber","Value":"1"},{"Tag":"bAlternateSetting","Value":"0"},{"Tag":"bNumEndpoints","Value":"1"},{"Tag":"bInterfaceClass","Value":"3"},{"Tag":"bInterfaceSubClass","Value":"1"},{"Tag":"bInterfaceProtocol","Value":"2"},{"Tag":"iInterface","Value":"5"},{"Tag":"HID","Value":"Device"},{"Tag":"bLength","Value":"9"},{"Tag":"bDescriptorType","Value":"33"},{"Tag":"bcdHID","Value":"1.10"},{"Tag":"bCountryCode","Value":"0"},{"Tag":"bNumDescriptors","Value":"1"},{"Tag":"bDescriptorType","Value":"34"},{"Tag":"wDescriptorLength","Value":"63"},{"Tag":"Report","Value":"Descriptors:"},{"Tag":"**","Value":"UNAVAILABLE"},{"Tag":"Endpoint","Value":"Descriptor:"},{"Tag":"bLength","Value":"7"},{"Tag":"bDescriptorType","Value":"5"},{"Tag":"bEndpointAddress","Value":"0x82"},{"Tag":"bmAttributes","Value":"3"},{"Tag":"Transfer","Value":"Type"},{"Tag":"Synch","Value":"Type"},{"Tag":"Usage","Value":"Type"},{"Tag":"wMaxPacketSize","Value":"0x0008"},{"Tag":"bInterval","Value":"7"},{"Tag":"Device","Value":"Qualifier"},{"Tag":"bLength","Value":"10"},{"Tag":"bDescriptorType","Value":"6"},{"Tag":"bcdUSB","Value":"2.00"},{"Tag":"bDeviceClass","Value":"0"},{"Tag":"bDeviceSubClass","Value":"0"},{"Tag":"bDeviceProtocol","Value":"0"},{"Tag":"bMaxPacketSize0","Value":"64"},{"Tag":"bNumConfigurations","Value":"1"},{"Tag":"Device","Value":"Status:"},{"Tag":"Self","Value":"Powered"},{"Tag":"Remote","Value":"Wakeup"}]}]},"sysinfo.nic": "collection failed","sysinfo.smbios": {"Version":"2.6","Entries":[{"Category":"BIOS Information","Details":[{"Tag":"Vendor","Value":"InventecESC"},{"Tag":"Version","Value":"1.04"},{"Tag":"Release Date","Value":"09/11/2009"},{"Tag":"Address","Value":"0xF0000"},{"Tag":"Runtime Size","Value":"64 kB"},{"Tag":"ROM Size","Value":"2048 kB"},{"Tag":"Characteristics","Value":""},{"Tag":"BIOS Revision","Value":"8.15"}]},{"Category":"System Information","Details":[{"Tag":"Manufacturer","Value":"Dell"},{"Tag":"Product Name","Value":"XS23-TY3"},{"Tag":"Version","Value":"XS2"},{"Tag":"Serial Number","Value":"FTL83H1.3"},{"Tag":"UUID","Value":"4C4C4544-0054-4C10-8038-C6C04F334831"},{"Tag":"Wake-up Type","Value":"Power Switch"},{"Tag":"SKU Number","Value":""},{"Tag":"Family","Value":"Server"}]},{"Category":"Base Board Information","Details":[{"Tag":"Manufacturer","Value":"Dell"},{"Tag":"Product Name","Value":"PLUTONIUM"},{"Tag":"Version","Value":"1395T2285001"},{"Tag":"Serial Number","Value":"PV9CMP4096"},{"Tag":"Asset Tag","Value":""},{"Tag":"Features","Value":""},{"Tag":"Location In Chassis","Value":""},{"Tag":"Chassis Handle","Value":"0x0003"},{"Tag":"Type","Value":"Motherboard"},{"Tag":"Contained Object Handles","Value":"0"}]},{"Category":"Chassis Information","Details":[{"Tag":"Manufacturer","Value":"Rack Mount Chassis"},{"Tag":"Type","Value":"Other"},{"Tag":"Lock","Value":"Not Present"},{"Tag":"Version","Value":"A00"},{"Tag":"Serial Number","Value":"2500888"},{"Tag":"Asset Tag","Value":""},{"Tag":"Boot-up State","Value":"Safe"},{"Tag":"Power Supply State","Value":"Safe"},{"Tag":"Thermal State","Value":"Safe"},{"Tag":"Security Status","Value":"None"},{"Tag":"OEM Information","Value":"0x00000000"},{"Tag":"Height","Value":"2 U"},{"Tag":"Number Of Power Cords","Value":"1"},{"Tag":"Contained Elements","Value":"0"}]},{"Category":"Processor Information","Details":[{"Tag":"Socket Designation","Value":"CPU 0"},{"Tag":"Type","Value":"Central Processor"},{"Tag":"Family","Value":"Xeon"},{"Tag":"Manufacturer","Value":"Intel"},{"Tag":"ID","Value":"A5 06 01 00 FF FB EB BF"},{"Tag":"Signature","Value":"Type 0, Family 6, Model 26, Stepping 5"},{"Tag":"Flags","Value":""},{"Tag":"Version","Value":"Intel(R) Xeon(R) CPU L5520 @ 2.27GHz"},{"Tag":"Voltage","Value":"1.3 V"},{"Tag":"External Clock","Value":"133 MHz"},{"Tag":"Max Speed","Value":"2266 MHz"},{"Tag":"Current Speed","Value":"2266 MHz"},{"Tag":"Status","Value":"Populated, Enabled"},{"Tag":"Upgrade","Value":"Other"},{"Tag":"L1 Cache Handle","Value":"0x0005"},{"Tag":"L2 Cache Handle","Value":"0x0006"},{"Tag":"L3 Cache Handle","Value":"0x0007"},{"Tag":"Serial Number","Value":""},{"Tag":"Asset Tag","Value":""},{"Tag":"Part Number","Value":""},{"Tag":"Core Count","Value":"4"},{"Tag":"Core Enabled","Value":"4"},{"Tag":"Thread Count","Value":"8"},{"Tag":"Characteristics","Value":""}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L1-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 1"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"128 kB"},{"Tag":"Maximum Size","Value":"128 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Data"},{"Tag":"Associativity","Value":"8-way Set-associative"}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L2-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 2"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"1024 kB"},{"Tag":"Maximum Size","Value":"1024 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Unified"},{"Tag":"Associativity","Value":"8-way Set-associative"}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L3-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 3"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"8192 kB"},{"Tag":"Maximum Size","Value":"8192 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Unified"},{"Tag":"Associativity","Value":"16-way Set-associative"}]},{"Category":"Processor Information","Details":[{"Tag":"Socket Designation","Value":"CPU 1"},{"Tag":"Type","Value":"Central Processor"},{"Tag":"Family","Value":"Xeon"},{"Tag":"Manufacturer","Value":"Intel"},{"Tag":"ID","Value":"A5 06 01 00 FF FB EB BF"},{"Tag":"Signature","Value":"Type 0, Family 6, Model 26, Stepping 5"},{"Tag":"Flags","Value":""},{"Tag":"Version","Value":"Intel(R) Xeon(R) CPU L5520 @ 2.27GHz"},{"Tag":"Voltage","Value":"1.3 V"},{"Tag":"External Clock","Value":"133 MHz"},{"Tag":"Max Speed","Value":"2266 MHz"},{"Tag":"Current Speed","Value":"2266 MHz"},{"Tag":"Status","Value":"Populated, Enabled"},{"Tag":"Upgrade","Value":"Other"},{"Tag":"L1 Cache Handle","Value":"0x0009"},{"Tag":"L2 Cache Handle","Value":"0x000A"},{"Tag":"L3 Cache Handle","Value":"0x000B"},{"Tag":"Serial Number","Value":""},{"Tag":"Asset Tag","Value":""},{"Tag":"Part Number","Value":""},{"Tag":"Core Count","Value":"4"},{"Tag":"Core Enabled","Value":"4"},{"Tag":"Thread Count","Value":"8"},{"Tag":"Characteristics","Value":""}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L1-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 1"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"128 kB"},{"Tag":"Maximum Size","Value":"128 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Data"},{"Tag":"Associativity","Value":"8-way Set-associative"}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L2-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 2"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"1024 kB"},{"Tag":"Maximum Size","Value":"1024 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Unified"},{"Tag":"Associativity","Value":"8-way Set-associative"}]},{"Category":"Cache Information","Details":[{"Tag":"Socket Designation","Value":"L3-Cache"},{"Tag":"Configuration","Value":"Enabled, Not Socketed, Level 3"},{"Tag":"Operational Mode","Value":"Write Back"},{"Tag":"Location","Value":"Internal"},{"Tag":"Installed Size","Value":"8192 kB"},{"Tag":"Maximum Size","Value":"8192 kB"},{"Tag":"Supported SRAM Types","Value":""},{"Tag":"Installed SRAM Type","Value":"Other"},{"Tag":"Speed","Value":"Unknown"},{"Tag":"Error Correction Type","Value":"Single-bit ECC"},{"Tag":"System Type","Value":"Unified"},{"Tag":"Associativity","Value":"16-way Set-associative"}]},{"Category":"System Slot Information","Details":[{"Tag":"Designation","Value":"PCIe Slot"},{"Tag":"Type","Value":"x16 PCI Express"},{"Tag":"Current Usage","Value":"Available"},{"Tag":"Length","Value":"Short"},{"Tag":"ID","Value":"55"},{"Tag":"Characteristics","Value":""}]},{"Category":"On Board Device Information","Details":[{"Tag":"Type","Value":"Video"},{"Tag":"Status","Value":"Enabled"},{"Tag":"Description","Value":"AST2050 VGA"}]},{"Category":"On Board Device Information","Details":[{"Tag":"Type","Value":"Ethernet"},{"Tag":"Status","Value":"Enabled"},{"Tag":"Description","Value":"WG82576EB NIC1"}]},{"Category":"On Board Device Information","Details":[{"Tag":"Type","Value":"Ethernet"},{"Tag":"Status","Value":"Enabled"},{"Tag":"Description","Value":"WG82576EB NIC2"}]},{"Category":"OEM Strings","Details":[{"Tag":"String 1","Value":"J15-A"},{"Tag":"String 2","Value":"J15-B"},{"Tag":"String 3","Value":"J15-C"},{"Tag":"String 4","Value":"J15-D"},{"Tag":"String 5","Value":"J15-E"}]},{"Category":"BIOS Language Information","Details":[{"Tag":"Language Description Format","Value":"Long"},{"Tag":"Installable Languages","Value":"1"},{"Tag":"Currently Installed Language","Value":"en|US|iso8859-1"}]},{"Category":"Physical Memory Array","Details":[{"Tag":"Location","Value":"System Board Or Motherboard"},{"Tag":"Use","Value":"System Memory"},{"Tag":"Error Correction Type","Value":"Multi-bit ECC"},{"Tag":"Maximum Capacity","Value":"48 GB"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Number Of Devices","Value":"6"}]},{"Category":"Memory Array Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00000000000"},{"Tag":"Ending Address","Value":"0x005FFFFFFFF"},{"Tag":"Range Size","Value":"24 GB"},{"Tag":"Physical Array Handle","Value":"0x0012"},{"Tag":"Partition Width","Value":"1"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM01"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"5182C121"},{"Tag":"Asset Tag","Value":"AssetTagNum01"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00000000000"},{"Tag":"Ending Address","Value":"0x000FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0014"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM02"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"EE03B026"},{"Tag":"Asset Tag","Value":"AssetTagNum02"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00100000000"},{"Tag":"Ending Address","Value":"0x001FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0016"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM03"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"50827121"},{"Tag":"Asset Tag","Value":"AssetTagNum03"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00200000000"},{"Tag":"Ending Address","Value":"0x002FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x001A"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM04"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"6D822121"},{"Tag":"Asset Tag","Value":"AssetTagNum04"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00300000000"},{"Tag":"Ending Address","Value":"0x003FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x001C"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM05"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"75CC610F"},{"Tag":"Asset Tag","Value":"AssetTagNum05"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00400000000"},{"Tag":"Ending Address","Value":"0x004FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0020"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0012"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU0"},{"Tag":"Bank Locator","Value":"DIMM06"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"0802200B"},{"Tag":"Asset Tag","Value":"AssetTagNum06"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00500000000"},{"Tag":"Ending Address","Value":"0x005FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0022"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0013"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"Physical Memory Array","Details":[{"Tag":"Location","Value":"System Board Or Motherboard"},{"Tag":"Use","Value":"System Memory"},{"Tag":"Error Correction Type","Value":"Multi-bit ECC"},{"Tag":"Maximum Capacity","Value":"24 GB"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Number Of Devices","Value":"6"}]},{"Category":"Memory Array Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00600000000"},{"Tag":"Ending Address","Value":"0x00BFFFFFFFF"},{"Tag":"Range Size","Value":"24 GB"},{"Tag":"Physical Array Handle","Value":"0x0026"},{"Tag":"Partition Width","Value":"1"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM07"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"91625014"},{"Tag":"Asset Tag","Value":"AssetTagNum07"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00600000000"},{"Tag":"Ending Address","Value":"0x006FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0028"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM08"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"13044026"},{"Tag":"Asset Tag","Value":"AssetTagNum08"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00700000000"},{"Tag":"Ending Address","Value":"0x007FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x002A"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM09"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"A30C821C"},{"Tag":"Asset Tag","Value":"AssetTagNum09"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00800000000"},{"Tag":"Ending Address","Value":"0x008FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x002E"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM10"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"651C2026"},{"Tag":"Asset Tag","Value":"AssetTagNum10"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00900000000"},{"Tag":"Ending Address","Value":"0x009FFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0030"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM11"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"90AFA115"},{"Tag":"Asset Tag","Value":"AssetTagNum11"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00A00000000"},{"Tag":"Ending Address","Value":"0x00AFFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0034"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Memory Device","Details":[{"Tag":"Array Handle","Value":"0x0026"},{"Tag":"Error Information Handle","Value":"Not Provided"},{"Tag":"Total Width","Value":"72 bits"},{"Tag":"Data Width","Value":"64 bits"},{"Tag":"Size","Value":"4096 MB"},{"Tag":"Form Factor","Value":"DIMM"},{"Tag":"Set","Value":"None"},{"Tag":"Locator","Value":"CPU1"},{"Tag":"Bank Locator","Value":"DIMM12"},{"Tag":"Type","Value":"Other"},{"Tag":"Type Detail","Value":"Other"},{"Tag":"Speed","Value":"1066 MHz"},{"Tag":"Manufacturer","Value":"Hyundai"},{"Tag":"Serial Number","Value":"3004B026"},{"Tag":"Asset Tag","Value":"AssetTagNum12"},{"Tag":"Part Number","Value":"HMT151R7BFR4C-G7"},{"Tag":"Rank","Value":"Unknown"}]},{"Category":"Memory Device Mapped Address","Details":[{"Tag":"Starting Address","Value":"0x00B00000000"},{"Tag":"Ending Address","Value":"0x00BFFFFFFFF"},{"Tag":"Range Size","Value":"4 GB"},{"Tag":"Physical Device Handle","Value":"0x0036"},{"Tag":"Memory Array Mapped Address Handle","Value":"0x0027"},{"Tag":"Partition Row Position","Value":"1"},{"Tag":"Interleave Position","Value":"Unknown"},{"Tag":"Interleaved Data Depth","Value":"2"}]},{"Category":"Inactive","Details":[]},{"Category":"Inactive","Details":[]},{"Category":"System Boot Information","Details":[{"Tag":"Status","Value":"No errors detected"}]},{"Category":"IPMI Device Information","Details":[{"Tag":"Interface Type","Value":"KCS (Keyboard Control Style)"},{"Tag":"Specification Version","Value":"2.0"},{"Tag":"I2C Slave Address","Value":"0x10"},{"Tag":"NV Storage Device","Value":"Not Present"},{"Tag":"Base Address","Value":"0x0000000000000CA2 (I/O)"},{"Tag":"Register Spacing","Value":"Successive Byte Boundaries"}]}]},"sysinfo.bmc": {"Entries":[{"Category":"bmc","Details":[{"Tag":"Device ID","Value":"37"},{"Tag":"Device Revision","Value":"1"},{"Tag":"Device SDRs","Value":"unsupported"},{"Tag":"Firmware Revision","Value":"1.04"},{"Tag":"Device Available","Value":"yes (normal operation)"},{"Tag":"IPMI Version","Value":"2.0"},{"Tag":"Sensor Device","Value":"supported"},{"Tag":"SDR Repository Device","Value":"supported"},{"Tag":"SEL Device","Value":"supported"},{"Tag":"FRU Inventory Device","Value":"supported"},{"Tag":"IPMB Event Receiver","Value":"supported"},{"Tag":"IPMB Event Generator","Value":"supported"},{"Tag":"Bridge","Value":"unsupported"},{"Tag":"Chassis Device","Value":"supported"},{"Tag":"Manufacturer ID","Value":"Inventec Enterprise System Corp. (20569)"},{"Tag":"Product ID","Value":"52"},{"Tag":"Auxiliary Firmware Revision Information","Value":"00006D6Eh"},{"Tag":"GUID","Value":"77b0ebd1-a000-7265-6967-542031343435"},{"Tag":"System Firmware Version","Value":"5441A104"},{"Tag":"System Name","Value":""},{"Tag":"Primary Operating System Name","Value":""},{"Tag":"Operating System Name","Value":""},{"Tag":"Channel Number","Value":"0"},{"Tag":"Medium Type","Value":"IPMB (I2C)"},{"Tag":"Protocol Type","Value":"IPMB-1.0"},{"Tag":"Active Session Count","Value":"0"},{"Tag":"Session Support","Value":"session-less"},{"Tag":"Vendor ID","Value":"Intelligent Platform Management Interface forum (7154)"},{"Tag":"Channel Number","Value":"1"},{"Tag":"Medium Type","Value":"802.3 LAN"},{"Tag":"Protocol Type","Value":"IPMB-1.0"},{"Tag":"Active Session Count","Value":"0"},{"Tag":"Session Support","Value":"multi-session"},{"Tag":"Vendor ID","Value":"Intelligent Platform Management Interface forum (7154)"},{"Tag":"Channel Number","Value":"6"},{"Tag":"Medium Type","Value":"IPMB (I2C)"},{"Tag":"Protocol Type","Value":"IPMB-1.0"},{"Tag":"Active Session Count","Value":"0"},{"Tag":"Session Support","Value":"session-less"},{"Tag":"Vendor ID","Value":"Intelligent Platform Management Interface forum (7154)"}]}]}}}` + sampleDigits = `[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]` + sampleStrings = `["abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc","abc"]` + sampleLiterals = `[null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false,null,true,false]` + sampleArrays = `[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]` + sampleObjects = `{"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": {"child": "value"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}` +) + +func BenchmarkUnmarshalMix(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleMix), b) } +func BenchmarkDecodeMix(b *testing.B) { benchmarkStdLibDecode([]byte(sampleMix), b) } +func BenchmarkSliceMix(b *testing.B) { benchmarkBytesLexer([]byte(sampleMix), b) } +func BenchmarkReaderMix(b *testing.B) { benchmarkReaderLexer([]byte(sampleMix), b) } + +func BenchmarkUnmarshalDigits(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleDigits), b) } +func BenchmarkDecodeDigits(b *testing.B) { benchmarkStdLibDecode([]byte(sampleDigits), b) } +func BenchmarkSliceDigits(b *testing.B) { benchmarkBytesLexer([]byte(sampleDigits), b) } +func BenchmarkReaderDigits(b *testing.B) { benchmarkReaderLexer([]byte(sampleDigits), b) } + +func BenchmarkUnmarshalStrings(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleStrings), b) } +func BenchmarkDecodeStrings(b *testing.B) { benchmarkStdLibDecode([]byte(sampleStrings), b) } +func BenchmarkSliceStrings(b *testing.B) { benchmarkBytesLexer([]byte(sampleStrings), b) } +func BenchmarkReaderStrings(b *testing.B) { benchmarkReaderLexer([]byte(sampleStrings), b) } + +func BenchmarkUnmarshalLiterals(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleLiterals), b) } +func BenchmarkDecodeLiterals(b *testing.B) { benchmarkStdLibDecode([]byte(sampleLiterals), b) } +func BenchmarkSliceLiterals(b *testing.B) { benchmarkBytesLexer([]byte(sampleLiterals), b) } +func BenchmarkReaderLiterals(b *testing.B) { benchmarkReaderLexer([]byte(sampleLiterals), b) } + +func BenchmarkUnmarshalArrays(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleArrays), b) } +func BenchmarkDecodeArrays(b *testing.B) { benchmarkStdLibDecode([]byte(sampleArrays), b) } +func BenchmarkSliceArrays(b *testing.B) { benchmarkBytesLexer([]byte(sampleArrays), b) } +func BenchmarkReaderArrays(b *testing.B) { benchmarkReaderLexer([]byte(sampleArrays), b) } + +func BenchmarkUnmarshalObjects(b *testing.B) { benchmarkStdUnmarshal([]byte(sampleObjects), b) } +func BenchmarkDecodeObjects(b *testing.B) { benchmarkStdLibDecode([]byte(sampleObjects), b) } +func BenchmarkSliceObjects(b *testing.B) { benchmarkBytesLexer([]byte(sampleObjects), b) } +func BenchmarkReaderObjects(b *testing.B) { benchmarkReaderLexer([]byte(sampleObjects), b) } + +func TestBytesLexerReset(t *testing.T) { + as := assert.New(t) + lexer := NewSliceLexer([]byte(sampleMix), JSON) + sitems := readerToArray(lexer) + + lexer.reset() + sitems2 := readerToArray(lexer) + + as.EqualValues(sitems, sitems2) +} + +func TestReaderLexerReset(t *testing.T) { + as := assert.New(t) + reader := bytes.NewReader([]byte(sampleMix)) + lexer := NewReaderLexer(reader, JSON) + ritems := readerToArray(lexer) + + lexer.reset() + reader.Seek(0, 0) + ritems2 := readerToArray(lexer) + + as.EqualValues(ritems, ritems2, "Item slices are not equal") +} + +func TestLexersAgainstEachOther(t *testing.T) { + as := assert.New(t) + slexer := NewSliceLexer([]byte(sampleMix), JSON) + sitems := readerToArray(slexer) + + reader := strings.NewReader(sampleMix) + rlexer := NewReaderLexer(reader, JSON) + ritems := readerToArray(rlexer) + + as.EqualValues(sitems, ritems) +} + +func TestLargeJSON(t *testing.T) { + as := assert.New(t) + input, err := ioutil.ReadFile("large.test") + if err != nil { + t.SkipNow() + return + } + + lexer := NewSliceLexer(input, JSON) + for { + i, ok := lexer.next() + if i.typ == jsonError { + as.Fail(string(i.val)) + } + _ = i + if !ok { + break + } + } +} + +func benchmarkBytesLexer(input []byte, b *testing.B) { + lexer := NewSliceLexer(input, JSON) + b.ResetTimer() + for n := 0; n < b.N; n++ { + for { + _, ok := lexer.next() + if !ok { + break + } + } + lexer.reset() + } +} + +func benchmarkReaderLexer(input []byte, b *testing.B) { + reader := bytes.NewReader(input) + lexer := NewReaderLexer(reader, JSON) + b.ResetTimer() + for n := 0; n < b.N; n++ { + for { + _, ok := lexer.next() + if !ok { + break + } + } + lexer.reset() + reader.Seek(0, 0) + } +} + +func benchmarkStdLibDecode(input []byte, b *testing.B) { + reader := bytes.NewReader(input) + dec := json.NewDecoder(reader) + b.ResetTimer() + for n := 0; n < b.N; n++ { + var x struct{} + dec.Decode(&x) + reader.Seek(0, 0) + } +} + +// Not comparable to previous benchmarks +func benchmarkStdUnmarshal(input []byte, b *testing.B) { + b.ResetTimer() + for n := 0; n < b.N; n++ { + var x interface{} + err := json.Unmarshal(input, &x) + if err != nil { + b.Error(err) + } + } +} + +func benchmarkStdUnmarshalJSONLarge(b *testing.B) { + input, err := ioutil.ReadFile("large.test") + if err != nil { + b.SkipNow() + } + b.ResetTimer() + for n := 0; n < b.N; n++ { + var x interface{} + err := json.Unmarshal(input, &x) + if err != nil { + b.Error(err) + } + } +} + +func BenchmarkStdUnmarshalLarge(b *testing.B) { + input, err := ioutil.ReadFile("large.test") + if err != nil { + b.SkipNow() + } + benchmarkStdUnmarshal(input, b) +} + +func BenchmarkStdLibDecodeLarge(b *testing.B) { + input, err := ioutil.ReadFile("large.test") + reader := bytes.NewReader(input) + if err != nil { + b.SkipNow() + } + dec := json.NewDecoder(reader) + b.ResetTimer() + for n := 0; n < b.N; n++ { + var x struct{} + dec.Decode(&x) + reader.Seek(0, 0) + } +} + +func BenchmarkSliceLexerLarge(b *testing.B) { + input, err := ioutil.ReadFile("large.test") + if err != nil { + b.SkipNow() + } + benchmarkBytesLexer(input, b) +} + +func BenchmarkReaderLexerLarge(b *testing.B) { + input, err := os.Open("large.test") + if err != nil { + b.SkipNow() + } + // reader := io.NewReader(input) + // reader, _ := os.Open("large.test") + lexer := NewReaderLexer(input, JSON) + b.ResetTimer() + for n := 0; n < b.N; n++ { + for { + _, ok := lexer.next() + if !ok { + break + } + } + lexer.reset() + input.Seek(0, 0) + } +} + +type lexTest struct { + name string + input string + tokenTypes []int +} + +func i(tokenType int) Item { + return Item{tokenType, 0, []byte{}} +} + +func typeIsEqual(i1, i2 []Item, printError bool) bool { + for k := 0; k < int(math.Max(float64(len(i1)), float64(len(i2)))); k++ { + if k < len(i1) { + if i1[k].typ == jsonError && printError { + fmt.Println(string(i1[k].val)) + } + } else if k < len(i2) { + if i2[k].typ == jsonError && printError { + fmt.Println(string(i2[k].val)) + } + } + + if k >= len(i1) || k >= len(i2) { + return false + } + + if i1[k].typ != i2[k].typ { + return false + } + } + + return true +} diff --git a/misc.go b/misc.go new file mode 100644 index 0000000..0e9ec2c --- /dev/null +++ b/misc.go @@ -0,0 +1,181 @@ +package jsonpath + +import ( + "errors" + "fmt" +) + +func takeExponent(l lexer) error { + r := l.peek() + if r != 'e' && r != 'E' { + return nil + } + l.take() + r = l.take() + switch r { + case '+', '-': + // Check digit immediately follows sign + if d := l.peek(); !(d >= '0' && d <= '9') { + return fmt.Errorf("Expected digit after numeric sign instead of %#U", d) + } + takeDigits(l) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + takeDigits(l) + default: + return fmt.Errorf("Expected digit after 'e' instead of %#U", r) + } + return nil +} + +func takeJSONNumeric(l lexer) error { + cur := l.take() + switch cur { + case '-': + // Check digit immediately follows sign + if d := l.peek(); !(d >= '0' && d <= '9') { + return fmt.Errorf("Expected digit after dash instead of %#U", d) + } + takeDigits(l) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + takeDigits(l) + default: + return fmt.Errorf("Expected digit or dash instead of %#U", cur) + } + + // fraction or exponent + cur = l.peek() + switch cur { + case '.': + l.take() + // Check digit immediately follows period + if d := l.peek(); !(d >= '0' && d <= '9') { + return fmt.Errorf("Expected digit after '.' instead of %#U", d) + } + takeDigits(l) + if err := takeExponent(l); err != nil { + return err + } + case 'e', 'E': + if err := takeExponent(l); err != nil { + return err + } + } + + return nil +} + +func takeDigits(l lexer) { + for { + d := l.peek() + if d >= '0' && d <= '9' { + l.take() + } else { + break + } + } +} + +// Only used at the very beginning of parsing. After that, the emit() function +// automatically skips whitespace. +func ignoreSpaceRun(l lexer) { + for { + r := l.peek() + if r == ' ' || r == '\t' || r == '\r' || r == '\n' { + l.take() + } else { + break + } + } + l.ignore() +} + +func takeExactSequence(l lexer, str []byte) bool { + for _, r := range str { + v := l.take() + if v != int(r) { + return false + } + } + return true +} + +func readerToArray(tr tokenReader) []Item { + vals := make([]Item, 0) + for { + i, ok := tr.next() + if !ok { + break + } + v := *i + s := make([]byte, len(v.val)) + copy(s, v.val) + v.val = s + vals = append(vals, v) + } + return vals +} + +func findErrors(items []Item) (Item, bool) { + for _, i := range items { + if i.typ == lexError { + return i, true + } + } + return Item{}, false +} + +func byteSlicesEqual(a, b []byte) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func firstError(errors ...error) error { + for _, e := range errors { + if e != nil { + return e + } + } + return nil +} + +func abs(x int) int { + switch { + case x < 0: + return -x + case x == 0: + return 0 // return correctly abs(-0) + } + return x +} + +//TODO: Kill the need for this +func getJsonTokenType(val []byte) (int, error) { + if len(val) == 0 { + return -1, errors.New("No Value") + } + switch val[0] { + case '{': + return jsonBraceLeft, nil + case '"': + return jsonString, nil + case '[': + return jsonBracketLeft, nil + case 'n': + return jsonNull, nil + case 't', 'b': + return jsonBool, nil + case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return jsonNumber, nil + default: + return -1, errors.New("Unrecognized Json Value") + } +} diff --git a/path.go b/path.go new file mode 100644 index 0000000..9d142b5 --- /dev/null +++ b/path.go @@ -0,0 +1,208 @@ +package jsonpath + +import ( + "errors" + "fmt" + "strconv" +) + +const ( + opTypeIndex = iota + opTypeIndexRange + opTypeIndexWild + opTypeName + opTypeNameList + opTypeNameWild +) + +type Path struct { + stringValue string + operators []*operator + captureEndValue bool +} + +type operator struct { + typ int + indexStart int + indexEnd int + hasIndexEnd bool + keyStrings map[string]struct{} + + whereClauseBytes []byte + dependentPaths []*Path + whereClause []Item +} + +func genIndexKey(tr tokenReader) (*operator, error) { + k := &operator{} + var t *Item + var ok bool + if t, ok = tr.next(); !ok { + return nil, errors.New("Expected number, key, or *, but got none") + } + + switch t.typ { + case pathWildcard: + k.typ = opTypeIndexWild + k.indexStart = 0 + if t, ok = tr.next(); !ok { + return nil, errors.New("Expected ] after *, but got none") + } + if t.typ != pathBracketRight { + return nil, fmt.Errorf("Expected ] after * instead of %q", t.val) + } + case pathIndex: + v, err := strconv.Atoi(string(t.val)) + if err != nil { + return nil, fmt.Errorf("Could not parse %q into int64", t.val) + } + k.indexStart = v + k.indexEnd = v + k.hasIndexEnd = true + + if t, ok = tr.next(); !ok { + return nil, errors.New("Expected number or *, but got none") + } + switch t.typ { + case pathIndexRange: + if t, ok = tr.next(); !ok { + return nil, errors.New("Expected number or *, but got none") + } + switch t.typ { + case pathIndex: + v, err := strconv.Atoi(string(t.val)) + if err != nil { + return nil, fmt.Errorf("Could not parse %q into int64", t.val) + } + k.indexEnd = v - 1 + k.hasIndexEnd = true + + if t, ok = tr.next(); !ok || t.typ != pathBracketRight { + return nil, errors.New("Expected ], but got none") + } + case pathBracketRight: + k.hasIndexEnd = false + default: + return nil, fmt.Errorf("Unexpected value within brackets after index: %q", t.val) + } + + k.typ = opTypeIndexRange + case pathBracketRight: + k.typ = opTypeIndex + default: + return nil, fmt.Errorf("Unexpected value within brackets after index: %q", t.val) + } + case pathKey: + k.keyStrings = map[string]struct{}{string(t.val[1 : len(t.val)-1]): struct{}{}} + k.typ = opTypeName + + if t, ok = tr.next(); !ok || t.typ != pathBracketRight { + return nil, errors.New("Expected ], but got none") + } + default: + return nil, fmt.Errorf("Unexpected value within brackets: %q", t.val) + } + + return k, nil +} + +func parsePath(pathString string) (*Path, error) { + lexer := NewSliceLexer([]byte(pathString), PATH) + p, err := tokensToOperators(lexer) + if err != nil { + return nil, err + } + + p.stringValue = pathString + + //Generate dependent paths + for _, op := range p.operators { + if len(op.whereClauseBytes) > 0 { + var err error + trimmed := op.whereClauseBytes[1 : len(op.whereClauseBytes)-1] + whereLexer := NewSliceLexer(trimmed, EXPRESSION) + items := readerToArray(whereLexer) + if errItem, found := findErrors(items); found { + return nil, errors.New(string(errItem.val)) + } + + // transform expression into postfix form + op.whereClause, err = infixToPostFix(items[:len(items)-1]) // trim EOF + if err != nil { + return nil, err + } + op.dependentPaths = make([]*Path, 0) + // parse all paths in expression + for _, item := range op.whereClause { + if item.typ == exprPath { + p, err := parsePath(string(item.val)) + if err != nil { + return nil, err + } + op.dependentPaths = append(op.dependentPaths, p) + } + } + } + } + return p, nil +} + +func tokensToOperators(tr tokenReader) (*Path, error) { + q := &Path{ + stringValue: "", + captureEndValue: false, + operators: make([]*operator, 0), + } + for { + p, ok := tr.next() + if !ok { + break + } + switch p.typ { + case pathRoot: + if len(q.operators) != 0 { + return nil, errors.New("Unexpected root node after start") + } + continue + case pathCurrent: + if len(q.operators) != 0 { + return nil, errors.New("Unexpected current node after start") + } + continue + case pathPeriod: + continue + case pathBracketLeft: + k, err := genIndexKey(tr) + if err != nil { + return nil, err + } + q.operators = append(q.operators, k) + case pathKey: + keyName := p.val + if len(p.val) == 0 { + return nil, fmt.Errorf("Key length is zero at %d", p.pos) + } + if p.val[0] == '"' && p.val[len(p.val)-1] == '"' { + keyName = p.val[1 : len(p.val)-1] + } + q.operators = append(q.operators, &operator{typ: opTypeName, keyStrings: map[string]struct{}{string(keyName): struct{}{}}}) + case pathWildcard: + q.operators = append(q.operators, &operator{typ: opTypeNameWild}) + case pathValue: + q.captureEndValue = true + case pathWhere: + case pathExpression: + if len(q.operators) == 0 { + return nil, errors.New("Cannot add where clause on last key") + } + last := q.operators[len(q.operators)-1] + if last.whereClauseBytes != nil { + return nil, errors.New("Expression on last key already set") + } + last.whereClauseBytes = p.val + case pathError: + return q, errors.New(string(p.val)) + } + } + return q, nil +} diff --git a/path_states.go b/path_states.go new file mode 100644 index 0000000..b1e82de --- /dev/null +++ b/path_states.go @@ -0,0 +1,213 @@ +package jsonpath + +const ( + pathError = iota + pathEOF + + pathRoot + pathCurrent + pathKey + pathBracketLeft + pathBracketRight + pathIndex + pathOr + pathIndexRange + pathLength + pathWildcard + pathPeriod + pathValue + pathWhere + pathExpression +) + +var pathTokenNames = map[int]string{ + pathError: "ERROR", + pathEOF: "EOF", + + pathRoot: "$", + pathCurrent: "@", + pathKey: "KEY", + pathBracketLeft: "[", + pathBracketRight: "]", + pathIndex: "INDEX", + pathOr: "|", + pathIndexRange: ":", + pathLength: "LENGTH", + pathWildcard: "*", + pathPeriod: ".", + pathValue: "+", + pathWhere: "?", + pathExpression: "EXPRESSION", +} + +var PATH = lexPathStart + +func lexPathStart(l lexer, state *intStack) stateFn { + ignoreSpaceRun(l) + cur := l.take() + switch cur { + case '$': + l.emit(pathRoot) + case '@': + l.emit(pathCurrent) + default: + return l.errorf("Expected $ or @ at start of path instead of %#U", cur) + } + + return lexPathAfterKey +} + +func lexPathAfterKey(l lexer, state *intStack) stateFn { + cur := l.take() + switch cur { + case '.': + l.emit(pathPeriod) + return lexKey + case '[': + l.emit(pathBracketLeft) + return lexPathBracketOpen + case '+': + l.emit(pathValue) + return lexPathAfterValue + case '?': + l.emit(pathWhere) + return lexPathExpression + case eof: + l.emit(pathEOF) + default: + return l.errorf("Unrecognized rune after path element %#U", cur) + } + return nil +} + +func lexPathExpression(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != '(' { + return l.errorf("Expected $ at start of path instead of %#U", cur) + } + + parenLeftCount := 1 + for { + cur = l.take() + switch cur { + case '(': + parenLeftCount++ + case ')': + parenLeftCount-- + case eof: + return l.errorf("Unexpected EOF within expression") + } + + if parenLeftCount == 0 { + break + } + } + l.emit(pathExpression) + return lexPathAfterKey +} + +func lexPathBracketOpen(l lexer, state *intStack) stateFn { + switch l.peek() { + case '*': + l.take() + l.emit(pathWildcard) + return lexPathBracketClose + case '"': + l.takeString() + l.emit(pathKey) + return lexPathBracketClose + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + l.take() + takeDigits(l) + l.emit(pathIndex) + return lexPathIndexRange + case eof: + l.emit(pathEOF) + } + return nil +} + +func lexPathBracketClose(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != ']' { + return l.errorf("Expected ] instead of %#U", cur) + } + l.emit(pathBracketRight) + return lexPathAfterKey +} + +func lexKey(l lexer, state *intStack) stateFn { + // TODO: Support globbing of keys + switch l.peek() { + case '*': + l.take() + l.emit(pathWildcard) + return lexPathAfterKey + case '"': + l.takeString() + l.emit(pathKey) + return lexPathAfterKey + case eof: + l.take() + l.emit(pathEOF) + return nil + default: + for { + v := l.peek() + if v == '.' || v == '[' || v == '+' || v == '?' || v == eof { + break + } + l.take() + } + l.emit(pathKey) + return lexPathAfterKey + } +} + +func lexPathIndexRange(l lexer, state *intStack) stateFn { + // TODO: Expand supported operations + // Currently only supports single index or wildcard (1 or all) + cur := l.peek() + switch cur { + case ':': + l.take() + l.emit(pathIndexRange) + return lexPathIndexRangeSecond + case ']': + return lexPathBracketClose + default: + return l.errorf("Expected digit or ] instead of %#U", cur) + } +} + +func lexPathIndexRangeSecond(l lexer, state *intStack) stateFn { + cur := l.peek() + switch cur { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + takeDigits(l) + l.emit(pathIndex) + return lexPathBracketClose + case ']': + return lexPathBracketClose + default: + return l.errorf("Expected digit or ] instead of %#U", cur) + } +} + +func lexPathArrayClose(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != ']' { + return l.errorf("Expected ] instead of %#U", cur) + } + l.emit(pathBracketRight) + return lexPathAfterKey +} + +func lexPathAfterValue(l lexer, state *intStack) stateFn { + cur := l.take() + if cur != eof { + return l.errorf("Expected EOF instead of %#U", cur) + } + l.emit(pathEOF) + return nil +} diff --git a/path_states_test.go b/path_states_test.go new file mode 100644 index 0000000..42d5b41 --- /dev/null +++ b/path_states_test.go @@ -0,0 +1,30 @@ +package jsonpath + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var pathTests = []lexTest{ + {"simple root node", `$.akey`, []int{pathRoot, pathPeriod, pathKey, pathEOF}}, + {"simple current node", `@.akey`, []int{pathCurrent, pathPeriod, pathKey, pathEOF}}, + {"simple root node w/ value", `$.akey+`, []int{pathRoot, pathPeriod, pathKey, pathValue, pathEOF}}, + {"nested object", `$.akey.akey2`, []int{pathRoot, pathPeriod, pathKey, pathPeriod, pathKey, pathEOF}}, + {"nested objects", `$.akey.akey2.akey3`, []int{pathRoot, pathPeriod, pathKey, pathPeriod, pathKey, pathPeriod, pathKey, pathEOF}}, + {"quoted keys", `$.akey["akey2"].akey3`, []int{pathRoot, pathPeriod, pathKey, pathBracketLeft, pathKey, pathBracketRight, pathPeriod, pathKey, pathEOF}}, + {"wildcard key", `$.akey.*.akey3`, []int{pathRoot, pathPeriod, pathKey, pathPeriod, pathWildcard, pathPeriod, pathKey, pathEOF}}, + {"wildcard index", `$.akey[*]`, []int{pathRoot, pathPeriod, pathKey, pathBracketLeft, pathWildcard, pathBracketRight, pathEOF}}, + {"key with where expression", `$.akey?(@.ten = 5)`, []int{pathRoot, pathPeriod, pathKey, pathWhere, pathExpression, pathEOF}}, + {"bracket notation", `$["aKey"][*][32][23:42]`, []int{pathRoot, pathBracketLeft, pathKey, pathBracketRight, pathBracketLeft, pathWildcard, pathBracketRight, pathBracketLeft, pathIndex, pathBracketRight, pathBracketLeft, pathIndex, pathIndexRange, pathIndex, pathBracketRight, pathEOF}}, +} + +func TestValidPaths(t *testing.T) { + as := assert.New(t) + for _, test := range pathTests { + lexer := NewSliceLexer([]byte(test.input), PATH) + types := itemsToTypes(readerToArray(lexer)) + + as.EqualValues(types, test.tokenTypes, "Testing of %s: \nactual\n\t%+v\nexpected\n\t%v", test.name, typesDescription(types, pathTokenNames), typesDescription(test.tokenTypes, pathTokenNames)) + } +} diff --git a/path_test.go b/path_test.go new file mode 100644 index 0000000..ab7f2a2 --- /dev/null +++ b/path_test.go @@ -0,0 +1,40 @@ +package jsonpath + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type optest struct { + name string + path string + expected []int +} + +var optests = []optest{ + optest{"single key (period) ", `$.aKey`, []int{opTypeName}}, + optest{"single key (bracket)", `$["aKey"]`, []int{opTypeName}}, + optest{"single key (period) ", `$.*`, []int{opTypeNameWild}}, + optest{"single index", `$[12]`, []int{opTypeIndex}}, + optest{"single key", `$[23:45]`, []int{opTypeIndexRange}}, + optest{"single key", `$[*]`, []int{opTypeIndexWild}}, + + optest{"double key", `$["aKey"]["bKey"]`, []int{opTypeName, opTypeName}}, + optest{"double key", `$["aKey"].bKey`, []int{opTypeName, opTypeName}}, +} + +func TestQueryOperators(t *testing.T) { + as := assert.New(t) + + for _, t := range optests { + path, err := parsePath(t.path) + as.NoError(err) + + as.EqualValues(len(t.expected), len(path.operators)) + + for x, op := range t.expected { + as.EqualValues(pathTokenNames[op], pathTokenNames[path.operators[x].typ]) + } + } +} diff --git a/queue.go b/queue.go new file mode 100644 index 0000000..67566b7 --- /dev/null +++ b/queue.go @@ -0,0 +1,55 @@ +package jsonpath + +type Results struct { + nodes []*Result + head int + tail int + count int +} + +func newResults() *Results { + return &Results{ + nodes: make([]*Result, 3, 3), + } +} + +func (q *Results) push(n *Result) { + if q.head == q.tail && q.count > 0 { + nodes := make([]*Result, len(q.nodes)*2, len(q.nodes)*2) + copy(nodes, q.nodes[q.head:]) + copy(nodes[len(q.nodes)-q.head:], q.nodes[:q.head]) + q.head = 0 + q.tail = len(q.nodes) + q.nodes = nodes + } + q.nodes[q.tail] = n + q.tail = (q.tail + 1) % len(q.nodes) + q.count++ +} + +func (q *Results) Pop() *Result { + if q.count == 0 { + return nil + } + node := q.nodes[q.head] + q.head = (q.head + 1) % len(q.nodes) + q.count-- + return node +} + +func (q *Results) peek() *Result { + if q.count == 0 { + return nil + } + return q.nodes[q.head] +} + +func (q *Results) len() int { + return q.count +} + +func (q *Results) clear() { + q.head = 0 + q.count = 0 + q.tail = 0 +} diff --git a/result.go b/result.go new file mode 100644 index 0000000..8078ea9 --- /dev/null +++ b/result.go @@ -0,0 +1,57 @@ +package jsonpath + +import ( + "bytes" + "fmt" +) + +const ( + JsonObject = iota + JsonArray + JsonString + JsonNumber + JsonNull + JsonBool +) + +type Result struct { + Keys []interface{} + Value []byte + Type int +} + +func (r *Result) Pretty(showPath bool) string { + b := bytes.NewBufferString("") + printed := false + if showPath { + for _, k := range r.Keys { + switch v := k.(type) { + case int: + b.WriteString(fmt.Sprintf("%d", v)) + default: + b.WriteString(fmt.Sprintf("%q", v)) + } + b.WriteRune('\t') + printed = true + } + } else if r.Value == nil { + if len(r.Keys) > 0 { + printed = true + switch v := r.Keys[len(r.Keys)-1].(type) { + case int: + b.WriteString(fmt.Sprintf("%d", v)) + default: + b.WriteString(fmt.Sprintf("%q", v)) + } + } + } + + if r.Value != nil { + printed = true + b.WriteString(fmt.Sprintf("%s", r.Value)) + } + if printed { + b.WriteRune('\n') + } + return b.String() +} diff --git a/run.go b/run.go new file mode 100644 index 0000000..e484fe5 --- /dev/null +++ b/run.go @@ -0,0 +1,27 @@ +package jsonpath + +import "io" + +func EvalPathsInBytes(input []byte, paths []*Path) (*Eval, error) { + lexer := NewSliceLexer(input, JSON) + eval := newEvaluation(lexer, paths...) + return eval, nil +} + +func EvalPathsInReader(r io.Reader, paths []*Path) (*Eval, error) { + lexer := NewReaderLexer(r, JSON) + eval := newEvaluation(lexer, paths...) + return eval, nil +} + +func ParsePaths(pathStrings ...string) ([]*Path, error) { + paths := make([]*Path, len(pathStrings)) + for x, p := range pathStrings { + path, err := parsePath(p) + if err != nil { + return nil, err + } + paths[x] = path + } + return paths, nil +} diff --git a/stack.go b/stack.go new file mode 100644 index 0000000..58818a4 --- /dev/null +++ b/stack.go @@ -0,0 +1,148 @@ +package jsonpath + +// Integer Stack + +type intStack struct { + values []int +} + +func newIntStack() *intStack { + return &intStack{ + values: make([]int, 0, 100), + } +} + +func (s *intStack) len() int { + return len(s.values) +} + +func (s *intStack) push(r int) { + s.values = append(s.values, r) +} + +func (s *intStack) pop() (int, bool) { + if s.len() == 0 { + return 0, false + } + v, _ := s.peek() + s.values = s.values[:len(s.values)-1] + return v, true +} + +func (s *intStack) peek() (int, bool) { + if s.len() == 0 { + return 0, false + } + v := s.values[len(s.values)-1] + return v, true +} + +func (s *intStack) clone() *intStack { + d := intStack{ + values: make([]int, s.len()), + } + copy(d.values, s.values) + return &d +} + +func (s *intStack) toArray() []int { + return s.values +} + +// Result Stack + +type resultStack struct { + values []Result +} + +func newResultStack() *resultStack { + return &resultStack{ + values: make([]Result, 0), + } +} + +func (s *resultStack) len() int { + return len(s.values) +} + +func (s *resultStack) push(r Result) { + s.values = append(s.values, r) +} + +func (s *resultStack) pop() (Result, bool) { + if s.len() == 0 { + return Result{}, false + } + v, _ := s.peek() + s.values = s.values[:len(s.values)-1] + return v, true +} + +func (s *resultStack) peek() (Result, bool) { + if s.len() == 0 { + return Result{}, false + } + v := s.values[len(s.values)-1] + return v, true +} + +func (s *resultStack) clone() *resultStack { + d := resultStack{ + values: make([]Result, s.len()), + } + copy(d.values, s.values) + return &d +} + +func (s *resultStack) toArray() []Result { + return s.values +} + +// Interface Stack + +type stack struct { + values []interface{} +} + +func newStack() *stack { + return &stack{ + values: make([]interface{}, 0, 100), + } +} + +func (s *stack) len() int { + return len(s.values) +} + +func (s *stack) push(r interface{}) { + s.values = append(s.values, r) +} + +func (s *stack) pop() (interface{}, bool) { + if s.len() == 0 { + return nil, false + } + v, _ := s.peek() + s.values = s.values[:len(s.values)-1] + return v, true +} + +func (s *stack) peek() (interface{}, bool) { + if s.len() == 0 { + return nil, false + } + v := s.values[len(s.values)-1] + return v, true +} + +func (s *stack) clone() *stack { + d := stack{ + values: make([]interface{}, s.len()), + } + copy(d.values, s.values) + return &d +} + +func (s *stack) toArray() []interface{} { + return s.values +} diff --git a/stack_test.go b/stack_test.go new file mode 100644 index 0000000..d1b1d2f --- /dev/null +++ b/stack_test.go @@ -0,0 +1,56 @@ +package jsonpath + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStackPush(t *testing.T) { + as := assert.New(t) + s := newIntStack() + + s.push(5) + as.EqualValues(s.len(), 1) + + s.push(12) + as.EqualValues(s.len(), 2) +} + +func TestStackPop(t *testing.T) { + as := assert.New(t) + s := newIntStack() + + s.push(99) + as.EqualValues(s.len(), 1) + + v, ok := s.pop() + as.True(ok) + as.EqualValues(99, v) + + as.EqualValues(s.len(), 0) +} + +func TestStackPeek(t *testing.T) { + as := assert.New(t) + s := newIntStack() + + s.push(99) + v, ok := s.peek() + as.True(ok) + as.EqualValues(99, v) + + s.push(54) + v, ok = s.peek() + as.True(ok) + as.EqualValues(54, v) + + s.pop() + v, ok = s.peek() + as.True(ok) + as.EqualValues(99, v) + + s.pop() + _, ok = s.peek() + as.False(ok) +}