Skip to content

Commit

Permalink
Refactor Tomlib::Dumper to be a littel faster and generate squashed n…
Browse files Browse the repository at this point in the history
…ested tables
  • Loading branch information
kgiszczak committed Aug 4, 2022
1 parent 409538d commit 94cd761
Show file tree
Hide file tree
Showing 52 changed files with 250 additions and 696 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
## [0.2.0] - [unreleased]

- Refactor Tomlib::Dumper to be a littel faster and generate squashed nested tables
```
e.g. instead of this:
[a]
[a.b]
[a.b.c]
the output will be this:
[a.b.c]
```
- Add mention about compliance and passed tests in README.
- Declare global variables with `rb_global_variable`.
It may prevent VM from crashing in some cases.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ Tomlib.dump(hash, indent: false)

## Performance

`Tomlib` parsing is ~300x faster than `toml-rb` and ~15x faster than `Tomlrb`
for usual use case (~5KB TOML document size).
`Tomlib` parsing is ~300x faster than `toml-rb`, ~15x faster than `Tomlrb`
and ~3x faster than `perfect_toml` for usual use case (~5KB TOML document size).

Generating TOML document is about 1.7x faster than `toml-rb`.
Generating TOML document is about 2x faster than `toml-rb`.

For full comparison take a look at
[benchmarks](https://github.com/kgiszczak/tomlib/tree/master/benchmarks)
Expand Down
7 changes: 4 additions & 3 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ and run benchmarks:

### Generating (i/s)

Note - Tomlrb gem doesn't support generating TOML documents.
Note - Tomlrb gem doesn't support generating TOML documents
and perfect_toml doesn't work under Ruby 3.

|gem|small.toml|default.toml|big.toml|
|---|----------|------------|--------|
|Tomlib|137.698k|4.741k|22.785|
|toml-rb|64.312k (2.14x slower)|2.672k (1.77x slower)|0.129 (175.98x slower)
|Tomlib|133.456k|5.615k|23.105|
|toml-rb|65.968k (2.02x slower)|2.695k (2.08x slower)|0.146 (158.59x slower)
2 changes: 1 addition & 1 deletion lib/tomlib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ module Tomlib
#
# @api public
def self.dump(hash, indent: true)
Dumper.new.dump(hash, use_indent: indent)
Dumper.new(indent).dump(hash)
end
end
47 changes: 34 additions & 13 deletions lib/tomlib/dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,73 @@ class Dumper
# @api private
NAN = 'nan'.freeze

def initialize(use_indent = true)
@use_indent = use_indent
end

# Generate TOML string from ruby Hash
#
# @param [Hash] hash
#
# @return [String]
#
# @api private
def dump(hash)
result = dump_hash(hash)

result[0] = '' if result[0] == "\n"

result
end

private

# Generate TOML string from ruby Hash
#
# @param [Hash] hash
# @param [String, nil] base_key
# @param [String] indent
# @param [true, false] use_indent
# @param [Integer] indent_level
#
# @return [String]
#
# @api private
def dump(hash, base_key = nil, indent = '', use_indent: true)
def dump_hash(hash, base_key = nil, indent_level = 0)
header = ''
footer = ''

hash.each do |key, value|
toml_key = to_toml_key(key)

if value.is_a?(Hash)
skip = !value.empty? && value.values.all? { |e| e.is_a?(Hash) }
compound_key = to_toml_compound_key(base_key, toml_key)
next_indent = use_indent && base_key ? indent + INDENT : indent

footer << "\n".freeze << next_indent << '['.freeze << compound_key << "]\n".freeze
footer << dump(value, compound_key, next_indent, use_indent: use_indent)
unless skip
indent = @use_indent ? INDENT * indent_level : ''.freeze
footer << "\n".freeze << indent << '['.freeze << compound_key << "]\n".freeze
end

footer << dump_hash(value, compound_key, skip ? indent_level : indent_level + 1)
elsif value.is_a?(Array) && value.all? { |e| e.is_a?(Hash) }
compound_key = to_toml_compound_key(base_key, toml_key)
next_indent = use_indent && base_key ? indent + INDENT : indent
indent = @use_indent ? INDENT * indent_level : ''.freeze

value.each do |el|
footer << "\n".freeze << next_indent << '[['.freeze << compound_key << "]]\n".freeze
footer << dump(el, compound_key, next_indent, use_indent: use_indent)
footer << "\n".freeze << indent << '[['.freeze << compound_key << "]]\n".freeze
footer << dump_hash(el, compound_key, indent_level + 1)
end
else
indent = @use_indent ? INDENT * (indent_level > 0 ? indent_level - 1 : 0) : ''.freeze
header << indent << toml_key << ' = '.freeze << to_toml_value(value) << "\n".freeze
end
end

footer.gsub!(/\A\n/, ''.freeze) if header.empty?
header << footer
end

private

# Generate TOML key from Hash key
#
# @param [String] key
# @param [true, false] use_indent
#
# @return [String]
#
Expand Down
1 change: 1 addition & 0 deletions spec/examples/dumper/array/nested-inline-table.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[[a]]

[a.b]
1 change: 0 additions & 1 deletion spec/examples/dumper/implicit-and-explicit-after.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[a]
better = 43

[a.b]
[a.b.c]
answer = 42
1 change: 0 additions & 1 deletion spec/examples/dumper/implicit-and-explicit-before.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[a]
better = 43

[a.b]
[a.b.c]
answer = 42
2 changes: 0 additions & 2 deletions spec/examples/dumper/implicit-groups.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
[a]
[a.b]
[a.b.c]
answer = 42
1 change: 0 additions & 1 deletion spec/examples/dumper/inline-table/empty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ not_empty = 1

[[many_empty]]

[nested_empty]
[nested_empty.empty]
27 changes: 2 additions & 25 deletions spec/examples/dumper/inline-table/key-dotted.toml
Original file line number Diff line number Diff line change
@@ -1,66 +1,43 @@
[a]
[a.a]
b = 1

[[arr]]
[arr.T]

[arr.T.a]
b = 1

[arr.t]
[arr.t.a]
b = 1

[[arr]]
[arr.T]

[arr.T.a]
b = 2

[arr.t]
[arr.t.a]
b = 2

[b]
[b.a]
b = 1

[c]
[c.a]
b = 1

[d]
[d.a]
b = 1

[e]
[e.a]
b = 1

[inline]
[inline.a]
b = 42

[many]
[many.dots]
[many.dots.here]
[many.dots.here.dot]
[many.dots.here.dot.dot]
[many.dots.here.dot.dot.dot]
[many.dots.here.dot.dot.dot.a]
[many.dots.here.dot.dot.dot.a.b]
c = 1
d = 2

[tbl]
[tbl.a]
[tbl.a.b]
[tbl.a.b.c]
[tbl.a.b.c.d]
e = 1

[tbl.x]
[tbl.x.a]
[tbl.x.a.b]
[tbl.x.a.b.c]
[tbl.x.a.b.c.d]
e = 1
4 changes: 2 additions & 2 deletions spec/examples/dumper/inline-table/nest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ arr_arr_tbl_val = [ [ { one = 1 } ] ]
arr_arr_tbls = [ [ { one = 1 }, { two = 2 } ] ]

[[arr_tbl_tbl]]

[arr_tbl_tbl.tbl]
one = 1

[tbl_arr_tbl]

[[tbl_arr_tbl.arr_tbl]]
one = 1

[tbl_tbl_empty]
[tbl_tbl_empty.tbl_0]

[tbl_tbl_val]
[tbl_tbl_val.tbl_1]
one = 1
13 changes: 2 additions & 11 deletions spec/examples/dumper/key/dotted.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
[a]
[a.few]
[a.few.dots]
[a.few.dots.polka]
dance-with = "Dot"
dot = "again?"

[[arr]]
[arr.a]

[arr.a.b]
c = 1
d = 2

[[arr]]
[arr.a]

[arr.a.b]
c = 3
d = 4
Expand All @@ -31,18 +28,12 @@ j = 10
k = 11
l = 12

[many]
[many.dots]
[many.dots.here]
[many.dots.here.dot]
[many.dots.here.dot.dot]
dot = 42

[name]
first = "Arthur"
last = "Dent"

[tbl]
[tbl.a]
[tbl.a.b]
c = 42.666
1 change: 0 additions & 1 deletion spec/examples/dumper/key/escapes.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
["\"quoted\""]
quote = true

["a.b"]
["a.b"."À"]

["backsp\b\b"]
1 change: 0 additions & 1 deletion spec/examples/dumper/key/quoted-dots.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ plain = 1
plain = 3
"with.dot" = 4

[table]
[table.withdot]
"key.with.dots" = 6
plain = 5
19 changes: 19 additions & 0 deletions spec/examples/dumper/new-lines/all.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
foo = "foo"
bar = "bar"

[a]

[b]
foo = "foo"

[c]

[[d]]

[[d]]

[[e]]

[[f]]
foo = "foo"
bar = "bar"
14 changes: 14 additions & 0 deletions spec/examples/dumper/new-lines/all.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
foo: 'foo'
bar: 'bar'
a: {}
b:
foo: 'foo'
c: {}
d:
- {}
- {}
e:
- {}
f:
- foo: 'foo'
bar: 'bar'
9 changes: 9 additions & 0 deletions spec/examples/dumper/new-lines/arrays-only.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[[a]]

[[a]]

[[b]]

[[c]]
foo = "foo"
bar = "bar"
10 changes: 10 additions & 0 deletions spec/examples/dumper/new-lines/arrays-only.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
a:
- {}
- {}

b:
- {}

c:
- foo: 'foo'
bar: 'bar'
Empty file.
1 change: 1 addition & 0 deletions spec/examples/dumper/new-lines/empty.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
12 changes: 12 additions & 0 deletions spec/examples/dumper/new-lines/keys-and-arrays.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
foo = "foo"
bar = "bar"

[[a]]

[[a]]

[[b]]

[[c]]
foo = "foo"
bar = "bar"
10 changes: 10 additions & 0 deletions spec/examples/dumper/new-lines/keys-and-arrays.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
foo: 'foo'
bar: 'bar'
a:
- {}
- {}
b:
- {}
c:
- foo: 'foo'
bar: 'bar'
Loading

0 comments on commit 94cd761

Please sign in to comment.