diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2016-02-25 20:16:58 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2016-02-25 20:16:58 +0100 |
commit | cce64c335722bbf6bda39a9d8ca74154e0a3ad4c (patch) | |
tree | fafff633f9dc20883f26065c0ddf6528c38e80b3 /lib | |
parent | 7177e0058b96a3f737d954ea3b146f4d21469fdf (diff) | |
parent | 5d50cd6c269ff33600ce033797a67fe254159c1b (diff) | |
download | Nim-cce64c335722bbf6bda39a9d8ca74154e0a3ad4c.tar.gz |
Merge pull request #3810 from def-/json-speedup
Use tables instead of seqs for json objects
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pure/json.nim | 79 |
1 files changed, 38 insertions, 41 deletions
diff --git a/lib/pure/json.nim b/lib/pure/json.nim index f672a0c1b..3565e2feb 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -51,7 +51,7 @@ ## ] import - hashes, strutils, lexbase, streams, unicode, macros + hashes, tables, strutils, lexbase, streams, unicode, macros type JsonEventKind* = enum ## enumeration of all events that may occur when parsing @@ -567,7 +567,7 @@ type of JNull: nil of JObject: - fields*: seq[tuple[key: string, val: JsonNode]] + fields*: Table[string, JsonNode] of JArray: elems*: seq[JsonNode] @@ -617,7 +617,7 @@ proc newJObject*(): JsonNode = ## Creates a new `JObject JsonNode` new(result) result.kind = JObject - result.fields = @[] + result.fields = initTable[string, JsonNode](4) proc newJArray*(): JsonNode = ## Creates a new `JArray JsonNode` @@ -657,8 +657,8 @@ proc getBVal*(n: JsonNode, default: bool = false): bool = else: return n.bval proc getFields*(n: JsonNode, - default: seq[tuple[key: string, val: JsonNode]] = @[]): - seq[tuple[key: string, val: JsonNode]] = + default = initTable[string, JsonNode](4)): + Table[string, JsonNode] = ## Retrieves the key, value pairs of a `JObject JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil. @@ -700,8 +700,8 @@ proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode = ## Generic constructor for JSON data. Creates a new `JObject JsonNode` new(result) result.kind = JObject - newSeq(result.fields, keyVals.len) - for i, p in pairs(keyVals): result.fields[i] = p + result.fields = initTable[string, JsonNode](4) + for key, val in items(keyVals): result.fields[key] = val proc `%`*(elements: openArray[JsonNode]): JsonNode = ## Generic constructor for JSON data. Creates a new `JArray JsonNode` @@ -761,7 +761,9 @@ proc `==`* (a,b: JsonNode): bool = of JObject: a.fields == b.fields -proc hash* (n:JsonNode): Hash = +proc hash*(n: Table[string, JsonNode]): Hash {.noSideEffect.} + +proc hash*(n: JsonNode): Hash = ## Compute the hash for a JSON node case n.kind of JArray: @@ -779,6 +781,11 @@ proc hash* (n:JsonNode): Hash = of JNull: result = hash(0) +proc hash*(n: Table[string, JsonNode]): Hash = + for key, val in n: + result = result !& hash(key) !& hash(val) + result = !$result + proc len*(n: JsonNode): int = ## If `n` is a `JArray`, it returns the number of elements. ## If `n` is a `JObject`, it returns the number of pairs. @@ -793,10 +800,7 @@ proc `[]`*(node: JsonNode, name: string): JsonNode {.inline.} = ## If the value at `name` does not exist, returns nil assert(not isNil(node)) assert(node.kind == JObject) - for key, item in items(node.fields): - if key == name: - return item - return nil + result = node.fields.getOrDefault(name) proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} = ## Gets the node at `index` in an Array. Result is undefined if `index` @@ -808,8 +812,7 @@ proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} = proc hasKey*(node: JsonNode, key: string): bool = ## Checks if `key` exists in `node`. assert(node.kind == JObject) - for k, item in items(node.fields): - if k == key: return true + result = node.fields.hasKey(key) proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key) ## Deprecated for `hasKey` @@ -820,20 +823,14 @@ proc add*(father, child: JsonNode) = father.elems.add(child) proc add*(obj: JsonNode, key: string, val: JsonNode) = - ## Adds ``(key, val)`` pair to the JObject node `obj`. For speed - ## reasons no check for duplicate keys is performed! - ## But ``[]=`` performs the check. + ## Sets a field from a `JObject`. assert obj.kind == JObject - obj.fields.add((key, val)) + obj.fields[key] = val proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = - ## Sets a field from a `JObject`. Performs a check for duplicate keys. + ## Sets a field from a `JObject`. assert(obj.kind == JObject) - for i in 0..obj.fields.len-1: - if obj.fields[i].key == key: - obj.fields[i].val = val - return - obj.fields.add((key, val)) + obj.fields[key] = val proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = ## Traverses the node and gets the given value. If any of the @@ -856,13 +853,11 @@ proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) = node[keys[keys.len-1]] = value proc delete*(obj: JsonNode, key: string) = - ## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs. + ## Deletes ``obj[key]``. assert(obj.kind == JObject) - for i in 0..obj.fields.len-1: - if obj.fields[i].key == key: - obj.fields.delete(i) - return - raise newException(IndexError, "key not in object") + if not obj.fields.hasKey(key): + raise newException(IndexError, "key not in object") + obj.fields.del(key) proc copy*(p: JsonNode): JsonNode = ## Performs a deep copy of `a`. @@ -879,8 +874,8 @@ proc copy*(p: JsonNode): JsonNode = result = newJNull() of JObject: result = newJObject() - for key, field in items(p.fields): - result.fields.add((key, copy(field))) + for key, val in pairs(p.fields): + result.fields[key] = copy(val) of JArray: result = newJArray() for i in items(p.elems): @@ -924,15 +919,17 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, if node.fields.len > 0: result.add("{") result.nl(ml) # New line - for i in 0..len(node.fields)-1: + var i = 0 + for key, val in pairs(node.fields): if i > 0: result.add(", ") result.nl(ml) # New Line + inc i # Need to indent more than { result.indent(newIndent(currIndent, indent, ml)) - result.add(escapeJson(node.fields[i].key)) + result.add(escapeJson(key)) result.add(": ") - toPretty(result, node.fields[i].val, indent, ml, false, + toPretty(result, val, indent, ml, false, newIndent(currIndent, indent, ml)) result.nl(ml) result.indent(currIndent) # indent the same as { @@ -994,7 +991,7 @@ proc toUgly*(result: var string, node: JsonNode) = result.add "]" of JObject: result.add "{" - for key, value in items(node.fields): + for key, value in pairs(node.fields): if comma: result.add "," else: comma = true result.add key.escapeJson() @@ -1033,15 +1030,15 @@ iterator mitems*(node: var JsonNode): var JsonNode = iterator pairs*(node: JsonNode): tuple[key: string, val: JsonNode] = ## Iterator for the child elements of `node`. `node` has to be a JObject. assert node.kind == JObject - for key, val in items(node.fields): + for key, val in pairs(node.fields): yield (key, val) -iterator mpairs*(node: var JsonNode): var tuple[key: string, val: JsonNode] = +iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] = ## Iterator for the child elements of `node`. `node` has to be a JObject. - ## Items can be modified + ## Values can be modified assert node.kind == JObject - for keyVal in mitems(node.fields): - yield keyVal + for key, val in mpairs(node.fields): + yield (key, val) proc eat(p: var JsonParser, tok: TokKind) = if p.tok == tok: discard getTok(p) |