From 6bed953887bf08ae6999dce77a2c344324dfad0f Mon Sep 17 00:00:00 2001 From: kputnam Date: Sat, 15 Jun 2019 01:40:59 -0500 Subject: [PATCH 01/38] =?UTF-8?q?WIP:=20Reduce=20a=20benchmark=20from=204.?= =?UTF-8?q?6GB=20to=20650MB=20=F0=9F=98=85=20by=20eliminating=20String#[]?= =?UTF-8?q?=20calls=20(GH-65)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ruby/string.rb | 106 ++- lib/stupidedi/reader/input.rb | 5 +- lib/stupidedi/reader/token_reader.rb | 9 +- prof/memprof-pp-20190614T170606.txt | 1164 ++++++++++++++++++++++++++ prof/memprof-pp-20190615T012552.txt | 1159 +++++++++++++++++++++++++ prof/memprof-pp.rb | 40 + prof/mkfile.rb | 63 ++ 7 files changed, 2538 insertions(+), 8 deletions(-) create mode 100644 prof/memprof-pp-20190614T170606.txt create mode 100644 prof/memprof-pp-20190615T012552.txt create mode 100755 prof/memprof-pp.rb create mode 100755 prof/mkfile.rb diff --git a/lib/ruby/string.rb b/lib/ruby/string.rb index d585adfd3..b37b083d8 100644 --- a/lib/ruby/string.rb +++ b/lib/ruby/string.rb @@ -13,7 +13,7 @@ module Refinements # @return [String] def at(n) raise ArgumentError, "n must be positive" if n < 0 - self[n, 1] unless n >= length + self[n] unless n >= length # FIXME: Big source of memory allocation end # Return the string with `n` characters removed from the front @@ -27,7 +27,8 @@ def at(n) # @return [String] def drop(n) raise ArgumentError, "n must be positive" if n < 0 - (length >= n) ? self[n..-1] : "" + (length >= n) ? self[n..-1] : "" # FIXME: Big source of memory allocation + # FIXME: Big source of object allocation end # Return the first `n` characters from the front @@ -64,7 +65,7 @@ def split_at(n) # "abc".defined_at?(0) #=> true # "abc".defined_at?(3) #=> false def defined_at?(n) - n < length + n < length # FIXME, should be false or raise error n < 0 end # To make String compatible with the {Stupidedi::Reader::Input} interface, @@ -87,3 +88,102 @@ def join end end end + +class Substring + attr_reader :whole + attr_reader :m + attr_reader :n + + def initialize(whole, m = 0, n = whole.length - 1) + if n < m - 1 + n = m - 1 + end + + if m < 0 + raise ArgumentError, "start index must be non-negative" + end + + if n >= whole.length + raise ArgumentError, "end index must not exceed underlying String length" + end + + @whole, @m, @n = whole, m, n + + # w = @whole[@m, [@n - @m + 1, 60].min] + # p [w, " "*(60 - w.length), @m, @n] + end + + # @note: Avoid calling this unless needed because it allocates another String + def repro + @whole[@m..@n] + end + + def to_str + repro + end + + def to_s + repro + end + + def length + @n - @m + 1 + end + + def empty? + @m > @n + end + + def inspect + repro.inspect + end + + def ==(other) + eql?(other) or repro == other + end + + def =~(other) + repro =~ other + end + + def at(n) + raise ArgumentError, "n must be positive" if n < 0 + @whole[@m + n] unless n > @n - @m + end + + def defined_at?(n) + n <= @n - @m # FIXME should be false or exception when n < 0 + end + + def take(n) + raise ArgumentError, "n must be positive" if n < 0 + Substring.new(@whole, @m, [@m + n - 1, @whole.length - 1].min) + end + + def drop(n) + raise ArgumentError, "n must be positive" if n < 0 + Substring.new(@whole, [@m + n, @n + 1].min, @n) + end + + def count(other) + k, m = 0, @m - 1 + + while true + m = @whole.index(other, m + 1) + m and m <= @n or break + k += 1 + end + + k + end + + def index(other, m = 0) + z = @whole.index(other, @m + m) + z - @m if z and z <= @n + end + + def rindex(other, n = @whole.length - 1) + z = @whole.rindex(other, [@m + n, @n].min) + z - @m if z and z >= @m + end +end diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb index 24b5305af..bec320589 100644 --- a/lib/stupidedi/reader/input.rb +++ b/lib/stupidedi/reader/input.rb @@ -17,7 +17,10 @@ def build(o, *args) o when IO FileInput.new(o, *args) - when String, Array + when String + # DelegatedInput.new(Substring.new(o), *args) + DelegatedInput.new(o, *args) + when Array DelegatedInput.new(o, *args) else raise TypeError diff --git a/lib/stupidedi/reader/token_reader.rb b/lib/stupidedi/reader/token_reader.rb index c12c71412..b77f99ae9 100644 --- a/lib/stupidedi/reader/token_reader.rb +++ b/lib/stupidedi/reader/token_reader.rb @@ -234,7 +234,8 @@ def read_component_elements(repeatable = false) # @return [Either>] def read_segment_id position = 0 - buffer = "" + buffer = String.new("") # Intentionally using a mutable here, to + # avoid allocations when building buffer below while true unless @input.defined_at?(position) @@ -253,7 +254,7 @@ def read_segment_id break end - buffer = buffer + character + buffer << character end end @@ -302,8 +303,8 @@ def read_simple_element(repeatable = false) position = 0 buffer = "" - while @input.defined_at?(position) - character = @input.at(position) + while @input.defined_at?(position) # FIXME: Big source of memory & object allocation + character = @input.at(position) # FIXME: Big source of memory & object allocation position += 1 if is_control?(character) diff --git a/prof/memprof-pp-20190614T170606.txt b/prof/memprof-pp-20190614T170606.txt new file mode 100644 index 000000000..dc26aedeb --- /dev/null +++ b/prof/memprof-pp-20190614T170606.txt @@ -0,0 +1,1164 @@ +Total allocated: 4.62 GB (9667215 objects) +Total retained: 53.78 MB (727182 objects) + +allocated memory by gem +----------------------------------- + 4.62 GB stupidedi/lib + 4.38 MB rubygems + 2.17 MB cantor-1.2.1 + 720.0 B ruby-2.6.0 + 272.0 B other + +allocated memory by file +----------------------------------- + 4.11 GB /Users/mr/wd/stupidedi/lib/ruby/string.rb + 132.46 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 96.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 46.79 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 32.97 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb + 25.73 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 24.55 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb + 14.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 13.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb + 9.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb + 7.22 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb + 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 6.15 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 6.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 5.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 4.36 MB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb + 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 2.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb + 2.17 MB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 896.15 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb + 841.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 806.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 805.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb + 804.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb + 429.69 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 402.48 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb + 401.97 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb + 401.71 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb + 286.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 238.34 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 207.46 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 201.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb + 133.75 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb + 115.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 74.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 50.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 36.61 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 26.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 19.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 15.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb + 13.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + +allocated memory by location +----------------------------------- + 4.08 GB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 72.35 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:63 + 26.11 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 + 23.77 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 + 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:103 + 20.91 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:103 + 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 18.64 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:97 + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 + 13.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 + 13.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:307 + 13.63 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:51 + 12.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 + 12.6 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 + 10.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 9.65 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 + 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 + 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:415 + 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:418 + 7.19 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 + 5.83 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 4.83 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 4.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:40 + 4.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 + 4.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:284 + 4.32 MB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 + 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 + 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 + 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 + 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 + 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 + 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 + 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 + 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:24 + 2.33 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:111 + +allocated memory by class +----------------------------------- + 4.14 GB String + 216.69 MB Array + 103.32 MB Hash + 27.34 MB Proc + 26.78 MB MatchData + 22.45 MB Stupidedi::Reader::DelegatedInput + 15.21 MB Stupidedi::Reader::Position + 12.86 MB Stupidedi::Reader::Success + 12.48 MB Range + 9.25 MB Stupidedi::Reader::TokenReader + 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 3.22 MB Stupidedi::Zipper::EditedCursor + 3.02 MB Stupidedi::Reader::SimpleElementTok + 2.42 MB Stupidedi::Zipper::Hole + 2.4 MB Stupidedi::Parser::LoopState + 1.45 MB Stupidedi::Reader::SegmentTok + 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 1.35 MB BigDecimal + 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 952.61 kB File + 811.2 kB Stupidedi::Either::Success + 806.56 kB Integer + 805.6 kB Stupidedi::Parser::TableState + 804.8 kB Stupidedi::Parser::StateMachine + 804.0 kB Stupidedi::Values::SegmentVal + 800.8 kB Stupidedi::Values::LoopVal + 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 200.0 kB Stupidedi::Reader::ComponentElementTok + 200.0 kB Stupidedi::Reader::CompositeElementTok + 200.0 kB Stupidedi::Values::CompositeElementVal + 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 144.39 kB Class + 120.52 kB Thread::Backtrace + 33.24 kB Module + 33.2 kB Stupidedi::Parser::Instruction + 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID + 20.16 kB Stupidedi::Parser::ConstraintTable::Stub + 19.44 kB Stupidedi::Schema::SimpleElementUse + 10.72 kB Stupidedi::Parser::InstructionTable::NonEmpty + 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN + 7.28 kB Regexp + 4.88 kB Stupidedi::Schema::CodeList::Internal + 4.8 kB Stupidedi::Parser::TransactionSetState + 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn + 4.03 kB Stupidedi::Versions::Common::ElementTypes::R + 3.84 kB Stupidedi::Parser::FunctionalGroupState + 3.2 kB Stupidedi::Parser::InterchangeState + 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + +allocated objects by gem +----------------------------------- + 9599945 stupidedi/lib + 59089 rubygems + 8171 cantor-1.2.1 + 8 ruby-2.6.0 + 2 other + +allocated objects by file +----------------------------------- + 2285651 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 1821615 /Users/mr/wd/stupidedi/lib/ruby/string.rb + 1146755 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 643062 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 516605 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 442323 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 340987 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 316938 /Users/mr/wd/stupidedi/lib/ruby/object.rb + 295534 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb + 221416 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 211231 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb + 170612 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 151009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 130734 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb + 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb + 115234 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 80589 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 71321 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 70044 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 61008 /Users/mr/wd/stupidedi/lib/ruby/array.rb + 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb + 58842 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb + 50103 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 45240 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 40251 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 30038 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 20312 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 20184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 20120 /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb + 20109 /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb + 20067 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 10017 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb + 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb + 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb + 8151 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 5359 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 5154 /Users/mr/wd/stupidedi/lib/stupidedi.rb + 5041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 5007 /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb + 1876 /Users/mr/wd/stupidedi/lib/ruby/module.rb + 1768 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 1352 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 813 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 448 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 347 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb + 265 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 205 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 175 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 164 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 149 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + +allocated objects by location +----------------------------------- + 915478 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 594240 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 347980 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 + 347980 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:307 + 340730 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:51 + 321540 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 + 311841 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 + 311840 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:103 + 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:63 + 295520 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 + 272490 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 270520 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 261330 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:103 + 241170 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 + 231360 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 + 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:415 + 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:418 + 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 + 160820 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 145736 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 145736 /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 + 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 110450 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 + 110450 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:284 + 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 80518 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 + 80440 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 + 80370 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 + 80370 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:97 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 + 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 + 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 + 60426 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 60409 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 + 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 60260 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 58433 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 45302 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:59 + 40240 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:73 + 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 40220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 30160 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + +allocated objects by class +----------------------------------- + 4146334 Array + 2547369 String + 441908 Hash + 341745 Proc + 321540 Stupidedi::Reader::Success + 312014 Range + 311841 Stupidedi::Reader::DelegatedInput + 231360 Stupidedi::Reader::TokenReader + 211220 Stupidedi::Reader::Position + 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 95642 MatchData + 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 80518 Stupidedi::Zipper::EditedCursor + 75500 Stupidedi::Reader::SimpleElementTok + 60380 Stupidedi::Zipper::Hole + 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 30010 Stupidedi::Parser::LoopState + 20280 Stupidedi::Either::Success + 20121 Integer + 20120 Stupidedi::Parser::StateMachine + 20120 Stupidedi::Reader::SegmentTok + 20100 Stupidedi::Values::SegmentVal + 20020 Stupidedi::Values::LoopVal + 15320 BigDecimal + 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 10070 Stupidedi::Parser::TableState + 5000 Stupidedi::Reader::ComponentElementTok + 5000 Stupidedi::Reader::CompositeElementTok + 5000 Stupidedi::Values::CompositeElementVal + 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 504 Stupidedi::Parser::ConstraintTable::Stub + 415 Stupidedi::Parser::Instruction + 243 Stupidedi::Schema::SimpleElementUse + 239 Stupidedi::Versions::Common::ElementTypes::ID + 192 Class + 134 Stupidedi::Parser::InstructionTable::NonEmpty + 122 Stupidedi::Schema::CodeList::Internal + 116 File + 116 Stupidedi::Versions::Common::ElementTypes::AN + 107 Thread::Backtrace + 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 72 Stupidedi::Parser::ConstraintTable::Shallowest + 60 Stupidedi::Parser::TransactionSetState + 60 Stupidedi::Values::TableVal + 54 Cantor::AbsoluteSet + 51 Stupidedi::Reader::StreamReader + 43 Stupidedi::Schema::ComponentElementUse + 42 Stupidedi::Versions::Common::ElementTypes::Nn + +retained memory by gem +----------------------------------- + 52.72 MB stupidedi/lib + 744.52 kB rubygems + 321.88 kB cantor-1.2.1 + 520.0 B ruby-2.6.0 + +retained memory by file +----------------------------------- + 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb + 7.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 4.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 4.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 2.58 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb + 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 810.86 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 803.53 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 742.46 kB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb + 428.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 407.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 401.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 317.47 kB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 220.5 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 203.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 114.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 38.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 27.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 14.57 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 11.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 10.49 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb + 10.45 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 8.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 7.81 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb + 7.61 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb + 6.71 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb + 6.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb + 5.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + 5.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb + 5.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb + 4.97 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb + 4.92 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 4.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb + 4.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 4.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 3.45 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 3.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/special_val.rb + 3.07 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 3.06 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb + 3.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/separator_val.rb + 2.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb + 2.81 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb + +retained memory by location +----------------------------------- + 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 + 7.6 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 2.58 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 + 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 + 1.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 + 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 804.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 + 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 + 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 + 741.74 kB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 600.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 + 403.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 + 402.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 + 311.78 kB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 200.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 + 200.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 39.67 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 + 36.56 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 23.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 + 20.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 + 20.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 + 19.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 10.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 + 9.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 9.53 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 + +retained memory by class +----------------------------------- + 19.87 MB Array + 7.6 MB Stupidedi::Reader::Position + 5.15 MB String + 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 2.0 MB Stupidedi::Parser::LoopState + 1.41 MB Stupidedi::Zipper::EditedCursor + 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 1.33 MB BigDecimal + 1.21 MB Stupidedi::Zipper::Hole + 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 827.1 kB Hash + 804.0 kB Stupidedi::Values::SegmentVal + 800.8 kB Stupidedi::Values::LoopVal + 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 404.8 kB Stupidedi::Parser::TableState + 200.0 kB Stupidedi::Values::CompositeElementVal + 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 144.39 kB Class + 33.24 kB Module + 33.2 kB Stupidedi::Parser::Instruction + 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID + 20.16 kB Stupidedi::Parser::ConstraintTable::Stub + 19.44 kB Stupidedi::Schema::SimpleElementUse + 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.72 kB Stupidedi::Parser::InstructionTable::NonEmpty + 6.04 kB Regexp + 4.88 kB Stupidedi::Schema::CodeList::Internal + 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn + 4.03 kB Stupidedi::Versions::Common::ElementTypes::R + 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 3.18 kB Stupidedi::Schema::SegmentDef + 3.1 kB Stupidedi::Schema::ComponentElementUse + 2.88 kB Stupidedi::Parser::ConstraintTable::Shallowest + 2.88 kB Stupidedi::Parser::FunctionalGroupState + 2.4 kB Stupidedi::Parser::InterchangeState + 2.4 kB Stupidedi::Parser::TransactionSetState + 2.4 kB Stupidedi::Values::TableVal + 2.4 kB Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty + 2.16 kB Cantor::AbsoluteSet + 1.6 kB Stupidedi::Parser::FailureState + 1.6 kB Stupidedi::Reader::SimpleElementTok + 1.44 kB Date + 1.44 kB Stupidedi::Reader::SegmentTok + 1.44 kB Stupidedi::Reader::Separators + 1.44 kB Stupidedi::Schema::SegmentUse + 1.25 kB Integer + 840.0 B Stupidedi::Zipper::DanglingCursor + 800.0 B Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty + +retained objects by gem +----------------------------------- + 712809 stupidedi/lib + 14238 rubygems + 132 cantor-1.2.1 + 3 ruby-2.6.0 + +retained objects by file +----------------------------------- + 170539 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 115227 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 105635 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 65404 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 50052 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 40307 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 35029 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 25128 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 20165 /Users/mr/wd/stupidedi/lib/ruby/object.rb + 20163 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 20041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 14203 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb + 10014 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 5190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 5089 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 5020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 935 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 718 /Users/mr/wd/stupidedi/lib/ruby/string.rb + 591 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 425 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 148 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 129 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 123 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 91 /Users/mr/wd/stupidedi/lib/ruby/array.rb + 84 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb + 83 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb + 75 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 68 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb + 67 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 63 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb + 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 52 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + 51 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb + 51 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 46 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 41 /Users/mr/wd/stupidedi/lib/ruby/module.rb + 41 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 39 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 38 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb + 34 /Users/mr/wd/stupidedi/lib/stupidedi/values/table_val.rb + 29 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb + 29 /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb + 27 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 25 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb + 25 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb + 24 /Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb + 22 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/interchange_def.rb + 22 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb + +retained objects by location +----------------------------------- + 105620 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 30215 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 + 20141 /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 + 20138 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 + 20020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 + 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 + 14189 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10071 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 + 5040 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 + 5020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 + 5019 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 + 645 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 576 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 + 504 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 + 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 + 160 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 + 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 + 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 + 74 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:45 + 72 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:477 + 69 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 + 67 /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 + 65 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 64 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 64 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:23 + 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 60 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb:98 + 54 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 54 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + +retained objects by class +----------------------------------- + 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 106669 Array + 105620 Stupidedi::Reader::Position + 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 60429 String + 35211 Stupidedi::Zipper::EditedCursor + 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 30192 Stupidedi::Zipper::Hole + 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 25010 Stupidedi::Parser::LoopState + 20100 Stupidedi::Values::SegmentVal + 20020 Stupidedi::Values::LoopVal + 15080 BigDecimal + 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 5060 Stupidedi::Parser::TableState + 5000 Stupidedi::Values::CompositeElementVal + 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 504 Stupidedi::Parser::ConstraintTable::Stub + 446 Hash + 415 Stupidedi::Parser::Instruction + 243 Stupidedi::Schema::SimpleElementUse + 239 Stupidedi::Versions::Common::ElementTypes::ID + 192 Class + 122 Stupidedi::Schema::CodeList::Internal + 116 Stupidedi::Versions::Common::ElementTypes::AN + 84 Stupidedi::Parser::InstructionTable::NonEmpty + 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 72 Stupidedi::Parser::ConstraintTable::Shallowest + 60 Stupidedi::Values::TableVal + 54 Cantor::AbsoluteSet + 43 Stupidedi::Schema::ComponentElementUse + 42 Stupidedi::Versions::Common::ElementTypes::Nn + 42 Stupidedi::Versions::Common::ElementTypes::R + 40 Stupidedi::Reader::SimpleElementTok + 36 Stupidedi::Schema::SegmentDef + 33 Module + 30 Stupidedi::Parser::FunctionalGroupState + 30 Stupidedi::Parser::InterchangeState + 30 Stupidedi::Parser::TransactionSetState + 30 Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty + 21 Stupidedi::Zipper::DanglingCursor + 20 Date + 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty + 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::NonEmpty + 20 Stupidedi::Parser::FailureState + 20 Stupidedi::Reader::SegmentDict::Constants + 20 Stupidedi::Reader::SegmentDict::NonEmpty + 20 Stupidedi::Reader::SegmentTok + 20 Stupidedi::Reader::Separators + + +Allocated String Report +----------------------------------- + 261681 "*" + 160990 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 100530 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 150 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:64 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 80351 "~" + 50230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 30120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 60150 "P" + 45070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:371 + 40 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 52928 "1" + 22780 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 15070 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5020 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5020 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 14 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:388 + + 48240 "0" + 38090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10140 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/requirement.rb:108 + + 45004 "CTP" + 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 + 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 45004 "PID" + 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 + 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 41910 "\n" + 20120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 20110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 1668 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb:1168 + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + + 35031 "08" + 15020 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb:1752 + + 35005 "LIN" + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 + 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 35002 "PO4" + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 + 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 30193 "2" + 25120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5030 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 10 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:388 + + 27527 "12" + 7500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 15 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 25149 "02" + 20090 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:300 + 19 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 25123 "01" + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 23 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 25101 "C" + 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 25066 "03" + 20050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 16 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 25050 "L" + 15040 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 25037 "F" + 5010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 17 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20149 "" + 20030 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 89 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:302 + + 20130 "N" + 20090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20081 "E" + 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20067 "04" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 17 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20063 "05" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 13 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20062 "06" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 12 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20018 "O" + 20010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20010 "EA" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20006 "K" + 20000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 6 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20005 "PK" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:371 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20005 "VN" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20004 "CON" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 17629 "4" + 17600 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 9 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 15173 " " + 15100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 30 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 13 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 15060 "07" + 15030 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:527 + + 15022 "09" + 15010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 12 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10126 "A" + 10100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 16 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 + + 10118 "3" + 10110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10110 "I" + 10080 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + + 10082 "10" + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:526 + 18 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 + 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 10066 "B" + 10050 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 + 6 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10053 "11" + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 + 19 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 10024 "8" + 10010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10023 "V" + 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10000 "000001" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "000002" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "1234321" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "14.80" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + + 10000 "20.80" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + + 10000 "2345432" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "LENS PAPER BOOK 12BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + +Retained String Report +----------------------------------- + 5001 "08" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "CON" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "EA" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "PK" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "VN" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 5000 "F" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 2500 "000001" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "000002" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "1234321" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "2345432" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "LENS PAPER BOOK 12BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "LENS PAPER BOOK 24BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 21 "0001" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 20 "00" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 20 "004010" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 20 "7777777777" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 20 "SC" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 12 "ST" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "009" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "14" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "91" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "PA" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "ZZ" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 10 "*" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 10 "00400" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "004321519" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "004321519IBMP" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "1000 PENNSYLVANIA AVE" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + + 10 "15222" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + + 10 "832" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "999999001" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 ">" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 10 "P" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "PC" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "PITTSBURGH" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 + + 10 "SPECIAL LABS" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "U" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "X" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "~" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 4 "To specify identifying information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 4 "To specify pertinent dates and times" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "To define the end of an interchange of zero or more functional groups and interchange-related control segments" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To describe a product or process in coded or free-form format" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To identify a party by type of organization, name, and code" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the beginning of the Price/Sales Catalog Transaction Set and specify catalog purpose and number information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the end of the transaction set and provide the count of the transmitted segments (including the beginning (ST) and ending (SE) segments)" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the start of a transaction set and assign a control number" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + diff --git a/prof/memprof-pp-20190615T012552.txt b/prof/memprof-pp-20190615T012552.txt new file mode 100644 index 000000000..2730ec041 --- /dev/null +++ b/prof/memprof-pp-20190615T012552.txt @@ -0,0 +1,1159 @@ +Total allocated: 650.38 MB (11396376 objects) +Total retained: 58.87 MB (907783 objects) + +allocated memory by gem +----------------------------------- + 643.36 MB stupidedi/lib + 5.17 MB rubygems + 1.85 MB cantor-1.2.1 + 760.0 B ruby-2.4.6 + 280.0 B other + +allocated memory by file +----------------------------------- + 177.14 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 119.99 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 73.46 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb + 40.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 32.97 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb + 25.73 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 24.55 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb + 16.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb + 13.64 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb + 10.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb + 7.22 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb + 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 6.15 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 6.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 5.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 5.15 MB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb + 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 3.69 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 2.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb + 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 1.84 MB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb + 903.08 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb + 837.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 806.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 805.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb + 804.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb + 427.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 401.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb + 401.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb + 263.06 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb + 237.27 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 230.34 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 210.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 206.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 201.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb + 74.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 50.08 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 36.29 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 36.29 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb + 30.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 17.08 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 13.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + +allocated memory by location +----------------------------------- + 59.87 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:59 + 40.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:52 + 37.08 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 31.94 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 27.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:305 + 27.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 + 23.77 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 + 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:102 + 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 18.51 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:414 + 15.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 + 12.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 + 12.6 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 + 10.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 9.65 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 + 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 + 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:417 + 8.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:282 + 8.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 + 7.19 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:192 + 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:193 + 5.11 MB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 4.83 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 4.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:209 + 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 3.87 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:41 + 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 + 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 + 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 + 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 + 2.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:116 + 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 + 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:24 + 2.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:108 + +allocated memory by class +----------------------------------- + 273.49 MB Array + 102.66 MB String + 86.56 MB Hash + 55.41 MB Proc + 26.78 MB MatchData + 22.45 MB Stupidedi::Reader::DelegatedInput + 15.21 MB Stupidedi::Reader::Position + 12.86 MB Stupidedi::Reader::Success + 12.48 MB Range + 9.25 MB Stupidedi::Reader::TokenReader + 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 3.22 MB Stupidedi::Zipper::EditedCursor + 3.02 MB Stupidedi::Reader::SimpleElementTok + 2.42 MB Stupidedi::Zipper::Hole + 2.4 MB Stupidedi::Parser::LoopState + 1.45 MB Stupidedi::Reader::SegmentTok + 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 1.35 MB BigDecimal + 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 953.54 kB File + 895.25 kB RubyVM::InstructionSequence + 811.2 kB Stupidedi::Either::Success + 805.6 kB Stupidedi::Parser::TableState + 804.8 kB Stupidedi::Parser::StateMachine + 804.0 kB Stupidedi::Values::SegmentVal + 800.8 kB Stupidedi::Values::LoopVal + 608.08 kB Integer + 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 200.0 kB Stupidedi::Reader::ComponentElementTok + 200.0 kB Stupidedi::Reader::CompositeElementTok + 200.0 kB Stupidedi::Values::CompositeElementVal + 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 125.24 kB Class + 120.52 kB Thread::Backtrace + 33.2 kB Stupidedi::Parser::Instruction + 31.27 kB Module + 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID + 20.16 kB Stupidedi::Parser::ConstraintTable::Stub + 19.44 kB Stupidedi::Schema::SimpleElementUse + 10.72 kB Stupidedi::Parser::InstructionTable::NonEmpty + 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN + 7.28 kB Regexp + 4.88 kB Stupidedi::Schema::CodeList::Internal + 4.8 kB Stupidedi::Parser::TransactionSetState + 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn + 4.03 kB Stupidedi::Versions::Common::ElementTypes::R + 3.84 kB Stupidedi::Parser::FunctionalGroupState + 3.2 kB Stupidedi::Parser::InterchangeState + +allocated objects by gem +----------------------------------- + 11326025 stupidedi/lib + 62169 rubygems + 8171 cantor-1.2.1 + 9 ruby-2.4.6 + 2 other + +allocated objects by file +----------------------------------- + 3986533 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 1821615 /Users/mr/wd/stupidedi/lib/ruby/string.rb + 1146761 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 681718 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 643062 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 516605 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 316938 /Users/mr/wd/stupidedi/lib/ruby/object.rb + 295536 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb + 221416 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 211231 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb + 170681 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 151009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 150854 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb + 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb + 115238 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 100660 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 80589 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 70044 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 66349 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 61917 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb + 61008 /Users/mr/wd/stupidedi/lib/ruby/array.rb + 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb + 50149 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 45240 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 40251 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 30040 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 20330 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 20184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 20120 /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb + 20110 /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb + 20079 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 20018 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb + 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb + 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb + 8151 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 5363 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 5154 /Users/mr/wd/stupidedi/lib/stupidedi.rb + 5047 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 5008 /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb + 1994 /Users/mr/wd/stupidedi/lib/ruby/module.rb + 1768 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 1455 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 814 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 678 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb + 456 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 325 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 206 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 189 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + 184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 179 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + +allocated objects by location +----------------------------------- + 915478 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 695960 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:305 + 695960 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 + 681460 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:52 + 594240 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 462640 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:414 + 321540 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 + 311841 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 + 311840 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:102 + 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:59 + 295520 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 + 291472 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 272490 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 270520 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 241170 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 + 231360 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 + 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:417 + 220900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:282 + 220900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 + 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 + 160880 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 + 160820 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 160740 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 + 160740 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 + 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 80518 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:192 + 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:193 + 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 + 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 + 61491 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 60426 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 60409 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 + 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 60230 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:209 + 40335 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:59 + 40240 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:73 + 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 40220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 30160 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 + +allocated objects by class +----------------------------------- + 5566458 Array + 2509358 String + 692595 Proc + 441908 Hash + 321540 Stupidedi::Reader::Success + 312014 Range + 311841 Stupidedi::Reader::DelegatedInput + 231360 Stupidedi::Reader::TokenReader + 211220 Stupidedi::Reader::Position + 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 95642 MatchData + 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 80518 Stupidedi::Zipper::EditedCursor + 75500 Stupidedi::Reader::SimpleElementTok + 60380 Stupidedi::Zipper::Hole + 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 30010 Stupidedi::Parser::LoopState + 20280 Stupidedi::Either::Success + 20120 Stupidedi::Parser::StateMachine + 20120 Stupidedi::Reader::SegmentTok + 20100 Stupidedi::Values::SegmentVal + 20020 Stupidedi::Values::LoopVal + 15320 BigDecimal + 15159 Integer + 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 10070 Stupidedi::Parser::TableState + 5000 Stupidedi::Reader::ComponentElementTok + 5000 Stupidedi::Reader::CompositeElementTok + 5000 Stupidedi::Values::CompositeElementVal + 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 1160 RubyVM::InstructionSequence + 504 Stupidedi::Parser::ConstraintTable::Stub + 415 Stupidedi::Parser::Instruction + 243 Stupidedi::Schema::SimpleElementUse + 239 Stupidedi::Versions::Common::ElementTypes::ID + 192 Class + 134 Stupidedi::Parser::InstructionTable::NonEmpty + 122 Stupidedi::Schema::CodeList::Internal + 116 File + 116 Stupidedi::Versions::Common::ElementTypes::AN + 107 Thread::Backtrace + 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 72 Stupidedi::Parser::ConstraintTable::Shallowest + 60 Stupidedi::Parser::TransactionSetState + 60 Stupidedi::Values::TableVal + 54 Cantor::AbsoluteSet + 51 Stupidedi::Reader::StreamReader + 43 Stupidedi::Schema::ComponentElementUse + +retained memory by gem +----------------------------------- + 57.8 MB stupidedi/lib + 744.98 kB rubygems + 316.74 kB cantor-1.2.1 + 520.0 B ruby-2.4.6 + +retained memory by file +----------------------------------- + 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb + 7.72 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb + 7.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 4.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 4.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 810.53 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 803.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 743.11 kB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb + 426.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 407.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 401.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 313.16 kB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 219.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 202.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 91.83 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 37.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 27.22 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 12.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 10.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 9.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 8.97 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb + 8.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 7.62 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb + 7.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb + 6.22 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb + 5.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb + 5.32 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + 5.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb + 4.86 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb + 4.79 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb + 4.78 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb + 4.18 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 4.18 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 3.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 3.1 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/special_val.rb + 2.99 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 2.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb + 2.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb + 2.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb + 2.69 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/separator_val.rb + 2.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb + +retained memory by location +----------------------------------- + 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 7.71 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 7.6 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 + 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 + 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 + 1.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 + 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 804.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 + 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 + 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 + 742.34 kB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 600.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 + 403.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 + 402.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 + 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 + 308.13 kB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 200.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 + 200.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 + 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 36.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 26.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 + 23.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 + 20.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 + 16.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 + 16.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 10.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 + 9.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5 + +retained memory by class +----------------------------------- + 19.87 MB Array + 10.29 MB String + 7.6 MB Stupidedi::Reader::Position + 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 2.0 MB Stupidedi::Parser::LoopState + 1.41 MB Stupidedi::Zipper::EditedCursor + 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 1.33 MB BigDecimal + 1.21 MB Stupidedi::Zipper::Hole + 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 804.0 kB Stupidedi::Values::SegmentVal + 800.8 kB Stupidedi::Values::LoopVal + 797.64 kB Hash + 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 404.8 kB Stupidedi::Parser::TableState + 200.0 kB Stupidedi::Values::CompositeElementVal + 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 125.24 kB Class + 33.2 kB Stupidedi::Parser::Instruction + 31.27 kB Module + 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID + 20.16 kB Stupidedi::Parser::ConstraintTable::Stub + 19.44 kB Stupidedi::Schema::SimpleElementUse + 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.72 kB Stupidedi::Parser::InstructionTable::NonEmpty + 6.04 kB Regexp + 4.88 kB Stupidedi::Schema::CodeList::Internal + 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn + 4.03 kB Stupidedi::Versions::Common::ElementTypes::R + 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 3.18 kB Stupidedi::Schema::SegmentDef + 3.1 kB Stupidedi::Schema::ComponentElementUse + 2.88 kB Stupidedi::Parser::ConstraintTable::Shallowest + 2.88 kB Stupidedi::Parser::FunctionalGroupState + 2.4 kB Stupidedi::Parser::InterchangeState + 2.4 kB Stupidedi::Parser::TransactionSetState + 2.4 kB Stupidedi::Values::TableVal + 2.4 kB Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty + 2.16 kB Cantor::AbsoluteSet + 1.6 kB Stupidedi::Parser::FailureState + 1.6 kB Stupidedi::Reader::SimpleElementTok + 1.44 kB Date + 1.44 kB Stupidedi::Reader::SegmentTok + 1.44 kB Stupidedi::Reader::Separators + 1.44 kB Stupidedi::Schema::SegmentUse + 1.25 kB Integer + 840.0 B Stupidedi::Zipper::DanglingCursor + 800.0 B Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty + +retained objects by gem +----------------------------------- + 893395 stupidedi/lib + 14253 rubygems + 132 cantor-1.2.1 + 3 ruby-2.4.6 + +retained objects by file +----------------------------------- + 181303 /Users/mr/wd/stupidedi/lib/ruby/string.rb + 170539 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb + 115227 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb + 105635 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb + 65404 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb + 50052 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb + 40307 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb + 35029 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb + 25128 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb + 20165 /Users/mr/wd/stupidedi/lib/ruby/object.rb + 20163 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb + 20041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb + 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb + 14218 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb + 10014 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb + 5190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb + 5089 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb + 5020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb + 935 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb + 591 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb + 425 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb + 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb + 148 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb + 129 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb + 123 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb + 91 /Users/mr/wd/stupidedi/lib/ruby/array.rb + 84 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb + 83 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb + 75 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb + 68 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb + 68 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb + 63 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb + 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb + 52 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb + 51 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb + 51 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb + 46 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb + 41 /Users/mr/wd/stupidedi/lib/ruby/module.rb + 41 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb + 39 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb + 38 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb + 34 /Users/mr/wd/stupidedi/lib/stupidedi/values/table_val.rb + 29 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb + 29 /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb + 27 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb + 25 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb + 25 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb + 24 /Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb + 22 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/interchange_def.rb + 22 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb + +retained objects by location +----------------------------------- + 181230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + 105620 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 + 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 + 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 + 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 30215 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 + 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 + 20141 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 + 20138 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 + 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 + 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 + 20020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 + 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 + 14204 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10071 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 + 5040 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 + 5020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 + 5019 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 + 576 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 + 504 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 + 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 + 160 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 + 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 + 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 + 74 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:45 + 72 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:477 + 69 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 + 67 /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 + 65 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 64 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 64 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:23 + 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 60 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb:98 + 54 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 54 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + +retained objects by class +----------------------------------- + 241030 String + 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty + 106669 Array + 105620 Stupidedi::Reader::Position + 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty + 35211 Stupidedi::Zipper::EditedCursor + 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty + 30192 Stupidedi::Zipper::Hole + 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty + 25010 Stupidedi::Parser::LoopState + 20100 Stupidedi::Values::SegmentVal + 20020 Stupidedi::Values::LoopVal + 15080 BigDecimal + 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty + 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty + 5060 Stupidedi::Parser::TableState + 5000 Stupidedi::Values::CompositeElementVal + 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty + 504 Stupidedi::Parser::ConstraintTable::Stub + 446 Hash + 415 Stupidedi::Parser::Instruction + 243 Stupidedi::Schema::SimpleElementUse + 239 Stupidedi::Versions::Common::ElementTypes::ID + 192 Class + 122 Stupidedi::Schema::CodeList::Internal + 116 Stupidedi::Versions::Common::ElementTypes::AN + 84 Stupidedi::Parser::InstructionTable::NonEmpty + 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty + 72 Stupidedi::Parser::ConstraintTable::Shallowest + 60 Stupidedi::Values::TableVal + 54 Cantor::AbsoluteSet + 43 Stupidedi::Schema::ComponentElementUse + 42 Stupidedi::Versions::Common::ElementTypes::Nn + 42 Stupidedi::Versions::Common::ElementTypes::R + 40 Stupidedi::Reader::SimpleElementTok + 36 Stupidedi::Schema::SegmentDef + 33 Module + 30 Stupidedi::Parser::FunctionalGroupState + 30 Stupidedi::Parser::InterchangeState + 30 Stupidedi::Parser::TransactionSetState + 30 Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty + 21 Stupidedi::Zipper::DanglingCursor + 20 Date + 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty + 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::NonEmpty + 20 Stupidedi::Parser::FailureState + 20 Stupidedi::Reader::SegmentDict::Constants + 20 Stupidedi::Reader::SegmentDict::NonEmpty + 20 Stupidedi::Reader::SegmentTok + 20 Stupidedi::Reader::Separators + + +Allocated String Report +----------------------------------- + 261681 "*" + 160990 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 100530 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 150 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:64 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 80351 "~" + 50230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 30120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 52929 "1" + 22780 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 15070 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5020 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5020 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 15 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:388 + + 50151 "P" + 45070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:370 + 40 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 48240 "0" + 38090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10140 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 8 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/requirement.rb:108 + + 45005 "CTP" + 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 + 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 45005 "PID" + 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 + 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 41906 "\n" + 20120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 20110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 1664 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb:1168 + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 + + 35031 "08" + 15020 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb:1752 + + 35006 "LIN" + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 + 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 35003 "PO4" + 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 + 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 30194 "2" + 25120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5030 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 10 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:388 + + 27528 "12" + 7500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 25149 "02" + 20090 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:300 + 19 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 25123 "01" + 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 23 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 25066 "03" + 20050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 25037 "F" + 5010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 17 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20149 "" + 20030 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 89 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:302 + + 20100 "N" + 20090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20092 "C" + 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20081 "E" + 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20067 "04" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 17 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20063 "05" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 13 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20062 "06" + 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 + 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20051 "L" + 15040 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20018 "O" + 20010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 8 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20010 "EA" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20006 "K" + 20000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20005 "PK" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:370 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20005 "VN" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20004 "CON" + 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 4 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 17630 "4" + 17600 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 15173 " " + 15100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 30 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 13 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 15060 "07" + 15030 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:527 + + 15022 "09" + 15010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10126 "A" + 10100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 + + 10119 "3" + 10110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 9 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10100 "I" + 10080 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 + + 10082 "10" + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:526 + 18 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 + 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 10056 "B" + 10050 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10053 "11" + 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 + 19 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 10025 "8" + 10010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10023 "V" + 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10000 "000001" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "000002" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "1234321" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "14.80" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + + 10000 "20.80" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + + 10000 "2345432" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10000 "LENS PAPER BOOK 12BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 + 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + +Retained String Report +----------------------------------- + 5001 "08" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "CON" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "EA" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "PK" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 5001 "VN" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 5000 "F" + 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 2500 "000001" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "000002" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "1234321" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "2345432" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "LENS PAPER BOOK 12BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 2500 "LENS PAPER BOOK 24BK/PK" + 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 21 "0001" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 20 "00" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 20 "004010" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 20 "7777777777" + 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 20 "SC" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 12 "ST" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "009" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "14" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "91" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "P" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "PA" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 11 "ZZ" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 10 "*" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 10 "00400" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "004321519" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "004321519IBMP" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "1000 PENNSYLVANIA AVE" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + + 10 "15222" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + + 10 "832" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "999999001" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 ">" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 10 "PC" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "PITTSBURGH" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 + + 10 "SPECIAL LABS" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 + + 10 "U" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "X" + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 + + 10 "~" + 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 + + 4 "To specify identifying information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 4 "To specify pertinent dates and times" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "To define the end of an interchange of zero or more functional groups and interchange-related control segments" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To describe a product or process in coded or free-form format" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To identify a party by type of organization, name, and code" + 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the beginning of the Price/Sales Catalog Transaction Set and specify catalog purpose and number information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the end of the transaction set and provide the count of the transmitted segments (including the beginning (ST) and ending (SE) segments)" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + + 3 "To indicate the start of a transaction set and assign a control number" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 + diff --git a/prof/memprof-pp.rb b/prof/memprof-pp.rb new file mode 100755 index 000000000..01dddfefe --- /dev/null +++ b/prof/memprof-pp.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby -Ilib +require "stupidedi" +require "memory_profiler" + +# This will be auto-enabled when $stdout.tty?, but -C forces color output +require "term/ansicolor" if ARGV.delete("-C") +if idx = ARGV.index("--format") + ARGV.delete("--format") + format = ARGV.delete_at(idx) +else + format = "tree" +end + +unless %w(html tree x12).include?(format) + $stderr.puts "unrecognized format (expected html, tree, or x12)" + exit(1) +end + +config = Stupidedi::Config.contrib(Stupidedi::Config.hipaa(Stupidedi::Config.default)) +parser = Stupidedi::Parser.build(config) +start = Time.now + +MemoryProfiler.report do + ARGV.each do |path| + # Reading the entire input at once is slightly faster than streaming + # from a file handle. + # + # content = File.read(path, :encoding => "ISO-8859-1") + # parser, r = parser.read(Stupidedi::Reader.build(content)) + # + reader = Stupidedi::Reader.build(File.read(path)) + parser, = parser.read(reader) + end +end.pretty_print(to_file: "prof/memprof-pp-#{start.strftime("%Y%m%dT%H%M%S")}.txt", + color_output: false, retained_strings: 100, + allocated_strings: 100, detailed_report: true, + scale_bytes: true) + +stop = Time.now +$stderr.puts "%0.3f seconds" % (stop - start) diff --git a/prof/mkfile.rb b/prof/mkfile.rb new file mode 100755 index 000000000..14aab3846 --- /dev/null +++ b/prof/mkfile.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +base = <<-X12 +ISA*00* *00* *14*004321519IBMP *ZZ*7777777777 *071011*0239*U*00400*000409939* *P*>~ +GS*SC*004321519*7777777777*20071011*0239*409939*X*004010~ +ST*832*0001~ +BCT*PC*FISHER 10/10/07****CATALOG 0000001~ +DTM*009*20071010*1632~ +N1*ST*SPECIAL LABS*91*999999001~ +N3*1000 PENNSYLVANIA AVE~ +N4*PITTSBURGH*PA*15222~ +X12 + +# P: 60151 +# + +prod1 = <<-X12 +LIN*000001*VN*1234321~ +PID*F*08***LENS PAPER BOOK 12BK/PK~ +PO4*12**EA~ +CTP**CON*14.80*1*PK~ +X12 + +prod2 = <<-X12 +LIN*000002*VN*2345432~ +PID*F*08***LENS PAPER BOOK 24BK/PK~ +PO4*12**EA~ +CTP**CON*20.80*1*PK~ +X12 + +# LIN: 35006 +# PID: 45005 +# 08: 35031 +# +# PO4: 35003 +# CTP: 45005 +# 1: 52929 + +footer = <<-X12 +CTT*2~ +SE*16*0001~ +GE*1*409939~ +IEA*1*000409939~ +X12 + +buffer = StringIO.new + +if ARGV.delete('--small') + n = 10 + m = 250 +else + n = 500 + m = 500 +end + +n.times do + buffer.write(base) + m.times { buffer.write(prod1) } + m.times { buffer.write(prod2) } + buffer.write(footer) +end + +STDOUT.write(buffer.string) From 8b5504b2ab78e7aed97d44cc1ef9b4ed18bc2a0d Mon Sep 17 00:00:00 2001 From: kputnam Date: Sat, 15 Jun 2019 11:28:28 -0500 Subject: [PATCH 02/38] Travis CI no longer supports 2.1.2-4 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 834c09249..6a0a79354 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,9 @@ rvm: # 2.0.0 - 2.1.0 - 2.1.1 -- 2.1.2 -- 2.1.3 -- 2.1.4 +# 2.1.2 +# 2.1.3 +# 2.1.4 - 2.1.5 - 2.1.6 - 2.1.7 From 901a17bc00bd69ab56f227ca5b41b846509d3f91 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 16 Jun 2019 17:04:39 -0500 Subject: [PATCH 03/38] Use Regexp#match? instead of =~ to avoid memory allocation --- lib/ruby/blank.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/ruby/blank.rb b/lib/ruby/blank.rb index 311bb2c24..d35a61327 100644 --- a/lib/ruby/blank.rb +++ b/lib/ruby/blank.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true module Stupidedi module Refinements + + NONBLANK = /\S/ + refine String do # True if the string is `empty?` or contains all whitespace # @@ -10,11 +13,11 @@ module Refinements # "".blank? #=> true # def blank? - self !~ /\S/ + not NONBLANK.match?(self) end def present? - self =~ /\S/ + NONBLANK.match?(self) end end From 24ea22201285b99e82712d63696047d5a304f032 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 16 Jun 2019 17:06:47 -0500 Subject: [PATCH 04/38] Fix issue with empty strings in Substring#repro --- lib/ruby/string.rb | 15 +++++++++------ lib/stupidedi/reader/input.rb | 3 +-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/ruby/string.rb b/lib/ruby/string.rb index b37b083d8..258ae22e5 100644 --- a/lib/ruby/string.rb +++ b/lib/ruby/string.rb @@ -13,7 +13,7 @@ module Refinements # @return [String] def at(n) raise ArgumentError, "n must be positive" if n < 0 - self[n] unless n >= length # FIXME: Big source of memory allocation + self[n] unless n >= length end # Return the string with `n` characters removed from the front @@ -27,8 +27,7 @@ def at(n) # @return [String] def drop(n) raise ArgumentError, "n must be positive" if n < 0 - (length >= n) ? self[n..-1] : "" # FIXME: Big source of memory allocation - # FIXME: Big source of object allocation + (length >= n) ? self[n..-1] : "" end # Return the first `n` characters from the front @@ -65,7 +64,7 @@ def split_at(n) # "abc".defined_at?(0) #=> true # "abc".defined_at?(3) #=> false def defined_at?(n) - n < length # FIXME, should be false or raise error n < 0 + n < length end # To make String compatible with the {Stupidedi::Reader::Input} interface, @@ -115,7 +114,11 @@ def initialize(whole, m = 0, n = whole.length - 1) # @note: Avoid calling this unless needed because it allocates another String def repro - @whole[@m..@n] + if @m <= @n + @whole[@m..@n] + else + "" + end end def to_str @@ -152,7 +155,7 @@ def at(n) end def defined_at?(n) - n <= @n - @m # FIXME should be false or exception when n < 0 + n <= @n - @m end def take(n) diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb index bec320589..1d38b1222 100644 --- a/lib/stupidedi/reader/input.rb +++ b/lib/stupidedi/reader/input.rb @@ -18,8 +18,7 @@ def build(o, *args) when IO FileInput.new(o, *args) when String - # DelegatedInput.new(Substring.new(o), *args) - DelegatedInput.new(o, *args) + DelegatedInput.new(Substring.new(o), *args) when Array DelegatedInput.new(o, *args) else From 8f5adc039a02d9b55a7f3814eec69c94e4b78aa0 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 16 Jun 2019 17:08:50 -0500 Subject: [PATCH 05/38] Remove #offset attribute from Position (only line, column, filename) --- lib/stupidedi/reader/input/delegated_input.rb | 20 +++++------ lib/stupidedi/reader/position.rb | 12 ++++--- .../reader/input/delegated_input_spec.rb | 35 ++++++++++--------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/lib/stupidedi/reader/input/delegated_input.rb b/lib/stupidedi/reader/input/delegated_input.rb index df3b6689f..105ccc5e6 100644 --- a/lib/stupidedi/reader/input/delegated_input.rb +++ b/lib/stupidedi/reader/input/delegated_input.rb @@ -5,15 +5,17 @@ module Stupidedi module Reader class DelegatedInput < AbstractInput def initialize(delegate, offset = 0, line = 1, column = 1) - @delegate, @offset, @line, @column = - delegate, offset, line, column + @delegate, @line, @column = + delegate, line, column end # @group Querying the Position ######################################################################## # (see AbstractInput#offset) - attr_reader :offset + def offset + nil + end # (see AbstractInput#line) attr_reader :line @@ -23,7 +25,7 @@ def initialize(delegate, offset = 0, line = 1, column = 1) # (see AbstractInput#position) def position - Position.new(@offset, @line, @column, nil) + Position.new(nil, @line, @column, nil) end # @group Reading the Input @@ -45,9 +47,7 @@ def position def drop(n) raise ArgumentError, "n must be positive" unless n >= 0 - suffix = @delegate.drop(n) prefix = @delegate.take(n) - length = prefix.length count = prefix.count("\n") @@ -57,8 +57,7 @@ def drop(n) @column + length end - copy(:delegate => suffix, - :offset => @offset + length, + copy(:delegate => @delegate.drop(n), :line => @line + count, :column => column) end @@ -72,7 +71,6 @@ def drop(n) # (see AbstractInput#empty?) def_delegators :@delegate, :empty? - # (see AbstractInput#==) def_delegators :@delegate, :== @@ -93,7 +91,7 @@ def pretty_print(q) end q.text preview - q.text " at line #{@line}, column #{@column}, offset #{@offset}" + q.text " at line #{@line}, column #{@column}, offset #{nil}" end end @@ -103,7 +101,7 @@ def pretty_print(q) def copy(changes = {}) DelegatedInput.new \ changes.fetch(:delegate, @delegate), - changes.fetch(:offset, @offset), + nil, changes.fetch(:line, @line), changes.fetch(:column, @column) end diff --git a/lib/stupidedi/reader/position.rb b/lib/stupidedi/reader/position.rb index d27ffb5e1..3181c5ef2 100644 --- a/lib/stupidedi/reader/position.rb +++ b/lib/stupidedi/reader/position.rb @@ -17,13 +17,15 @@ class Position attr_reader :path def initialize(offset, line, column, path) - @offset, @line, @column, @path = - offset, line, column, path + # @offset, @line, @column, @path = + # offset, line, column, path + @line, @column, @path = + line, column, path end def copy(changes = {}) Position.new \ - changes.fetch(:offset, @offset), + nil, #changes.fetch(:offset, @offset), changes.fetch(:line, @line), changes.fetch(:column, @column), changes.fetch(:path, @path) @@ -56,8 +58,8 @@ def pretty_print(q) q.text "line #{@line}," q.breakable q.text "column #{@column}," - q.breakable - q.text "offset #{@offset}" + #q.breakable + #q.text "offset #{@offset}" unless @path.nil? q.text "," diff --git a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb b/spec/lib/stupidedi/reader/input/delegated_input_spec.rb index c79cf1b57..69c81cda7 100644 --- a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb +++ b/spec/lib/stupidedi/reader/input/delegated_input_spec.rb @@ -1,4 +1,4 @@ -describe Stupidedi::Reader::DelegatedInput do +fdescribe Stupidedi::Reader::DelegatedInput do using Stupidedi::Refinements def mkinput(*args) @@ -6,7 +6,7 @@ def mkinput(*args) end describe "#offset" do - it "returns the value given to the constructor" do + pending "returns the value given to the constructor" do expect(mkinput("", 10, 20, 30).offset).to be == 10 end end @@ -28,7 +28,7 @@ def mkinput(*args) expect(mkinput("").position).to be_a(Stupidedi::Reader::Position) end - it "returns a Position value at the current offset" do + pending "returns a Position value at the current offset" do expect(mkinput("", 3).position.offset).to be == 3 end @@ -97,29 +97,30 @@ def mkinput(*args) end context "when less than n elements are available" do - it "increments the offset" do + pending "increments the offset" do expect(mkinput("abc", 10).drop(25).offset).to be == 13 end - property "increments the offset" do - with(:size, between(0, 25)) do - [string, between(size + 1, 1000), between(10, 1000)] - end - end.check do |s, n, offset| - expect(mkinput(s, offset).drop(n).offset).to be == offset + s.length - end + # TODO + # property "increments the offset" do + # with(:size, between(0, 25)) do + # [string, between(size + 1, 1000), between(10, 1000)] + # end + # end.check do |s, n, offset| + # expect(mkinput(s, offset).drop(n).offset).to be == offset + s.length + # end it "returns an empty input" do expect(mkinput("abc", 10).drop(10)).to be_empty end end - context "when n elements are available" do + pending "when n elements are available" do it "increments the offset" do expect(mkinput("abc", 10).drop(2).offset).to be == 12 end - property "increments the offset" do + pending "increments the offset" do with(:size, between(0, 25)) do [string, between(0, size), between(10, 1000)] end @@ -207,7 +208,7 @@ def mkinput(*args) expect(mkinput(%w(a b)).take(3)).to be == %w(a b) end - it "does not update the offset" do + pending "does not update the offset" do expect(mkinput("abc", 500).tap{|x| x.take(4) }.offset).to be == 500 end end @@ -221,7 +222,7 @@ def mkinput(*args) expect(mkinput(%w(a b c)).take(2)).to be == %w(a b) end - it "does not update the offset" do + pending "does not update the offset" do expect(mkinput("abc", 500).tap{|x| x.take(2) }.offset).to be == 500 end end @@ -243,7 +244,7 @@ def mkinput(*args) expect(mkinput(%w(a b c)).at(2)).to be == "c" end - it "does not update the offset" do + pending "does not update the offset" do expect(mkinput("abc", 500).tap{|x| x.at(5) }.offset).to be == 500 end end @@ -257,7 +258,7 @@ def mkinput(*args) expect(mkinput(%w(a b c)).at(3)).to be_nil end - it "does not update the offset" do + pending "does not update the offset" do expect(mkinput("abc", 500).tap{|x| x.at(1) }.offset).to be == 500 end end From cac1bd00a9e55c34291c2f35c69d236d31103904 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 16 Jun 2019 17:29:59 -0500 Subject: [PATCH 06/38] Cleanup messes --- lib/stupidedi/builder.rb | 1 + lib/stupidedi/reader/input/abstract_input.rb | 3 - lib/stupidedi/reader/token_reader.rb | 4 +- lib/stupidedi/schema/simple_element_use.rb | 2 +- lib/stupidedi/transaction_sets.rb | 1 + prof/memprof-pp-20190614T170606.txt | 1164 ------------------ prof/memprof-pp-20190615T012552.txt | 1159 ----------------- prof/memprof-pp.rb | 9 + prof/mkfile.rb | 9 +- 9 files changed, 20 insertions(+), 2332 deletions(-) delete mode 100644 prof/memprof-pp-20190614T170606.txt delete mode 100644 prof/memprof-pp-20190615T012552.txt diff --git a/lib/stupidedi/builder.rb b/lib/stupidedi/builder.rb index 1a8d0831d..ac2d3cc29 100644 --- a/lib/stupidedi/builder.rb +++ b/lib/stupidedi/builder.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module Stupidedi warn "DEPRECATION WARNING: Stupidedi::Builder is depecated, use Stupidedi::Parser instead" diff --git a/lib/stupidedi/reader/input/abstract_input.rb b/lib/stupidedi/reader/input/abstract_input.rb index b42cfc0af..7f03e37af 100644 --- a/lib/stupidedi/reader/input/abstract_input.rb +++ b/lib/stupidedi/reader/input/abstract_input.rb @@ -67,20 +67,17 @@ class AbstractInput # The line of the current position # # @return [Integer] - def_delegators :position, :line # The column of the current position. The column resets to `1` each time # a newline is read # # @return [Integer] - def_delegators :position, :column # The file name, URI, etc that identifies the input stream # # @return [String] - def_delegators :position, :path # @group Reading the Input diff --git a/lib/stupidedi/reader/token_reader.rb b/lib/stupidedi/reader/token_reader.rb index b77f99ae9..1d6f44b91 100644 --- a/lib/stupidedi/reader/token_reader.rb +++ b/lib/stupidedi/reader/token_reader.rb @@ -303,8 +303,8 @@ def read_simple_element(repeatable = false) position = 0 buffer = "" - while @input.defined_at?(position) # FIXME: Big source of memory & object allocation - character = @input.at(position) # FIXME: Big source of memory & object allocation + while @input.defined_at?(position) + character = @input.at(position) position += 1 if is_control?(character) diff --git a/lib/stupidedi/schema/simple_element_use.rb b/lib/stupidedi/schema/simple_element_use.rb index 502e73797..e3daead04 100644 --- a/lib/stupidedi/schema/simple_element_use.rb +++ b/lib/stupidedi/schema/simple_element_use.rb @@ -49,7 +49,7 @@ def descriptor end def repeatable? - @repeat_count.try{|r| r.include?(2) } + @repeat_count and @repeat_count.include?(2) end # @return true diff --git a/lib/stupidedi/transaction_sets.rb b/lib/stupidedi/transaction_sets.rb index ad3eb444d..3562aab63 100644 --- a/lib/stupidedi/transaction_sets.rb +++ b/lib/stupidedi/transaction_sets.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module Stupidedi module TransactionSets autoload :Builder, "stupidedi/transaction_sets/builder" diff --git a/prof/memprof-pp-20190614T170606.txt b/prof/memprof-pp-20190614T170606.txt deleted file mode 100644 index dc26aedeb..000000000 --- a/prof/memprof-pp-20190614T170606.txt +++ /dev/null @@ -1,1164 +0,0 @@ -Total allocated: 4.62 GB (9667215 objects) -Total retained: 53.78 MB (727182 objects) - -allocated memory by gem ------------------------------------ - 4.62 GB stupidedi/lib - 4.38 MB rubygems - 2.17 MB cantor-1.2.1 - 720.0 B ruby-2.6.0 - 272.0 B other - -allocated memory by file ------------------------------------ - 4.11 GB /Users/mr/wd/stupidedi/lib/ruby/string.rb - 132.46 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 96.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 46.79 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 32.97 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb - 25.73 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 24.55 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb - 14.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 13.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb - 9.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb - 7.22 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb - 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 6.15 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 6.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 5.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 4.36 MB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb - 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 2.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb - 2.17 MB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 896.15 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb - 841.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 806.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 805.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb - 804.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb - 429.69 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 402.48 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb - 401.97 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb - 401.71 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb - 286.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 238.34 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 207.46 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 201.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb - 133.75 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb - 115.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 74.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 50.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 36.61 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 26.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 19.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 15.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb - 13.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - -allocated memory by location ------------------------------------ - 4.08 GB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 72.35 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:63 - 26.11 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 - 23.77 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 - 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:103 - 20.91 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:103 - 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 18.64 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:97 - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 - 13.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 - 13.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:307 - 13.63 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:51 - 12.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 - 12.6 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 - 10.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 9.65 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 - 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 - 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:415 - 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:418 - 7.19 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 - 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 - 5.83 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 4.83 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 4.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:40 - 4.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 - 4.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:284 - 4.32 MB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 - 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 - 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 - 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 - 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 - 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 - 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 - 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 - 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:24 - 2.33 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:111 - -allocated memory by class ------------------------------------ - 4.14 GB String - 216.69 MB Array - 103.32 MB Hash - 27.34 MB Proc - 26.78 MB MatchData - 22.45 MB Stupidedi::Reader::DelegatedInput - 15.21 MB Stupidedi::Reader::Position - 12.86 MB Stupidedi::Reader::Success - 12.48 MB Range - 9.25 MB Stupidedi::Reader::TokenReader - 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 3.22 MB Stupidedi::Zipper::EditedCursor - 3.02 MB Stupidedi::Reader::SimpleElementTok - 2.42 MB Stupidedi::Zipper::Hole - 2.4 MB Stupidedi::Parser::LoopState - 1.45 MB Stupidedi::Reader::SegmentTok - 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 1.35 MB BigDecimal - 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 952.61 kB File - 811.2 kB Stupidedi::Either::Success - 806.56 kB Integer - 805.6 kB Stupidedi::Parser::TableState - 804.8 kB Stupidedi::Parser::StateMachine - 804.0 kB Stupidedi::Values::SegmentVal - 800.8 kB Stupidedi::Values::LoopVal - 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 200.0 kB Stupidedi::Reader::ComponentElementTok - 200.0 kB Stupidedi::Reader::CompositeElementTok - 200.0 kB Stupidedi::Values::CompositeElementVal - 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 144.39 kB Class - 120.52 kB Thread::Backtrace - 33.24 kB Module - 33.2 kB Stupidedi::Parser::Instruction - 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID - 20.16 kB Stupidedi::Parser::ConstraintTable::Stub - 19.44 kB Stupidedi::Schema::SimpleElementUse - 10.72 kB Stupidedi::Parser::InstructionTable::NonEmpty - 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN - 7.28 kB Regexp - 4.88 kB Stupidedi::Schema::CodeList::Internal - 4.8 kB Stupidedi::Parser::TransactionSetState - 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn - 4.03 kB Stupidedi::Versions::Common::ElementTypes::R - 3.84 kB Stupidedi::Parser::FunctionalGroupState - 3.2 kB Stupidedi::Parser::InterchangeState - 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - -allocated objects by gem ------------------------------------ - 9599945 stupidedi/lib - 59089 rubygems - 8171 cantor-1.2.1 - 8 ruby-2.6.0 - 2 other - -allocated objects by file ------------------------------------ - 2285651 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 1821615 /Users/mr/wd/stupidedi/lib/ruby/string.rb - 1146755 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 643062 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 516605 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 442323 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 340987 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 316938 /Users/mr/wd/stupidedi/lib/ruby/object.rb - 295534 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb - 221416 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 211231 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb - 170612 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 151009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 130734 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb - 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb - 115234 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 80589 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 71321 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 70044 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 61008 /Users/mr/wd/stupidedi/lib/ruby/array.rb - 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb - 58842 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb - 50103 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 45240 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 40251 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 30038 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 20312 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 20184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 20120 /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb - 20109 /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb - 20067 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 10017 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb - 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb - 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb - 8151 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 5359 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 5154 /Users/mr/wd/stupidedi/lib/stupidedi.rb - 5041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 5007 /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb - 1876 /Users/mr/wd/stupidedi/lib/ruby/module.rb - 1768 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 1352 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 813 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 448 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 347 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb - 265 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 205 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 175 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 164 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 149 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - -allocated objects by location ------------------------------------ - 915478 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 594240 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 347980 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 - 347980 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:307 - 340730 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:51 - 321540 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 - 311841 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 - 311840 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:103 - 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:63 - 295520 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 - 272490 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 270520 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 261330 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:103 - 241170 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 - 231360 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 - 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:415 - 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:418 - 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 - 160820 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 145736 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 145736 /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 - 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 110450 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 - 110450 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:284 - 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 80518 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 - 80440 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 - 80370 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 - 80370 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:97 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 - 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 - 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 - 60426 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 60409 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 - 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 - 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 60260 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 58433 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 45302 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:59 - 40240 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:73 - 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 40220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 30160 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - -allocated objects by class ------------------------------------ - 4146334 Array - 2547369 String - 441908 Hash - 341745 Proc - 321540 Stupidedi::Reader::Success - 312014 Range - 311841 Stupidedi::Reader::DelegatedInput - 231360 Stupidedi::Reader::TokenReader - 211220 Stupidedi::Reader::Position - 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 95642 MatchData - 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 80518 Stupidedi::Zipper::EditedCursor - 75500 Stupidedi::Reader::SimpleElementTok - 60380 Stupidedi::Zipper::Hole - 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 30010 Stupidedi::Parser::LoopState - 20280 Stupidedi::Either::Success - 20121 Integer - 20120 Stupidedi::Parser::StateMachine - 20120 Stupidedi::Reader::SegmentTok - 20100 Stupidedi::Values::SegmentVal - 20020 Stupidedi::Values::LoopVal - 15320 BigDecimal - 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 10070 Stupidedi::Parser::TableState - 5000 Stupidedi::Reader::ComponentElementTok - 5000 Stupidedi::Reader::CompositeElementTok - 5000 Stupidedi::Values::CompositeElementVal - 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 504 Stupidedi::Parser::ConstraintTable::Stub - 415 Stupidedi::Parser::Instruction - 243 Stupidedi::Schema::SimpleElementUse - 239 Stupidedi::Versions::Common::ElementTypes::ID - 192 Class - 134 Stupidedi::Parser::InstructionTable::NonEmpty - 122 Stupidedi::Schema::CodeList::Internal - 116 File - 116 Stupidedi::Versions::Common::ElementTypes::AN - 107 Thread::Backtrace - 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 72 Stupidedi::Parser::ConstraintTable::Shallowest - 60 Stupidedi::Parser::TransactionSetState - 60 Stupidedi::Values::TableVal - 54 Cantor::AbsoluteSet - 51 Stupidedi::Reader::StreamReader - 43 Stupidedi::Schema::ComponentElementUse - 42 Stupidedi::Versions::Common::ElementTypes::Nn - -retained memory by gem ------------------------------------ - 52.72 MB stupidedi/lib - 744.52 kB rubygems - 321.88 kB cantor-1.2.1 - 520.0 B ruby-2.6.0 - -retained memory by file ------------------------------------ - 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb - 7.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 4.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 4.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 2.58 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb - 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 810.86 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 803.53 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 742.46 kB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb - 428.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 407.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 401.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 317.47 kB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 220.5 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 203.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 114.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 38.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 27.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 14.57 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 11.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 10.49 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb - 10.45 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 8.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 7.81 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb - 7.61 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb - 6.71 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb - 6.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb - 5.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - 5.28 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb - 5.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb - 4.97 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb - 4.92 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 4.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb - 4.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 4.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 3.45 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 3.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/special_val.rb - 3.07 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 3.06 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb - 3.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/separator_val.rb - 2.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb - 2.81 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb - -retained memory by location ------------------------------------ - 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 - 7.6 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 2.58 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 - 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 - 1.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 - 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 804.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 - 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 - 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 - 741.74 kB /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 600.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 - 403.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 - 402.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 - 311.78 kB /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 - 200.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 - 200.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 - 39.67 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 - 36.56 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 - 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 - 23.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 - 20.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 - 20.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 - 19.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 - 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 - 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 - 10.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 - 9.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 - 9.53 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 - -retained memory by class ------------------------------------ - 19.87 MB Array - 7.6 MB Stupidedi::Reader::Position - 5.15 MB String - 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 2.0 MB Stupidedi::Parser::LoopState - 1.41 MB Stupidedi::Zipper::EditedCursor - 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 1.33 MB BigDecimal - 1.21 MB Stupidedi::Zipper::Hole - 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 827.1 kB Hash - 804.0 kB Stupidedi::Values::SegmentVal - 800.8 kB Stupidedi::Values::LoopVal - 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 404.8 kB Stupidedi::Parser::TableState - 200.0 kB Stupidedi::Values::CompositeElementVal - 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 144.39 kB Class - 33.24 kB Module - 33.2 kB Stupidedi::Parser::Instruction - 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID - 20.16 kB Stupidedi::Parser::ConstraintTable::Stub - 19.44 kB Stupidedi::Schema::SimpleElementUse - 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN - 6.72 kB Stupidedi::Parser::InstructionTable::NonEmpty - 6.04 kB Regexp - 4.88 kB Stupidedi::Schema::CodeList::Internal - 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn - 4.03 kB Stupidedi::Versions::Common::ElementTypes::R - 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 3.18 kB Stupidedi::Schema::SegmentDef - 3.1 kB Stupidedi::Schema::ComponentElementUse - 2.88 kB Stupidedi::Parser::ConstraintTable::Shallowest - 2.88 kB Stupidedi::Parser::FunctionalGroupState - 2.4 kB Stupidedi::Parser::InterchangeState - 2.4 kB Stupidedi::Parser::TransactionSetState - 2.4 kB Stupidedi::Values::TableVal - 2.4 kB Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty - 2.16 kB Cantor::AbsoluteSet - 1.6 kB Stupidedi::Parser::FailureState - 1.6 kB Stupidedi::Reader::SimpleElementTok - 1.44 kB Date - 1.44 kB Stupidedi::Reader::SegmentTok - 1.44 kB Stupidedi::Reader::Separators - 1.44 kB Stupidedi::Schema::SegmentUse - 1.25 kB Integer - 840.0 B Stupidedi::Zipper::DanglingCursor - 800.0 B Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty - -retained objects by gem ------------------------------------ - 712809 stupidedi/lib - 14238 rubygems - 132 cantor-1.2.1 - 3 ruby-2.6.0 - -retained objects by file ------------------------------------ - 170539 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 115227 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 105635 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 65404 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 50052 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 40307 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 35029 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 25128 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 20165 /Users/mr/wd/stupidedi/lib/ruby/object.rb - 20163 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 20041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 14203 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb - 10014 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 5190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 5089 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 5020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 935 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 718 /Users/mr/wd/stupidedi/lib/ruby/string.rb - 591 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 425 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 148 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 129 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 123 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 91 /Users/mr/wd/stupidedi/lib/ruby/array.rb - 84 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb - 83 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb - 75 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 68 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb - 67 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 63 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb - 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 52 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - 51 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb - 51 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 46 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 41 /Users/mr/wd/stupidedi/lib/ruby/module.rb - 41 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 39 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 38 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb - 34 /Users/mr/wd/stupidedi/lib/stupidedi/values/table_val.rb - 29 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb - 29 /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb - 27 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 25 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb - 25 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb - 24 /Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb - 22 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/interchange_def.rb - 22 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb - -retained objects by location ------------------------------------ - 105620 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 30215 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 - 20141 /Users/mr/wd/stupidedi/lib/ruby/object.rb:18 - 20138 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 - 20020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 - 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 - 14189 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10071 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 - 5040 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 - 5020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 - 5019 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 - 645 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 576 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 - 504 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 - 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 - 160 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 - 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 - 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 - 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 - 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 - 74 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:45 - 72 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:477 - 69 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 - 67 /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 - 65 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 - 64 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 - 64 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:23 - 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 60 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb:98 - 54 /Users/mr/.rvm/gems/ruby-2.6.0/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 - 54 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 - -retained objects by class ------------------------------------ - 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 106669 Array - 105620 Stupidedi::Reader::Position - 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 60429 String - 35211 Stupidedi::Zipper::EditedCursor - 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 30192 Stupidedi::Zipper::Hole - 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 25010 Stupidedi::Parser::LoopState - 20100 Stupidedi::Values::SegmentVal - 20020 Stupidedi::Values::LoopVal - 15080 BigDecimal - 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 5060 Stupidedi::Parser::TableState - 5000 Stupidedi::Values::CompositeElementVal - 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 504 Stupidedi::Parser::ConstraintTable::Stub - 446 Hash - 415 Stupidedi::Parser::Instruction - 243 Stupidedi::Schema::SimpleElementUse - 239 Stupidedi::Versions::Common::ElementTypes::ID - 192 Class - 122 Stupidedi::Schema::CodeList::Internal - 116 Stupidedi::Versions::Common::ElementTypes::AN - 84 Stupidedi::Parser::InstructionTable::NonEmpty - 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 72 Stupidedi::Parser::ConstraintTable::Shallowest - 60 Stupidedi::Values::TableVal - 54 Cantor::AbsoluteSet - 43 Stupidedi::Schema::ComponentElementUse - 42 Stupidedi::Versions::Common::ElementTypes::Nn - 42 Stupidedi::Versions::Common::ElementTypes::R - 40 Stupidedi::Reader::SimpleElementTok - 36 Stupidedi::Schema::SegmentDef - 33 Module - 30 Stupidedi::Parser::FunctionalGroupState - 30 Stupidedi::Parser::InterchangeState - 30 Stupidedi::Parser::TransactionSetState - 30 Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty - 21 Stupidedi::Zipper::DanglingCursor - 20 Date - 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty - 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::NonEmpty - 20 Stupidedi::Parser::FailureState - 20 Stupidedi::Reader::SegmentDict::Constants - 20 Stupidedi::Reader::SegmentDict::NonEmpty - 20 Stupidedi::Reader::SegmentTok - 20 Stupidedi::Reader::Separators - - -Allocated String Report ------------------------------------ - 261681 "*" - 160990 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 100530 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 150 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:64 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 80351 "~" - 50230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 30120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 60150 "P" - 45070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:371 - 40 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 52928 "1" - 22780 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 15070 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5020 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5020 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 14 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:387 - 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:388 - - 48240 "0" - 38090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10140 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/requirement.rb:108 - - 45004 "CTP" - 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 - 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 45004 "PID" - 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 - 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 41910 "\n" - 20120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 20110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 1668 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 7 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 - 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb:1168 - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - - 35031 "08" - 15020 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/specification.rb:1752 - - 35005 "LIN" - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 - 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 35002 "PO4" - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:263 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:270 - 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 30193 "2" - 25120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5030 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 10 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:387 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/version.rb:388 - - 27527 "12" - 7500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 15 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 25149 "02" - 20090 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:300 - 19 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 25123 "01" - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 23 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 25101 "C" - 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 25066 "03" - 20050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 16 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 25050 "L" - 15040 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 25037 "F" - 5010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 17 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20149 "" - 20030 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 89 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:302 - - 20130 "N" - 20090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20081 "E" - 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 11 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20067 "04" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 17 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20063 "05" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 13 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20062 "06" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 12 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20018 "O" - 20010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20010 "EA" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20006 "K" - 20000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 6 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20005 "PK" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:371 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20005 "VN" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 5 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20004 "CON" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 17629 "4" - 17600 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 9 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 15173 " " - 15100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 30 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 13 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 15060 "07" - 15030 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:527 - - 15022 "09" - 15010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 12 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10126 "A" - 10100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 16 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 - - 10118 "3" - 10110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 8 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10110 "I" - 10080 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - - 10082 "10" - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:526 - 18 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 - 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 10066 "B" - 10050 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:256 - 6 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10053 "11" - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 - 19 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 10024 "8" - 10010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 4 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10023 "V" - 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10000 "000001" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "000002" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "1234321" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "14.80" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - - 10000 "20.80" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - - 10000 "2345432" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "LENS PAPER BOOK 12BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - -Retained String Report ------------------------------------ - 5001 "08" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "CON" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "EA" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "PK" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "VN" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 5000 "F" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 2500 "000001" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "000002" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "1234321" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "2345432" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "LENS PAPER BOOK 12BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "LENS PAPER BOOK 24BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 21 "0001" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 20 "00" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 20 "004010" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 20 "7777777777" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 20 "SC" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 12 "ST" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 2 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "009" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "14" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "91" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "PA" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "ZZ" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 10 "*" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 10 "00400" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "004321519" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "004321519IBMP" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "1000 PENNSYLVANIA AVE" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - - 10 "15222" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - - 10 "832" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "999999001" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 ">" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 10 "P" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "PC" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "PITTSBURGH" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:335 - - 10 "SPECIAL LABS" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "U" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "X" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "~" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 4 "To specify identifying information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 4 "To specify pertinent dates and times" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "/Users/mr/wd/stupidedi/lib" - 3 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 - - 3 "To define the end of an interchange of zero or more functional groups and interchange-related control segments" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To describe a product or process in coded or free-form format" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To identify a party by type of organization, name, and code" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To indicate the beginning of a functional group and to provider control information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the beginning of the Price/Sales Catalog Transaction Set and specify catalog purpose and number information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the end of a functional group and provider control information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the end of the transaction set and provide the count of the transmitted segments (including the beginning (ST) and ending (SE) segments)" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the start of a transaction set and assign a control number" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - diff --git a/prof/memprof-pp-20190615T012552.txt b/prof/memprof-pp-20190615T012552.txt deleted file mode 100644 index 2730ec041..000000000 --- a/prof/memprof-pp-20190615T012552.txt +++ /dev/null @@ -1,1159 +0,0 @@ -Total allocated: 650.38 MB (11396376 objects) -Total retained: 58.87 MB (907783 objects) - -allocated memory by gem ------------------------------------ - 643.36 MB stupidedi/lib - 5.17 MB rubygems - 1.85 MB cantor-1.2.1 - 760.0 B ruby-2.4.6 - 280.0 B other - -allocated memory by file ------------------------------------ - 177.14 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 119.99 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 73.46 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb - 40.92 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 32.97 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb - 25.73 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 24.55 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb - 16.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb - 13.64 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb - 10.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb - 7.22 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb - 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 6.15 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 6.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 5.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 5.15 MB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb - 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 3.69 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 2.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb - 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 1.84 MB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb - 903.08 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb - 837.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 806.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 805.21 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb - 804.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb - 427.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 401.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb - 401.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb - 263.06 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb - 237.27 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 230.34 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 210.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 206.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 201.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb - 74.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 50.08 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 36.29 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 36.29 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb - 30.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 17.08 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 13.6 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - -allocated memory by location ------------------------------------ - 59.87 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:59 - 40.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:52 - 37.08 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 31.94 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 27.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:305 - 27.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 - 23.77 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 - 22.45 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:102 - 19.33 MB /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 18.51 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:414 - 15.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 15.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 - 12.86 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 - 12.6 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 11.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 - 10.9 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 9.65 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 - 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 - 9.25 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:417 - 8.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:282 - 8.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 - 7.19 MB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 - 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 6.44 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:192 - 6.43 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:193 - 5.11 MB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 4.83 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 4.82 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:209 - 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 3.87 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:41 - 3.22 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 - 3.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 - 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 - 3.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 - 2.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:116 - 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 2.42 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 - 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:24 - 2.02 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:108 - -allocated memory by class ------------------------------------ - 273.49 MB Array - 102.66 MB String - 86.56 MB Hash - 55.41 MB Proc - 26.78 MB MatchData - 22.45 MB Stupidedi::Reader::DelegatedInput - 15.21 MB Stupidedi::Reader::Position - 12.86 MB Stupidedi::Reader::Success - 12.48 MB Range - 9.25 MB Stupidedi::Reader::TokenReader - 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 3.22 MB Stupidedi::Zipper::EditedCursor - 3.02 MB Stupidedi::Reader::SimpleElementTok - 2.42 MB Stupidedi::Zipper::Hole - 2.4 MB Stupidedi::Parser::LoopState - 1.45 MB Stupidedi::Reader::SegmentTok - 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 1.35 MB BigDecimal - 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 953.54 kB File - 895.25 kB RubyVM::InstructionSequence - 811.2 kB Stupidedi::Either::Success - 805.6 kB Stupidedi::Parser::TableState - 804.8 kB Stupidedi::Parser::StateMachine - 804.0 kB Stupidedi::Values::SegmentVal - 800.8 kB Stupidedi::Values::LoopVal - 608.08 kB Integer - 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 200.0 kB Stupidedi::Reader::ComponentElementTok - 200.0 kB Stupidedi::Reader::CompositeElementTok - 200.0 kB Stupidedi::Values::CompositeElementVal - 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 125.24 kB Class - 120.52 kB Thread::Backtrace - 33.2 kB Stupidedi::Parser::Instruction - 31.27 kB Module - 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID - 20.16 kB Stupidedi::Parser::ConstraintTable::Stub - 19.44 kB Stupidedi::Schema::SimpleElementUse - 10.72 kB Stupidedi::Parser::InstructionTable::NonEmpty - 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN - 7.28 kB Regexp - 4.88 kB Stupidedi::Schema::CodeList::Internal - 4.8 kB Stupidedi::Parser::TransactionSetState - 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn - 4.03 kB Stupidedi::Versions::Common::ElementTypes::R - 3.84 kB Stupidedi::Parser::FunctionalGroupState - 3.2 kB Stupidedi::Parser::InterchangeState - -allocated objects by gem ------------------------------------ - 11326025 stupidedi/lib - 62169 rubygems - 8171 cantor-1.2.1 - 9 ruby-2.4.6 - 2 other - -allocated objects by file ------------------------------------ - 3986533 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 1821615 /Users/mr/wd/stupidedi/lib/ruby/string.rb - 1146761 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 681718 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 643062 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 516605 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 316938 /Users/mr/wd/stupidedi/lib/ruby/object.rb - 295536 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb - 221416 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 211231 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb - 170681 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 151009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 150854 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb - 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb - 115238 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 100660 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 80589 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 70044 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 66349 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 61917 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb - 61008 /Users/mr/wd/stupidedi/lib/ruby/array.rb - 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb - 50149 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 45240 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 40251 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 30040 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 20330 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 20184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 20120 /Users/mr/wd/stupidedi/lib/stupidedi/parser/state_machine.rb - 20110 /Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val.rb - 20079 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 20018 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb - 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb - 10009 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb - 8151 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 5363 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 5154 /Users/mr/wd/stupidedi/lib/stupidedi.rb - 5047 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 5008 /Users/mr/wd/stupidedi/lib/stupidedi/values/composite_element_val.rb - 1994 /Users/mr/wd/stupidedi/lib/ruby/module.rb - 1768 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 1455 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 814 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 678 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb - 456 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 325 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 206 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 189 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - 184 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 179 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - -allocated objects by location ------------------------------------ - 915478 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 695960 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:305 - 695960 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:306 - 681460 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:52 - 594240 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 462640 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:414 - 321540 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:60 - 311841 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:9 - 311840 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:102 - 311840 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:59 - 295520 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:35 - 291472 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 272490 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 270520 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 241170 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:42 - 231360 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:23 - 231320 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:417 - 220900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:282 - 220900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:283 - 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 211220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/position.rb:21 - 160880 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:82 - 160820 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 160740 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:240 - 160740 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:244 - 120800 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 80518 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:18 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/either.rb:96 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb:65 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:192 - 80340 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:193 - 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:20 - 75500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:69 - 61491 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 60426 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 60409 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 - 60380 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/path.rb:94 - 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 60230 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:209 - 40335 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:59 - 40240 /Users/mr/wd/stupidedi/lib/stupidedi/parser/generation.rb:73 - 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 40238 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 40220 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 30160 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 - -allocated objects by class ------------------------------------ - 5566458 Array - 2509358 String - 692595 Proc - 441908 Hash - 321540 Stupidedi::Reader::Success - 312014 Range - 311841 Stupidedi::Reader::DelegatedInput - 231360 Stupidedi::Reader::TokenReader - 211220 Stupidedi::Reader::Position - 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 95642 MatchData - 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 80518 Stupidedi::Zipper::EditedCursor - 75500 Stupidedi::Reader::SimpleElementTok - 60380 Stupidedi::Zipper::Hole - 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 30010 Stupidedi::Parser::LoopState - 20280 Stupidedi::Either::Success - 20120 Stupidedi::Parser::StateMachine - 20120 Stupidedi::Reader::SegmentTok - 20100 Stupidedi::Values::SegmentVal - 20020 Stupidedi::Values::LoopVal - 15320 BigDecimal - 15159 Integer - 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 10070 Stupidedi::Parser::TableState - 5000 Stupidedi::Reader::ComponentElementTok - 5000 Stupidedi::Reader::CompositeElementTok - 5000 Stupidedi::Values::CompositeElementVal - 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 1160 RubyVM::InstructionSequence - 504 Stupidedi::Parser::ConstraintTable::Stub - 415 Stupidedi::Parser::Instruction - 243 Stupidedi::Schema::SimpleElementUse - 239 Stupidedi::Versions::Common::ElementTypes::ID - 192 Class - 134 Stupidedi::Parser::InstructionTable::NonEmpty - 122 Stupidedi::Schema::CodeList::Internal - 116 File - 116 Stupidedi::Versions::Common::ElementTypes::AN - 107 Thread::Backtrace - 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 72 Stupidedi::Parser::ConstraintTable::Shallowest - 60 Stupidedi::Parser::TransactionSetState - 60 Stupidedi::Values::TableVal - 54 Cantor::AbsoluteSet - 51 Stupidedi::Reader::StreamReader - 43 Stupidedi::Schema::ComponentElementUse - -retained memory by gem ------------------------------------ - 57.8 MB stupidedi/lib - 744.98 kB rubygems - 316.74 kB cantor-1.2.1 - 520.0 B ruby-2.4.6 - -retained memory by file ------------------------------------ - 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb - 7.72 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb - 7.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 6.84 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 4.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 4.62 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 4.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 2.4 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 2.01 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 1.61 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 810.53 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 803.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 743.11 kB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb - 426.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 407.38 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 401.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 313.16 kB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 219.49 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 202.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 91.83 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 37.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 27.22 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 12.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 10.94 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 9.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 8.97 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb - 8.41 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 7.62 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb - 7.54 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb - 6.22 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb - 5.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb - 5.32 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - 5.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb - 4.86 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb - 4.79 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb - 4.78 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb - 4.18 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 4.18 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 3.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/either.rb - 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 3.1 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/special_val.rb - 2.99 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 2.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/result.rb - 2.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb - 2.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb - 2.69 kB /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/element_types/separator_val.rb - 2.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb - -retained memory by location ------------------------------------ - 10.79 MB /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 7.71 MB /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 7.6 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 4.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 3.89 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 3.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 2.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 2.41 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1.32 MB /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 - 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 1.21 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 - 1.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 - 1.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 - 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 804.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 804.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 - 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 - 800.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 - 742.34 kB /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 600.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 - 403.2 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 - 402.84 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 - 400.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 - 308.13 kB /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 - 200.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 - 200.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 - 200.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 - 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 - 36.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 - 31.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 - 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 - 26.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 - 23.04 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 - 20.16 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 - 16.7 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 - 16.13 kB /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 - 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 - 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 - 10.88 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 - 9.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 - 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5 - -retained memory by class ------------------------------------ - 19.87 MB Array - 10.29 MB String - 7.6 MB Stupidedi::Reader::Position - 4.4 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 3.4 MB Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 2.0 MB Stupidedi::Parser::LoopState - 1.41 MB Stupidedi::Zipper::EditedCursor - 1.4 MB Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 1.33 MB BigDecimal - 1.21 MB Stupidedi::Zipper::Hole - 1.21 MB Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 804.0 kB Stupidedi::Values::SegmentVal - 800.8 kB Stupidedi::Values::LoopVal - 797.64 kB Hash - 602.0 kB Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 600.0 kB Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 404.8 kB Stupidedi::Parser::TableState - 200.0 kB Stupidedi::Values::CompositeElementVal - 200.0 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 125.24 kB Class - 33.2 kB Stupidedi::Parser::Instruction - 31.27 kB Module - 22.94 kB Stupidedi::Versions::Common::ElementTypes::ID - 20.16 kB Stupidedi::Parser::ConstraintTable::Stub - 19.44 kB Stupidedi::Schema::SimpleElementUse - 10.22 kB Stupidedi::Versions::Common::ElementTypes::AN - 6.72 kB Stupidedi::Parser::InstructionTable::NonEmpty - 6.04 kB Regexp - 4.88 kB Stupidedi::Schema::CodeList::Internal - 4.03 kB Stupidedi::Versions::Common::ElementTypes::Nn - 4.03 kB Stupidedi::Versions::Common::ElementTypes::R - 3.2 kB Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 3.18 kB Stupidedi::Schema::SegmentDef - 3.1 kB Stupidedi::Schema::ComponentElementUse - 2.88 kB Stupidedi::Parser::ConstraintTable::Shallowest - 2.88 kB Stupidedi::Parser::FunctionalGroupState - 2.4 kB Stupidedi::Parser::InterchangeState - 2.4 kB Stupidedi::Parser::TransactionSetState - 2.4 kB Stupidedi::Values::TableVal - 2.4 kB Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty - 2.16 kB Cantor::AbsoluteSet - 1.6 kB Stupidedi::Parser::FailureState - 1.6 kB Stupidedi::Reader::SimpleElementTok - 1.44 kB Date - 1.44 kB Stupidedi::Reader::SegmentTok - 1.44 kB Stupidedi::Reader::Separators - 1.44 kB Stupidedi::Schema::SegmentUse - 1.25 kB Integer - 840.0 B Stupidedi::Zipper::DanglingCursor - 800.0 B Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty - -retained objects by gem ------------------------------------ - 893395 stupidedi/lib - 14253 rubygems - 132 cantor-1.2.1 - 3 ruby-2.4.6 - -retained objects by file ------------------------------------ - 181303 /Users/mr/wd/stupidedi/lib/ruby/string.rb - 170539 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb - 115227 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb - 105635 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb - 65404 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb - 50052 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb - 40307 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb - 35029 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb - 25128 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb - 20165 /Users/mr/wd/stupidedi/lib/ruby/object.rb - 20163 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb - 20041 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb - 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb - 14218 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb - 10014 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb - 5190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb - 5089 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb - 5020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb - 935 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb - 591 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb - 425 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb - 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb - 148 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb - 129 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb - 123 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb - 91 /Users/mr/wd/stupidedi/lib/ruby/array.rb - 84 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb - 83 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb - 75 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb - 68 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/failure_state.rb - 68 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb - 63 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb - 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb - 52 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/functional_group_state.rb - 51 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/interchange_state.rb - 51 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb - 46 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb - 41 /Users/mr/wd/stupidedi/lib/ruby/module.rb - 41 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb - 39 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/transaction_set_state.rb - 38 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb - 34 /Users/mr/wd/stupidedi/lib/stupidedi/values/table_val.rb - 29 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/element_defs.rb - 29 /Users/mr/wd/stupidedi/lib/stupidedi/schema/transaction_set_def.rb - 27 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb - 25 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb - 25 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb - 24 /Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb - 22 /Users/mr/wd/stupidedi/lib/stupidedi/interchanges/00400/interchange_def.rb - 22 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb - -retained objects by location ------------------------------------ - 181230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - 105620 /Users/mr/wd/stupidedi/lib/stupidedi/reader/input/delegated_input.rb:26 - 100060 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:276 - 80030 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:347 - 60320 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 30215 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:331 - 30140 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - 30020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:312 - 20141 /Users/mr/wd/stupidedi/lib/ruby/object.rb:17 - 20138 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:42 - 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:111 - 20121 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:112 - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:62 - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:68 - 20020 /Users/mr/wd/stupidedi/lib/stupidedi/schema/loop_def.rb:92 - 15000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:32 - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:29 - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:322 - 14204 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10071 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/abstract_cursor.rb:330 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:48 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/loop_state.rb:53 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/values/loop_val.rb:30 - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:284 - 5040 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/table_state.rb:29 - 5020 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:355 - 5019 /Users/mr/wd/stupidedi/lib/stupidedi/zipper/edited_cursor.rb:47 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:123 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:58 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:280 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:320 - 576 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:63 - 504 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:461 - 393 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction.rb:64 - 160 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:292 - 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 - 122 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 - 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:18 - 84 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:19 - 74 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:45 - 72 /Users/mr/wd/stupidedi/lib/stupidedi/parser/constraint_table.rb:477 - 69 /Users/mr/wd/stupidedi/lib/stupidedi/parser/instruction_table.rb:60 - 67 /Users/mr/wd/stupidedi/lib/ruby/array.rb:85 - 65 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 - 64 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 - 64 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:23 - 60 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 60 /Users/mr/wd/stupidedi/lib/stupidedi/schema/table_def.rb:98 - 54 /Users/mr/.rvm/gems/ruby-2.4.6/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 - 54 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 - -retained objects by class ------------------------------------ - 241030 String - 110070 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::Empty - 106669 Array - 105620 Stupidedi::Reader::Position - 85030 Stupidedi::Versions::Common::ElementTypes::StringVal::Empty - 35211 Stupidedi::Zipper::EditedCursor - 35020 Stupidedi::Versions::Common::ElementTypes::FloatVal::Empty - 30192 Stupidedi::Zipper::Hole - 30160 Stupidedi::Versions::Common::ElementTypes::IdentifierVal::NonEmpty - 25010 Stupidedi::Parser::LoopState - 20100 Stupidedi::Values::SegmentVal - 20020 Stupidedi::Values::LoopVal - 15080 BigDecimal - 15050 Stupidedi::Versions::Common::ElementTypes::StringVal::NonEmpty - 15000 Stupidedi::Versions::Common::ElementTypes::FloatVal::NonEmpty - 5060 Stupidedi::Parser::TableState - 5000 Stupidedi::Values::CompositeElementVal - 5000 Stupidedi::Versions::Common::ElementTypes::FixnumVal::Empty - 504 Stupidedi::Parser::ConstraintTable::Stub - 446 Hash - 415 Stupidedi::Parser::Instruction - 243 Stupidedi::Schema::SimpleElementUse - 239 Stupidedi::Versions::Common::ElementTypes::ID - 192 Class - 122 Stupidedi::Schema::CodeList::Internal - 116 Stupidedi::Versions::Common::ElementTypes::AN - 84 Stupidedi::Parser::InstructionTable::NonEmpty - 80 Stupidedi::Versions::Common::ElementTypes::FixnumVal::NonEmpty - 72 Stupidedi::Parser::ConstraintTable::Shallowest - 60 Stupidedi::Values::TableVal - 54 Cantor::AbsoluteSet - 43 Stupidedi::Schema::ComponentElementUse - 42 Stupidedi::Versions::Common::ElementTypes::Nn - 42 Stupidedi::Versions::Common::ElementTypes::R - 40 Stupidedi::Reader::SimpleElementTok - 36 Stupidedi::Schema::SegmentDef - 33 Module - 30 Stupidedi::Parser::FunctionalGroupState - 30 Stupidedi::Parser::InterchangeState - 30 Stupidedi::Parser::TransactionSetState - 30 Stupidedi::Versions::Common::ElementTypes::TimeVal::NonEmpty - 21 Stupidedi::Zipper::DanglingCursor - 20 Date - 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::Empty - 20 Stupidedi::Interchanges::ElementTypes::SpecialVal::NonEmpty - 20 Stupidedi::Parser::FailureState - 20 Stupidedi::Reader::SegmentDict::Constants - 20 Stupidedi::Reader::SegmentDict::NonEmpty - 20 Stupidedi::Reader::SegmentTok - 20 Stupidedi::Reader::Separators - - -Allocated String Report ------------------------------------ - 261681 "*" - 160990 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 100530 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 150 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:64 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 - 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 80351 "~" - 50230 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 30120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 52929 "1" - 22780 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 15070 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5020 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5020 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 15 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:387 - 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:388 - - 50151 "P" - 45070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:370 - 40 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 48240 "0" - 38090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10140 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 8 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/requirement.rb:108 - - 45005 "CTP" - 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 - 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 45005 "PID" - 25000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 - 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 41906 "\n" - 20120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 20110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 1664 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 7 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:130 - 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb:1168 - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:30 - - 35031 "08" - 15020 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/specification.rb:1752 - - 35006 "LIN" - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 - 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 35003 "PO4" - 15000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:76 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:237 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:262 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:269 - 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 30194 "2" - 25120 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5030 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 10 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:387 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/version.rb:388 - - 27528 "12" - 7500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 25149 "02" - 20090 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:300 - 19 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 25123 "01" - 20100 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 23 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 25066 "03" - 20050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 25037 "F" - 5010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 17 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20149 "" - 20030 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 89 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:302 - - 20100 "N" - 20090 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20092 "C" - 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5010 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20081 "E" - 15070 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20067 "04" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 17 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20063 "05" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 13 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20062 "06" - 15050 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:126 - 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20051 "L" - 15040 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 11 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20018 "O" - 20010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 8 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20010 "EA" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20006 "K" - 20000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20005 "PK" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:370 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20005 "VN" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20004 "CON" - 5000 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 5000 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 4 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 17630 "4" - 17600 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 15173 " " - 15100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 30 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 13 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 15060 "07" - 15030 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:527 - - 15022 "09" - 15010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 12 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10126 "A" - 10100 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 16 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 - - 10119 "3" - 10110 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 9 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10100 "I" - 10080 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/stream_reader.rb:140 - - 10082 "10" - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 30 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:526 - 18 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 - 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 10056 "B" - 10050 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 6 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10053 "11" - 10010 /Users/mr/wd/stupidedi/lib/stupidedi/parser/states/abstract_state.rb:65 - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:525 - 19 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - 4 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 - - 10025 "8" - 10010 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 5 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10023 "V" - 5020 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10000 "000001" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "000002" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "1234321" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "14.80" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - - 10000 "20.80" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/ruby/to_d.rb:31 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - - 10000 "2345432" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10000 "LENS PAPER BOOK 12BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/ruby/blank.rb:13 - 2500 /Users/mr/wd/stupidedi/lib/ruby/string.rb:45 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - -Retained String Report ------------------------------------ - 5001 "08" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "CON" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "EA" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "PK" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 5001 "VN" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 5000 "F" - 5000 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 2500 "000001" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "000002" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "1234321" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "2345432" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "LENS PAPER BOOK 12BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 2500 "LENS PAPER BOOK 24BK/PK" - 2500 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 21 "0001" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 20 "00" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 20 "004010" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 20 "7777777777" - 20 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 20 "SC" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 12 "ST" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 2 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "009" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "14" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "91" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "P" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "PA" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 11 "ZZ" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 10 "*" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 10 "00400" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "004321519" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "004321519IBMP" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "1000 PENNSYLVANIA AVE" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - - 10 "15222" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - - 10 "832" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "999999001" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 ">" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 10 "PC" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "PITTSBURGH" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/reader/token_reader.rb:334 - - 10 "SPECIAL LABS" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:359 - - 10 "U" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "X" - 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:286 - - 10 "~" - 10 /Users/mr/wd/stupidedi/lib/ruby/string.rb:16 - - 4 "To specify identifying information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 4 "To specify pertinent dates and times" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "/Users/mr/wd/stupidedi/lib" - 3 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:130 - - 3 "To define the end of an interchange of zero or more functional groups and interchange-related control segments" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To describe a product or process in coded or free-form format" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To identify a party by type of organization, name, and code" - 2 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - 1 /Users/mr/.rvm/rubies/ruby-2.4.6/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54 - - 3 "To indicate the beginning of a functional group and to provider control information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the beginning of the Price/Sales Catalog Transaction Set and specify catalog purpose and number information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the end of a functional group and provider control information" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the end of the transaction set and provide the count of the transmitted segments (including the beginning (ST) and ending (SE) segments)" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - - 3 "To indicate the start of a transaction set and assign a control number" - 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:86 - diff --git a/prof/memprof-pp.rb b/prof/memprof-pp.rb index 01dddfefe..432a1f6f7 100755 --- a/prof/memprof-pp.rb +++ b/prof/memprof-pp.rb @@ -22,6 +22,9 @@ MemoryProfiler.report do ARGV.each do |path| + mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 + puts "Startup: %0.2d MiB" % mem + # Reading the entire input at once is slightly faster than streaming # from a file handle. # @@ -30,11 +33,17 @@ # reader = Stupidedi::Reader.build(File.read(path)) parser, = parser.read(reader) + + mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 + puts "Finish : %0.2d MiB" % mem end end.pretty_print(to_file: "prof/memprof-pp-#{start.strftime("%Y%m%dT%H%M%S")}.txt", color_output: false, retained_strings: 100, allocated_strings: 100, detailed_report: true, scale_bytes: true) +mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 +puts "Reports: %0.2d MiB" % mem + stop = Time.now $stderr.puts "%0.3f seconds" % (stop - start) diff --git a/prof/mkfile.rb b/prof/mkfile.rb index 14aab3846..10c4574c0 100755 --- a/prof/mkfile.rb +++ b/prof/mkfile.rb @@ -45,12 +45,15 @@ buffer = StringIO.new -if ARGV.delete('--small') +if ARGV.delete('--tiny') + n = 1 + m = 1 +elsif ARGV.delete('--small') n = 10 m = 250 else - n = 500 - m = 500 + n = 100 + m = 250 end n.times do From 70a253b725cd4ac5fed74ce26e8361883e505900 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 16 Jun 2019 21:20:24 -0500 Subject: [PATCH 07/38] Eliminate 9.9MB of unused MatchData due to =~ --- lib/ruby/module.rb | 2 +- lib/ruby/regexp.rb | 12 ++++++++++++ lib/ruby/string.rb | 4 ++-- lib/ruby/to_d.rb | 2 +- lib/stupidedi.rb | 1 + lib/stupidedi/parser/builder_dsl.rb | 4 ++-- lib/stupidedi/reader.rb | 15 +++++++++++---- lib/stupidedi/reader/token_reader.rb | 2 +- .../versions/common/element_types/an.rb | 9 ++++++++- .../versions/common/element_types/id.rb | 4 ++++ .../versions/common/element_types/tm.rb | 5 ++++- .../reader/input/delegated_input_spec.rb | 18 +++++++++--------- 12 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 lib/ruby/regexp.rb diff --git a/lib/ruby/module.rb b/lib/ruby/module.rb index f174000d5..3c4e7cad3 100644 --- a/lib/ruby/module.rb +++ b/lib/ruby/module.rb @@ -40,7 +40,7 @@ def def_delegators(target, *methods) file, line, = Stupidedi.caller for m in methods - if m.to_s =~ /=$/ + if m.to_s.end_with?("=") class_eval(<<-RUBY, file, line.to_i - 1) def #{m}(value) #{target}.#{m}(value) diff --git a/lib/ruby/regexp.rb b/lib/ruby/regexp.rb new file mode 100644 index 000000000..ae7c89a2b --- /dev/null +++ b/lib/ruby/regexp.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +module Stupidedi + module Refinements + unless //.respond_to?(:match?) + refine Regexp do + def match?(string) + !!(self =~ string) + end + end + end + end +end diff --git a/lib/ruby/string.rb b/lib/ruby/string.rb index 258ae22e5..ac50fbb6a 100644 --- a/lib/ruby/string.rb +++ b/lib/ruby/string.rb @@ -145,8 +145,8 @@ def ==(other) eql?(other) or repro == other end - def =~(other) - repro =~ other + def =~(pattern) + pattern.match?(repro) end def at(n) diff --git a/lib/ruby/to_d.rb b/lib/ruby/to_d.rb index 117a20920..37f373671 100644 --- a/lib/ruby/to_d.rb +++ b/lib/ruby/to_d.rb @@ -28,7 +28,7 @@ def to_d # # @return [BigDecimal] def to_d - if BIGDECIMAL =~ self + if BIGDECIMAL.match?(self) BigDecimal(to_s) else raise ArgumentError, "#{inspect} is not a valid number" diff --git a/lib/stupidedi.rb b/lib/stupidedi.rb index 59e260cf6..869fe247d 100644 --- a/lib/stupidedi.rb +++ b/lib/stupidedi.rb @@ -12,6 +12,7 @@ $:.unshift(File.expand_path("..", __FILE__)) +require "ruby/regexp" require "ruby/array" require "ruby/blank" require "ruby/exception" diff --git a/lib/stupidedi/parser/builder_dsl.rb b/lib/stupidedi/parser/builder_dsl.rb index 559a1903b..37794db1b 100644 --- a/lib/stupidedi/parser/builder_dsl.rb +++ b/lib/stupidedi/parser/builder_dsl.rb @@ -29,7 +29,7 @@ def initialize(machine, strict = true) end def respond_to_missing?(name, include_private = false) - SEGMENT_ID =~ name.to_s || super + SEGMENT_ID.match?(name.to_s) || super end def strict? @@ -99,7 +99,7 @@ def segment!(name, position, *elements) private def method_missing(name, *args) - if SEGMENT_ID =~ name.to_s + if SEGMENT_ID.match?(name.to_s) segment!(name, Reader::Position.caller(2), *args) else super diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index 9cf1fa819..2adabc2ae 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -89,14 +89,21 @@ def is_extended_character?(character) H_EXTENDED.include?(character) end - # @private - def has_extended_characters?(string) - R_EXTENDED =~ string + if R_EXTENDED.respond_to?(:match?) + # @private + def has_extended_characters?(string) + R_EXTENDED.match?(string) + end + else + # @private + def has_extended_characters?(string) + R_EXTENDED.match?(string) + end end # @private def has_control_characters?(string) - #_CONTROL =~ string + #_CONTROL.match?(string) end # @return [Character] diff --git a/lib/stupidedi/reader/token_reader.rb b/lib/stupidedi/reader/token_reader.rb index 1d6f44b91..c64f31a98 100644 --- a/lib/stupidedi/reader/token_reader.rb +++ b/lib/stupidedi/reader/token_reader.rb @@ -260,7 +260,7 @@ def read_segment_id # We only arrive here if {character} is a delimiter, or if we read # three characters into {buffer} and an additional into {character} - if buffer =~ SEGMENT_ID + if SEGMENT_ID.match?(buffer) remainder = advance(position - 1) case character diff --git a/lib/stupidedi/versions/common/element_types/an.rb b/lib/stupidedi/versions/common/element_types/an.rb index ec7a376ee..1713e0ef5 100644 --- a/lib/stupidedi/versions/common/element_types/an.rb +++ b/lib/stupidedi/versions/common/element_types/an.rb @@ -100,6 +100,9 @@ def strftime(format, value) end end + # @private + THING_DASH_THING = /^[^-]+-[^-]+$/ + # Parse the string into a date (Date), date time (Time), or a range # of either according to the given format specifier. # @@ -112,7 +115,7 @@ def strptime(format, value) f, g = AN::DATE_FORMAT_RANGE.at(format) a, b = value.split("-", 2) - unless value =~ /^[^-]+-[^-]+$/ + unless THING_DASH_THING.match?(value) raise TypeError, "expected a String with format #{f}-#{g}, but got #{value.inspect}" end @@ -214,6 +217,10 @@ class Valid < StringVal :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, :empty?, :blank? + if "".respond_to?(:match?) + def_delegators :value, :match? + end + extend Operators::Wrappers wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, :center, :ljust, :rjust, :chomp, :delete, :tr, :tr_s, diff --git a/lib/stupidedi/versions/common/element_types/id.rb b/lib/stupidedi/versions/common/element_types/id.rb index 016e6a51f..42115b664 100644 --- a/lib/stupidedi/versions/common/element_types/id.rb +++ b/lib/stupidedi/versions/common/element_types/id.rb @@ -137,6 +137,10 @@ class Valid < IdentifierVal :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, :empty?, :blank? + if "".respond_to?(:match?) + def_delegators :value, :match? + end + # (string any* -> StringVal) extend Operators::Wrappers wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, diff --git a/lib/stupidedi/versions/common/element_types/tm.rb b/lib/stupidedi/versions/common/element_types/tm.rb index 318639490..ba9a78fba 100644 --- a/lib/stupidedi/versions/common/element_types/tm.rb +++ b/lib/stupidedi/versions/common/element_types/tm.rb @@ -287,6 +287,9 @@ def empty(usage, position) self::Empty.new(usage, position) end + # @private + FOUR_DIGITS = /^\d{4,}$/ + # @return [TimeVal] def value(object, usage, position) if object.is_a?(TimeVal) @@ -295,7 +298,7 @@ def value(object, usage, position) self::Empty.new(usage, position) elsif object.is_a?(String) or object.is_a?(StringVal) return self::Invalid.new(object, usage, position) \ - unless object =~ /^\d{4,}$/ + unless FOUR_DIGITS.match?(object) hour = object.to_s.slice(0, 2).to_i minute = object.to_s.slice(2, 2).try{|mm| mm.to_i unless mm.blank? } diff --git a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb b/spec/lib/stupidedi/reader/input/delegated_input_spec.rb index 69c81cda7..95c890eb8 100644 --- a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb +++ b/spec/lib/stupidedi/reader/input/delegated_input_spec.rb @@ -115,18 +115,18 @@ def mkinput(*args) end end - pending "when n elements are available" do - it "increments the offset" do + context "when n elements are available" do + pending "increments the offset" do expect(mkinput("abc", 10).drop(2).offset).to be == 12 end - pending "increments the offset" do - with(:size, between(0, 25)) do - [string, between(0, size), between(10, 1000)] - end - end.check do |s, n, offset| - expect(mkinput(s, offset).drop(n).offset).to be == offset + n - end + # property "increments the offset" do + # with(:size, between(0, 25)) do + # [string, between(0, size), between(10, 1000)] + # end + # end.check do |s, n, offset| + # expect(mkinput(s, offset).drop(n).offset).to be == offset + n + # end it "returns an input with the first n elements removed" do expect(mkinput(%w(a b c d)).drop(3)).to be == %w(d) From 05ef585aeffe2db1e415361b2b61abea8cb6af80 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 17 Jun 2019 01:02:00 -0500 Subject: [PATCH 08/38] Eliminate 10.9MB by using a mutable buffer --- lib/stupidedi/reader/token_reader.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/stupidedi/reader/token_reader.rb b/lib/stupidedi/reader/token_reader.rb index c64f31a98..f8990ae90 100644 --- a/lib/stupidedi/reader/token_reader.rb +++ b/lib/stupidedi/reader/token_reader.rb @@ -54,14 +54,14 @@ def consume_prefix(s) return success(self) if s.empty? position = 0 - buffer = "" + buffer = String.new("") while @input.defined_at?(position) character = @input.at(position) position += 1 unless is_control?(character) - buffer = buffer + character + buffer << character if s.length == buffer.length if s == buffer @@ -106,7 +106,8 @@ def consume(s) unless is_control?(character) # Slide the "window" forward one character - buffer = buffer.slice(1..-1) + character + buffer.slice!(0) + buffer << character end position += 1 @@ -234,8 +235,7 @@ def read_component_elements(repeatable = false) # @return [Either>] def read_segment_id position = 0 - buffer = String.new("") # Intentionally using a mutable here, to - # avoid allocations when building buffer below + buffer = String.new("") while true unless @input.defined_at?(position) @@ -301,7 +301,7 @@ def read_delimiter # @return [Either>] def read_simple_element(repeatable = false) position = 0 - buffer = "" + buffer = String.new("") while @input.defined_at?(position) character = @input.at(position) @@ -332,7 +332,7 @@ def read_simple_element(repeatable = false) # # @todo: Read this as data but sound the alarms end - buffer = buffer + character + buffer << character end failure("reached end of input without finding a simple data element") @@ -341,7 +341,7 @@ def read_simple_element(repeatable = false) # @return [Either>] def read_component_element(repeatable = false) position = 0 - buffer = "" + buffer = String.new("") while @input.defined_at?(position) character = @input.at(position) @@ -368,7 +368,7 @@ def read_component_element(repeatable = false) end end - buffer = buffer + character + buffer << character end failure("reached end of input without finding a component data element") From ca4728ec15948316d0f7d0d97945f376e9be1440 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 17 Jun 2019 01:02:45 -0500 Subject: [PATCH 09/38] Travis CI's 2.7.0 preview support seems broken --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6a0a79354..9041e0932 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,3 @@ rvm: - 2.5.1 - 2.5.3 - 2.6.3 -- 2.7.0-preview1 From 4452cd9eeaf5d08d053f243136c4a9e720dc2e9a Mon Sep 17 00:00:00 2001 From: kputnam Date: Sat, 22 Jun 2019 03:47:50 -0500 Subject: [PATCH 10/38] Rewrite tokenization code to minimize String allocations --- lib/ruby/string.rb | 103 --- lib/stupidedi/parser/generation.rb | 103 ++- lib/stupidedi/reader.rb | 23 +- lib/stupidedi/reader/input.rb | 33 - lib/stupidedi/reader/pointer.rb | 622 +++++++++++++++ lib/stupidedi/reader/position.rb | 145 ++-- lib/stupidedi/reader/result.rb | 172 ---- lib/stupidedi/reader/separators.rb | 10 + lib/stupidedi/reader/stream_reader.rb | 173 ---- lib/stupidedi/reader/token_reader.rb | 466 ----------- lib/stupidedi/reader/tokenizer.rb | 522 ++++++++++++ .../reader/tokens/component_element_tok.rb | 16 +- .../reader/tokens/composite_element_tok.rb | 20 +- lib/stupidedi/reader/tokens/ignored_tok.rb | 59 ++ .../reader/tokens/repeated_element_tok.rb | 7 - lib/stupidedi/reader/tokens/segment_tok.rb | 18 +- .../reader/tokens/simple_element_tok.rb | 19 +- spec/lib/stupidedi/reader/failure_spec.rb | 62 -- .../reader/input/delegated_input_spec.rb | 282 ------- .../stupidedi/reader/stream_reader_spec.rb | 44 - spec/lib/stupidedi/reader/success_spec.rb | 28 - .../lib/stupidedi/reader/token_reader_spec.rb | 755 ------------------ 22 files changed, 1400 insertions(+), 2282 deletions(-) delete mode 100644 lib/stupidedi/reader/input.rb create mode 100644 lib/stupidedi/reader/pointer.rb delete mode 100644 lib/stupidedi/reader/result.rb delete mode 100644 lib/stupidedi/reader/stream_reader.rb delete mode 100644 lib/stupidedi/reader/token_reader.rb create mode 100644 lib/stupidedi/reader/tokenizer.rb create mode 100644 lib/stupidedi/reader/tokens/ignored_tok.rb delete mode 100644 spec/lib/stupidedi/reader/failure_spec.rb delete mode 100644 spec/lib/stupidedi/reader/input/delegated_input_spec.rb delete mode 100644 spec/lib/stupidedi/reader/stream_reader_spec.rb delete mode 100644 spec/lib/stupidedi/reader/success_spec.rb delete mode 100644 spec/lib/stupidedi/reader/token_reader_spec.rb diff --git a/lib/ruby/string.rb b/lib/ruby/string.rb index ac50fbb6a..e0ed9a6bc 100644 --- a/lib/ruby/string.rb +++ b/lib/ruby/string.rb @@ -87,106 +87,3 @@ def join end end end - -class Substring - attr_reader :whole - attr_reader :m - attr_reader :n - - def initialize(whole, m = 0, n = whole.length - 1) - if n < m - 1 - n = m - 1 - end - - if m < 0 - raise ArgumentError, "start index must be non-negative" - end - - if n >= whole.length - raise ArgumentError, "end index must not exceed underlying String length" - end - - @whole, @m, @n = whole, m, n - - # w = @whole[@m, [@n - @m + 1, 60].min] - # p [w, " "*(60 - w.length), @m, @n] - end - - # @note: Avoid calling this unless needed because it allocates another String - def repro - if @m <= @n - @whole[@m..@n] - else - "" - end - end - - def to_str - repro - end - - def to_s - repro - end - - def length - @n - @m + 1 - end - - def empty? - @m > @n - end - - def inspect - repro.inspect - end - - def ==(other) - eql?(other) or repro == other - end - - def =~(pattern) - pattern.match?(repro) - end - - def at(n) - raise ArgumentError, "n must be positive" if n < 0 - @whole[@m + n] unless n > @n - @m - end - - def defined_at?(n) - n <= @n - @m - end - - def take(n) - raise ArgumentError, "n must be positive" if n < 0 - Substring.new(@whole, @m, [@m + n - 1, @whole.length - 1].min) - end - - def drop(n) - raise ArgumentError, "n must be positive" if n < 0 - Substring.new(@whole, [@m + n, @n + 1].min, @n) - end - - def count(other) - k, m = 0, @m - 1 - - while true - m = @whole.index(other, m + 1) - m and m <= @n or break - k += 1 - end - - k - end - - def index(other, m = 0) - z = @whole.index(other, @m + m) - z - @m if z and z <= @n - end - - def rindex(other, n = @whole.length - 1) - z = @whole.rindex(other, [@m + n, @n].min) - z - @m if z and z >= @m - end -end diff --git a/lib/stupidedi/parser/generation.rb b/lib/stupidedi/parser/generation.rb index f70a1ecc0..87206f104 100644 --- a/lib/stupidedi/parser/generation.rb +++ b/lib/stupidedi/parser/generation.rb @@ -22,40 +22,67 @@ module Generation # via the {StateMachine} to aide diagnosis. # # @return [(StateMachine, Reader::Result)] - def read(reader, options = {}) - limit = options.fetch(:nondeterminism, 1) - machine = self - reader_e = reader.read_segment - - while reader_e.defined? - reader_e = reader_e.flatmap do |segment_tok, reader_| - machine, reader__ = - machine.insert(segment_tok, false, reader_) - - if machine.active.length <= limit - reader__.read_segment + def read(tokenizer, options = {}) + drain(tokenizer) + end + + # @return [StateMachine] + def drain(tokenizer) + machine_ = machine.dup + + tokenizer.each do |token| + case token + when ErrorTok + if block_given? + yield token # TODO: Should user be able to signal something to us? else - matches = machine.active.map do |m| + # + end + + when IgnoreTok + if block_given? + yield token # TODO: Should user be able to signal something to us? + else + # + end + + when SegmentTok + machine_.insert!(token, false, tokenizer) + + if machine_.active.length > limit + matches = machine_.active.map do |m| if segment_use = m.node.zipper.node.usage - "SegmentUse(#{segment_use.position}, #{segment_use.id}, - #{segment_use.requirement.inspect}, #{segment_use.repeat_count.inspect})".join + "SegmentUse(%s, %s, %s, %s)" % [segment_use.position, + segment_use.id, + segment_use.requirement.inspect, + segment_use.repeat_count.inspect] else m.node.zipper.node.inspect end end.join(", ") - return machine, - Reader::Result.failure("too much non-determinism: #{matches}", reader__.input, true) + raise ... end end end + end + + # @return [StateMachine] + def insert(segment_tok, strict, tokenizer) + StateMachine.new(@config, insert_(segment_tok, strict, tokenizer)) + end - return machine, reader_e + # @return self + def insert!(segment_tok, strict, tokenizer) + @active = insert_(segment_tok, strict, tokenizer) + self end - # @return [(StateMachine, Reader::TokenReader)] - def insert(segment_tok, strict, reader) - active = @active.flat_map do |zipper| + private + + # @return [Array>] + def insert_(segment_tok, strict, tokenizer) + @active.flat_map do |zipper| state = zipper.node instructions = state.instructions.matches(segment_tok, strict, :insert) @@ -63,14 +90,19 @@ def insert(segment_tok, strict, reader) zipper.append(FailureState.mksegment(segment_tok, state)).cons else instructions.map do |op| - successor = execute(op, zipper, reader, segment_tok) - reader = update_reader(op, reader, successor) + successor = execute(op, zipper, tokenizer, segment_tok) + + # We might be moving up or down past the interchange or functional + # group envelope, which determine the separators and segment_dict + unless op.push.nil? and (op.pop_count.zero? or tokenizer.stream?) + tokenizer.separators = successor.node.separators + tokenizer.segment_dict = successor.node.segment_dict + end + successor end end end - - return StateMachine.new(@config, active), reader end # Three things change together when executing an {Instruction}: @@ -85,7 +117,7 @@ def insert(segment_tok, strict, reader) # two and are also updated using a zipper # # @return [AbstractCursor] - def execute(op, zipper, reader, segment_tok) + def execute(op, zipper, tokenizer, segment_tok) table = zipper.node.instructions # 1. value = zipper.node.zipper # 2. state = zipper # 3. @@ -116,32 +148,19 @@ def execute(op, zipper, reader, segment_tok) parent = state.node.copy \ :zipper => value, :children => [], - :separators => reader.try(&:separators), - :segment_dict => reader.try(&:segment_dict), + :separators => tokenizer.try(&:separators), + :segment_dict => tokenizer.try(&:segment_dict), :instructions => table.pop(op.pop_count).drop(op.drop_count) # Note, `state` is a cursor pointing at a state, while `parent` # is an actual state state = state.append(parent) unless state.root? - # Note, eg, op.push == TableState; op.push.push == TableState.push + # Note, op.push == TableState; op.push.push == TableState.push op.push.push(state, parent, segment_tok, op.segment_use, @config) end end - def update_reader(op, reader, successor) - # We might be moving up or down past the interchange or functional - # group envelope, which control the set of separators and the set - # of all possible segments - unless op.push.nil? and (op.pop_count.zero? or reader.stream?) - unless reader.separators.eql?(successor.node.separators) \ - and reader.segment_dict.eql?(successor.node.segment_dict) - reader.copy \ - :separators => successor.node.separators, - :segment_dict => successor.node.segment_dict - end - end || reader - end end end end diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index 2adabc2ae..0d11ef612 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -4,26 +4,23 @@ module Stupidedi using Refinements module Reader - autoload :Result, "stupidedi/reader/result" - autoload :Success, "stupidedi/reader/result" - autoload :Failure, "stupidedi/reader/result" - - autoload :StreamReader, "stupidedi/reader/stream_reader" - autoload :TokenReader, "stupidedi/reader/token_reader" autoload :Separators, "stupidedi/reader/separators" autoload :SegmentDict, "stupidedi/reader/segment_dict" + autoload :IgnoredTok, "stupidedi/reader/tokens/ignored_tok" + autoload :SegmentTok, "stupidedi/reader/tokens/segment_tok" + autoload :SimpleElementTok, "stupidedi/reader/tokens/simple_element_tok" autoload :ComponentElementTok, "stupidedi/reader/tokens/component_element_tok" autoload :CompositeElementTok, "stupidedi/reader/tokens/composite_element_tok" autoload :RepeatedElementTok, "stupidedi/reader/tokens/repeated_element_tok" - autoload :SegmentTok, "stupidedi/reader/tokens/segment_tok" - autoload :SimpleElementTok, "stupidedi/reader/tokens/simple_element_tok" - autoload :Input, "stupidedi/reader/input" - autoload :Position, "stupidedi/reader/position" - autoload :AbstractInput, "stupidedi/reader/input/abstract_input" - autoload :DelegatedInput, "stupidedi/reader/input/delegated_input" - autoload :FileInput, "stupidedi/reader/input/file_input" + autoload :Position, "stupidedi/reader/position" + autoload :NoPosition, "stupidedi/reader/position" + autoload :Tokenizer, "stupidedi/reader/tokenizer" + + autoload :Pointer, "stupidedi/reader/pointer" + autoload :ArrayPtr, "stupidedi/reader/pointer" + autoload :StringPtr, "stupidedi/reader/pointer" # @private # @return [Regexp] diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb deleted file mode 100644 index 1d38b1222..000000000 --- a/lib/stupidedi/reader/input.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - module Input - end - - class << Input - # @group Constructors - ######################################################################### - - # @return [Input] - def build(o, *args) - case o - when AbstractInput - o - when IO - FileInput.new(o, *args) - when String - DelegatedInput.new(Substring.new(o), *args) - when Array - DelegatedInput.new(o, *args) - else - raise TypeError - end - end - - # @endgroup - ######################################################################### - end - end -end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb new file mode 100644 index 000000000..6a62e13a7 --- /dev/null +++ b/lib/stupidedi/reader/pointer.rb @@ -0,0 +1,622 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + # + # Provides a "view" into a continuous substring of a larger string, without + # allocating a new string (or whatever the type of the whole is). This saves + # memory when many substrings are needed, or long substrings are needed. It + # also makes #take, #drop, #[a,b], #[a..b] and #split_at run in constant time + # and space rather than O(n). + # + # Each instance requires 40 bytes (in YARV), compared to making an actual + # substring, which consumes 40 bytes plus the number of bytes above 20 + # which will be copied to the result. It also takes some CPU time to copy + # from one string to the other. + # + # Some string operations (examples listed above) can be performed directly + # on the pointer to delay the need to allocate new strings. Allocations + # will happen automatically as needed, but you can also create a String by + # calling `#reify`. + # + # NOTE: Pointer is the type which represents storage of type S + # that has items of type E. For example, + # + # Pointer # represents an array of integers + # Pointer # represents a string + # + class Pointer + + # When this object is not `#frozen?`, only one pointer references it. + # In that case, certain operations can be optimized by destructively + # updating `@storage` in place. However, when another pointer shares + # with us, `@storage` will be frozen. + # + # @return [S] + attr_reader :storage + + # @return [Integer] + attr_reader :offset + + # @return [Integer] + attr_reader :length + + def initialize(storage, offset=0, length=storage.length) + raise ArgumentError, "offset must be non-negative" if offset < 0 + raise ArgumentError, "length must be non-negative" if length < 0 + raise ArgumentError, "given length cannot exceed storage length" if length > storage.length + + @storage, @offset, @length = + storage, offset, length + end + + # @return [String] + def inspect + "#<%s%s@storage=0x%s @offset=%d @length=%d>" % + [self.class.name.split("::").last, + @storage.frozen? ? "-" : " ", + (@storage.object_id << 1).to_s(16), @offset, @length] + end + + # Convert this pointer back into a String (or whatever the underlying + # type is). + # + # If this points to the entire length of the underlying object, then that + # object may be returned without any allocations. Otherwise, the `#[a, b]` + # method is called on the object. For most types, this will allocate a + # new object and copy items into it. + # + # @return [S] + def reify(always_allocate = false) + if @storage.frozen? \ + and @offset == 0 \ + and @length == @storage.length \ + and not always_allocate + $stderr.puts "reify: no allocation" + @storage + else + $stderr.puts "reify: allocate[#@offset, #@length]" + @storage[@offset, @length] + end + end + + # @return [Pointer] + def reset + self.class.new(@storage.freeze, 0, @storage.length) + end + + # @return [self] + def reset! + @offset = 0 + @length = @storage.length + self + end + + # @return [Boolean] + def empty? + @length <= 0 + end + + # Return the first element. If {empty?}, `nil` will be returned. + # + # @return [E] + def head + @storage[@offset] if @length > 0 + end + + # Return a new pointer with the first item removed. + # + # @return [Pointer] + def tail + drop(1) + end + + # Return the last element. If {empty?}, `nil` will be returned. + # + # @return [E] + def last + @storage[@offset + @length] if @length > 0 + end + + # True if `#at(n)` is defined. + # + # @return [Boolean] + def defined_at?(n) + raise ArgumentError, "argument must be non-negative" if 0 > n + n <= @length + end + + # Return the nth element. + # + # @return [E] + def at(n) + raise ArgumentError, "argument must be non-negative" if 0 > n + @storage[@offset + n] if @length > n + end + + # When given a range or a start index and length, returns a new pointer + # that spans the given indices. When given a single index, returns the + # single element at that index. + # + # cursor[n] == cursor.at(n) + # cursor[a, b] == cursor.drop(a).take(b) + # cursor[a...b] == cursor.drop(a).take(b) + # cursor[a..b] == cursor.drop(a).take(b+1) + # + # @return [Pointer | E] + def [](offset, length=nil) + if length.present? + raise ArgumentError, "offset must be non-negative" if 0 > offset + raise ArgumentError, "length must be non-negative" if 0 > length + return nil if offset >= @length + + if length > @length - offset + length = @length - offset + end + + self.class.new(@storage.freeze, @offset + offset, length) + + elsif offset.kind_of?(Range) + unless offset.last + length = @storage.length - offset.first + else + length = offset.last - offset.first + length += 1 unless offset.exclude_end? + end + self[offset.first, length] + + else + raise ArgumentError, "argument must be non-negative" if 0 > offset + @storage[@offset + offset] if @length > offset + end + end + + alias_method :slice, :[] + + # Return a new pointer, skipping the first n items. + # + # @return [Pointer] + def drop(n) + raise ArgumentError, "argument must be non-negative" if n < 0 + n = @length if n > @length + self.class.new(@storage.freeze, @offset + n, @length - n) + end + + # Return a new pointer, skipping the first n items, and destructively + # update this pointer to end at the nth element. + # + # x = Pointer.new("eyeball") + # x.drop!(5) == "ll" + # x == "eyeba" + # + # @return [Pointer] + def drop!(n) + raise ArgumentError, "argument must be non-negative" if n < 0 + n = @length if n > @length + offset = @offset + n + length = @length - n + suffix = self.class.new(@storage.freeze, offset, length) + + # We become the prefix that ends where suffix starts + @length = n + + suffix + end + + # Return a new pointer spanning only the first n items. + # + # @return [Pointer] + def take(n) + raise ArgumentError, "argument must be non-negative" if n < 0 + n = @length if n > @length + self.class.new(@storage.freeze, @offset, n) + end + + # Return a new pointer spanning only the first n items, and destructively + # update this pointer to start at the (n+1)th element. + # + # x = Pointer.new("eyeball") + # x.take!(5) == "eyeba" + # x == "ll" + # + # @return [Pointer] + def take!(n) + raise ArgumentError, "argument must be non-negative" if n < 0 + n = @length if n > @length + prefix = self.class.new(@storage.freeze, @offset, n) + + # We become the suffix starts where prefix ends + @offset += n + @length -= n + + prefix + end + + # Split the Pointer in two at the given position by creating two new + # Flyweights. + # + # @param [Integer] n number of items at which to split (`n > 0`) + # + # @return [Array(Pointer, Pointer)] + def split_at(n) + [take(n), drop(n)] + end + + # Concatenate two flyweights to form a third. + # + # When the two flyweights are backed by the same storage object, and the + # first pointer ends where the second begins, no allocation is needed + # (only extending `@length`). Otherwise, at least partial copies of each's + # `@storage` are made to create a third `@storage`. + # + # @return [Pointer] + def +(other) + if @storage.eql?(other.storage) and @offset + @length == other.offset + self.class.new(@storage.freeze, @offset, @length + other.length) + else + # It doesn't make much sense to allocate two new operands and then + # use `+` to allocate a third for the result. + # + # TODO: Should this be a new Pointer? Depends on how the result + # will be used. If more concatenation is done, then it's a waste, + # and slightly worse than plain String + String. + reify(true) << other.reify + end + end + + # @return [Boolean] + def ==(other) + if self.class == other.class + if @storage.eql?(other.storage) + @offset == other.offset and @length == other.length + else + length == other.length and reify == other.reify + end + else + length == other.length and reify == other + end + end + end + + class << Pointer + def build(object) + case object + when String + StringPtr.new(object) + when Array + ArrayPtr.new(object) + when Pointer + object + else + raise TypeError, "object must respond to []" \ + unless object.respond_to?(:[]) + + raise TypeError, "object must respond to length" \ + unless object.respond_to?(:length) + + Pointer.new(object) + end + end + end + + class StringPtr < Pointer + ANCHORED_A = /(? length + offset = @offset + offset + m = pattern.match(@storage, offset) + + if m and m.begin(0) <= @offset + @length + if m.end(0) <= @offset + @length + # The entire match is inside the bounds + true + else + # The match starts within bounds but ends outside, so we need to + # to allocate a new String of the correct length and try again + @storage[m.begin(0), @offset + @length - m.begin(0)].match?(pattern) + end + else + false + end + end + + # We can't correctly implement `String#match` -alike here, because it + # returns a {MatchData} that includes offsets and indexes relative to + # the whole @storage, not the start of this pointer string. + # + # We can't update the MatchData to have adjusted offsets, but we return + # the offset to let the caller make adjustments when needed. + # + # NOTE: The offset argument controls where the regex engine begins, but + # it doesn't change which part of the string anchors match like ^ $ \A + # \Z and \z. For example, match(/^/, n) with n > 0, will never succeed + # because ^ is at offset 0 of the substring. This behavior is the same + # as String#match. + # + # @return [MatchData, Integer] + def match_(pattern, offset) + if @offset != 0 and ANCHORED_A.match?(pattern.inspect) + # We can't match on @storage.directly unless our @offset is 0, + # because String#match(/^./, n) never matches unless n is 0. + return [reify.match(pattern, offset), 0] + end + + if @offset + @length != @storage.length and ANCHORED_Z.match?(pattern.inspect) + # Because the pattern is anchored to the end, we can't match on + # @storage directly, unless our end is also the end of @storage. + return [reify.match(pattern, offset), 0] + end + + offset = @length if offset > length + offset = @offset + offset + m = pattern.match(@storage, offset) + + if m and m.begin(0) <= @offset + @length + if m.end(0) <= @offset + @length + # The entire match is inside the bounds + [m, -@offset] + else + # The match starts within bounds but ends outside, so we need to + # to allocate a copy. We minimize the cost by not copying all of + # @storage[@offset, @length] when the match started after @offset + tail = @storage[m.begin(0), @offset + @length - m.begin(0)] + n = tail.match(pattern, offset - m.begin(0)) + [n, m.begin(0)] + end + end + end + + # Return offset of the first match, or `nil` if no match occurs. + # + # @return [Integer] + def =~(pattern) + m, offset = match_(pattern, 0) + m and m.begin(0) + offset + end + + # Return offset of first occurence of `other` that starts at or after + # the given `offset`. If not found, then `nil` is returned. + # + # @return [Integer] + def index(other, offset=0) + raise ArgumentError, "offset must be non-negative" if offset < 0 + return nil if offset > @length + + if other.is_a?(Regexp) + m, offset = self.match_(other, offset) + m and m.begin(0) + offset + else + n = @storage.index(other, @offset + offset) + n - @offset if n and n + other.length <= @offset + @length + end + end + + # Return offset of last occurence of `other` that starts at or before + # the given `offset`. If not found, then `nil` is returned. + # + # @return [Integer] + def rindex(other, offset=@length) + raise ArgumentError, "offset must be non-negative" if offset < 0 + offset = @length if offset > @length + + if other.is_a?(Regexp) + if n = @storage.rindex(other, @offset + offset) + if n + $&.length <= @offset + @length + n - @offset + else + # The match starts within bounds but ends outside, so we need to + # to allocate a new String of the correct length and try again + reify.rindex(other, offset) + end + else + # Pattern wasn't found anywhere before the offset, so that's that! + end + else + n = @storage.rindex(other, @offset + offset) + n - @offset if n and n >= @offset and n + other.length <= @offset + @length + end + end + + # Return number of occurrences of given character. + # + # NOTE: This only supports a subset of functionality of String#count. + # Namely, it only works for a single character. + # + # @return [Integer] + def count(char) + count, offset = 0, @offset + + while true + offset = @storage.index(char, offset) + offset and offset <= @offset + @length or break + offset += 1 + count += 1 + end + + count + end + + # If two flyweights share the same storage and are contiguous (one ends + # where the other starts), then string concatenation can be optimized. + # + # In the case where the operand is a string and happens to be a prefix + # of @storage[@offset+@length..], then we can also simply extend the + # length without allocating another string. + # + # For example: + # x = Pointer.build("abc xyz") + # y = x.drop(0) + # + # y << x.take(2) # no allocation, returns self + # y << "c x" # no allocation, returns self + # y << "mno" # allocates a new String for @storage, returns self + # + # @return [self] + def <<(other) + if other.is_a?(self.class) + if @storage.eql?(other.storage) and @offset + @length == other.offset + @length += other.length + elsif @storage.frozen? + # Other flyweights are sharing our storage. We need to make our + # own copy now. Be sure `reify` gives back a copy, not the original. + @storage = reify(true) + @storage << other.reify + @length += other.length + @offset = 0 + else + # Surely no one will notice if we destructively update @storage + @storage << other.reify + @length += other.length + end + + # NOTE: There doesn't seem to be a string comparison function in Ruby + # that allows the comparison to start a given offset. That means we'd + # have to allocate and copy from @storage a length of `other.length`. + # + # @storage[@offset + @length, other.length] == other + # + # @storage.index(other, @offset + @length) == n doesn't allocate + # memory, but there is no way to abort the search early. + # + elsif @storage.length - @offset - @length >= other.length \ + and @storage.index(other, @offset + @length) == @offset + @length + @length += other.length + elsif @storage.frozen? + # Other flyweights are sharing our storage. We need to make our + # own copy now. Be sure `reify` gives back a copy, not the original. + @storage = reify(true) + @storage << other + @length += other.length + @offset = 0 + else + # Surely no one will notice if we destructively update @storage + @storage << other + @length += other.length + end + + self + end + + # If two flyweights share the same storage and are contiguous (one ends + # where the other starts), then string concatenation can be optimized. + # + # In the case where the operand is a string and happens to be a prefix + # what follows @offset + @length, then we can also simply extend the + # length without allocating more strings. + # + # For example: + # x = Pointer.build("abc xyz") + # a, b = x.split_at(2) + # + # a + b # no String allocation, returns new Pointer + # a + "c x" # no String allocation, returns new Pointer + # a + "mno" # returns a new String + # + # NOTE: In the case where a string must be allocated, this method + # does NOT return a pointer; it returns a plain String. This is + # because the likely operation on the result is more concatenation, + # or things besides creating substrings. For example, + # + # ((x + "a") + "b") + "c" + # + # is more efficient when `x + "a"` returns a String. Otherwise the + # pointer wrapper is created only to immediately unwrap @storage + # for the next concatenation. + # + # @return [Pointer | String] + def +(other) + if other.is_a?(self.class) + if @storage.eql?(other.storage) and @offset + @length == other.offset + self.class.new(@storage.freeze, @offset, @length + other.length) + else + # TODO: Explain why we're not returning a Pointer + reify(true) << other.reify + end + + # NOTE: There doesn't seem to be a string comparison function in Ruby + # that allows the comparison to start a given offset. That means we + # have to allocate and copy from @storage a length of `other.length`. + # @storage[@offset + @length, other.length] == other + # + # @storage.index(other, @offset + @length) == n doesn't allocate + # memory, but there is no way to abort the search early. + # + elsif @storage.length - @offset - @length >= other.length \ + and @storage.index(other, @offset + @length) == @offset + @length + self.class.new(@storage.freeze, @offset, @length + other.length) + else + # TODO: Explain why we're not returning a Pointer + reify(true) << other + end + end + end + + class ArrayPtr < Pointer + # Used by Array methods to coerce us into a compatible type. + def to_ary + reify + end + + # all? + # any? + # assoc + # bsearch + # bsearch_index + # count + # dig + # drop_while + # each + # each_index + # fetch + # find_index + # include? + # index + # max + # min + # none? + # one? + # rassoc + # rindex + # sum + # take_while + # values_at + end + + end +end diff --git a/lib/stupidedi/reader/position.rb b/lib/stupidedi/reader/position.rb index 3181c5ef2..abfb01e32 100644 --- a/lib/stupidedi/reader/position.rb +++ b/lib/stupidedi/reader/position.rb @@ -3,77 +3,116 @@ module Stupidedi using Refinements module Reader - class Position - # @return [Integer] - attr_reader :offset - # @return [Integer] - attr_reader :line + # + # This module is intended to be used with a user-defined Struct. This + # scheme allows customization of what position information is tracked via + # the tokenizer and passed along into the parse tree. + # + # For example, + # + # TinyPosition = Struct.new(:offset) + # TinyPosition.include(Stupidedi::Reader::Position) + # + # anonClass = Struct.new(:path, :line) + # anonClass.include(Stupidedi::Reader::Position) + # + # class BigPosition < Struct.new(:path, :line, :column, :offset) + # include Stupidedi::Reader::Position + # + # # Return 50 chars before and after this position + # def context(input) + # input[offset - 50, 50] + " >> " + input[offset, 50] + # end + # end + # + # Normally it would be fine to just track everything and let the user + # disregard what's not interesting. However, to conserve memory, it's + # beneficial to track the minimum. + # + # Here's how the memory footprint works out: + # path: roughly 20 bytes + length of string in bytes, but minimum is 40 + # line: represented directly, so no overhead besides the VALUE struct + # column: same + # offset: same + # + # The Position object itself also consumes 40 bytes, as long as it has three + # or fewer fields. Once a fourth field is added, another 40 bytes are + # consumed. + # + # So tracking three or fewer numeric-only fields consumes 40 bytes. But + # adding the fourth field increases that to 100 bytes + length of path. + # Tracking the path and two or less integer fields consumes 60 + length of + # the path string. + # + # Because a position is attached to each individual part of syntax (the + # start of a segment, the start of each individual element), this can add + # up to a lot of space. In different situations, the user may independently + # know the file path and not need it stored here. Or they may not care + # about the offset and manage with only line and column numbers. + # + # The default NoPosition implementation is provided which still consumes + # 40 bytes, but only one instance is created. + # - # @return [Integer] - attr_reader :column - - # @return [String, Pathname] - attr_reader :path - - def initialize(offset, line, column, path) - # @offset, @line, @column, @path = - # offset, line, column, path - @line, @column, @path = - line, column, path + module Position + def self.included(base) + base.__send__(:extend, ClassMethods) + base.__send__(:include, InstanceMethods) end - def copy(changes = {}) - Position.new \ - nil, #changes.fetch(:offset, @offset), - changes.fetch(:line, @line), - changes.fetch(:column, @column), - changes.fetch(:path, @path) + module ClassMethods + def caller(offset = 1) + path, line, = Stupidedi.caller(offset + 1) + new.reset(path, line, nil, nil) + end end - def to_s - inspect - end + module InstanceMethods + def to_s + inspect + end - # @return [String] - def inspect - if @path.present? - parts = ["file #{@path}", "line #{@line}"] - else - parts = ["line #{@line}"] + # @return [String] + def inspect + parts = [] + parts << "path #{path}" if respond_to?(:path) + parts << "line #{line}" if respond_to?(:line) + parts << "column #{column}" if respond_to?(:column) + parts << "offset #{offset}" if respond_to?(:offset) + parts.join(", ") end - if @column.present? - parts << "column #{@column}" + def reset(path, line, column, offset) + self[:path] = path if respond_to?(:path) + self[:line] = line if respond_to?(:line) + self[:column] = column if respond_to?(:column) + self[:offset] = offset if respond_to?(:offset) + self end + end + end - parts.join(", ") + class NoPosition + def to_s + inspect end - # @return [void] - def pretty_print(q) - q.text "Position" - q.group(2, "(", ")") do - q.breakable "" - q.text "line #{@line}," - q.breakable - q.text "column #{@column}," - #q.breakable - #q.text "offset #{@offset}" + def inspect + "no position info" + end - unless @path.nil? - q.text "," - q.breakable - q.text "path #{@path}" - end - end + def reset(path, line, column, offset) end end - class << Position + class << NoPosition def caller(offset = 1) - path, line, = Stupidedi.caller(offset + 1) - new(nil, line, nil, path) + @instance ||= NoPosition.allocate + end + + def new(*args) + @instance ||= NoPosition.allocate end end end diff --git a/lib/stupidedi/reader/result.rb b/lib/stupidedi/reader/result.rb deleted file mode 100644 index a8a6bb71a..000000000 --- a/lib/stupidedi/reader/result.rb +++ /dev/null @@ -1,172 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - module Result - # @return [Position] - def position - if @remainder.respond_to?(:position) - @remainder.position - end - end - - # @return [Integer] - def offset - if @remainder.respond_to?(:offset) - @remainder.offset - end - end - - # @return [Integer] - def line - if @remainder.respond_to?(:line) - @remainder.line - end - end - - # @return [Integer] - def column - if @remainder.respond_to?(:column) - @remainder.column - end - end - end - - class << Result - # @group Constructors - ######################################################################### - - # @return [Result::Success] - def success(value, remainder) - Success.new(value, remainder) - end - - # @return [Result::Failure] - def failure(reason, remainder, fatal) - Failure.new(reason, remainder, fatal) - end - - # @endgroup - ######################################################################### - end - - class Success < Either::Success - include Result - - attr_reader :remainder - - def initialize(value, remainder) - @value, @remainder = value, remainder - end - - # @return [Success] - def copy(changes = {}) - Success.new \ - changes.fetch(:value, @value), - changes.fetch(:remainder, @remainder) - end - - def fatal? - false - end - - # @return [Boolean] - def ==(other) - if other.is_a?(self.class) - @value == other.value and @remainder == other.remainder - else - @value == other - end - end - - # @return [void] - # :nocov: - def pretty_print(q) - q.text "Result.success" - q.group(2, "(", ")") do - q.breakable "" - q.pp @value - q.text "," - q.breakable - q.pp @remainder - end - end - # :nocov: - - # Override `Enumerable#blank?` since we're not really `Enumerable` - def blank? - false - end - - private - - def deconstruct(block) - block.call(@value, @remainder) - end - - def failure(reason) - Failure.new(reason, @remainder, false) - end - end - - class Failure < Either::Failure - include Result - - attr_reader :remainder - - def initialize(reason, remainder, fatal) - @reason, @remainder, @fatal = - reason, remainder, fatal - end - - # @return [Failure] - def copy(changes = {}) - Failure.new \ - changes.fetch(:reason, @reason), - changes.fetch(:remainder, @remainder), - changes.fetch(:fatal, @fatal) - end - - def fatal? - @fatal - end - - def fatal - copy(:fatal => true) - end - - # @return [Boolean] - def ==(other) - if other.is_a?(self.class) - @reason == other.reason - else - @reason == other - end - end - - # @return [void] - # :nocov: - def pretty_print(q) - q.text "Result.failure" - q.group(2, "(", ")") do - q.breakable "" - q.pp @reason - q.text "," - q.breakable - q.pp @remainder - q.text "," - q.breakable - q.text "#{@fatal ? "" : "non-"}fatal" - end - end - # :nocov: - - private - - def deconstruct(block) - block.call(@reason, @remainder) - end - end - end -end diff --git a/lib/stupidedi/reader/separators.rb b/lib/stupidedi/reader/separators.rb index 1d17cb8f3..783832c9c 100644 --- a/lib/stupidedi/reader/separators.rb +++ b/lib/stupidedi/reader/separators.rb @@ -46,6 +46,16 @@ def merge(other) other.segment || @segment end + # Indicates if the given char is among one of the separators + # + # @return [Boolean] + def include?(char) + @component == char || + @repetition == char || + @element == char || + @segment == char + end + # @return [AbstractSet] def characters chars = diff --git a/lib/stupidedi/reader/stream_reader.rb b/lib/stupidedi/reader/stream_reader.rb deleted file mode 100644 index f41873310..000000000 --- a/lib/stupidedi/reader/stream_reader.rb +++ /dev/null @@ -1,173 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - # - # The {StreamReader} is intended to scan the input for a valid ISA segment, - # after which the {TokenReader} class can be used to tokenize the remaining - # input. - # - # Because X12 specifications have no bearing on what happens outside the - # interchange envelope (from `IEA` to `ISA`), out-of-band data like blank - # lines, human readable text, etc can occur between interchanges. This - # reader is designed to deal with that problem. - # - class StreamReader - include Inspect - - attr_reader :input - - def initialize(input) - @input = input - end - - # @return true - def stream? - true - end - - # True if there is no remaining input - def empty? - @input.empty? - end - - # Read a single character - # - # @return [Either>] - def read_character - unless @input.empty? - result(@input.at(0), advance(1)) - else - failure("less than one character available") - end - end - - # Skip a single character - # - # @return [Either] - def consume_character - unless @input.empty? - success(advance(1)) - else - failure("less than one character available") - end - end - - # This method is unaware of subtle differences between interchange - # versions, so it really only tokenizes 16 elements, plus the element - # terminator and segment separators. It does not presume to understand - # the meaning of the elements, like the repetition separator or the - # component separator, because these are interchange version-dependent. - # - # @return [Either>] - def read_segment - consume_isa.flatmap do |rest| - # The character after "ISA" is defined to be the element separator - rest.read_character.flatmap do |char, aR| - separators = Separators.new(nil, nil, char, nil) - remaining = success(TokenReader.new(aR.input, separators)) - elements = [] - - # Read 15 simple elements into an array. Consume/discard the element - # separator that follows each one. - 15.times do - remaining = - remaining.flatmap(&:read_simple_element).flatmap do |e, eR| - elements << e - - # Throw away the following element separator - eR.consume_prefix(separators.element) - end - end - - # We have to assume the last (16th) element is fixed-length because - # it is not terminated by an element separator. The {read_character} - # method defined by TokenReader skips past control characters. - remaining.flatmap do |w| - w.read_character.flatmap do |isa16, cR| - elements << SimpleElementTok.build(isa16, w.input, cR.input) - - # The character after the last element is defined to be the - # segment terminator. The {read_character} method here, defined - # by StreamReader, does not skip past control character, so the - # separator could be a control character. - cR.stream.read_character.flatmap do |char_, dR| - if char_ == separators.element - failure("element separator and segment terminator must be distinct", dR.input) - else - separators.segment = char_ - - token = SegmentTok.build(:ISA, elements, - rest.input.position, dR.input.position) - - result(token, TokenReader.new(dR.input, separators)) - end - end - end - end - end.or do |reason| - # We read "ISA" but failed to tokenize the input that followed. This - # was probably a random occurrence of the sequence "ISA", so we'll - # skip past it and try again. - # - # @todo: We should log this as a warning, because we could otherwise - # be silently discarding an entire interchange if the ISA segment - # was bogus - rest.read_segment - end - end - end - - protected - - # Consume the next occurence of "ISA" - # - # @return [Either] - def consume_isa - position = 0 - buffer = " " - - # @todo: This would probably more optimal if it was written like - # consume("I") >>= consume_prefix("S") >>= consume_prefix("A") - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - unless Reader.is_control_character?(character) - # Slide the "window" forward one character - buffer = buffer.slice(1..-1) - buffer = buffer + character.upcase - if buffer == "ISA" - return success(advance(position)) - end - end - end - - failure("reached end of input before finding ISA segment identifier") - end - - private - - def advance(n) - unless @input.defined_at?(n-1) - raise IndexError, "less than #{n} characters available" - else - StreamReader.new(@input.drop(n)) - end - end - - def failure(message, remainder = input) - Reader::Failure.new(message, remainder, false) - end - - def success(value) - Either.success(value) - end - - def result(value, remainder) - Reader::Success.new(value, remainder) - end - end - end -end diff --git a/lib/stupidedi/reader/token_reader.rb b/lib/stupidedi/reader/token_reader.rb deleted file mode 100644 index f8990ae90..000000000 --- a/lib/stupidedi/reader/token_reader.rb +++ /dev/null @@ -1,466 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - class TokenReader - # @private - SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ - - include Inspect - - # @return [String, Input] - attr_reader :input - - # @return [Separators] - attr_reader :separators - - # @return [SegmentDict] - attr_accessor :segment_dict - - def initialize(input, separators, segment_dict = SegmentDict.empty) - @input, @separators, @segment_dict = - input, separators, segment_dict - end - - # @return [TokenReader] - def copy(changes = {}) - TokenReader.new \ - changes.fetch(:input, @input), - changes.fetch(:separators, @separators), - changes.fetch(:segment_dict, @segment_dict) - end - - # @return false - def stream? - false - end - - # @return [StreamReader] - def stream - StreamReader.new(@input) - end - - def empty? - @input.empty? - end - - # If `s` is a prefix of {#input}, then `s` is skipped and the remaining - # input is returned as a new `TokenReader` wrapped by `Either.success`. - # Otherwise, an {Either::Failure} is returned. - # - # @return [Either] - def consume_prefix(s) - return success(self) if s.empty? - - position = 0 - buffer = String.new("") - - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - unless is_control?(character) - buffer << character - - if s.length == buffer.length - if s == buffer - return success(advance(position)) - else - return failure("found #{buffer.inspect} instead of #{s.inspect}") - end - end - end - end - - failure("reached end of input without finding #{s.inspect}") - end - - def consume_control_chars - position = 0 - - while @input.defined_at?(position) and is_control?(@input.at(position)) - position += 1 - end - - if position.zero? - success(self) - else - success(advance(position)) - end - end - - # If `s` occurs within {#input}, then the input up to and including `s` - # is skipped and the remaining input is returned as a new `TokenReader` - # wrapped by `Either.success`. Otherwise, {Either::Failure} is returned. - # - # @return [Either] - def consume(s) - return success(self) if s.empty? - - position = 0 - buffer = " " * s.length - - while @input.defined_at?(position) - character = @input.at(position) - - unless is_control?(character) - # Slide the "window" forward one character - buffer.slice!(0) - buffer << character - end - - position += 1 - - if s == buffer - return success(advance(position)) - end - end - - failure("reached end of input without finding #{s.inspect}") - end - - # Returns a single character and the remaining input as a {Result} with - # a `value` of the character and a `remainder` of the reamining input as - # a new instance of {TokenReader}. If {#input} has less than a single - # character, returns an {Either::Failure} - # - # @return [Either>] - def read_character - position = 0 - - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - if is_control?(character) - next - end - - return result(character, advance(position)) - end - - failure("less than one character available") - end - - # @return [Either>] - def read_segment - consume_control_chars.flatmap do |start| - # We might start reading a segment at "\nNM1...", where the "\n" is on - # line 5, but "NM1" is on line 6. So to ensure the segment position is - # line 6, we start with consume_control_characters. - start.read_segment_id.flatmap do |segment_id, aR| - if @segment_dict.defined_at?(segment_id) - element_uses = @segment_dict.at(segment_id).element_uses - else - element_uses = [] - end - - aR.read_delimiter.flatmap do |delim, bR| - case delim - when @separators.element - rest = bR.read_elements(segment_id, element_uses) - rest.map{|es, cR| segment(segment_id, start.input, cR.input, es) } - when @separators.segment - remainder = - if segment_id == :IEA - bR.stream - else - bR - end - - # Consume the segment terminator - result(segment(segment_id, start.input, bR.input), remainder) - end - end - end - end - end - - # @return [Either, TokenReader>>] - def read_elements(segment_id, element_uses) - if element_uses.empty? - read_simple_element - else - element_use = element_uses.head - repeatable = element_use.repeatable? - - if element_use.composite? - read_composite_element(repeatable) - else - read_simple_element(repeatable) - end - end.flatmap do |element, aR| - aR.read_delimiter.flatmap do |delim, bR| - case delim - when @separators.segment - remainder = - if segment_id == :IEA - bR.stream - else - bR - end - - # This is the last element before the segment terminator, make - # it into a singleton list and _do_ consume the delimiter - result(element.cons, remainder) - when @separators.element - # There is another element following the delimiter - rest = bR.read_elements(segment_id, element_uses.tail) - rest.map{|es, _| element.cons(es) } - end - end - end - end - - # @return [Either>>] - def read_component_elements(repeatable = false) - read_component_element(repeatable).flatmap do |component, aR| - aR.read_delimiter.flatmap do |delim, bR| - case delim - when @separators.segment, - @separators.element, - @separators.repetition - # This is the last component element within the composite element, - # so make it into a singleton list and don't consume the delimiter - result(component.cons, aR) - when @separators.component - rest = bR.read_component_elements(repeatable) - rest.map{|es, _| component.cons(es) } - end - end - end - end - - # @return [Either>] - def read_segment_id - position = 0 - buffer = String.new("") - - while true - unless @input.defined_at?(position) - return eof("reached end of input without finding a segment identifier") - end - - character = @input.at(position) - position += 1 - - if is_delimiter?(character) - break - end - - unless is_control?(character) - if buffer.length == 3 - break - end - - buffer << character - end - end - - # We only arrive here if {character} is a delimiter, or if we read - # three characters into {buffer} and an additional into {character} - if SEGMENT_ID.match?(buffer) - remainder = advance(position - 1) - - case character - when @separators.segment, - @separators.element - # Don't consume the delimiter - result(buffer.upcase.to_sym, remainder) - else - failure("found #{character.inspect} following segment identifier") - end - else - failure("found #{(buffer + character).inspect} instead of segment identifier") - end - end - - # @return [Either>] - def read_delimiter - position = 0 - - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - if is_control?(character) - next - end - - if is_delimiter?(character) - return result(character, advance(position)) - else - return failure("found #{character.inspect} instead of a delimiter") - end - end - - failure("reached end of input without finding a delimiter") - end - - # @return [Either>] - def read_simple_element(repeatable = false) - position = 0 - buffer = String.new("") - - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - if is_control?(character) - next - end - - case character - when @separators.segment, - @separators.element - # These delimiters mark the end of the element. We don't consume - # the delimiter because the next reader can use the delimiter to - # know which token to next expect. - token = simple(buffer, @input, @input.drop(position)) - token = token.repeated if repeatable - return result(token, advance(position - 1)) - when @separators.repetition - if repeatable - token = simple(buffer, @input, @input.drop(position)) - rest = advance(position).read_simple_element(repeatable) - return rest.map{|e, _| e.repeated(token) } - # else - # # @todo: Read this as data but sound the alarms - end - # when @separators.component - # # @todo: Read this as data but sound the alarms - end - - buffer << character - end - - failure("reached end of input without finding a simple data element") - end - - # @return [Either>] - def read_component_element(repeatable = false) - position = 0 - buffer = String.new("") - - while @input.defined_at?(position) - character = @input.at(position) - position += 1 - - if is_control?(character) - next - end - - case character - when @separators.element, - @separators.segment, - @separators.component - # Don't consume the separator/terminator - token = component(buffer, @input, @input.drop(position)) - return result(token, advance(position - 1)) - when @separators.repetition - if repeatable - # Don't consume the repetition separator - token = component(buffer, @input, @input.drop(position)) - return result(token, advance(position - 1)) - # else - # # @todo: Read this as data but sound the alarms - end - end - - buffer << character - end - - failure("reached end of input without finding a component data element") - end - - # @return [Either>] - def read_composite_element(repeatable = false) - read_component_elements(repeatable).flatmap do |components, aR| - token = composite(components, @input, aR.input) - - aR.read_delimiter.flatmap do |delim, bR| - case delim - when @separators.segment, - @separators.element - token = token.repeated if repeatable - result(token, aR) - when @separators.repetition - bR.read_composite_element(repeatable).map do |c, cR| - c.repeated(token) - end - end - end - end - end - - # @return [void] - def pretty_print(q) - q.text("TokenReader") - q.group(2, "(", ")") do - q.breakable "" - - q.pp @input - q.text "," - q.breakable - - q.pp @separators - end - end - - private - - # @return [TokenReader] - def advance(n) - unless @input.defined_at?(n-1) - raise IndexError, "less than #{n} characters available" - else - TokenReader.new(@input.drop(n), @separators, @segment_dict) - end - end - - def is_delimiter?(character) - character == @separators.segment or - character == @separators.element or - character == @separators.component or - character == @separators.repetition - end - - def is_control?(character) - Reader.is_control_character?(character) and not is_delimiter?(character) - end - - def failure(message, remainder = @input) - Result.failure(message, remainder, true) - end - - def eof(message, remainder = @input) - Result.failure(message, remainder, false) - end - - def success(value) - Either.success(value) - end - - def result(value, remainder) - Result.success(value, remainder) - end - - def segment(segment_id, input, remainder, elements = []) - SegmentTok.build(segment_id, elements, input.position, remainder.position) - end - - def simple(value, input, remainder) - SimpleElementTok.build(value, input.position, remainder.position) - end - - def component(value, input, remainder) - ComponentElementTok.build(value, input.position, remainder.position) - end - - def composite(value, input, remainder) - CompositeElementTok.build(value, input.position, remainder.position) - end - end - end -end diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb new file mode 100644 index 000000000..2f8812737 --- /dev/null +++ b/lib/stupidedi/reader/tokenizer.rb @@ -0,0 +1,522 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + + # + # + # + class Tokenizer + SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ + + def initialize(input) + @input = input + end + + def each + if block_given? + Tokenizer.each(@input){|t| yield t } + else + Tokenizer.each(@input) + end + end + + def each_isa + if block_given? + Tokenizer.each_isa(@input){|t| yield t } + else + Tokenizer.each_isa(@input) + end + end + end + + class Tokenizer + # + # Track the configuration info, which controls how the tokenizer reads + # input. The `separators` field is straightforward, but `segment_dict` + # is used by `read_element` to determine which kind of elements should + # be parsed, according to the segment definition. + # + class State + include Inspect + + # @return [Separators] + attr_accessor :separators + + # @return [SegmentDict] + attr_accessor :segment_dict + + def initialize(separators, segment_dict) + @separators, @segment_dict = + separators, segment_dict + end + + def self.todo + new(Separators.new(":", "^", "*", "~"), Hash.new) + end + end + + # + # Tokenizer operations return three bits of info: an error value, + # a success value, and the remaining unconsumed input + # + class Result + # @return [Input] + attr_reader :input + + def initialize(value, input) + @value, @input = + value, input + end + + def pass? + not fail? + end + + class Fail < Result + def error + @value + end + + def fail? + true + end + end + + class Pass < Result + def value + @value + end + + def fail? + false + end + end + end + end + + class << Tokenizer + # @yield [SegmentTok | IgnoredTok | ErrorTok] + def each(input) + return enum_for(:each, input) unless block_given? + state = Tokenizer::State.new(nil, nil) + + until input.empty? + next_segment(input, state) do |token| + # Ordinarily there's only one SegmentTok returned, but in other + # cases there could be a IgnoreTok at the beginning, with non-X12 + # header text. Or there could be an ErrorTok and IgnoreTok because + # the first ISA was malformed and the text after was ignored. + # + # Usually read_segment will only return a single SegmentTok, but + # there could also be unparseable text in an ErrorTok + yield token + end + end + end + + # @yield [SegmentTok | IgnoredTok | ErrorTok] + def each_isa(input) + return enum_for(:each, input) unless block_given? + state = Tokenizer::State.new(nil, nil) + + until input.empty? + next_isa_segment(input, state).each do |token| + yield token + end + end + end + + # Consume next occurrence of "ISA" and any control characters that + # immediately follow. Validation is done to skip over "ISA" where + # it is less likely to be X12 than part of a word. + # + # Returns the input that was consumed as an IgnoredTok. If no "ISA" + # was found, then the entire input will be consumed. + # + # @return [Result] + def next_isa_segment_id(input) + offset = 0 + + while input.defined_at?(offset) + # Skip ahead until we find next occurrence of characters ISA, not + # case sensitive. Control characters are ignored between I, S, A + i = input.index("I", offset) + + # In the next iteration, search for "I" begins right after this one + offset = i + 1 + + # There's no I in the rest of the input, so it's all ignored + return pass(IgnoredTok.new(input, :position), input.end) if i.nil? + + s = input.index("S", i + 1) + + # There's no S in the rest of the input, so it's all ignored + return pass(IgnoredTok.new(input, :position), input.end) if s.nil? + + # There's something between I..S but it's not a control character + next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER) + + a = input.index("A", offset) + + # There's no A in the rest of the input, so it's all ignored + return pass(IgnoredTok.new(input, :position), input.end) if a.nil? + + # There's something between S..A but it's not a control character + next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER) + + # Needed to perform the extra validation below + a = skip_control_characters(input, a) + + # The next character determines the element separator. If it's an + # alphanumeric or space, we assume this is not the start of an ISA + # segment. Perhaps a word like "L[ISA] " or "D[ISA]RRAY" + next if input[a+1].match?(/[a-zA-Z0-9 ]/) + + # Success + return pass(IgnoredTok.new(input.take(i), :position), input.drop(a+1)) + end + + # Give up at end of input + return pass(IgnoredTok.new(input, :position), input.end) + end + + # This should be called when `input.head` is pointing at the first + # element separator (commonly "*"). There is no validation done here + # so take care, the results can be really strange otherwise. + # + # @return [Result>] + def read_isa_elements(input, state) + # The next character is a declaration of the element separator + state.separators = Separators.new(nil, nil, input.head, nil) + + # Read the first 15 simple elements into an array + element_toks = 15.times.map do |n| + result = read_simple_element(input, state.separators, false) + return result if result.fail? + input = result.input + result.value + end + + # We have to assume the last (16th) element is fixed-length because + # it is not terminated by an element separator. First we will skip + # past control characters, then read the following character. + offset = skip_control_characters(input, 1) + return fail("reached eof before ISA16", input) \ + unless input.defined_at?(offset) + element_toks << SimpleElementTok.build(input[offset], :position) + + # The character after the last element is defined to be the + # segment terminator. Here we do not skip past control characters, + # so the separator could be a control character + return fail("reached eof before segment terminator for ISA", input) \ + unless input.defined_at?(offset + 1) + state.separators.segment = input[offset + 1] + + pass(element_toks, input.drop(offset + 2)) + end + + # @yield [IgnoreTok] + # @return [Result] + def next_isa_segment(input, state) + result = next_isa_segment_id(input) + return result if result.fail? + + unless result.value.value.empty? + # It's not unusual for IgnoreTok to have no content, because "ISA" + # was found at the first character of the input. + yield result.value if block_given? + end + + result = read_isa_elements(result.input, state) + return result if result.fail? + + pass(SegmentTok.build(:ISA, result.value, :position), result.input) + end + + # Works similarly to `next_isa_segment_id`, except the result is the + # segment identifier. The remaining input begins where an element or + # segment separator should be. + # + # This consumes any control characters following the segment identifier + # and the remaining input is guaranteed not to be EOF. However, there is + # no validation done on the next character, even though it should be a + # segment or element separator. + # + # @return [Symbol] + def next_segment_id(input, state) + offset = skip_control_characters(input) + buffer = input.drop(offset).take(0) + + while true + return fail("reached eof before segment identifier", input) \ + unless input.defined_at?(offset) + + char = input[offset] + break if state.separators.element == char + break if state.separators.segment == char + + offset += 1 + next if Reader.is_control_character?(char) + + buffer += char + break if buffer.length >= 3 + end + + return fail("found '#{buffer}' instead of segment identifier", input) \ + unless buffer.match?(Tokenizer::SEGMENT_ID) + + offset = skip_control_characters(input, offset) + return pass(buffer.to_sym, input.drop(offset)) + end + + # @return [SegmentTok] + def next_segment(input, state) + if state.separators.nil? + return next_isa_segment(input, state){|t| yield t } + end + + result = next_segment_id(input, state) + return result if result.fail? # TODO yield + + segment_id = result.value + + unless result.input.head == state.separators.element \ + or result.input.head == state.separators.segment + # This is a redundant check, because we have to check in read_elements + # but here we can provide a better error location + return fail("invalid %s after segment identifier for %s" % [ + result.input.head, segment_id], input) # TODO yield + end + + if segment_id == :ISA + # We encountered a new ISA segment without having seen the previous + # ISA's matching IEA segment. + return read_isa_elements(result.input, state){|t| yield t } + end + + if state.segment_dict.defined_at?(segment_id) + element_uses = state.segment_dict.at(segment_id).element_uses + else + element_uses = [] + end + + # Note, next_segment_id guarantees result.input isn't EOF + result = read_elements(segment_id, result.input, state.separators, element_uses) + return result if result.fail? # TODO yield + + if segment_id == :IEA + # Stop parsing X12 and search for the next ISA before resuming + state.separators = nil + end + + pass(SegmentTok.build(segment_id, result.value, :position), result.input) + end + + # Input should be positioned on an element separator: "NM1[*]..*..*..~" + # + # @return [Array] + def read_elements(segment_id, input, separators, element_uses=[]) + elements = [] + + # We are placed on an element separator at the start of each iteration + until input.empty? or input.head != separators.element + result = + if element_uses.defined_at?(elements.length) + element_use = element_uses[elements.length] + repeatable = element_use.repeatable? + + if element_use.composite? + read_composite_element(input.tail, separatorse, repeatable) + else + read_simple_element(input, separators, repeatable) + end + else + # We either don't have a corresponding SegmentDef or it has + # fewer elements than are present in the input. We'll make + # the assumption that it's a simple non-repeatable element. + # + # If the input contains a component or repetition separator, + # they will be interpreted as ordinary characters. + read_simple_element(input, separators, repeatable) + end + + return result if result.fail? + elements << result.value + input = result.input + end + + if input.empty? + fail("reached eof before segment terminator for %s" % segment_id, input) + elsif input.head != separators.segment + fail("expected segment terminator for %s but saw '%s'" % [segment_id, input.head], input) + else + # Skip past the segment separator + pass(elements, input.tail) + end + end + + # @return [CompositeElementTok] + def read_composite_element(input, separators, repeatable, descriptor="a composite element") + return fail("reached eof while expecting #{descriptor}", input) \ + if input.empty? + + return fail("expected an element separator before #{descriptor}", input) \ + unless input.head == separators.element + + builder = ElementTokBuilder.build(repeatable, :position) + component_toks = [] + + until input.empty? + result = read_component_element(input, separators, false) + return result if result.fail? + input = result.input + component_toks << result.value + + if repeatable and input.head == separators.repetition + builder.add(CompositeElementTok.build(component_toks, :position)) + component_toks.clear + + elsif input.head == separators.element \ + or input.head == separators.segment + builder.add(CompositeElementTok.build(component_toks, :position)) + return pass(builder.build, input) + end + end + + fail("reached eof while reading #{descriptor}", input) + end + + # @return [ComponentElementTok] + def read_component_element(input, separators, repeatable, descriptor="a component element") + return fail("reached eof while expecting #{descriptor}", input) \ + if input.empty? + + return fail("expected an element or component separator before #{descriptor}", input) \ + unless input.head == separators.element or input.head == separators.component + + offset = skip_control_characters(input, 1) + buffer = input.drop(offset).take(0) + builder = ElementTokBuilder.build(repeatable, :position) + + while input.defined_at?(offset) + char = input[offset] + + if repeatable and char == separators.repetition + builder.add(SimpleElementTok.build(buffer, :position)) + offset += 1 + + elsif char == separators.segment \ + or char == separators.element \ + or char == separators.component \ + or char == separators.repetition + builder.add(ComponentElementTok.build(buffer, :position)) + return pass(builder.token, input.drop(offset)) + + else + buffer << char unless Reader.is_control_character?(char) + offset += 1 + end + end + + return fail("reached eof while reading #{descriptor}", input) + end + + # Input should be positioned on an element separator: "NM1[*]..*..*..~" + # + # @return [SimpleElementTok] + def read_simple_element(input, separators, repeatable, descriptor="an element") + return fail("reached eof while expecting #{descriptor}", input) \ + if input.empty? + + return fail("expected an element separator before #{descriptor}", input) \ + unless input.head == separators.element + + offset = skip_control_characters(input, 1) + buffer = input.drop(offset).take(0) + builder = ElementTokBuilder.build(repeatable, :position) + + while input.defined_at?(offset) + char = input[offset] + + if char == separators.element \ + or char == separators.segment + builder.add(SimpleElementTok.build(buffer, :position)) + return pass(builder.build, input.drop(offset)) + + elsif repeatable and char == separators.repetition + builder.add(SimpleElementTok.build(buffer, :position)) + offset += 1 + + else + buffer << char unless Reader.is_control_character?(char) + offset += 1 + end + end + + return fail("reached eof while reading #{descriptor}", input) + end + + # @return [Integer] + def skip_control_characters(input, offset=0) + while input.defined_at?(offset) \ + and Reader.is_control_character?(input[offset]) + offset += 1 + end + + offset + end + + # @param value [Object] Result + # @param input [Input] Remaining unconsumed input + def pass(value, input) + Tokenizer::Result::Pass.new(value, input) + end + + # @param value [Object] Error message + # @param input [Input] Input beginning where the error occurred + def fail(value, input) + Tokenizer::Result::Fail.new(value, input) + end + + class ElementTokBuilder + def self.build(repeatable, position) + if repeatable + Repeatable.new(position) + else + NonRepeatable.new(position) + end + end + + class Repeatable + def initialize(position) + @position, @element_toks = position, [] + end + + def add(element_tok) + @element_toks.push(element_tok) + end + + def build + RepeatedElementTok.build(@element_toks, @element_toks.head.position) + end + end + + class NonRepeatable + def initialize(position) + @position = position + end + + def add(element_tok) + @element_tok = element_tok + end + + def build + @element_tok + end + end + end + end + end +end diff --git a/lib/stupidedi/reader/tokens/component_element_tok.rb b/lib/stupidedi/reader/tokens/component_element_tok.rb index 14605aa47..406a7b3bd 100644 --- a/lib/stupidedi/reader/tokens/component_element_tok.rb +++ b/lib/stupidedi/reader/tokens/component_element_tok.rb @@ -12,20 +12,16 @@ class ComponentElementTok # @return [Position] attr_reader :position - # @return [Position] - attr_reader :remainder - - def initialize(value, position, remainder) - @value, @position, @remainder = - value, position, remainder + def initialize(value, position) + @value, @position = + value, position end # @return [CompositeElementTok] def copy(changes = {}) ComponentElementTok.new \ changes.fetch(:value, @value), - changes.fetch(:position, @position), - changes.fetch(:remainder, @remainder) + changes.fetch(:position, @position) end # :nocov: @@ -60,8 +56,8 @@ class << ComponentElementTok ######################################################################### # @return [ComponentElementTok] - def build(value, position, remainder) - new(value, position, remainder) + def build(value, position) + new(value, position) end # @endgroup diff --git a/lib/stupidedi/reader/tokens/composite_element_tok.rb b/lib/stupidedi/reader/tokens/composite_element_tok.rb index 07e7bad91..9d2a8fe91 100644 --- a/lib/stupidedi/reader/tokens/composite_element_tok.rb +++ b/lib/stupidedi/reader/tokens/composite_element_tok.rb @@ -12,20 +12,16 @@ class CompositeElementTok # @return [Position] attr_reader :position - # @return [Position] - attr_reader :remainder - - def initialize(component_toks, position, remainder) - @component_toks, @position, @remainder = - component_toks, position, remainder + def initialize(component_toks, position) + @component_toks, @position = + component_toks, position end # @return [CompositeElementTok] def copy(changes = {}) CompositeElementTok.new \ changes.fetch(:component_toks, @component_toks), - changes.fetch(:position, @position), - changes.fetch(:remainder, @remainder) + changes.fetch(:position, @position) end # :nocov: @@ -34,10 +30,6 @@ def pretty_print(q) end # :nocov: - def repeated - RepeatedElementTok.new(self.cons, @position) - end - def repeated? false end @@ -74,8 +66,8 @@ class << CompositeElementTok ######################################################################### # @return [CompositeElementTok] - def build(component_toks, position, remainder) - new(component_toks, position, remainder) + def build(component_toks, position) + new(component_toks, position) end # @endgroup diff --git a/lib/stupidedi/reader/tokens/ignored_tok.rb b/lib/stupidedi/reader/tokens/ignored_tok.rb new file mode 100644 index 000000000..2ebb919eb --- /dev/null +++ b/lib/stupidedi/reader/tokens/ignored_tok.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + class IgnoredTok + # include Inspect + + # @return [String] + attr_reader :value + + # @return [Position] + attr_reader :position + + def initialize(value, position) + @value, @position = + value, position + end + + # @return [IgnoredTok] + def copy(changes = {}) + IgnoredTok.new \ + changes.fetch(:value, @value), + changes.fetch(:position, @position) + end + + # :nocov: + def pretty_print(q) + q.pp(:ignored.cons(@value)) + end + # :nocov: + + def blank? + @value.all?(&:blank?) + end + + def present? + not blank? + end + + def to_x12(separators) + "" + end + end + + class << IgnoredTok + # @group Constructors + ######################################################################### + + # @return [IgnoredTok] + def build(value, position) + new(value, position) + end + + # @endgroup + ######################################################################### + end + end +end diff --git a/lib/stupidedi/reader/tokens/repeated_element_tok.rb b/lib/stupidedi/reader/tokens/repeated_element_tok.rb index 915b35d8c..874f42366 100644 --- a/lib/stupidedi/reader/tokens/repeated_element_tok.rb +++ b/lib/stupidedi/reader/tokens/repeated_element_tok.rb @@ -12,18 +12,11 @@ class RepeatedElementTok attr_reader :position - def_delegators "element_toks.last", :remainder - def initialize(element_toks, position) @element_toks, @position = element_toks, position end - def repeated(element_tok) - @element_toks.unshift(element_tok) - self - end - def pretty_print(q) q.pp(:repeated.cons(@element_toks)) end diff --git a/lib/stupidedi/reader/tokens/segment_tok.rb b/lib/stupidedi/reader/tokens/segment_tok.rb index 2891fb543..80fedead5 100644 --- a/lib/stupidedi/reader/tokens/segment_tok.rb +++ b/lib/stupidedi/reader/tokens/segment_tok.rb @@ -15,12 +15,9 @@ class SegmentTok # @return [Position] attr_reader :position - # @return [Position] - attr_reader :remainder - - def initialize(id, element_toks, position, remainder) - @id, @element_toks, @position, @remainder = - id, element_toks, position, remainder + def initialize(id, element_toks, position) + @id, @element_toks, @position = + id, element_toks, position end # @return [SegmentTok] @@ -28,8 +25,7 @@ def copy(changes = {}) SegmentTok.new \ changes.fetch(:id, @id), changes.fetch(:element_toks, @element_toks), - changes.fetch(:position, @position), - changes.fetch(:remainder, @remainder) + changes.fetch(:position, @position) end # :nocov: @@ -48,7 +44,7 @@ def present? def to_x12(separators) if blank? - "#{id}#{separators.segment}" + "#{id}#{(separators.segment || "~").strip}" else es = @element_toks.map{|x| x.to_x12(separators) } sep = separators.element || "*" @@ -63,8 +59,8 @@ class << SegmentTok ######################################################################### # @return [SegmentTok] - def build(id, element_toks, position, remainder) - new(id, element_toks, position, remainder) + def build(id, element_toks, position) + new(id, element_toks, position) end # @endgroup diff --git a/lib/stupidedi/reader/tokens/simple_element_tok.rb b/lib/stupidedi/reader/tokens/simple_element_tok.rb index 36890214a..6c6ecf9fc 100644 --- a/lib/stupidedi/reader/tokens/simple_element_tok.rb +++ b/lib/stupidedi/reader/tokens/simple_element_tok.rb @@ -12,30 +12,21 @@ class SimpleElementTok # @return [Position] attr_reader :position - # @return [Position] - attr_reader :remainder - - def initialize(value, position, remainder) - @value, @position, @remainder = - value, position, remainder + def initialize(value, position) + @value, @position = value, position end # @return [SimpleElementTok] def copy(changes = {}) SimpleElementTok.new \ changes.fetch(:value, @value), - changes.fetch(:position, @position), - changes.fetch(:remainder, @remainder) + changes.fetch(:position, @position) end def pretty_print(q) q.pp(:simple.cons(@value.cons)) end - def repeated - RepeatedElementTok.new(self.cons, @position) - end - def repeated? false end @@ -65,8 +56,8 @@ class << SimpleElementTok # @group Constructors ######################################################################### - def build(value, position, remainder) - new(value, position, remainder) + def build(value, position) + new(value, position) end # @endgroup diff --git a/spec/lib/stupidedi/reader/failure_spec.rb b/spec/lib/stupidedi/reader/failure_spec.rb deleted file mode 100644 index 176d5f89f..000000000 --- a/spec/lib/stupidedi/reader/failure_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -describe Stupidedi::Reader::Failure do - def mkfailure(reason, remainder) - Stupidedi::Reader::Result.failure(reason, remainder, false) - end - - describe "#reason" do - property "is the value given to the constructor" do - string - end.check do |s| - expect(mkfailure(s, "remainder").reason).to be == s - end - end - - describe "#remainder" do - property "is the value given to the constructor" do - string - end.check do |s| - expect(mkfailure("reason", s).remainder).to be == s - end - end - - # Note: `method` is dynamically scoped (bound by the caller's environment) - shared_examples_for "wrapped input delegator method" do - context "when remainder is wrapped" do - let(:input) { double("input", :offset => 400, :line => 90, :column => 10) } - - it "returns input.method" do - expect(mkfailure("reason", input).__send__(method)).to be == input.__send__(method) - end - end - - context "when remainder is not wrapped" do - it "should return nil" do - expect(mkfailure("reason", "remainder").__send__(method)).to be_nil - end - end - - context "when remainder is nil" do - it "should return nil" do - expect(mkfailure("reason", nil).__send__(method)).to be_nil - end - end - end - - describe "#offset" do - it_should_behave_like "wrapped input delegator method" do - let(:method) { :offset } - end - end - - describe "#line" do - it_should_behave_like "wrapped input delegator method" do - let(:method) { :line } - end - end - - describe "#column" do - it_should_behave_like "wrapped input delegator method" do - let(:method) { :column } - end - end -end diff --git a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb b/spec/lib/stupidedi/reader/input/delegated_input_spec.rb deleted file mode 100644 index 95c890eb8..000000000 --- a/spec/lib/stupidedi/reader/input/delegated_input_spec.rb +++ /dev/null @@ -1,282 +0,0 @@ -fdescribe Stupidedi::Reader::DelegatedInput do - using Stupidedi::Refinements - - def mkinput(*args) - Stupidedi::Reader::Input.build(*args) - end - - describe "#offset" do - pending "returns the value given to the constructor" do - expect(mkinput("", 10, 20, 30).offset).to be == 10 - end - end - - describe "#line" do - it "returns the value given to the constructor" do - expect(mkinput("", 10, 20, 30).line).to be == 20 - end - end - - describe "#column" do - it "returns the value given to the constructor" do - expect(mkinput("", 10, 20, 30).column).to be == 30 - end - end - - describe "#position" do - it "returns a Position value" do - expect(mkinput("").position).to be_a(Stupidedi::Reader::Position) - end - - pending "returns a Position value at the current offset" do - expect(mkinput("", 3).position.offset).to be == 3 - end - - it "returns a Position value at the current line" do - expect(mkinput("", 0, 3).position.line).to be == 3 - end - - it "returns a Position value at the current column" do - expect(mkinput("", 0, 0, 3).position.column).to be == 3 - end - end - - describe "#defined_at?(n)" do - context "when n is less than input length" do - property "is true" do - with(:size, between(1, 25)) { [string, between(0, size - 1)] } - end.check do |s, n| - expect(mkinput(s).defined_at?(n)).to be true - end - end - - context "when n is equal to input length" do - property "is false" do - with(:size, between(0, 25)) { [string, size] } - end.check do |s, n| - expect(mkinput(s).defined_at?(n)).to be false - end - end - - context "when n is greater than input length" do - property "is false" do - with(:size, between(0, 25)) { [string, size + integer.abs] } - end.check do |s, n| - expect(mkinput(s).defined_at?(n)).to be false - end - end - end - - describe "#empty?" do - context "when the input is empty" do - it "is true" do - expect(mkinput("")).to be_empty - expect(mkinput([])).to be_empty - end - end - - context "when the input is not empty" do - it "is false" do - expect(mkinput(" ")).not_to be_empty - expect(mkinput([1])).not_to be_empty - end - end - end - - describe "#drop(n)" do - context "when n is zero" do - it "returns itself" do - expect(mkinput("abc").drop(0)).to be == "abc" - end - end - - context "when n is negative" do - it "raises an error" do - expect(lambda { mkinput("abc").drop(-1) }).to raise_error("n must be positive") - end - end - - context "when less than n elements are available" do - pending "increments the offset" do - expect(mkinput("abc", 10).drop(25).offset).to be == 13 - end - - # TODO - # property "increments the offset" do - # with(:size, between(0, 25)) do - # [string, between(size + 1, 1000), between(10, 1000)] - # end - # end.check do |s, n, offset| - # expect(mkinput(s, offset).drop(n).offset).to be == offset + s.length - # end - - it "returns an empty input" do - expect(mkinput("abc", 10).drop(10)).to be_empty - end - end - - context "when n elements are available" do - pending "increments the offset" do - expect(mkinput("abc", 10).drop(2).offset).to be == 12 - end - - # property "increments the offset" do - # with(:size, between(0, 25)) do - # [string, between(0, size), between(10, 1000)] - # end - # end.check do |s, n, offset| - # expect(mkinput(s, offset).drop(n).offset).to be == offset + n - # end - - it "returns an input with the first n elements removed" do - expect(mkinput(%w(a b c d)).drop(3)).to be == %w(d) - end - - property "returns an input with the first n elements removed" do - with(:size, between(0, 25)) do - [string, between(0, size)] - end - end.check do |s, n| - expect(mkinput(s).drop(n)).to be == s.drop(n) - end - end - - property "increments the line count" do - string = array { with(:size, between(0, 15)) { string }}.join("\n") - n = between(0, string.length * 2) - offset = integer - line = integer - - [string, n, offset, line] - end.check do |s, n, o, l| - expect(mkinput(s, o, l).drop(n).line).to be == l + s.take(n).count("\n") - end - - it "calculates the column" do - input = mkinput("abc\nxyz") - expect(input.drop(0).column).to be == 1 - expect(input.drop(1).column).to be == 2 - expect(input.drop(2).column).to be == 3 - expect(input.drop(3).column).to be == 4 - expect(input.drop(4).column).to be == 1 - expect(input.drop(5).column).to be == 2 - expect(input.drop(6).column).to be == 3 - end - - property "calculates the column" do - string = array { with(:size, between(0, 50)) { string }}.join("\n") - n = between(0, string.length * 2) - offset = integer - line = integer - column = integer - - [string, n, offset, line, column] - end.check do |s, n, o, l, c| - prefix = s.take(n) - - if prefix.include?("\n") - expect(mkinput(s, o, l, c).drop(n).column).to be == prefix.length - prefix.rindex("\n") - else - expect(mkinput(s, o, l, c).drop(n).column).to be == c + prefix.length - end - end - end - - describe "#take(n)" do - context "when n is zero" do - it "returns an empty value" do - expect(mkinput("abc").take(0)).to be == "" - end - - it "returns an empty value" do - expect(mkinput(%w(a b c)).take(0)).to be == [] - end - end - - context "when n is negative" do - it "raises an error" do - expect(lambda { mkinput("abc").take(-1) }).to raise_error("n must be positive") - end - end - - context "when less than n elements are available" do - it "returns all available elements" do - expect(mkinput("ab").take(3)).to be == "ab" - end - - it "returns all available elements" do - expect(mkinput(%w(a b)).take(3)).to be == %w(a b) - end - - pending "does not update the offset" do - expect(mkinput("abc", 500).tap{|x| x.take(4) }.offset).to be == 500 - end - end - - context "when n elements are available" do - it "returns the first n elements" do - expect(mkinput("abc").take(2)).to be == "ab" - end - - it "returns the first n elements" do - expect(mkinput(%w(a b c)).take(2)).to be == %w(a b) - end - - pending "does not update the offset" do - expect(mkinput("abc", 500).tap{|x| x.take(2) }.offset).to be == 500 - end - end - end - - describe "#at(n)" do - context "when n is negative" do - it "raises an error" do - expect(lambda { mkinput("abc").at(-1) }).to raise_error("n must be positive") - end - end - - context "when the input is defined_at?(n)" do - it "returns the element at index n" do - expect(mkinput("abc").at(2)).to be == "c" - end - - it "returns the element at index n" do - expect(mkinput(%w(a b c)).at(2)).to be == "c" - end - - pending "does not update the offset" do - expect(mkinput("abc", 500).tap{|x| x.at(5) }.offset).to be == 500 - end - end - - context "when the input is not defined_at?(n)" do - it "returns nil" do - expect(mkinput("abc").at(3)).to be_nil - end - - it "returns nil" do - expect(mkinput(%w(a b c)).at(3)).to be_nil - end - - pending "does not update the offset" do - expect(mkinput("abc", 500).tap{|x| x.at(1) }.offset).to be == 500 - end - end - end - - describe "#index(search)" do - context "when search is an element in the input" do - it "returns the smallest index" do - expect(mkinput("abcabc").index("b")).to be == 1 - expect(mkinput(%w(a b c a b c)).index("b")).to be == 1 - end - end - - context "when search is not an element in the input" do - it "returns nil" do - expect(mkinput("abc").index("d")).to be_nil - expect(mkinput(%w(a b c)).index("d")).to be_nil - end - end - end -end diff --git a/spec/lib/stupidedi/reader/stream_reader_spec.rb b/spec/lib/stupidedi/reader/stream_reader_spec.rb deleted file mode 100644 index 5bdec3f08..000000000 --- a/spec/lib/stupidedi/reader/stream_reader_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -describe Stupidedi::Reader::StreamReader do - let(:success) { Stupidedi::Reader::Success } - let(:stream) { Stupidedi::Reader::StreamReader } - - def input(string) - Stupidedi::Reader::Input.from_string(string) - end - - describe "#stream?" do - it "returns true" - end - - describe "#empty?" do - context "with no input available" - context "with some input available" - end - - describe "#read_character" do - context "with no input available" do - it "produces an error message" - it "encodes the error position" - end - - context "with only one input character available" - context "with more than one input character available" - end - - describe "#consume_character" do - context "with no input available" do - it "produces an error message" - it "encodes the error position" - end - - context "with only one input character available" - context "with more than one input character available" - end - - describe "#read_segment" do - context "with no input available" - context "with an isa segment at the start of input" - context "with an isa segment after the start of input" - context "with input that doesnt have an isa segment" - end -end diff --git a/spec/lib/stupidedi/reader/success_spec.rb b/spec/lib/stupidedi/reader/success_spec.rb deleted file mode 100644 index 2cf80489e..000000000 --- a/spec/lib/stupidedi/reader/success_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -describe Stupidedi::Reader::Success do - def mksuccess(value, remainder) - Stupidedi::Reader::Result.success(value, remainder) - end - - describe "#value" do - it "returns the argument given to the constructor" do - mksuccess("a", "b").tap{|a, b| expect(a).to be == "a" } - end - end - - describe "#remainder" do - it "returns the argument given to the constructor" do - expect(mksuccess("a", "b").remainder).to be == "b" - mksuccess("a", "b").tap{|a, b| expect(b).to be == "b" } - end - end - - describe "#map" do - it "returns another result" do - expect(mksuccess("a", "b").map { }).to be_a(Stupidedi::Reader::Success) - end - - it "yields the value" - it "changes the value" - it "preserves the remainder" - end -end diff --git a/spec/lib/stupidedi/reader/token_reader_spec.rb b/spec/lib/stupidedi/reader/token_reader_spec.rb deleted file mode 100644 index dc1e52959..000000000 --- a/spec/lib/stupidedi/reader/token_reader_spec.rb +++ /dev/null @@ -1,755 +0,0 @@ -describe Stupidedi::Reader::TokenReader do - using Stupidedi::Refinements - - let(:ty) { Stupidedi::Versions::FiftyTen::ElementTypes } - let(:r) { ty::R .new("R" , "Float", 1, 1) } - let(:n0) { ty::N0.new("N0", "Whole Number", 1, 1) } - let(:n1) { ty::N1.new("N1", "Tenths Place", 1, 1) } - let(:n2) { ty::N2.new("N2", "Money", 1, 1) } - let(:n3) { ty::N3.new("N3", "N.nnn", 1, 1) } - let(:n4) { ty::N4.new("N4", "N.nnnn", 1, 1) } - let(:n5) { ty::N5.new("N5", "N.nnnnn", 1, 1) } - let(:n6) { ty::N6.new("N6", "N.nnnnnn", 1, 1) } - let(:n7) { ty::N7.new("N7", "N.nnnnnnn", 1, 1) } - let(:n8) { ty::N8.new("N8", "N.nnnnnnnnn", 1, 1) } - let(:n9) { ty::N9.new("N9", "N.nnnnnnnnnn", 1, 1) } - let(:id) { ty::ID.new("ID", "Qualifier", 1, 1) } - let(:an) { ty::AN.new("AN", "Free Text", 1, 1) } - let(:dt) { ty::DT.new("DT", "Date", 8, 8) } - let(:tm) { ty::TM.new("TM", "Time", 4, 6) } - - let(:rq) { Stupidedi::Versions::FiftyTen::ElementReqs } - let(:s) { Stupidedi::Schema } - - let(:separators) do - Stupidedi::Reader::Separators.new(":", "^", "*", "~") - end - - let(:dictionary) do - Stupidedi::Reader::SegmentDict.empty - end - - def mkseparators(component = ":", repetition = "^", element = "*", segment = "~") - Stupidedi::Reader::Separators.new(component, repetition, element, segment) - end - - def mkreader(input, separators = separators(), segment_dict = dictionary()) - Stupidedi::Reader::TokenReader.new(input, separators, segment_dict) - end - - describe "#stream?" do - it "returns false" do - expect(mkreader("")).not_to be_stream - end - end - - describe "#empty?" do - context "with no input available" do - it "is true" do - expect(mkreader("")).to be_empty - end - end - - context "with some input available" do - it "is false" do - expect(mkreader("abc")).not_to be_empty - end - end - end - - describe "#consume_prefix(s)" do - context "when s is empty" do - it "returns self" do - reader = mkreader("abc") - result = reader.consume_prefix("") - expect(result).to be_defined - result.map{|x| expect(x).to be == reader } - end - end - - context "when s is the entire input" do - property "returns empty reader" do - string(:alnum) - end.check do |s| - reader = mkreader(s) - result = reader.consume_prefix(s) - expect(result).to be_defined - result.map{|x| expect(x).to be_empty } - end - end - - context "when s is a prefix of the input" do - property "returns the remaining input" do - string(:alnum).bind{|s| s.split_at(between(0, s.length)) } - end.check do |p, s| - reader = mkreader("#{p}#{s}") - result = reader.consume_prefix(p) - expect(result).to be_defined - result.map{|x| expect(x.input).to be == s } - end - end - - context "when s is not a prefix of the input" do - property "returns a failure" do - a = with(:size, between(0, 5)) { string(:alnum) } - b = with(:size, between(0, 9)) { string(:alnum) } - guard(a.take(b.length) != b) - [a, b] - end.check do |a, b| - reader = mkreader(a) - result = reader.consume_prefix(b) - expect(result).to be_failure - expect(result.remainder).to be == a - end - end - end - - describe "#consume(s)" do - context "when s is empty" do - it "returns self" do - reader = mkreader("abc") - result = reader.consume("") - expect(result).to be_defined - result.map{|x| expect(x).to be == reader } - end - end - - context "when s is the entire input" do - property "returns empty reader" do - string(:alnum) - end.check do |s| - reader = mkreader(s) - result = reader.consume(s) - expect(result).to be_defined - result.map{|x| expect(x).to be_empty } - end - end - - context "when s is a prefix of the input" do - property "returns the remaining input" do - string(:alnum).bind{|s| s.split_at(between(0, s.length)) } - end.check do |p, s| - reader = mkreader("#{p}#{s}") - result = reader.consume(p) - expect(result).to be_defined - result.map{|x| expect(x.input).to be == s } - end - end - - context "when s is a suffix of the input" do - property "returns empty reader" do - p, s = string(:alnum).bind{|t| t.split_at(between(0, t.length)) } - guard("#{p}#{s}".index(s) >= p.length) - [p, s] - end.check do |p, s| - reader = mkreader("#{p}#{s}") - result = reader.consume(s) - expect(result).to be_defined - result.map{|x| expect(x).to be_empty } - end - end - - context "when s does not occur in the input" do - property "returns a failure" do - a = with(:size, between(0, 5)) { string(:alnum) } - b = with(:size, between(0, 9)) { string(:alnum) } - guard(a.index(b).nil?) - - [a, b] - end.check do |a, b| - reader = mkreader(a) - result = reader.consume(b) - expect(result).to be_failure - expect(result.remainder).to be == a - end - end - - context "when s occurs in the middle of the input" do - property "returns the remaining input" do - a = string(:alnum) - b = string(:alnum) - c = string(:alnum) - guard(a != b) - [a, b, c] - end.check do |a, b, c| - reader = mkreader("#{a}#{b}#{c}") - result = reader.consume(b) - expect(result).to be_defined - result.map{|x| expect(x.input).to be == c } - end - end - end - - describe "#read_character" do - context "when the input is empty" do - it "returns a failure" do - reader = mkreader("") - result = reader.read_character - expect(result).to be_failure - expect(result.remainder).to be == "" - expect(result.reason).to be =~ /less than one character available/ - end - end - - context "when the input is not empty" do - it "returns the remaining input" do - reader = mkreader("abc") - result = reader.read_character - expect(result).to be_defined - result.map{|x, remainder| expect(remainder.input).to be == "bc" } - end - - it "returns one character" do - reader = mkreader("abc") - result = reader.read_character - expect(result).to be_defined - result.map{|x, remainder| expect(x).to be == "a" } - end - end - end - - describe "#read_segment" do - context "when the input is empty" do - it "returns a failure" do - reader = mkreader("") - result = reader.read_segment - expect(result).to be_failure - expect(result).not_to be_fatal - expect(result.remainder).to be == "" - expect(result.reason).to be =~ /reached end of input/ - end - end - - context "when the input does not start with a valid segment identifier" do - it "returns a failure" do - # Segment ID cannot start with element separator "*" - reader = mkreader("*ABC") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "*ABC" - expect(result.reason).to be =~ /found "\*" instead of segment identifier/ - end - - it "returns a failure" do - # Segment ID cannot start with segment terminator "~" - reader = mkreader("~ABC") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "~ABC" - expect(result.reason).to be =~ /found "~" instead of segment identifier/ - end - - it "returns a failure" do - # Segment ID cannot start with a number - reader = mkreader("0ABC") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "0ABC" - expect(result.reason).to be =~ /found "0ABC" instead of segment identifier/ - end - - it "returns a failure" do - # Segment ID cannot be followed by component separator ":" - reader = mkreader("ABC:") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "ABC:" - expect(result.reason).to be =~ /found ":" following segment identifier/ - end - - it "returns a failure" do - # Segment ID cannot be followed by repetition separator "^" - reader = mkreader("ABC^") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "ABC^" - expect(result.reason).to be =~ /found "\^" following segment identifier/ - end - - it "returns a failure" do - # Segment ID must be followed by element delimiter "~" or segment terminator "*" - reader = mkreader("ABCD") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "ABCD" - expect(result.reason).to be =~ /found "D" following segment identifier/ - end - end - - context "when the input does not have a segment terminator" do - it "returns a failure" do - reader = mkreader("XYZ*A*B") - result = reader.read_segment - expect(result).to be_fatal - expect(result).to be_failure - expect(result.remainder).to be == "B" - expect(result.reason).to be =~ /reached end of input/ - end - end - - context "when the input starts with an ise segment" do - it "the remainder is a StreamReader" do - reader = mkreader("IEA~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :IEA - expect(value.element_toks.size).to be == 0 - expect(remainder.input).to be == "..." - expect(remainder).to be_stream - end - end - - it "the remainder is a StreamReader" do - reader = mkreader("IEA*A*B~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :IEA - expect(value.element_toks.size).to be == 2 - expect(remainder.input).to be == "..." - expect(remainder).to be_stream - end - end - end - - context "when the input contains no elements" do - it "returns the empty segment token" do - reader = mkreader("ABC~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 0 - expect(remainder.input).to be == "..." - end - end - - it "returns the remaining input" do - reader = mkreader("ABC~...") - result = reader.read_segment - expect(result).to be_defined - result.map{|value, remainder| expect(remainder.input).to be == "..." } - end - end - - context "when the input contains one element" do - it "returns the segment token" do - reader = mkreader("ABC*O~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 1 - expect(value.element_toks.head.value).to be == "O" - end - end - - it "returns the segment token" do - reader = mkreader("ABC*~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 1 - expect(value.element_toks.head.value).to be == "" - end - end - - it "returns the remaining input" do - reader = mkreader("ABC*O~...") - result = reader.read_segment - expect(result).to be_defined - result.map{|value, remainder| expect(remainder.input).to be == "..." } - end - end - - context "when the input contains many elements" do - it "returns the segment token" do - reader = mkreader("ABC*M*N*O~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.map(&:value)).to be == %w(M N O) - end - end - - it "returns the segment token" do - reader = mkreader("ABC***~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.map(&:value)).to be == ["", "", ""] - end - end - - it "returns the segment token" do - reader = mkreader("ABC***O~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.id).to be == :ABC - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.map(&:value)).to be == ["", "", "O"] - end - end - - it "returns the remaining input" do - reader = mkreader("ABC*M*O~...") - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "when the input contains too many elements" do - let(:segment_def) do - s::SegmentDef.build(:SEG, "Dummy Segment", "", - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(1)), - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(1))) - end - - let(:dictionary) do - Stupidedi::Reader::SegmentDict.build(:SEG => segment_def) - end - - it "returns the segment token" do - reader = mkreader("SEG*W*X*Y*Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 4 - expect(value.element_toks.map(&:value)).to be == %w(W X Y Z) - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W*X*Y*Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "when the input has a non-repeatable simple element" do - context "with a repetition separator" do - it "reads the separator as data" do - reader = mkreader("SEG*W^X*Y^Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.head.value).to be == "W^X" - expect(value.element_toks.last.value).to be == "Y^Z" - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W^X*Y^Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "with a component separator" do - it "reads the separator as data" do - reader = mkreader("SEG*W:X*Y:Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.head.value).to be == "W:X" - expect(value.element_toks.last.value).to be == "Y:Z" - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W:X*Y:Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - end - - context "when the input has a non-repeatable composite element" do - let(:segment_def) do - s::SegmentDef.build(:SEG, "Dummy Segment", "", - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(1)), - s::CompositeElementDef.build(:C000, "Dummy Composite", "", - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory)). - simple_use(rq::Mandatory, s::RepeatCount.bounded(1)), - s::CompositeElementDef.build(:C000, "Dummy Composite", "", - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory)). - simple_use(rq::Mandatory, s::RepeatCount.bounded(1))) - end - - let(:dictionary) do - Stupidedi::Reader::SegmentDict.build(:SEG => segment_def) - end - - context "and one component" do - it "returns the segment token" do - reader = mkreader("SEG*W*X*Y~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.head.value).to be == "W" - expect(value.element_toks.at(1).component_toks.size).to be == 1 - expect(value.element_toks.at(1).component_toks.head.value).to be == "X" - expect(value.element_toks.at(2).component_toks.size).to be == 1 - expect(value.element_toks.at(2).component_toks.head.value).to be == "Y" - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W*X*Y~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "and many components" do - it "returns the segment token" do - reader = mkreader("SEG*W*X:Y*M:N~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.head.value).to be == "W" - expect(value.element_toks.at(1).component_toks.size).to be == 2 - expect(value.element_toks.at(1).component_toks.head.value).to be == "X" - expect(value.element_toks.at(1).component_toks.last.value).to be == "Y" - expect(value.element_toks.at(2).component_toks.size).to be == 2 - expect(value.element_toks.at(2).component_toks.head.value).to be == "M" - expect(value.element_toks.at(2).component_toks.last.value).to be == "N" - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W*X:Y*M:N~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "and too many components" do - it "returns the segment token" do - reader = mkreader("SEG*W*X::Z*M:N:O:P~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.head.value).to be == "W" - expect(value.element_toks.at(1).component_toks.map(&:value)).to be == ["X", "", "Z"] - expect(value.element_toks.at(2).component_toks.map(&:value)).to be == %w(M N O P) - end - end - - it "returns the remaining input" do - reader = mkreader("SEG*W*X:Y:Z*M:N:O:P~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "and a component with a repetition separator" do - it "returns the segment token" do - reader = mkreader("SEG*W*X^Y:Z*M:N^O~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.head.value).to be == "W" - expect(value.element_toks.at(1).component_toks.map(&:value)).to be == %w(X^Y Z) - expect(value.element_toks.at(2).component_toks.map(&:value)).to be == %w(M N^O) - end - end - end - end - - context "" do - let(:segment_def) do - s::SegmentDef.build(:SEG, "Dummy Segment", "", - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(1)), - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(2))) - end - - let(:dictionary) do - Stupidedi::Reader::SegmentDict.build(:SEG => segment_def) - end - - context "when the input has no occurances of a repeatable simple element" do - it "returns the segment token" do - reader = mkreader("SEG**~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.head.value).to be == "" - end - end - - it "returns the remaining input" do - reader = mkreader("SEG**~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(remainder.input).to be == "..." - end - end - end - - context "when the input has one occurance of a repeatable simple element" do - it "returns the segment token" do - reader = mkreader("SEG**X~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.last.element_toks.size).to be == 1 - expect(value.element_toks.last.element_toks.head.value).to be == "X" - end - end - end - - context "when the input has many occurances of a repeatable simple element" do - it "returns the segment token" do - reader = mkreader("SEG**X^Y~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.last.element_toks.size).to be == 2 - expect(value.element_toks.last.element_toks.head.value).to be == "X" - expect(value.element_toks.last.element_toks.last.value).to be == "Y" - end - end - end - - context "when the input has too many occurances of a repeatable simple element" do - it "returns the segment token" do - reader = mkreader("SEG**X^Y^Z~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 2 - expect(value.element_toks.last.element_toks.size).to be == 3 - expect(value.element_toks.last.element_toks.map(&:value)).to be == %w(X Y Z) - end - end - end - end - - context "" do - let(:segment_def) do - s::SegmentDef.build(:SEG, "Dummy Segment", "", - id.simple_use(rq::Mandatory, s::RepeatCount.bounded(1)), - s::CompositeElementDef.build(:C000, "Dummy Composite", "", - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory)). - simple_use(rq::Mandatory, s::RepeatCount.bounded(2)), - s::CompositeElementDef.build(:C000, "Dummy Composite", "", - id.component_use(rq::Mandatory), - id.component_use(rq::Mandatory)). - simple_use(rq::Mandatory, s::RepeatCount.bounded(1))) - end - - let(:dictionary) do - Stupidedi::Reader::SegmentDict.build(:SEG => segment_def) - end - - context "when the input has no occurances of a repeatable composite element" do - it "returns the segment token" do - reader = mkreader("SEG***~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - end - end - end - - context "when the input has one occurance of a repeatable composite element" do - it "returns the segment token" do - reader = mkreader("SEG**A:B*~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.at(1).element_toks.size).to be == 1 - expect(value.element_toks.at(1).element_toks.head.component_toks.size).to be == 2 - expect(value.element_toks.at(1).element_toks.head.component_toks.head.value).to be == "A" - expect(value.element_toks.at(1).element_toks.head.component_toks.last.value).to be == "B" - end - end - end - - context "when the input has many occurances of a repeatable composite element" do - it "returns the segment token" do - reader = mkreader("SEG**A:B^C:D*~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.at(1).element_toks.head.component_toks.size).to be == 2 - expect(value.element_toks.at(1).element_toks.head.component_toks.head.value).to be == "A" - expect(value.element_toks.at(1).element_toks.head.component_toks.last.value).to be == "B" - expect(value.element_toks.at(1).element_toks.last.component_toks.head.value).to be == "C" - expect(value.element_toks.at(1).element_toks.last.component_toks.last.value).to be == "D" - end - end - end - - context "when the input has too many occurances of a repeatable composite element" do - it "returns the segment token" do - reader = mkreader("SEG**A:B^C^D:E:F*~...", separators, dictionary) - result = reader.read_segment - expect(result).to be_defined - result.map do |value, remainder| - expect(value.element_toks.size).to be == 3 - expect(value.element_toks.at(1).element_toks.head.component_toks.size).to be == 2 - expect(value.element_toks.at(1).element_toks.at(0).component_toks.head.value).to be == "A" - expect(value.element_toks.at(1).element_toks.at(0).component_toks.last.value).to be == "B" - expect(value.element_toks.at(1).element_toks.at(1).component_toks.head.value).to be == "C" - expect(value.element_toks.at(1).element_toks.at(2).component_toks.head.value).to be == "D" - expect(value.element_toks.at(1).element_toks.at(2).component_toks.last.value).to be == "F" - end - end - end - end - end -end From f25c82b9c6495773cd3da09fc40ade24932e1cb7 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sat, 22 Jun 2019 12:28:01 -0500 Subject: [PATCH 11/38] Housekeeping --- lib/ruby/array.rb | 2 +- lib/stupidedi/inspect.rb | 2 +- prof/memprof-pp.rb | 32 ++------ prof/microbench.rb | 80 +++++++++++++++++++ prof/mkfile.rb | 3 + spec/lib/stupidedi/parser/builder_dsl_spec.rb | 4 +- 6 files changed, 93 insertions(+), 30 deletions(-) create mode 100755 prof/microbench.rb diff --git a/lib/ruby/array.rb b/lib/ruby/array.rb index d34c41c38..392a20de2 100644 --- a/lib/ruby/array.rb +++ b/lib/ruby/array.rb @@ -174,7 +174,7 @@ def split_when(&block) # The concatenation of the result is equal to the original argument. # # @example - # "abba".split(//).group_seq(&:==) #=> [["y"], ["a"], ["b", "b"], ["a"]] + # "yabba".split(//).group_seq(&:==) #=> [["y"], ["a"], ["b", "b"], ["a"]] # # @return [[Array]] def runs(&block) diff --git a/lib/stupidedi/inspect.rb b/lib/stupidedi/inspect.rb index 122410f04..75e32bc8f 100644 --- a/lib/stupidedi/inspect.rb +++ b/lib/stupidedi/inspect.rb @@ -20,7 +20,7 @@ def inspect "#<\#" else "#<#{self.class.name}" - end + ":0x#{object_id.abs.to_s(16)} ...>" + end + ":0x#{(object_id << 1).to_s(16)} ...>" end end end diff --git a/prof/memprof-pp.rb b/prof/memprof-pp.rb index 432a1f6f7..dd0d5b0a3 100755 --- a/prof/memprof-pp.rb +++ b/prof/memprof-pp.rb @@ -1,46 +1,26 @@ #!/usr/bin/env ruby -Ilib require "stupidedi" -require "memory_profiler" - -# This will be auto-enabled when $stdout.tty?, but -C forces color output -require "term/ansicolor" if ARGV.delete("-C") -if idx = ARGV.index("--format") - ARGV.delete("--format") - format = ARGV.delete_at(idx) -else - format = "tree" -end - -unless %w(html tree x12).include?(format) - $stderr.puts "unrecognized format (expected html, tree, or x12)" - exit(1) -end +# require "memory_profiler" config = Stupidedi::Config.contrib(Stupidedi::Config.hipaa(Stupidedi::Config.default)) parser = Stupidedi::Parser.build(config) start = Time.now -MemoryProfiler.report do +# MemoryProfiler.report do ARGV.each do |path| mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 puts "Startup: %0.2d MiB" % mem - # Reading the entire input at once is slightly faster than streaming - # from a file handle. - # - # content = File.read(path, :encoding => "ISO-8859-1") - # parser, r = parser.read(Stupidedi::Reader.build(content)) - # reader = Stupidedi::Reader.build(File.read(path)) parser, = parser.read(reader) mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 puts "Finish : %0.2d MiB" % mem end -end.pretty_print(to_file: "prof/memprof-pp-#{start.strftime("%Y%m%dT%H%M%S")}.txt", - color_output: false, retained_strings: 100, - allocated_strings: 100, detailed_report: true, - scale_bytes: true) +# end.pretty_print(to_file: "prof/memprof-pp-#{start.strftime("%Y%m%dT%H%M%S")}.txt", +# color_output: false, retained_strings: 100, +# allocated_strings: 100, detailed_report: true, +# scale_bytes: true) mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 puts "Reports: %0.2d MiB" % mem diff --git a/prof/microbench.rb b/prof/microbench.rb new file mode 100755 index 000000000..de20e389c --- /dev/null +++ b/prof/microbench.rb @@ -0,0 +1,80 @@ +#!/usr/bin/env ruby +require "pp" +require "benchmark/ips" +require "memory_profiler" + +GC.disable + +def mem(label, &block) + result = MemoryProfiler::Reporter.report(&block) + print label.ljust(30); pp result.allocated_memory_by_class +end + +XS = [1, 2, 3, 4].freeze + +mem("<<") { s = ""; x = "x"; 1000.times { s << x }} +mem("+=") { s = ""; x = "x"; 1000.times { s += x }} + +exit + +############################################################################### + +XS = [1, 2, 3, 4].freeze +RX = /\S/ +RY = "abc" + +puts "REGEXP MATCHING #{"="*64}" +mem("===") { RX === RY } +mem("not") { not RX === RY } +mem("=~") { RX =~ RY } +mem("!~") { RX !~ RY } +mem("match?") { RX.match?(RY) } + +############################################################################### + +def cons_a(n, xs); i = 0; x = nil; while i < n; i += 1; x = [0] + xs ;end;end +def cons_b(n, xs); i = 0; x = nil; while i < n; i += 1; x = [0, *xs] ;end;end +def cons_c(n, xs); i = 0; x = nil; while i < n; i += 1; x = [0].push(*xs) ;end;end +def cons_d(n, xs); i = 0; x = nil; while i < n; i += 1; x = xs.dup.unshift(0) ;end;end + +puts +puts "CONS #{"="*73}" +mem("[0] + xs") { cons_a(500_000, XS) } +mem("[0, *xs]") { cons_b(500_000, XS) } +mem("[0].push(*xs)") { cons_c(500_000, XS) } +mem("xs.dup.unshift(0)") { cons_d(500_000, XS) } + +Benchmark.ips do |x| + x.report("[0] + xs") {|n| cons_a(n, XS) } + x.report("[0, *xs]") {|n| cons_b(n, XS) } + x.report("[0].push(*xs)") {|n| cons_c(n, XS) } + x.report("xs.dup.unshift(0)") {|n| cons_d(n, XS) } + x.compare! +end + +############################################################################### + +def snoc_a(n, xs); i = 0; x = nil; while i < n; i += 1; x = xs + [0] ;end;end +def snoc_b(n, xs); i = 0; x = nil; while i < n; i += 1; x = [*xs, 0] ;end;end +def snoc_c(n, xs); i = 0; x = nil; while i < n; i += 1; x = xs.dup.push(0) ;end;end +def snoc_d(n, xs); i = 0; x = nil; while i < n; i += 1; x = [0, *xs.reverse].reverse ;end;end + +puts +puts "SNOC #{"="*73}" +mem("xs + [5]") { snoc_a(500_000, XS) } +mem("[*xs, 5]") { snoc_b(500_000, XS) } +mem("xs.dup.push(5)") { snoc_c(500_000, XS) } +mem("[5, *xs.reverse].reverse") { snoc_d(500_000, XS) } + +Benchmark.ips do |x| + x.report("xs + [5]") {|n| snoc_a(n, XS) } + x.report("[*xs, 5]") {|n| snoc_b(n, XS) } + x.report("xs.dup.push(5)") {|n| snoc_c(n, XS) } + x.report("[5, *xs.reverse].reverse") {|n| snoc_d(n, XS) } + x.compare! +end + +############################################################################### + +mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 +puts "Process: %0.2d MiB" % mem diff --git a/prof/mkfile.rb b/prof/mkfile.rb index 10c4574c0..571e280ce 100755 --- a/prof/mkfile.rb +++ b/prof/mkfile.rb @@ -51,6 +51,9 @@ elsif ARGV.delete('--small') n = 10 m = 250 +elsif ARGV.delete('--large') + n = 1000 + m = 250 else n = 100 m = 250 diff --git a/spec/lib/stupidedi/parser/builder_dsl_spec.rb b/spec/lib/stupidedi/parser/builder_dsl_spec.rb index 346d79040..f3245db7e 100644 --- a/spec/lib/stupidedi/parser/builder_dsl_spec.rb +++ b/spec/lib/stupidedi/parser/builder_dsl_spec.rb @@ -748,7 +748,7 @@ def config(details, version = "005010") # an improvement for the error to happen immediately, like this: expect do b.ANA("123 MAIN") - end.raise_error(/segment NNA .+? is missing/) + end.to raise_error(/segment NNA .+? is missing/) end end @@ -1416,7 +1416,7 @@ def config(details, version = "005010") end end - fcontext "composite" do + context "composite" do context "when element is missing" do let(:b) do strict(Detail("2", Segment(10, COS(), s_mandatory, bounded(1)))) From c128060ba412170c3c1b4d6416dd0f14461ce3cc Mon Sep 17 00:00:00 2001 From: kputnam Date: Sat, 22 Jun 2019 16:52:32 -0500 Subject: [PATCH 12/38] Tokenizer works, allocates one 3-byte String per segment --- lib/stupidedi/reader/pointer.rb | 8 +- lib/stupidedi/reader/tokenizer.rb | 341 ++++++++++++--------- lib/stupidedi/reader/tokens/ignored_tok.rb | 4 +- 3 files changed, 210 insertions(+), 143 deletions(-) diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 6a62e13a7..6f6a63850 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -55,7 +55,7 @@ def initialize(storage, offset=0, length=storage.length) def inspect "#<%s%s@storage=0x%s @offset=%d @length=%d>" % [self.class.name.split("::").last, - @storage.frozen? ? "-" : " ", + @storage.frozen? ? "+" : "-", (@storage.object_id << 1).to_s(16), @offset, @length] end @@ -119,6 +119,10 @@ def last @storage[@offset + @length] if @length > 0 end + def end + self.class.new(@storage.freeze, @length, 0) + end + # True if `#at(n)` is defined. # # @return [Boolean] @@ -332,7 +336,7 @@ def match?(pattern, offset = 0) return reify.match?(pattern) end - if @offset + @length != @storage.length and ANCHORED_B.match?(pattern.inspect) + if @offset + @length != @storage.length and ANCHORED_Z.match?(pattern.inspect) # Because the pattern is anchored to the end, we can't match on # @storage directly, unless our end is also the end of @storage. return reify.match?(pattern) diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index 2f8812737..7a10d3d0b 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -4,9 +4,12 @@ module Stupidedi module Reader - # - # - # + # TODO: + # - improve errors, descriptor=... + # - determine what is yield'd, what is return'd + # - determine error recovery + # - mark correct positions + class Tokenizer SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ @@ -58,40 +61,45 @@ def self.todo end # - # Tokenizer operations return three bits of info: an error value, - # a success value, and the remaining unconsumed input + # Tokenizer operations return three pieces of information: either an + # error value or a success value (one piece is which one, the other + # piece is the value), and the remaining unconsumed input. # class Result - # @return [Input] - attr_reader :input + def done? + not fail? + end + end - def initialize(value, input) - @value, @input = - value, input + class Fail < Result + # @return [String] + attr_reader :error + + # @return [Position] + attr_reader :position + + def initialize(error, position) + @error, @position = error, position end - def pass? - not fail? + def fail? + true end + end - class Fail < Result - def error - @value - end + class Done < Result + # @return [Object] + attr_reader :value - def fail? - true - end - end + # @return [StringPtr] + attr_reader :rest - class Pass < Result - def value - @value - end + def initialize(value, rest) + @value, @rest = value, rest + end - def fail? - false - end + def fail? + false end end end @@ -116,6 +124,9 @@ def each(input) end end + # This method will skip over without tokenizing input, until an ISA + # segment is found. + # # @yield [SegmentTok | IgnoredTok | ErrorTok] def each_isa(input) return enum_for(:each, input) unless block_given? @@ -135,8 +146,9 @@ def each_isa(input) # Returns the input that was consumed as an IgnoredTok. If no "ISA" # was found, then the entire input will be consumed. # - # @return [Result] - def next_isa_segment_id(input) + # @yield [IgnoreTok] + # @return [Result] + def _next_isa_segment_id(input) offset = 0 while input.defined_at?(offset) @@ -144,16 +156,16 @@ def next_isa_segment_id(input) # case sensitive. Control characters are ignored between I, S, A i = input.index("I", offset) + # There's no I in the rest of the input, so it's all ignored + return eof("ISA", :position) if i.nil? + # In the next iteration, search for "I" begins right after this one offset = i + 1 - # There's no I in the rest of the input, so it's all ignored - return pass(IgnoredTok.new(input, :position), input.end) if i.nil? - s = input.index("S", i + 1) # There's no S in the rest of the input, so it's all ignored - return pass(IgnoredTok.new(input, :position), input.end) if s.nil? + return eof("ISA", :position) if s.nil? # There's something between I..S but it's not a control character next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER) @@ -161,81 +173,85 @@ def next_isa_segment_id(input) a = input.index("A", offset) # There's no A in the rest of the input, so it's all ignored - return pass(IgnoredTok.new(input, :position), input.end) if a.nil? + return eof("ISA", :position) if a.nil? # There's something between S..A but it's not a control character next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER) # Needed to perform the extra validation below - a = skip_control_characters(input, a) + a = _skip_control_characters(input, a) # The next character determines the element separator. If it's an # alphanumeric or space, we assume this is not the start of an ISA # segment. Perhaps a word like "L[ISA] " or "D[ISA]RRAY" - next if input[a+1].match?(/[a-zA-Z0-9 ]/) + next if not input.defined_at?(a+1) or input[a+1].match?(/[a-zA-Z0-9 ]/) - # Success - return pass(IgnoredTok.new(input.take(i), :position), input.drop(a+1)) + # Success, ignore everything before "I", resume parsing after "A". + yield IgnoredTok.new(input.take(i), :position) + return done(:position, input.drop(a+1)) end - # Give up at end of input - return pass(IgnoredTok.new(input, :position), input.end) + return eof("ISA", :position) end + # Read ISA segment and update element and segment separators in `state`. + # # This should be called when `input.head` is pointing at the first # element separator (commonly "*"). There is no validation done here # so take care, the results can be really strange otherwise. # # @return [Result>] - def read_isa_elements(input, state) + def _read_isa_elements(input, state) # The next character is a declaration of the element separator - state.separators = Separators.new(nil, nil, input.head, nil) + separators = Separators.new(nil, nil, input.head, nil) + descriptor = String.new("ISA01") # Read the first 15 simple elements into an array element_toks = 15.times.map do |n| - result = read_simple_element(input, state.separators, false) + result = _read_simple_element(input, separators, false, descriptor) return result if result.fail? - input = result.input + + input = result.rest + descriptor.succ! result.value end # We have to assume the last (16th) element is fixed-length because # it is not terminated by an element separator. First we will skip - # past control characters, then read the following character. - offset = skip_control_characters(input, 1) - return fail("reached eof before ISA16", input) \ - unless input.defined_at?(offset) + # past control characters, then read the next character. + offset = _skip_control_characters(input, 1) + return eof("ISA16", :position) unless input.defined_at?(offset) + element_toks << SimpleElementTok.build(input[offset], :position) - # The character after the last element is defined to be the + # The character immediately after ISA16 is defined to be the # segment terminator. Here we do not skip past control characters, # so the separator could be a control character - return fail("reached eof before segment terminator for ISA", input) \ + return eof("segment terminator for ISA", :position) \ unless input.defined_at?(offset + 1) - state.separators.segment = input[offset + 1] - pass(element_toks, input.drop(offset + 2)) + state.separators = separators + state.separators.segment = input[offset + 1] + done(element_toks, input.drop(offset + 2)) end # @yield [IgnoreTok] # @return [Result] def next_isa_segment(input, state) - result = next_isa_segment_id(input) - return result if result.fail? - - unless result.value.value.empty? + position = _next_isa_segment_id(input) do |t| # It's not unusual for IgnoreTok to have no content, because "ISA" # was found at the first character of the input. - yield result.value if block_given? + yield t if block_given? and not t.value.blank? end + return position if position.fail? - result = read_isa_elements(result.input, state) + result = _read_isa_elements(position.rest, state) return result if result.fail? - pass(SegmentTok.build(:ISA, result.value, :position), result.input) + done(SegmentTok.build(:ISA, result.value, position.value), result.rest) end - # Works similarly to `next_isa_segment_id`, except the result is the + # Works similarly to `_next_isa_segment_id`, except the result is the # segment identifier. The remaining input begins where an element or # segment separator should be. # @@ -244,13 +260,13 @@ def next_isa_segment(input, state) # no validation done on the next character, even though it should be a # segment or element separator. # - # @return [Symbol] - def next_segment_id(input, state) - offset = skip_control_characters(input) + # @return [Array(Symbol, Position)] + def _next_segment_id(input, state) + offset = _skip_control_characters(input) buffer = input.drop(offset).take(0) while true - return fail("reached eof before segment identifier", input) \ + return eof("segment identifier", :position) \ unless input.defined_at?(offset) char = input[offset] @@ -260,40 +276,49 @@ def next_segment_id(input, state) offset += 1 next if Reader.is_control_character?(char) + # Zero-copy as long as we've not skipped over any characters yet buffer += char break if buffer.length >= 3 end - return fail("found '#{buffer}' instead of segment identifier", input) \ - unless buffer.match?(Tokenizer::SEGMENT_ID) + segment_id = buffer.to_s + + return expected("segment identifier, found %s" % segment_id.inspect, :position) \ + unless segment_id.match?(Tokenizer::SEGMENT_ID) - offset = skip_control_characters(input, offset) - return pass(buffer.to_sym, input.drop(offset)) + offset = _skip_control_characters(input, offset) + return done([segment_id.to_sym, :position], input.drop(offset)) end # @return [SegmentTok] def next_segment(input, state) if state.separators.nil? - return next_isa_segment(input, state){|t| yield t } + # We haven't yet found an ISA segment, which requires a different + # method than `next_segment`. + return next_isa_segment(input, state){|t| yield t if block_given? } end - result = next_segment_id(input, state) - return result if result.fail? # TODO yield - - segment_id = result.value + result = _next_segment_id(input, state) + return result if result.fail? + segment_id, position = result.value - unless result.input.head == state.separators.element \ - or result.input.head == state.separators.segment - # This is a redundant check, because we have to check in read_elements - # but here we can provide a better error location - return fail("invalid %s after segment identifier for %s" % [ - result.input.head, segment_id], input) # TODO yield - end + # Note, _next_segment_id does not guarantee result.rest isn't eof + return unexpected("eof after %s" % segment_id, :position) \ + if result.rest.empty? if segment_id == :ISA # We encountered a new ISA segment without having seen the previous # ISA's matching IEA segment. - return read_isa_elements(result.input, state){|t| yield t } + result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } + return result if result.fail? + + return done(SegmentTok.build(:ISA, result.value, position), result.rest) + end + + unless result.rest.head == state.separators.element \ + or result.rest.head == state.separators.segment + return unexpected("%s after segment identifier %s" % [ + result.rest.head.inspect, segment_id], :position) # TODO yield end if state.segment_dict.defined_at?(segment_id) @@ -302,35 +327,36 @@ def next_segment(input, state) element_uses = [] end - # Note, next_segment_id guarantees result.input isn't EOF - result = read_elements(segment_id, result.input, state.separators, element_uses) + result = _read_elements(segment_id, result.rest, state.separators, element_uses) return result if result.fail? # TODO yield - if segment_id == :IEA - # Stop parsing X12 and search for the next ISA before resuming - state.separators = nil - end + # We've parsed an IEA segment, so reset and look for an ISA next time + state.separators = nil if segment_id == :IEA - pass(SegmentTok.build(segment_id, result.value, :position), result.input) + done(SegmentTok.build(segment_id, result.value, :position), result.rest) end - # Input should be positioned on an element separator: "NM1[*]..*..*..~" + # @params element_uses Indicates which elements are composite and/or + # repeatable # # @return [Array] - def read_elements(segment_id, input, separators, element_uses=[]) - elements = [] + # + # @note Input should be positioned on an element separator: "NM1[*]..*..*..~" + def _read_elements(segment_id, input, separators, element_uses=[]) + element_toks = [] + descriptor = String.new("#{segment_id}01") # We are placed on an element separator at the start of each iteration - until input.empty? or input.head != separators.element + while not input.empty? and input.head == separators.element result = - if element_uses.defined_at?(elements.length) - element_use = element_uses[elements.length] + if element_uses.defined_at?(element_toks.length) + element_use = element_uses[element_toks.length] repeatable = element_use.repeatable? if element_use.composite? - read_composite_element(input.tail, separatorse, repeatable) + _read_composite_element(input, separatorse, repeatable, descriptor) else - read_simple_element(input, separators, repeatable) + _read_simple_element(input, separators, repeatable, descriptor) end else # We either don't have a corresponding SegmentDef or it has @@ -339,64 +365,81 @@ def read_elements(segment_id, input, separators, element_uses=[]) # # If the input contains a component or repetition separator, # they will be interpreted as ordinary characters. - read_simple_element(input, separators, repeatable) + _read_simple_element(input, separators, repeatable, descriptor) end return result if result.fail? - elements << result.value - input = result.input + element_toks << result.value + input = result.rest + descriptor.succ! end - if input.empty? - fail("reached eof before segment terminator for %s" % segment_id, input) - elsif input.head != separators.segment - fail("expected segment terminator for %s but saw '%s'" % [segment_id, input.head], input) - else - # Skip past the segment separator - pass(elements, input.tail) - end + return eof("segment terminator for %s" % segment_id, :position) \ + if input.empty? + + return expected("segment terminator for %s, found %s" % + [segment_id, input.head.inspect], :position) \ + if input.head != separators.segment + + # Skip past the segment separator + done(element_toks, input.tail) end + # @param repeatable When false, repetition separator is treated as data + # @param descriptor "CLM01" + # # @return [CompositeElementTok] - def read_composite_element(input, separators, repeatable, descriptor="a composite element") - return fail("reached eof while expecting #{descriptor}", input) \ + def _read_composite_element(input, separators, repeatable, descriptor) + return eof("element separator before %s" % descriptor, :position) \ if input.empty? - return fail("expected an element separator before #{descriptor}", input) \ + return expected("element separator before %s, found %s" % + [descriptor, input.head.inspect], :position) \ unless input.head == separators.element builder = ElementTokBuilder.build(repeatable, :position) component_toks = [] + descriptor_ = "#{descriptor}-01" until input.empty? - result = read_component_element(input, separators, false) + result = _read_component_element(input, separators, false, descriptor_) return result if result.fail? - input = result.input + + input = result.rest component_toks << result.value if repeatable and input.head == separators.repetition + # TODO: We could return unexpected("repetition separator for + # non-repeatable %s" % descriptor) builder.add(CompositeElementTok.build(component_toks, :position)) component_toks.clear elsif input.head == separators.element \ or input.head == separators.segment builder.add(CompositeElementTok.build(component_toks, :position)) - return pass(builder.build, input) + return done(builder.build, input) end + + descriptor_.succ! end - fail("reached eof while reading #{descriptor}", input) + eof("element or segment separator after %s" % descriptor, input) end + # @param repeatable So far, X12 does not allow components to repeat + # @param descriptor "CLM01-04" + # # @return [ComponentElementTok] - def read_component_element(input, separators, repeatable, descriptor="a component element") - return fail("reached eof while expecting #{descriptor}", input) \ - if input.empty? + def _read_component_element(input, separators, repeatable, descriptor) + return eof("element or component separator before %s" % descriptor, + :position) if input.empty? - return fail("expected an element or component separator before #{descriptor}", input) \ - unless input.head == separators.element or input.head == separators.component + return expected("element or component separator before %s, found %s" % + [descriptor, input.head.inspect], :position) \ + unless input.head == separators.element \ + or input.head == separators.component - offset = skip_control_characters(input, 1) + offset = _skip_control_characters(input, 1) buffer = input.drop(offset).take(0) builder = ElementTokBuilder.build(repeatable, :position) @@ -404,36 +447,45 @@ def read_component_element(input, separators, repeatable, descriptor="a componen char = input[offset] if repeatable and char == separators.repetition + # TODO: We could return unexpected("repetition separator for + # non-repeatable %s" % descriptor) builder.add(SimpleElementTok.build(buffer, :position)) offset += 1 elsif char == separators.segment \ or char == separators.element \ - or char == separators.component \ - or char == separators.repetition + or char == separators.component builder.add(ComponentElementTok.build(buffer, :position)) - return pass(builder.token, input.drop(offset)) + return done(builder.token, input.drop(offset)) else + # This is zero-copy as long as we haven't skipped any characters buffer << char unless Reader.is_control_character?(char) offset += 1 end end - return fail("reached eof while reading #{descriptor}", input) + if repeatable + eof("segment, element, component or repetition separator after %s" % + descriptor, :position) + else + eof("segment, element or repetition separator after %s" % + descriptor, :position) + end end # Input should be positioned on an element separator: "NM1[*]..*..*..~" # # @return [SimpleElementTok] - def read_simple_element(input, separators, repeatable, descriptor="an element") - return fail("reached eof while expecting #{descriptor}", input) \ + def _read_simple_element(input, separators, repeatable, descriptor) + return eof("element separator before %s" % descriptor, :position) \ if input.empty? - return fail("expected an element separator before #{descriptor}", input) \ + return expected("element separator before %s, found %s" % + [descriptor, input.head.inspect], :position) \ unless input.head == separators.element - offset = skip_control_characters(input, 1) + offset = _skip_control_characters(input, 1) buffer = input.drop(offset).take(0) builder = ElementTokBuilder.build(repeatable, :position) @@ -443,23 +495,26 @@ def read_simple_element(input, separators, repeatable, descriptor="an element") if char == separators.element \ or char == separators.segment builder.add(SimpleElementTok.build(buffer, :position)) - return pass(builder.build, input.drop(offset)) + return done(builder.build, input.drop(offset)) elsif repeatable and char == separators.repetition + # TODO: We could return unexpected("repetition separator for + # non-repeatable %s" % descriptor) builder.add(SimpleElementTok.build(buffer, :position)) offset += 1 else + # This is zero-copy as long as we haven't skipped any characters buffer << char unless Reader.is_control_character?(char) offset += 1 end end - return fail("reached eof while reading #{descriptor}", input) + eof("segment or element separator after %s" % descriptor, :position) end # @return [Integer] - def skip_control_characters(input, offset=0) + def _skip_control_characters(input, offset=0) while input.defined_at?(offset) \ and Reader.is_control_character?(input[offset]) offset += 1 @@ -468,16 +523,24 @@ def skip_control_characters(input, offset=0) offset end - # @param value [Object] Result - # @param input [Input] Remaining unconsumed input - def pass(value, input) - Tokenizer::Result::Pass.new(value, input) + def done(value, rest) + Tokenizer::Done.new(value, rest) + end + + def fail(error, position) + Tokenizer::Fail.new(error, position) + end + + def expected(error, position) + Tokenizer::Fail.new("expected #{error}", position) + end + + def unexpected(error, positiion) + Tokenizer::Fail.new("unexpected #{error}", position) end - # @param value [Object] Error message - # @param input [Input] Input beginning where the error occurred - def fail(value, input) - Tokenizer::Result::Fail.new(value, input) + def eof(error, position) + expected("expected #{error}, found eof", position) end class ElementTokBuilder diff --git a/lib/stupidedi/reader/tokens/ignored_tok.rb b/lib/stupidedi/reader/tokens/ignored_tok.rb index 2ebb919eb..c6ca4785a 100644 --- a/lib/stupidedi/reader/tokens/ignored_tok.rb +++ b/lib/stupidedi/reader/tokens/ignored_tok.rb @@ -26,12 +26,12 @@ def copy(changes = {}) # :nocov: def pretty_print(q) - q.pp(:ignored.cons(@value)) + q.pp([:ignored, @value]) end # :nocov: def blank? - @value.all?(&:blank?) + @value.blank? end def present? From f80b6b7ad1cdc3e55b0db16b563da43146866aa9 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 23 Jun 2019 01:34:22 -0500 Subject: [PATCH 13/38] Grind away the memory allocations, from 985MB allocated to 877MB in test benchmark --- lib/stupidedi/reader/pointer.rb | 41 +- lib/stupidedi/reader/tokenizer.rb | 233 +++--- lib/stupidedi/reader/tokens/segment_tok.rb | 20 +- prof/memprof-tk-20190622T211556.txt | 765 ++++++++++++++++++++ prof/memprof-tk-20190622T212056.txt | 771 ++++++++++++++++++++ prof/memprof-tk-20190622T233539.txt | 770 ++++++++++++++++++++ prof/memprof-tk-20190623T010633.txt | 779 +++++++++++++++++++++ prof/microbench.rb | 31 +- 8 files changed, 3284 insertions(+), 126 deletions(-) create mode 100644 prof/memprof-tk-20190622T211556.txt create mode 100644 prof/memprof-tk-20190622T212056.txt create mode 100644 prof/memprof-tk-20190622T233539.txt create mode 100644 prof/memprof-tk-20190623T010633.txt diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 6f6a63850..509bce817 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -73,10 +73,10 @@ def reify(always_allocate = false) and @offset == 0 \ and @length == @storage.length \ and not always_allocate - $stderr.puts "reify: no allocation" + #stderr.puts "reify: no allocation" @storage else - $stderr.puts "reify: allocate[#@offset, #@length]" + #stderr.puts "reify: allocate[#@offset, #@length]" @storage[@offset, @length] end end @@ -257,6 +257,7 @@ def split_at(n) # @return [Pointer] def +(other) if @storage.eql?(other.storage) and @offset + @length == other.offset + # allocations: 0 strings, 1 pointer self.class.new(@storage.freeze, @offset, @length + other.length) else # It doesn't make much sense to allocate two new operands and then @@ -265,6 +266,8 @@ def +(other) # TODO: Should this be a new Pointer? Depends on how the result # will be used. If more concatenation is done, then it's a waste, # and slightly worse than plain String + String. + # + # allocations: 2 string, 0 pointers reify(true) << other.reify end end @@ -495,18 +498,21 @@ def count(char) def <<(other) if other.is_a?(self.class) if @storage.eql?(other.storage) and @offset + @length == other.offset + # allocations: 0 strings, 0 pointers @length += other.length - elsif @storage.frozen? + elsif not @storage.frozen? + # Surely no one will notice if we destructively update @storage + # allocations: 1 string, 0 pointers + @storage << other.reify + @length += other.length + else # Other flyweights are sharing our storage. We need to make our # own copy now. Be sure `reify` gives back a copy, not the original. + # allocations: 2 strings, 0 pointers @storage = reify(true) @storage << other.reify @length += other.length @offset = 0 - else - # Surely no one will notice if we destructively update @storage - @storage << other.reify - @length += other.length end # NOTE: There doesn't seem to be a string comparison function in Ruby @@ -520,18 +526,23 @@ def <<(other) # elsif @storage.length - @offset - @length >= other.length \ and @storage.index(other, @offset + @length) == @offset + @length + # allocations: 0 strings, 0 pointers @length += other.length - elsif @storage.frozen? + elsif not @storage.frozen? + # Surely no one will notice if we destructively update @storage + # + # allocations: 0 strings, 0 pointers + @storage << other + @length += other.length + else # Other flyweights are sharing our storage. We need to make our # own copy now. Be sure `reify` gives back a copy, not the original. + # + # allocations: 1 string, 0 pointers @storage = reify(true) @storage << other @length += other.length @offset = 0 - else - # Surely no one will notice if we destructively update @storage - @storage << other - @length += other.length end self @@ -567,9 +578,10 @@ def <<(other) def +(other) if other.is_a?(self.class) if @storage.eql?(other.storage) and @offset + @length == other.offset + # allocations: 0 strings, 1 pointer self.class.new(@storage.freeze, @offset, @length + other.length) else - # TODO: Explain why we're not returning a Pointer + # allocations: 2 strings, 0 pointers reify(true) << other.reify end @@ -583,9 +595,10 @@ def +(other) # elsif @storage.length - @offset - @length >= other.length \ and @storage.index(other, @offset + @length) == @offset + @length + # allocations: 0 strings, 1 pointer self.class.new(@storage.freeze, @offset, @length + other.length) else - # TODO: Explain why we're not returning a Pointer + # allocations: 2 strings, 0 pointers reify(true) << other end end diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index 7a10d3d0b..c9ecff70a 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -5,10 +5,11 @@ module Stupidedi module Reader # TODO: - # - improve errors, descriptor=... + # - review/improve errors # - determine what is yield'd, what is return'd # - determine error recovery # - mark correct positions + # - switch char-by-char concat with regexps that grab whole elements class Tokenizer SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ @@ -32,9 +33,7 @@ def each_isa Tokenizer.each_isa(@input) end end - end - class Tokenizer # # Track the configuration info, which controls how the tokenizer reads # input. The `separators` field is straightforward, but `segment_dict` @@ -50,13 +49,16 @@ class State # @return [SegmentDict] attr_accessor :segment_dict - def initialize(separators, segment_dict) - @separators, @segment_dict = - separators, segment_dict + # @return [ElementTokSwitch] + attr_accessor :builder + + def initialize(separators, segment_dict, builder) + @separators, @segment_dict, @builder = + separators, segment_dict, builder end def self.todo - new(Separators.new(":", "^", "*", "~"), Hash.new) + new(Separators.new(":", "^", "*", "~"), SegmentDict.empty, ElementTokSwitch.new) end end @@ -102,6 +104,71 @@ def fail? false end end + + module ElementTokBuilder + class Repeatable + def initialize(position) + @position, @element_toks = position, [] + end + + def add(element_tok) + @element_toks.push(element_tok) + end + + def build + RepeatedElementTok.build(@element_toks, @element_toks.head.position) + end + + def reset!(position) + @position = position + @element_toks = element_toks + self + end + end + + class NonRepeatable + def initialize(position) + @position = position + end + + def add(element_tok) + @element_tok = element_tok + end + + def build + @element_tok + end + + def reset!(position) + @position = position + @element_tok = nil + self + end + end + end + + # + # This is a bit clunky, but we can save quite a lot of memory allocation + # by reusing our ElementTokBuilders. This is done by keeping a pair and + # reseting them before giving to a caller. We thread this switch through + # various parts of the Tokenizer storing it in State. + # + # Otherwise, one of these would be allocated for every time a simple or + # composite is tokenized. + # + class ElementTokSwitch + def_delegators :@active, :add, :build, :reset! + + def initialize + @repeatable = ElementTokBuilder::Repeatable.new(nil) + @nonrepeatable = ElementTokBuilder::NonRepeatable.new(nil) + end + + def switch(repeatable, position) + @active = repeatable ? @repeatable : @nonrepeatable + @active.reset!(position) + end + end end class << Tokenizer @@ -204,15 +271,16 @@ def _next_isa_segment_id(input) def _read_isa_elements(input, state) # The next character is a declaration of the element separator separators = Separators.new(nil, nil, input.head, nil) - descriptor = String.new("ISA01") + + # NOTE: This destructive update would make backtracking more painful + state.separators = separators # Read the first 15 simple elements into an array - element_toks = 15.times.map do |n| - result = _read_simple_element(input, separators, false, descriptor) + element_toks = 15.times.map do |element_idx| + result = _read_simple_element(input, state, false, :ISA, element_idx+1) return result if result.fail? input = result.rest - descriptor.succ! result.value end @@ -230,7 +298,6 @@ def _read_isa_elements(input, state) return eof("segment terminator for ISA", :position) \ unless input.defined_at?(offset + 1) - state.separators = separators state.separators.segment = input[offset + 1] done(element_toks, input.drop(offset + 2)) end @@ -262,22 +329,23 @@ def next_isa_segment(input, state) # # @return [Array(Symbol, Position)] def _next_segment_id(input, state) - offset = _skip_control_characters(input) - buffer = input.drop(offset).take(0) + offset = _skip_control_characters(input) + buffer = input.drop(offset).take(0) + separators = state.separators while true return eof("segment identifier", :position) \ unless input.defined_at?(offset) char = input[offset] - break if state.separators.element == char - break if state.separators.segment == char + break if separators.element == char + break if separators.segment == char offset += 1 next if Reader.is_control_character?(char) # Zero-copy as long as we've not skipped over any characters yet - buffer += char + buffer << char break if buffer.length >= 3 end @@ -292,7 +360,9 @@ def _next_segment_id(input, state) # @return [SegmentTok] def next_segment(input, state) - if state.separators.nil? + separators = state.separators + + if separators.nil? # We haven't yet found an ISA segment, which requires a different # method than `next_segment`. return next_isa_segment(input, state){|t| yield t if block_given? } @@ -315,8 +385,8 @@ def next_segment(input, state) return done(SegmentTok.build(:ISA, result.value, position), result.rest) end - unless result.rest.head == state.separators.element \ - or result.rest.head == state.separators.segment + unless result.rest.head == separators.element \ + or result.rest.head == separators.segment return unexpected("%s after segment identifier %s" % [ result.rest.head.inspect, segment_id], :position) # TODO yield end @@ -327,7 +397,7 @@ def next_segment(input, state) element_uses = [] end - result = _read_elements(segment_id, result.rest, state.separators, element_uses) + result = _read_elements(result.rest, state, segment_id, element_uses) return result if result.fail? # TODO yield # We've parsed an IEA segment, so reset and look for an ISA next time @@ -342,9 +412,10 @@ def next_segment(input, state) # @return [Array] # # @note Input should be positioned on an element separator: "NM1[*]..*..*..~" - def _read_elements(segment_id, input, separators, element_uses=[]) + def _read_elements(input, state, segment_id, element_uses=[]) element_toks = [] - descriptor = String.new("#{segment_id}01") + element_idx = 1 + separators = state.separators # We are placed on an element separator at the start of each iteration while not input.empty? and input.head == separators.element @@ -354,9 +425,9 @@ def _read_elements(segment_id, input, separators, element_uses=[]) repeatable = element_use.repeatable? if element_use.composite? - _read_composite_element(input, separatorse, repeatable, descriptor) + _read_composite_element(input, state, repeatable, segment_id, element_idx) else - _read_simple_element(input, separators, repeatable, descriptor) + _read_simple_element(input, state, repeatable, segment_id, element_idx) end else # We either don't have a corresponding SegmentDef or it has @@ -365,13 +436,13 @@ def _read_elements(segment_id, input, separators, element_uses=[]) # # If the input contains a component or repetition separator, # they will be interpreted as ordinary characters. - _read_simple_element(input, separators, repeatable, descriptor) + _read_simple_element(input, state, repeatable, segment_id, element_idx) end return result if result.fail? element_toks << result.value input = result.rest - descriptor.succ! + element_idx += 1 end return eof("segment terminator for %s" % segment_id, :position) \ @@ -386,31 +457,31 @@ def _read_elements(segment_id, input, separators, element_uses=[]) end # @param repeatable When false, repetition separator is treated as data - # @param descriptor "CLM01" # # @return [CompositeElementTok] - def _read_composite_element(input, separators, repeatable, descriptor) - return eof("element separator before %s" % descriptor, :position) \ + def _read_composite_element(input, state, repeatable, segment_id, element_idx) + separators = state.separators + + return eof("element separator before %s%02d" % [segment_id, element_idx], :position) \ if input.empty? - return expected("element separator before %s, found %s" % - [descriptor, input.head.inspect], :position) \ + return expected("element separator before %s%02d, found %s" % + [segment_id, element_idx, input.head.inspect], :position) \ unless input.head == separators.element - builder = ElementTokBuilder.build(repeatable, :position) + builder = state.builder.switch(repeatable, :position) component_toks = [] - descriptor_ = "#{descriptor}-01" + component_idx = 1 until input.empty? - result = _read_component_element(input, separators, false, descriptor_) + result = _read_component_element(input, state, false, segment_id, element_idx, component_idx) return result if result.fail? input = result.rest component_toks << result.value if repeatable and input.head == separators.repetition - # TODO: We could return unexpected("repetition separator for - # non-repeatable %s" % descriptor) + # TODO: We could return unexpected("repetition separator for ...") builder.add(CompositeElementTok.build(component_toks, :position)) component_toks.clear @@ -423,32 +494,32 @@ def _read_composite_element(input, separators, repeatable, descriptor) descriptor_.succ! end - eof("element or segment separator after %s" % descriptor, input) + eof("element or segment separator after %s%02d" % [segment_id, element_idx], input) end # @param repeatable So far, X12 does not allow components to repeat - # @param descriptor "CLM01-04" # # @return [ComponentElementTok] - def _read_component_element(input, separators, repeatable, descriptor) - return eof("element or component separator before %s" % descriptor, - :position) if input.empty? + def _read_component_element(input, state, repeatable, segment_id, element_idx, component_idx) + separators = state.separators + + return eof("element or component separator before %s%02d-%02d" % [ + segment_id, element_idx, component_idx], :position) if input.empty? - return expected("element or component separator before %s, found %s" % - [descriptor, input.head.inspect], :position) \ + return expected("element or component separator before %s%02d-%02d, found %s" % + [segment_id, element_idx, component_idx, input.head.inspect], :position) \ unless input.head == separators.element \ or input.head == separators.component offset = _skip_control_characters(input, 1) buffer = input.drop(offset).take(0) - builder = ElementTokBuilder.build(repeatable, :position) + builder = state.builder.switch(repeatable, :position) while input.defined_at?(offset) char = input[offset] if repeatable and char == separators.repetition - # TODO: We could return unexpected("repetition separator for - # non-repeatable %s" % descriptor) + # TODO: We could return unexpected("repetition separator for ...") builder.add(SimpleElementTok.build(buffer, :position)) offset += 1 @@ -456,7 +527,7 @@ def _read_component_element(input, separators, repeatable, descriptor) or char == separators.element \ or char == separators.component builder.add(ComponentElementTok.build(buffer, :position)) - return done(builder.token, input.drop(offset)) + return done(builder.build, input.drop(offset)) else # This is zero-copy as long as we haven't skipped any characters @@ -466,28 +537,30 @@ def _read_component_element(input, separators, repeatable, descriptor) end if repeatable - eof("segment, element, component or repetition separator after %s" % - descriptor, :position) + eof("segment, element, component or repetition separator after %s%02d-%02d" % [ + segment_id, element_idx, component_idx], :position) else - eof("segment, element or repetition separator after %s" % - descriptor, :position) + eof("segment, element or repetition separator after %s%02d-%02d" % [ + segment_id, element_idx, component_idx], :position) end end # Input should be positioned on an element separator: "NM1[*]..*..*..~" # # @return [SimpleElementTok] - def _read_simple_element(input, separators, repeatable, descriptor) - return eof("element separator before %s" % descriptor, :position) \ - if input.empty? + def _read_simple_element(input, state, repeatable, segment_id, element_idx) + separators = state.separators - return expected("element separator before %s, found %s" % - [descriptor, input.head.inspect], :position) \ + return eof("element separator before %s%02d" % [segment_id, element_idx], + :position) if input.empty? + + return expected("element separator before %s%02d, found %s" % [ + segment_id, element_idx, input.head.inspect], :position) \ unless input.head == separators.element offset = _skip_control_characters(input, 1) buffer = input.drop(offset).take(0) - builder = ElementTokBuilder.build(repeatable, :position) + builder = state.builder.switch(repeatable, :position) while input.defined_at?(offset) char = input[offset] @@ -498,8 +571,7 @@ def _read_simple_element(input, separators, repeatable, descriptor) return done(builder.build, input.drop(offset)) elsif repeatable and char == separators.repetition - # TODO: We could return unexpected("repetition separator for - # non-repeatable %s" % descriptor) + # TODO: We could return unexpected("repetition separator for ...") builder.add(SimpleElementTok.build(buffer, :position)) offset += 1 @@ -510,7 +582,8 @@ def _read_simple_element(input, separators, repeatable, descriptor) end end - eof("segment or element separator after %s" % descriptor, :position) + eof("segment or element separator after %s%02d" % [ + segment_id, element_idx], :position) end # @return [Integer] @@ -542,44 +615,6 @@ def unexpected(error, positiion) def eof(error, position) expected("expected #{error}, found eof", position) end - - class ElementTokBuilder - def self.build(repeatable, position) - if repeatable - Repeatable.new(position) - else - NonRepeatable.new(position) - end - end - - class Repeatable - def initialize(position) - @position, @element_toks = position, [] - end - - def add(element_tok) - @element_toks.push(element_tok) - end - - def build - RepeatedElementTok.build(@element_toks, @element_toks.head.position) - end - end - - class NonRepeatable - def initialize(position) - @position = position - end - - def add(element_tok) - @element_tok = element_tok - end - - def build - @element_tok - end - end - end end end end diff --git a/lib/stupidedi/reader/tokens/segment_tok.rb b/lib/stupidedi/reader/tokens/segment_tok.rb index 80fedead5..2ef0ea92b 100644 --- a/lib/stupidedi/reader/tokens/segment_tok.rb +++ b/lib/stupidedi/reader/tokens/segment_tok.rb @@ -7,7 +7,7 @@ class SegmentTok include Inspect # @return [Symbol] - attr_reader :id + attr_reader :segment_id # @return [Array] attr_reader :element_toks @@ -15,22 +15,22 @@ class SegmentTok # @return [Position] attr_reader :position - def initialize(id, element_toks, position) - @id, @element_toks, @position = - id, element_toks, position + def initialize(segment_id, element_toks, position) + @segment_id, @element_toks, @position = + segment_id, element_toks, position end # @return [SegmentTok] def copy(changes = {}) SegmentTok.new \ - changes.fetch(:id, @id), + changes.fetch(:segment_id, @segment_id), changes.fetch(:element_toks, @element_toks), changes.fetch(:position, @position) end # :nocov: def pretty_print(q) - q.pp(:segment.cons(@id.cons(@element_toks))) + q.pp(:segment.cons(@segment_id.cons(@element_toks))) end # :nocov: @@ -44,12 +44,12 @@ def present? def to_x12(separators) if blank? - "#{id}#{(separators.segment || "~").strip}" + "#{segment_id}#{(separators.segment || "~").strip}" else es = @element_toks.map{|x| x.to_x12(separators) } sep = separators.element || "*" eos = separators.segment || "~" - id.cons(es).join(sep).gsub(/#{Regexp.escape(sep)}+$/, "") + eos.strip + segment_id.cons(es).join(sep).gsub(/#{Regexp.escape(sep)}+$/, "") + eos.strip end end end @@ -59,8 +59,8 @@ class << SegmentTok ######################################################################### # @return [SegmentTok] - def build(id, element_toks, position) - new(id, element_toks, position) + def build(segment_id, element_toks, position) + new(segment_id, element_toks, position) end # @endgroup diff --git a/prof/memprof-tk-20190622T211556.txt b/prof/memprof-tk-20190622T211556.txt new file mode 100644 index 000000000..f2ac8f3cc --- /dev/null +++ b/prof/memprof-tk-20190622T211556.txt @@ -0,0 +1,765 @@ +# Baseline since c128060b + +Total allocated: 985.65 MB (23360511 objects) +Total retained: 1.56 MB (15229 objects) + +allocated memory by location +----------------------------------- + 225.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 152.88 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 + 94.47 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 88.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 + 40.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 + 24.1 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:586 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:598 + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:99 + 34.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:159 + 24.13 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 24.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:410 + 8.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:352 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:468 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 + + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + + 1.73 MB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 581.84 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 342.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 92.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:137 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 52.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:24 + 51.08 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 47.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 37.21 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 32.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:272 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 24.48 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 19.93 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 19.03 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 18.55 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 13.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 11.36 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 10.74 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + +allocated objects by location +----------------------------------- + 5641800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3822000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 + 2361700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 2213300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:598 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:99 + 1006100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 + 854900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:159 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 603300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 602600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:586 + 402401 prof/memprof-tk.rb:26 + 201300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:352 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:410 + 51638 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:468 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + 6422 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 3067 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 543 /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 498 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 371 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 340 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 261 /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 237 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:108 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:272 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:391 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb:11 + 200 prof/memprof-tk.rb:31 + 200 prof/memprof-tk.rb:35 + 199 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 137 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:43 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:51 + 121 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:29 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 121 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + +allocated memory by class +----------------------------------- + 366.02 MB String + 289.92 MB Array + 152.88 MB Stupidedi::Reader::StringPtr + 58.34 MB Stupidedi::Reader::Tokenizer::Done + 41.16 MB Hash + 34.2 MB Stupidedi::Reader::Tokenizer::ElementTokBuilder::NonRepeatable <-- + 30.2 MB Stupidedi::Reader::SimpleElementTok + 8.05 MB Stupidedi::Reader::SegmentTok + 2.0 MB Stupidedi::Reader::ComponentElementTok + 2.0 MB Stupidedi::Reader::CompositeElementTok +================================================== + 581.72 kB File + 99.82 kB Class + 79.94 kB Thread::Backtrace + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 13.06 kB Enumerator + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.69 kB Regexp + 7.2 kB Stupidedi::Reader::Separators + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 5.6 kB MatchData + 4.52 kB Stupidedi::Schema::CodeList::Internal + 4.0 kB Stupidedi::Reader::IgnoredTok + 4.0 kB Stupidedi::Reader::SegmentDict::Constants + 4.0 kB Stupidedi::Reader::SegmentDict::NonEmpty + 4.0 kB Stupidedi::Values::FunctionalGroupVal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 2.42 kB Integer + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 880.0 B Range + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 320.0 B Stupidedi::Schema::SegmentUse + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 258.0 B Time + 160.0 B <> + 160.0 B Gem::Requirement + 120.0 B Process::Status + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 88.0 B Binding + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B LoadError + +allocated objects by class +----------------------------------- + 9117869 String + 6841400 Array + 3822000 Stupidedi::Reader::StringPtr + 1458500 Stupidedi::Reader::Tokenizer::Done + 854900 Stupidedi::Reader::Tokenizer::ElementTokBuilder::NonRepeatable <-- + 755000 Stupidedi::Reader::SimpleElementTok + 207965 Hash + 201200 Stupidedi::Reader::SegmentTok + 50000 Stupidedi::Reader::ComponentElementTok + 50000 Stupidedi::Reader::CompositeElementTok + 168 Stupidedi::Versions::Common::ElementTypes::ID + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 102 Enumerator + 100 Stupidedi::Reader::IgnoredTok + 100 Stupidedi::Reader::SegmentDict::Constants + 100 Stupidedi::Reader::SegmentDict::NonEmpty + 100 Stupidedi::Reader::Separators + 100 Stupidedi::Values::FunctionalGroupVal + 80 Stupidedi::Versions::Common::ElementTypes::AN + 71 File + 69 Thread::Backtrace + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 27 Integer + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 22 Range + 20 MatchData + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 13 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Gem::Requirement + 4 Stupidedi::Schema::SegmentUse + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Process::Status + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 3 Time + 1 Binding + 1 Float + 1 Gem::Specification + 1 LoadError + +retained memory by location +----------------------------------- + 716.67 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 249.94 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9510 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4494 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4897 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5188 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6030 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6230 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:64 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7880 + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb:118 + 4.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 4.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7680 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9383 + 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:195 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:130 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 2.74 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 2.72 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 2.19 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 2.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:272 + 2.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:304 + 2.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 1.96 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:357 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4076 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4805 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8012 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8167 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8244 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9338 + 1.72 kB /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:502 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:279 + 1.62 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 1.59 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:8 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 1.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + +retained objects by location +----------------------------------- + 13825 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 52 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 50 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 43 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 34 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 23 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 21 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 20 /Users/mr/wd/stupidedi/lib/ruby/array.rb:73 + 20 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 18 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 17 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:92 + 16 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:39 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:2039 + 14 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:115 + 13 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 11 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:38 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:160 + 9 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:45 + 8 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 7 /Users/mr/wd/stupidedi/lib/ruby/module.rb:30 + 7 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:142 + 6 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:96 + 5 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/abstract_set.rb:180 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb:34 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:50 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb:51 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb:18 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:136 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:149 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:264 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:346 + +retained memory by class +----------------------------------- + 727.58 kB String + 642.44 kB Hash + 99.82 kB Class + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.97 kB Array + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.45 kB Regexp + 4.52 kB Stupidedi::Schema::CodeList::Internal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 1.02 kB Integer + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 160.0 B <> + 160.0 B Stupidedi::Schema::SegmentUse + 120.0 B Gem::Requirement + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 86.0 B Time + 80.0 B Range + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B Thread::Mutex + 40.0 B Float + 40.0 B Process::Status + +retained objects by class +----------------------------------- + 14027 String + 177 Array + 168 Stupidedi::Versions::Common::ElementTypes::ID + 159 Hash + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 80 Stupidedi::Versions::Common::ElementTypes::AN + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 12 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 9 Integer + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Gem::Requirement + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 2 Range + 2 Stupidedi::Schema::SegmentUse + 1 Float + 1 Gem::Specification + 1 Process::Status + 1 Stupidedi::Schema::CompositeElementUse + 1 Thread::Mutex + 1 Time + + +Allocated String Report +----------------------------------- + 2865511 "*" + 1859500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 1006000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 703402 "~" + 502200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 501109 "P" + 501100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 482309 "0" + 482300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/requirement.rb:108 + + 378517 "1" + 378500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 13 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 301513 "2" + 301500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 202073 "\n" + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 963 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + + 200910 "N" + 200900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200811 "C" + 200800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200710 "E" + 200700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200410 "L" + 200400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200107 "O" + 200100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200006 "K" + 200000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 176209 "4" + 176200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 151307 " " + 151300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101108 "3" + 101100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 8 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101015 "A" + 101000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100710 "I" + 100700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100506 "B" + 100500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100217 "F" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 17 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100204 "8" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100203 "V" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "LIN" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "PID" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "CTP" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "PO4" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 51510 "S" + 51500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50910 "T" + 50900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50290 "/" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 69 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + + 50214 "D" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50210 "R" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50000 "." + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + + 50000 ":CTP" + 50000 prof/memprof-tk.rb:26 + + 50000 ":LIN" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PID" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PO4" + 50000 prof/memprof-tk.rb:26 + + 50000 "CTP01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "CTP05-01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 + + 50000 "CTP06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "LIN01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "LIN04" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PID01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PID06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PO401" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PO404" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 25310 "5" + 25300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2605 "9" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + + 2604 "7" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 690 "/Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'" + 690 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 609 "G" + 600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + +Retained String Report +----------------------------------- + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "/Users/mr/wd/stupidedi/lib/stupidedi/color.rb" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:142 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/specifications/cantor-1.2.1.gemspec" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:84 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1171 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/element_req.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/sets.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/functional_group_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val_group.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_reqs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_types.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/BCT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTP.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/DTM.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GE.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GS.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/LIN.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/N1.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + diff --git a/prof/memprof-tk-20190622T212056.txt b/prof/memprof-tk-20190622T212056.txt new file mode 100644 index 000000000..936bd6b4b --- /dev/null +++ b/prof/memprof-tk-20190622T212056.txt @@ -0,0 +1,771 @@ +# Singletons ElementTokBuilder::Repeatable and NonRepeatable (-34MB) + +Total allocated: 951.45 MB (22505611 objects) +Total retained: 1.56 MB (15231 objects) + +allocated memory by location +----------------------------------- + 225.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 <-- [n] + 152.88 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 <-- initialize + 94.47 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 <-- head + 88.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 <-- drop(n) + 40.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 <-- take(n) + 24.1 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:586 <-- + shared + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 <-- reify + + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:101 <-- Done.initialize + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:598 <-- Done.new + + 24.13 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 <-- read_elements String.new + 24.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:410 <-- read_elements elements = [] + 8.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:352 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:468 <-- read_composite component_toks = [] + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 <-- read_composite String.new + + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + + 1.73 MB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 581.84 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 342.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 92.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:137 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 52.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:24 + 51.08 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 47.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 37.21 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 32.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:272 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 24.48 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 19.93 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 19.03 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 18.55 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 13.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 11.36 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 10.74 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 10.44 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + +allocated objects by location +----------------------------------- + 5641800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3822000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 + 2361700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 2213300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:101 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:598 + 1006100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 603300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 602600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:586 + 402401 prof/memprof-tk.rb:26 + 201300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:352 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:410 + 51638 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:468 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + 6422 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 3067 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 543 /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 498 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 371 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 340 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 261 /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 237 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:108 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:272 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:391 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb:11 + 200 prof/memprof-tk.rb:31 + 200 prof/memprof-tk.rb:35 + 199 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 137 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:43 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:51 + 121 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:29 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 121 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 102 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:266 + +allocated memory by class +----------------------------------- + 366.02 MB String <-- 9.1M objects + 289.92 MB Array <-- 6.8M objects + 152.88 MB Stupidedi::Reader::StringPtr + 58.34 MB Stupidedi::Reader::Tokenizer::Done + 41.16 MB Hash <-- 200K objects + + 30.2 MB Stupidedi::Reader::SimpleElementTok + 8.05 MB Stupidedi::Reader::SegmentTok + 2.0 MB Stupidedi::Reader::ComponentElementTok + 2.0 MB Stupidedi::Reader::CompositeElementTok +================================================== + 581.72 kB File + 99.82 kB Class + 79.94 kB Thread::Backtrace + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 13.06 kB Enumerator + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.69 kB Regexp + 7.2 kB Stupidedi::Reader::Separators + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 5.6 kB MatchData + 4.52 kB Stupidedi::Schema::CodeList::Internal + 4.0 kB Stupidedi::Reader::IgnoredTok + 4.0 kB Stupidedi::Reader::SegmentDict::Constants + 4.0 kB Stupidedi::Reader::SegmentDict::NonEmpty + 4.0 kB Stupidedi::Values::FunctionalGroupVal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 2.42 kB Integer + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 880.0 B Range + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 320.0 B Stupidedi::Schema::SegmentUse + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 258.0 B Time + 160.0 B <> + 160.0 B Gem::Requirement + 120.0 B Process::Status + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 88.0 B Binding + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B LoadError + 72.0 B Thread::Mutex + +allocated objects by class +----------------------------------- + 9117869 String <-- 9.1M + 6841400 Array <-- 6.8M + 3822000 Stupidedi::Reader::StringPtr + 1458500 Stupidedi::Reader::Tokenizer::Done + 755000 Stupidedi::Reader::SimpleElementTok + 207965 Hash + 201200 Stupidedi::Reader::SegmentTok + 50000 Stupidedi::Reader::ComponentElementTok + 50000 Stupidedi::Reader::CompositeElementTok + 168 Stupidedi::Versions::Common::ElementTypes::ID + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 102 Enumerator + 100 Stupidedi::Reader::IgnoredTok + 100 Stupidedi::Reader::SegmentDict::Constants + 100 Stupidedi::Reader::SegmentDict::NonEmpty + 100 Stupidedi::Reader::Separators + 100 Stupidedi::Values::FunctionalGroupVal + 80 Stupidedi::Versions::Common::ElementTypes::AN + 71 File + 69 Thread::Backtrace + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 27 Integer + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 22 Range + 20 MatchData + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 13 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Gem::Requirement + 4 Stupidedi::Schema::SegmentUse + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Process::Status + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 3 Time + 1 Binding + 1 Float + 1 Gem::Specification + 1 LoadError + 1 Stupidedi::Reader::Tokenizer::Fail + +retained memory by location +----------------------------------- + 716.67 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 249.94 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9510 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4494 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4897 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5188 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6030 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6230 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:64 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7880 + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb:118 + 4.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 4.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7680 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9383 + 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:195 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:130 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 2.74 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 2.72 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 2.19 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 2.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:272 + 2.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:304 + 2.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 1.96 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:357 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4076 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4805 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8012 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8167 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8244 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9338 + 1.72 kB /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:502 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:279 + 1.62 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 1.59 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:8 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 1.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + +retained objects by location +----------------------------------- + 13825 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 52 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 50 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 43 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 34 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 23 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 21 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 20 /Users/mr/wd/stupidedi/lib/ruby/array.rb:73 + 20 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 18 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 17 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:92 + 16 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:39 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:2039 + 14 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:115 + 13 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 11 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:38 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:160 + 9 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:45 + 8 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 7 /Users/mr/wd/stupidedi/lib/ruby/module.rb:30 + 7 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:142 + 6 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:96 + 5 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/abstract_set.rb:180 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb:34 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:50 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb:51 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb:18 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:136 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:149 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:264 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:346 + +retained memory by class +----------------------------------- + 727.58 kB String + 642.44 kB Hash + 99.82 kB Class + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.97 kB Array + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.45 kB Regexp + 4.52 kB Stupidedi::Schema::CodeList::Internal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 1.02 kB Integer + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 160.0 B <> + 160.0 B Stupidedi::Schema::SegmentUse + 120.0 B Gem::Requirement + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 86.0 B Time + 80.0 B Range + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B Thread::Mutex + 40.0 B Float + 40.0 B Process::Status + 40.0 B Stupidedi::Reader::SimpleElementTok + 40.0 B Stupidedi::Reader::StringPtr + +retained objects by class +----------------------------------- + 14027 String + 177 Array + 168 Stupidedi::Versions::Common::ElementTypes::ID + 159 Hash + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 80 Stupidedi::Versions::Common::ElementTypes::AN + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 12 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 9 Integer + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Gem::Requirement + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 2 Range + 2 Stupidedi::Schema::SegmentUse + 1 Float + 1 Gem::Specification + 1 Process::Status + 1 Stupidedi::Reader::SimpleElementTok + 1 Stupidedi::Reader::StringPtr + 1 Stupidedi::Schema::CompositeElementUse + 1 Thread::Mutex + 1 Time + + +Allocated String Report +----------------------------------- + 2865511 "*" + 1859500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 1006000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 703402 "~" + 502200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 501109 "P" + 501100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 482309 "0" + 482300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/requirement.rb:108 + + 378517 "1" + 378500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 13 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 301513 "2" + 301500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 202073 "\n" + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 963 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + + 200910 "N" + 200900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200811 "C" + 200800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200710 "E" + 200700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200410 "L" + 200400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200107 "O" + 200100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200006 "K" + 200000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 176209 "4" + 176200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 151307 " " + 151300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101108 "3" + 101100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 8 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101015 "A" + 101000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100710 "I" + 100700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100506 "B" + 100500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100217 "F" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 17 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100204 "8" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100203 "V" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "LIN" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "PID" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "CTP" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "PO4" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 51510 "S" + 51500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50910 "T" + 50900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50290 "/" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 69 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + + 50214 "D" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50210 "R" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50000 "." + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + + 50000 ":CTP" + 50000 prof/memprof-tk.rb:26 + + 50000 ":LIN" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PID" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PO4" + 50000 prof/memprof-tk.rb:26 + + 50000 "CTP01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "CTP05-01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:469 + + 50000 "CTP06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "LIN01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "LIN04" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PID01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PID06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PO401" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 50000 "PO404" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:411 + + 25310 "5" + 25300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2605 "9" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + + 2604 "7" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 690 "/Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'" + 690 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 609 "G" + 600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + +Retained String Report +----------------------------------- + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "/Users/mr/wd/stupidedi/lib/stupidedi/color.rb" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:142 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/specifications/cantor-1.2.1.gemspec" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:84 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1171 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/element_req.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/sets.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/functional_group_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val_group.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_reqs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_types.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/BCT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTP.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/DTM.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GE.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GS.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/LIN.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/N1.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + diff --git a/prof/memprof-tk-20190622T233539.txt b/prof/memprof-tk-20190622T233539.txt new file mode 100644 index 000000000..a003aaed9 --- /dev/null +++ b/prof/memprof-tk-20190622T233539.txt @@ -0,0 +1,770 @@ +# Change one remaining buffer += char to buffer << char (-49MB) + +Total allocated: 903.25 MB (21300434 objects) +Total retained: 1.56 MB (15231 objects) + +allocated memory by location +----------------------------------- + 225.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 <-- [n] + 128.78 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 <-- initialize + 94.47 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 <-- head + 88.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 <-- drop(n) + 40.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 <-- take(n) + + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 <-- reify + + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:100 <-- Done.initialize + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:606 <-- Done.new + + 24.13 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 <-- read_elements String.new + 24.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:418 <-- read_elements elements = [] + 8.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:360 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:476 <-- read_composite component_toks = [] + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:477 <-- read_composite String.new + + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + + 1.73 MB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 581.84 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 342.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 92.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:137 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 52.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:24 + 51.08 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 47.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 37.21 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 32.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:280 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 24.48 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 19.93 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 19.03 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 18.55 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 13.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 11.36 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 10.74 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 10.44 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 9.03 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + +allocated objects by location +----------------------------------- + 5641800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3219400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 + 2361700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 2213300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:100 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:606 + 1006100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 603300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + 402401 prof/memprof-tk.rb:26 + 201300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:360 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:418 + 51638 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:476 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:477 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + 6422 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 3067 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 543 /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 498 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 371 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 340 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 261 /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 237 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:108 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:280 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:399 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb:11 + 200 prof/memprof-tk.rb:31 + 200 prof/memprof-tk.rb:35 + 199 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 137 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:43 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:51 + 121 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:29 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 121 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 102 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:274 + 100 /Users/mr/wd/stupidedi/lib/ruby/try.rb:22 + +allocated memory by class +----------------------------------- + 366.02 MB String + 265.81 MB Array + 128.78 MB Stupidedi::Reader::StringPtr + 58.34 MB Stupidedi::Reader::Tokenizer::Done + 41.16 MB Hash + 30.2 MB Stupidedi::Reader::SimpleElementTok + 8.05 MB Stupidedi::Reader::SegmentTok + 2.0 MB Stupidedi::Reader::ComponentElementTok + 2.0 MB Stupidedi::Reader::CompositeElementTok + 581.72 kB File + 99.82 kB Class + 79.94 kB Thread::Backtrace + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 13.06 kB Enumerator + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.69 kB Regexp + 7.2 kB Stupidedi::Reader::Separators + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 5.6 kB MatchData + 4.52 kB Stupidedi::Schema::CodeList::Internal + 4.0 kB Stupidedi::Reader::IgnoredTok + 4.0 kB Stupidedi::Reader::SegmentDict::Constants + 4.0 kB Stupidedi::Reader::SegmentDict::NonEmpty + 4.0 kB Stupidedi::Values::FunctionalGroupVal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 2.42 kB Integer + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 880.0 B Range + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 320.0 B Stupidedi::Schema::SegmentUse + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 258.0 B Time + 160.0 B <> + 160.0 B Gem::Requirement + 120.0 B Process::Status + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 88.0 B Binding + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B LoadError + 72.0 B Thread::Mutex + +allocated objects by class +----------------------------------- + 9117889 String + 6238803 Array + 3219400 Stupidedi::Reader::StringPtr + 1458500 Stupidedi::Reader::Tokenizer::Done + 755000 Stupidedi::Reader::SimpleElementTok + 207965 Hash + 201200 Stupidedi::Reader::SegmentTok + 50000 Stupidedi::Reader::ComponentElementTok + 50000 Stupidedi::Reader::CompositeElementTok + 168 Stupidedi::Versions::Common::ElementTypes::ID + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 102 Enumerator + 100 Stupidedi::Reader::IgnoredTok + 100 Stupidedi::Reader::SegmentDict::Constants + 100 Stupidedi::Reader::SegmentDict::NonEmpty + 100 Stupidedi::Reader::Separators + 100 Stupidedi::Values::FunctionalGroupVal + 80 Stupidedi::Versions::Common::ElementTypes::AN + 71 File + 69 Thread::Backtrace + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 27 Integer + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 22 Range + 20 MatchData + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 13 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Gem::Requirement + 4 Stupidedi::Schema::SegmentUse + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Process::Status + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 3 Time + 1 Binding + 1 Float + 1 Gem::Specification + 1 LoadError + 1 Stupidedi::Reader::Tokenizer::Fail + +retained memory by location +----------------------------------- + 716.67 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 249.94 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9510 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4494 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4897 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5188 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6030 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6230 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:64 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7880 + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb:118 + 4.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 4.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7680 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9383 + 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:195 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:130 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 2.74 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 2.72 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 2.19 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 2.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:272 + 2.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:304 + 2.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 1.96 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:357 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4076 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4805 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8012 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8167 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8244 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9338 + 1.72 kB /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:502 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:279 + 1.62 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 1.59 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:8 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 1.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + +retained objects by location +----------------------------------- + 13825 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 52 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 50 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 43 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 34 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 23 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 21 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 20 /Users/mr/wd/stupidedi/lib/ruby/array.rb:73 + 20 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 18 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 17 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:92 + 16 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:39 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:2039 + 14 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:115 + 13 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 11 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:38 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:160 + 9 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:45 + 8 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 7 /Users/mr/wd/stupidedi/lib/ruby/module.rb:30 + 7 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:142 + 6 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:96 + 5 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/abstract_set.rb:180 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb:34 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:50 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb:51 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb:18 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:136 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:149 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:264 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:346 + +retained memory by class +----------------------------------- + 727.58 kB String + 642.44 kB Hash + 99.82 kB Class + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.97 kB Array + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.45 kB Regexp + 4.52 kB Stupidedi::Schema::CodeList::Internal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 1.02 kB Integer + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 160.0 B <> + 160.0 B Stupidedi::Schema::SegmentUse + 120.0 B Gem::Requirement + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 86.0 B Time + 80.0 B Range + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B Thread::Mutex + 40.0 B Float + 40.0 B Process::Status + 40.0 B Stupidedi::Reader::SimpleElementTok + 40.0 B Stupidedi::Reader::StringPtr + +retained objects by class +----------------------------------- + 14027 String + 177 Array + 168 Stupidedi::Versions::Common::ElementTypes::ID + 159 Hash + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 80 Stupidedi::Versions::Common::ElementTypes::AN + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 12 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 9 Integer + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Gem::Requirement + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 2 Range + 2 Stupidedi::Schema::SegmentUse + 1 Float + 1 Gem::Specification + 1 Process::Status + 1 Stupidedi::Reader::SimpleElementTok + 1 Stupidedi::Reader::StringPtr + 1 Stupidedi::Schema::CompositeElementUse + 1 Thread::Mutex + 1 Time + + +Allocated String Report +----------------------------------- + 2865511 "*" + 1859500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 1006000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 703402 "~" + 502200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 501109 "P" + 501100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 482309 "0" + 482300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/requirement.rb:108 + + 378517 "1" + 378500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 13 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 301513 "2" + 301500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 202073 "\n" + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 963 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + + 200910 "N" + 200900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200811 "C" + 200800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200710 "E" + 200700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200410 "L" + 200400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200107 "O" + 200100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200006 "K" + 200000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 176209 "4" + 176200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 151307 " " + 151300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101108 "3" + 101100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 8 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101015 "A" + 101000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100710 "I" + 100700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100506 "B" + 100500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100217 "F" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 17 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100204 "8" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100203 "V" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "LIN" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100004 "PID" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "CTP" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100002 "PO4" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 51510 "S" + 51500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50910 "T" + 50900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50290 "/" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 69 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + + 50214 "D" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50210 "R" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50000 "." + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + + 50000 ":CTP" + 50000 prof/memprof-tk.rb:26 + + 50000 ":LIN" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PID" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PO4" + 50000 prof/memprof-tk.rb:26 + + 50000 "CTP01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "CTP05-01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:477 + + 50000 "CTP06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "LIN01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "LIN04" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "PID01" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "PID06" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "PO401" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 50000 "PO404" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:419 + + 25310 "5" + 25300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2605 "9" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + + 2604 "7" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 690 "/Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'" + 690 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 609 "G" + 600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + +Retained String Report +----------------------------------- + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "/Users/mr/wd/stupidedi/lib/stupidedi/color.rb" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:142 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/specifications/cantor-1.2.1.gemspec" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:84 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1171 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/element_req.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/sets.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/functional_group_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val_group.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_reqs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_types.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/BCT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTP.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/DTM.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GE.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GS.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/LIN.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/N1.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + diff --git a/prof/memprof-tk-20190623T010633.txt b/prof/memprof-tk-20190623T010633.txt new file mode 100644 index 000000000..c6dc30869 --- /dev/null +++ b/prof/memprof-tk-20190623T010633.txt @@ -0,0 +1,779 @@ +# Don't preallocate descriptor strings for error messages (-26MB) + +Total allocated: 877.11 MB (20647035 objects) +Total retained: 1.56 MB (15232 objects) + +allocated memory by location +----------------------------------- + 225.67 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 <-- [n] + 128.78 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 <-- initialize + 94.47 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 <-- head + 88.53 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 <-- drop(n) + 40.24 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 <-- take(n) + + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 <-- reify + + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:100 <-- Done.initialize + 58.34 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:607 <-- Done.new + + + 24.09 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:418 <-- read_elements elements = [] + 8.04 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:360 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:478 <-- read_composite component_toks = [] + + + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 30.2 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 8.05 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 2.0 MB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + + 1.73 MB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 581.84 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 342.4 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 92.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:137 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 52.65 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:24 + 51.08 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 47.37 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 37.21 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 32.8 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:280 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 24.48 kB /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 19.93 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 19.03 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 18.55 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 13.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 11.36 kB /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 10.74 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 10.44 kB /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 9.03 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9510 + 8.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:108 + +allocated objects by location +----------------------------------- + 5641800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3219400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:51 + 2361700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 2213300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:187 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:100 + 1458500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:607 + 1006100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:217 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:16 + 755000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:60 + 402401 prof/memprof-tk.rb:26 + 201300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:20 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:63 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:360 + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:418 + 51641 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:478 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:60 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:17 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:70 + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb:46 + 6422 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 3067 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + 543 /Users/mr/wd/stupidedi/lib/ruby/module.rb:50 + 498 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 371 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 340 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + 261 /Users/mr/wd/stupidedi/lib/ruby/module.rb:52 + 237 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:108 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:280 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokenizer.rb:399 + 200 /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb:11 + 200 prof/memprof-tk.rb:31 + 200 prof/memprof-tk.rb:35 + 199 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:116 + 190 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 137 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:14 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:43 + 131 /Users/mr/wd/stupidedi/lib/ruby/module.rb:51 + 121 /Users/mr/wd/stupidedi/lib/ruby/array.rb:46 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:29 + 121 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 121 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 100 /Users/mr/wd/stupidedi/lib/ruby/try.rb:22 + 100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:136 + 100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:137 + 100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/segment_dict.rb:42 + +allocated memory by class +----------------------------------- + 339.88 MB String + 265.81 MB Array + 128.78 MB Stupidedi::Reader::StringPtr + 58.34 MB Stupidedi::Reader::Tokenizer::Done + 41.16 MB Hash + 30.2 MB Stupidedi::Reader::SimpleElementTok + 8.05 MB Stupidedi::Reader::SegmentTok + 2.0 MB Stupidedi::Reader::ComponentElementTok + 2.0 MB Stupidedi::Reader::CompositeElementTok + 581.72 kB File + 99.82 kB Class + 79.94 kB Thread::Backtrace + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 13.06 kB Enumerator + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.69 kB Regexp + 7.2 kB Stupidedi::Reader::Separators + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 5.6 kB MatchData + 4.52 kB Stupidedi::Schema::CodeList::Internal + 4.0 kB Stupidedi::Reader::IgnoredTok + 4.0 kB Stupidedi::Reader::SegmentDict::Constants + 4.0 kB Stupidedi::Reader::SegmentDict::NonEmpty + 4.0 kB Stupidedi::Values::FunctionalGroupVal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 2.42 kB Integer + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 880.0 B Range + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 320.0 B Stupidedi::Schema::SegmentUse + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 258.0 B Time + 160.0 B <> + 160.0 B Gem::Requirement + 120.0 B Process::Status + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 88.0 B Binding + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B LoadError + 72.0 B Thread::Mutex + +allocated objects by class +----------------------------------- + 8464490 String + 6238803 Array + 3219400 Stupidedi::Reader::StringPtr + 1458500 Stupidedi::Reader::Tokenizer::Done + 755000 Stupidedi::Reader::SimpleElementTok + 207965 Hash + 201200 Stupidedi::Reader::SegmentTok + 50000 Stupidedi::Reader::ComponentElementTok + 50000 Stupidedi::Reader::CompositeElementTok + 168 Stupidedi::Versions::Common::ElementTypes::ID + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 102 Enumerator + 100 Stupidedi::Reader::IgnoredTok + 100 Stupidedi::Reader::SegmentDict::Constants + 100 Stupidedi::Reader::SegmentDict::NonEmpty + 100 Stupidedi::Reader::Separators + 100 Stupidedi::Values::FunctionalGroupVal + 80 Stupidedi::Versions::Common::ElementTypes::AN + 71 File + 69 Thread::Backtrace + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 27 Integer + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 22 Range + 20 MatchData + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 13 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Gem::Requirement + 4 Stupidedi::Schema::SegmentUse + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Process::Status + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 3 Time + 1 Binding + 1 Float + 1 Gem::Specification + 1 LoadError + 1 Stupidedi::Reader::Tokenizer::Fail + +retained memory by location +----------------------------------- + 716.75 kB /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 249.94 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:2257 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:437 + 57.44 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6406 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:3381 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5417 + 28.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8288 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:1948 + 14.43 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4168 + 8.77 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9510 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4494 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4897 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:5188 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6030 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:6230 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:64 + 7.26 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7880 + 4.68 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb:118 + 4.52 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 4.0 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:7680 + 3.42 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9383 + 3.17 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:195 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:130 + 3.11 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 2.74 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 2.72 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 2.19 kB /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 2.15 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/nn.rb:272 + 2.14 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/r.rb:304 + 2.02 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 1.96 kB /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 1.82 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:357 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4076 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:4805 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8012 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8167 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:8244 + 1.76 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb:9338 + 1.72 kB /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/dt.rb:502 + 1.63 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/tm.rb:279 + 1.62 kB /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 1.59 kB /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:8 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 1.51 kB /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 1.47 kB /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + +retained objects by location +----------------------------------- + 13827 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 113 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:82 + 52 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:288 + 50 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:34 + 43 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:286 + 34 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:36 + 23 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:92 + 21 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 20 /Users/mr/wd/stupidedi/lib/ruby/array.rb:73 + 20 /Users/mr/wd/stupidedi/lib/ruby/object.rb:30 + 18 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + 17 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:92 + 16 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:39 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:2039 + 14 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:115 + 13 /Users/mr/wd/stupidedi/lib/stupidedi/color.rb:26 + 11 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:38 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb:37 + 10 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:160 + 9 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/id.rb:45 + 8 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:48 + 7 /Users/mr/wd/stupidedi/lib/ruby/module.rb:30 + 7 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/syntax_notes.rb:142 + 6 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:96 + 5 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/simple_element.rb:47 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/absolute_set.rb:20 + 3 /Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib/cantor/abstract_set.rb:180 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb:75 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb:34 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb:6 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb:9 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb:50 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb:51 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb:18 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb:132 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb:79 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:136 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:149 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:211 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:264 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/an.rb:346 + +retained memory by class +----------------------------------- + 727.62 kB String + 642.44 kB Hash + 99.82 kB Class + 27.14 kB Module + 16.13 kB Stupidedi::Versions::Common::ElementTypes::ID + 8.4 kB Stupidedi::Schema::SimpleElementUse + 7.97 kB Array + 7.05 kB Stupidedi::Versions::Common::ElementTypes::AN + 6.45 kB Regexp + 4.52 kB Stupidedi::Schema::CodeList::Internal + 3.17 kB Stupidedi::Versions::Common::ElementTypes::Nn + 2.88 kB Stupidedi::Versions::Common::ElementTypes::R + 1.81 kB Stupidedi::Schema::ComponentElementUse + 1.72 kB Cantor::AbsoluteSet + 1.42 kB Stupidedi::Schema::SegmentDef + 1.02 kB Integer + 680.0 B Stupidedi::Schema::CodeList::External + 536.0 B Stupidedi::Schema::CompositeElementDef + 440.0 B Gem::Specification + 400.0 B Stupidedi::Versions::Common::SyntaxNotes::C + 360.0 B Stupidedi::Versions::Common::ElementTypes::DT + 280.0 B Stupidedi::Versions::Common::SyntaxNotes::P + 272.0 B Stupidedi::Versions::Common::ElementTypes::TM + 160.0 B <> + 160.0 B Stupidedi::Schema::SegmentUse + 120.0 B Gem::Requirement + 120.0 B Stupidedi::Schema::ElementReq + 120.0 B Stupidedi::Versions::Common::SyntaxNotes::R + 86.0 B Time + 80.0 B Range + 80.0 B Stupidedi::Schema::CompositeElementUse + 72.0 B Thread::Mutex + 40.0 B Float + 40.0 B Process::Status + 40.0 B Stupidedi::Reader::SimpleElementTok + 40.0 B Stupidedi::Reader::StringPtr + +retained objects by class +----------------------------------- + 14028 String + 177 Array + 168 Stupidedi::Versions::Common::ElementTypes::ID + 159 Hash + 139 Class + 113 Stupidedi::Schema::CodeList::Internal + 105 Stupidedi::Schema::SimpleElementUse + 80 Stupidedi::Versions::Common::ElementTypes::AN + 43 Cantor::AbsoluteSet + 33 Stupidedi::Versions::Common::ElementTypes::Nn + 30 Stupidedi::Versions::Common::ElementTypes::R + 25 Stupidedi::Schema::ComponentElementUse + 23 Module + 17 Stupidedi::Schema::CodeList::External + 16 Stupidedi::Schema::SegmentDef + 12 Regexp + 10 Stupidedi::Versions::Common::SyntaxNotes::C + 9 Integer + 7 Stupidedi::Versions::Common::SyntaxNotes::P + 6 Stupidedi::Schema::CompositeElementDef + 4 <> + 4 Stupidedi::Versions::Common::ElementTypes::DT + 3 Gem::Requirement + 3 Stupidedi::Schema::ElementReq + 3 Stupidedi::Versions::Common::ElementTypes::TM + 3 Stupidedi::Versions::Common::SyntaxNotes::R + 2 Range + 2 Stupidedi::Schema::SegmentUse + 1 Float + 1 Gem::Specification + 1 Process::Status + 1 Stupidedi::Reader::SimpleElementTok + 1 Stupidedi::Reader::StringPtr + 1 Stupidedi::Schema::CompositeElementUse + 1 Thread::Mutex + 1 Time + + +Allocated String Report +----------------------------------- + 2865511 "*" + 1859500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 1006000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:117 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:118 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 703402 "~" + 502200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:105 + 201200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 501109 "P" + 501100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 482309 "0" + 482300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/requirement.rb:108 + + 378517 "1" + 378500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 13 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 301513 "2" + 301500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:387 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/version.rb:388 + + 202073 "\n" + 201100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 963 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1168 + + 200910 "N" + 200900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200811 "C" + 200800 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200710 "E" + 200700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200410 "L" + 200400 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200107 "O" + 200100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 200006 "K" + 200000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 176209 "4" + 176200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 151307 " " + 151300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101108 "3" + 101100 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 8 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 101015 "A" + 101000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100710 "I" + 100700 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100506 "B" + 100500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100217 "F" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 17 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100204 "8" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 100203 "V" + 100200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 51510 "S" + 51500 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50910 "T" + 50900 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50290 "/" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 69 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 15 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:49 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:51 + 2 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:55 + + 50214 "D" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 14 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50210 "R" + 50200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50004 "LIN" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50004 "PID" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50002 "CTP" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50002 "PO4" + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:80 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 50000 "." + 50000 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + + 50000 ":CTP" + 50000 prof/memprof-tk.rb:26 + + 50000 ":LIN" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PID" + 50000 prof/memprof-tk.rb:26 + + 50000 ":PO4" + 50000 prof/memprof-tk.rb:26 + + 25310 "5" + 25300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 10 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2605 "9" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 2 /Users/mr/wd/stupidedi/lib/stupidedi.rb:55 + + 2604 "7" + 2600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 4 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 690 "/Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'" + 690 /Users/mr/wd/stupidedi/lib/stupidedi.rb:54 + + 609 "G" + 600 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 9 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 306 "U" + 300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 6 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 302 "Z" + 300 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 262 " end\n" + 262 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 234 " end\n" + 200 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + 27 /Users/mr/wd/stupidedi/lib/ruby/module.rb:23 + 7 /Users/mr/wd/stupidedi/lib/ruby/module.rb:30 + + 213 "X" + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 13 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 211 "M" + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 11 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 209 ">" + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:93 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:95 + 3 /Users/mr/wd/stupidedi/lib/stupidedi/versions/common/element_types/operators.rb:99 + + 207 "H" + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 7 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 205 "6" + 200 /Users/mr/wd/stupidedi/lib/stupidedi/reader/pointer.rb:175 + 5 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + +Retained String Report +----------------------------------- + 3 "/Users/mr/wd/stupidedi/lib" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 3 "/Users/mr/wd/stupidedi/lib/stupidedi/color.rb" + 3 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 3 "To indicate the beginning of a functional group and to provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 3 "To indicate the end of a functional group and provider control information" + 3 /Users/mr/wd/stupidedi/lib/ruby/string.rb:85 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/gems/cantor-1.2.1/lib" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:142 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130 + + 2 "/Users/mr/.rvm/gems/ruby-2.6.3/specifications/cantor-1.2.1.gemspec" + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/basic_specification.rb:84 + 1 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/specification.rb:1171 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/component_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/composite_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/ignored_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/segment_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/reader/tokens/simple_element_tok.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/abstract_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/code_list.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/component_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/composite_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/element_req.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/repeat_count.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/segment_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/simple_element_use.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/schema/syntax_note.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/sets.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/abstract_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/functional_group_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/segment_val_group.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/values/simple_element_val.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_reqs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/element_types.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/functional_group_def.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/BCT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTP.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/CTT.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/DTM.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GE.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/GS.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/LIN.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + + 2 "/Users/mr/wd/stupidedi/lib/stupidedi/versions/004010/segment_defs/N1.rb" + 2 /Users/mr/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54 + diff --git a/prof/microbench.rb b/prof/microbench.rb index de20e389c..446012757 100755 --- a/prof/microbench.rb +++ b/prof/microbench.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require "pp" require "benchmark/ips" require "memory_profiler" @@ -10,10 +11,34 @@ def mem(label, &block) print label.ljust(30); pp result.allocated_memory_by_class end -XS = [1, 2, 3, 4].freeze +N = 1 + +########## +r = String.new(" nai a al.,j,a"); x = "x"; mem("<<") { N.times { r << x }} # none +s = String.new("nhaoe aoeuntho"); y = "x"; mem("+=") { N.times { s += y }} # 1 String +t = String.new("nhaoe aoeuntho"); z = "x"; mem("+") { N.times { s = s + y }} # 1 String +puts + -mem("<<") { s = ""; x = "x"; 1000.times { s << x }} -mem("+=") { s = ""; x = "x"; 1000.times { s += x }} +########## +a = "alpha"; b = "beta"; c = "charlie"; d = :delta; x = String.new("10"); void = nil +mem("lit") { N.times { void = "lit" }} # none, allocated statically +mem("% a") { N.times { void = "%s" % a }} # 1 String +mem("% d") { N.times { void = "%s" % d }} # 2 String +mem("% [a]") { N.times { void = "%s" % [a] }} # 1 String, 1 Array +mem("% [a,b,c]") { N.times { void = "%s-%s-%s" % [a,b,c] }}# 1 String, 1 Array +mem('"#{a}"') { N.times { void = "#{a}" }} # 1 String +mem('"#{d}"') { N.times { void = "#{d}" }} # 2 String +mem("succ") { N.times { void = x.succ }} # 1 String +mem("succ!") { N.times { void = x.succ! }} # none +puts + + +########## +xs = %w(abc def ghi jkl mno) +mem("map") { N.times { void = xs.map {|x| x }} } # 1 array +mem("map!") { N.times { void = xs.map!{|x| x }} } # none +puts exit From dfc5566c2791142f688234c6b319d5e4420bbe9b Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 23 Jun 2019 13:48:08 -0500 Subject: [PATCH 14/38] Optimize StringPtr#== --- lib/stupidedi/reader/pointer.rb | 40 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 509bce817..494f211ad 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -4,7 +4,7 @@ module Stupidedi module Reader # - # Provides a "view" into a continuous substring of a larger string, without + # Provides a pointer into a continuous substring of a larger string, without # allocating a new string (or whatever the type of the whole is). This saves # memory when many substrings are needed, or long substrings are needed. It # also makes #take, #drop, #[a,b], #[a..b] and #split_at run in constant time @@ -33,6 +33,10 @@ class Pointer # updating `@storage` in place. However, when another pointer shares # with us, `@storage` will be frozen. # + # NOTE: We can't know if there are references to `@storage` apart from + # the ones created by this class. If there are outside references to + # `@storage`, destructive updates to it may cause unexpected behavior. + # # @return [S] attr_reader :storage @@ -312,12 +316,10 @@ class StringPtr < Pointer ANCHORED_Z = /(? Date: Mon, 24 Jun 2019 01:56:53 -0500 Subject: [PATCH 15/38] Eliminate all calls to `StringPtr#reify`; benchmark runs slower at 14.9s --- lib/stupidedi/reader.rb | 12 ++-- lib/stupidedi/reader/pointer.rb | 62 ++++++++++++++------- lib/stupidedi/reader/tokenizer.rb | 91 ++++++++++++++++++++----------- prof/memprof-tk.rb | 82 ++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 59 deletions(-) create mode 100755 prof/memprof-tk.rb diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index 0d11ef612..1f2992106 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -24,15 +24,15 @@ module Reader # @private # @return [Regexp] - R_BASIC = /[A-Z0-9!"&'()*+,.\/:;?= -]/.freeze + R_BASIC = /[A-Z0-9!"&'()*+,.\/:;?= -]/ # @private # @return [Regexp] - R_EXTENDED = /[a-z%@\[\]_{}\\|<>~^`#\$ÀÃÂÄàáâäÈÉÊèéêëÌÃÎìíîïÒÓÔÖòóôöÙÚÛÜùúûüÇçÑñ¿¡]/.freeze + R_EXTENDED = /[a-z%@\[\]_{}\\|<>~^`#\$ÀÃÂÄàáâäÈÉÊèéêëÌÃÎìíîïÒÓÔÖòóôöÙÚÛÜùúûüÇçÑñ¿¡]/ # @private # @return [Regexp] - R_EITHER = Regexp.union(R_BASIC, R_EXTENDED).freeze + R_EITHER = Regexp.union(R_BASIC, R_EXTENDED) # @private # @return [String] @@ -40,15 +40,15 @@ module Reader # @private # @return [Hash] - H_BASIC = C_BYTES.scan(R_BASIC).inject({}){|h,c| h[c] = nil; h }.freeze + H_BASIC = C_BYTES.scan(R_BASIC).inject({}){|h,c| h[c] = nil; h } # @private # @return [Hash] - H_EXTENDED = C_BYTES.scan(R_EXTENDED).inject({}){|h,c| h[c] = nil; h }.freeze + H_EXTENDED = C_BYTES.scan(R_EXTENDED).inject({}){|h,c| h[c] = nil; h } # @private # @return [Hash] - H_EITHER = C_BYTES.scan(R_EITHER).inject({}){|h,c| h[c] = nil; h }.freeze + H_EITHER = C_BYTES.scan(R_EITHER).inject({}){|h,c| h[c] = nil; h } # @private # @return [Regexp] diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 494f211ad..bb80def1e 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -80,7 +80,8 @@ def reify(always_allocate = false) #stderr.puts "reify: no allocation" @storage else - #stderr.puts "reify: allocate[#@offset, #@length]" + $stderr.puts "reify: allocate[#@offset, #@length]" + raise @storage[@offset, @length] end end @@ -312,8 +313,6 @@ def build(object) end class StringPtr < Pointer - ANCHORED_A = /(? 1024 + return reify.match?(pattern, offset) + end + + if not anchorless and @offset != 0 \ + and ANCHORS_A.match?(pattern.inspect) return reify.match?(pattern) end - if @offset + @length != @storage.length and ANCHORED_Z.match?(pattern.inspect) - # Because the pattern is anchored to the end, we can't match on - # @storage directly, unless our end is also the end of @storage. + if anchorless and @offset + @length != @storage.length \ + and ANCHORS_Z.match?(pattern.inspect) return reify.match?(pattern) end offset = @length if offset > length offset = @offset + offset + + # Unfortunately we can't use pattern.match?, which doesn't allocate + # a MatchData object, because we need to check bounds on the match m = pattern.match(@storage, offset) if m and m.begin(0) <= @offset + @length if m.end(0) <= @offset + @length - # The entire match is inside the bounds true else - # The match starts within bounds but ends outside, so we need to - # to allocate a new String of the correct length and try again @storage[m.begin(0), @offset + @length - m.begin(0)].match?(pattern) end else @@ -379,17 +386,30 @@ def match?(pattern, offset = 0) # as String#match. # # @return [MatchData, Integer] - def match_(pattern, offset) - if @offset != 0 and ANCHORED_A.match?(pattern.inspect) + def match_(pattern, offset, anchorless=false) + if @length < 1024 and @storage.length - @offset > 1024 + # We are pointing to a short segment, but there is still a very + # long string that follows in @storage. There's no way to tell + # the regexp engine to stop after a given offset, so it will start + # at @offset and continue searching well past @offset + @length. + # + # It's faster at this point to allocate the relatively shorter + # string, so the regexp engine has less work. + return [reify.match(pattern), 0] + end + + if not anchorless and @offset != 0 \ + and ANCHORS_A.match?(pattern.inspect) # We can't match on @storage.directly unless our @offset is 0, # because String#match(/^./, n) never matches unless n is 0. - return [reify.match(pattern, offset), 0] + return [reify.match(pattern), 0] end - if @offset + @length != @storage.length and ANCHORED_Z.match?(pattern.inspect) + if not anchorless and @offset + @length != @storage.length \ + and ANCHORS_Z.match?(pattern.inspect) # Because the pattern is anchored to the end, we can't match on # @storage directly, unless our end is also the end of @storage. - return [reify.match(pattern, offset), 0] + return [reify.match(pattern), 0] end offset = @length if offset > length @@ -423,12 +443,12 @@ def =~(pattern) # the given `offset`. If not found, then `nil` is returned. # # @return [Integer] - def index(other, offset=0) + def index(other, offset=0, anchorless=false) raise ArgumentError, "offset must be non-negative" if offset < 0 return nil if offset > @length if other.is_a?(Regexp) - m, offset = self.match_(other, offset) + m, offset = match_(other, offset) m and m.begin(0) + offset else n = @storage.index(other, @offset + offset) @@ -618,7 +638,7 @@ def ==(other) length == other.length and \ @storage.index(other.reify, @offset) == @offset end - else + elsif other.is_a?(String) # length == other.length and reify == other length == other.length and \ @storage.index(other, @offset) == @offset diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index c9ecff70a..cea5f2fab 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -5,6 +5,7 @@ module Stupidedi module Reader # TODO: + # - fix shared builder from caller to callee # - review/improve errors # - determine what is yield'd, what is return'd # - determine error recovery @@ -12,7 +13,8 @@ module Reader # - switch char-by-char concat with regexps that grab whole elements class Tokenizer - SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ + SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ + NOT_SEGMENT_ID = /[^A-Z0-9]/ def initialize(input) @input = input @@ -175,20 +177,19 @@ class << Tokenizer # @yield [SegmentTok | IgnoredTok | ErrorTok] def each(input) return enum_for(:each, input) unless block_given? - state = Tokenizer::State.new(nil, nil) + state = Tokenizer::State.new(nil, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) - until input.empty? - next_segment(input, state) do |token| - # Ordinarily there's only one SegmentTok returned, but in other - # cases there could be a IgnoreTok at the beginning, with non-X12 - # header text. Or there could be an ErrorTok and IgnoreTok because - # the first ISA was malformed and the text after was ignored. - # - # Usually read_segment will only return a single SegmentTok, but - # there could also be unparseable text in an ErrorTok + while true + result = next_segment(input, state) do |token| yield token end + + break if result.fail? + yield result.value + input = result.rest end + + result end # This method will skip over without tokenizing input, until an ISA @@ -197,15 +198,23 @@ def each(input) # @yield [SegmentTok | IgnoredTok | ErrorTok] def each_isa(input) return enum_for(:each, input) unless block_given? - state = Tokenizer::State.new(nil, nil) + state = Tokenizer::State.new(nil, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) - until input.empty? - next_isa_segment(input, state).each do |token| - yield token + while true + result = next_isa_segment(input, state) do |token| + yield token # IgnoreTok end + + break if result.fail? + yield result.value + input = result.rest end + + result end + BAD_SEPARATOR = /[a-zA-Z0-9 ]/ + # Consume next occurrence of "ISA" and any control characters that # immediately follow. Validation is done to skip over "ISA" where # it is less likely to be X12 than part of a word. @@ -235,7 +244,7 @@ def _next_isa_segment_id(input) return eof("ISA", :position) if s.nil? # There's something between I..S but it's not a control character - next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER) + next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER, 0, true) a = input.index("A", offset) @@ -243,7 +252,7 @@ def _next_isa_segment_id(input) return eof("ISA", :position) if a.nil? # There's something between S..A but it's not a control character - next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER) + next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER, 0, true) # Needed to perform the extra validation below a = _skip_control_characters(input, a) @@ -251,7 +260,7 @@ def _next_isa_segment_id(input) # The next character determines the element separator. If it's an # alphanumeric or space, we assume this is not the start of an ISA # segment. Perhaps a word like "L[ISA] " or "D[ISA]RRAY" - next if not input.defined_at?(a+1) or input[a+1].match?(/[a-zA-Z0-9 ]/) + next if not input.defined_at?(a+1) or input[a+1].match?(BAD_SEPARATOR) # Success, ignore everything before "I", resume parsing after "A". yield IgnoredTok.new(input.take(i), :position) @@ -272,7 +281,7 @@ def _read_isa_elements(input, state) # The next character is a declaration of the element separator separators = Separators.new(nil, nil, input.head, nil) - # NOTE: This destructive update would make backtracking more painful + # NOTE: Doing this destructive update here would complicate backtracking state.separators = separators # Read the first 15 simple elements into an array @@ -349,13 +358,21 @@ def _next_segment_id(input, state) break if buffer.length >= 3 end - segment_id = buffer.to_s + # This is the only String allocation we cannot get around. The `match?` + # call either has a pattern with \A..\z, or the length of segment_id + # is very small compared to the `@storage` after it, so `match?` will + # allocate the short substring. Next, we need to convert segment_id to + # a symbol, and this also requires reifying the string to call `to_sym` + # + # So it's better to make one copy here instead of them being created + # implicitly twice below. + segment_id = buffer #.to_s - return expected("segment identifier, found %s" % segment_id.inspect, :position) \ - unless segment_id.match?(Tokenizer::SEGMENT_ID) + # return expected("segment identifier, found %s" % segment_id.inspect, :position) \ + # if segment_id.match?(Tokenizer::NOT_SEGMENT_ID, 0, true) offset = _skip_control_characters(input, offset) - return done([segment_id.to_sym, :position], input.drop(offset)) + return done([segment_id, :position], input.drop(offset)) end # @return [SegmentTok] @@ -376,7 +393,17 @@ def next_segment(input, state) return unexpected("eof after %s" % segment_id, :position) \ if result.rest.empty? - if segment_id == :ISA + # This is the only String allocation we cannot get around. The `match?` + # call either has a pattern with \A..\z, or the length of segment_id + # is very small compared to the `@storage` after it, so `match?` will + # allocate the short substring. Next, we need to convert segment_id to + # a symbol, and this also requires reifying the string to call `to_sym` + # + # So it's better to make one copy here instead of them being created + # implicitly twice below. + # segment_id = segment_id.to_sym + + if segment_id == "ISA" # :ISA # We encountered a new ISA segment without having seen the previous # ISA's matching IEA segment. result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } @@ -388,20 +415,20 @@ def next_segment(input, state) unless result.rest.head == separators.element \ or result.rest.head == separators.segment return unexpected("%s after segment identifier %s" % [ - result.rest.head.inspect, segment_id], :position) # TODO yield + result.rest.head.inspect, segment_id], :position) end - if state.segment_dict.defined_at?(segment_id) - element_uses = state.segment_dict.at(segment_id).element_uses - else - element_uses = [] - end + # if state.segment_dict.defined_at?(segment_id) + # element_uses = state.segment_dict.at(segment_id).element_uses + # else + element_uses = [] + # end result = _read_elements(result.rest, state, segment_id, element_uses) - return result if result.fail? # TODO yield + return result if result.fail? # We've parsed an IEA segment, so reset and look for an ISA next time - state.separators = nil if segment_id == :IEA + state.separators = nil if segment_id == "IEA" # :IEA done(SegmentTok.build(segment_id, result.value, :position), result.rest) end diff --git a/prof/memprof-tk.rb b/prof/memprof-tk.rb new file mode 100755 index 000000000..c3e9661b5 --- /dev/null +++ b/prof/memprof-tk.rb @@ -0,0 +1,82 @@ +#!/usr/bin/env ruby -Ilib +require "stupidedi" +require "memory_profiler" +require "pp" + +using Stupidedi::Refinements + +if ARGV.length < 1 + $stderr.puts "usage: #{$0} [--fast] file.x12" + exit +end + +def run(config, input, state) + mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 + $stderr.puts "Pre-init: %0.2d MiB" % mem + + start = Time.now + + mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 + $stderr.puts "Post-init: %0.2d MiB" % mem + + result = Stupidedi::Reader::Tokenizer.next_isa_segment(input, state) + + until result.fail? + segment_tok = result.value + #p segment_tok.segment_id + + if false + case segment_tok.segment_id + when "GS" + # GS08: Version / Release / Industry Identifier Code + version = segment_tok.element_toks.at(7).try(:value).try(:to_s) + gscode = version.try(:slice, 0, 6) + + # GS01: Functional Identifier Code + fgcode = segment_tok.element_toks.at(0).try(:value) + + if config.functional_group.defined_at?(gscode) + envelope_def = config.functional_group.at(gscode) + envelope_val = envelope_def.empty + segment_dict = state.segment_dict.push(envelope_val.segment_dict) + state.segment_dict = segment_dict + end + when "GE" + unless state.segment_dict.empty? + segment_dict = state.segment_dict.pop + state.segment_dict = segment_dict + end + end + end + + result = Stupidedi::Reader::Tokenizer.next_segment(result.rest, state) + end + + # pp result + # puts + + mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 + $stderr.puts "Finish: %0.2d MiB" % mem + + stop = Time.now + $stderr.puts "%0.3f seconds" % (stop - start) +end + +if ARGV.delete('--fast') + config = Stupidedi::Config.contrib + input = Stupidedi::Reader::Pointer.build(File.read(ARGV[0])) + state = Stupidedi::Reader::Tokenizer::State.todo + run(config, input, state) +else + config = Stupidedi::Config.contrib + input = Stupidedi::Reader::Pointer.build(File.read(ARGV[0])) + state = Stupidedi::Reader::Tokenizer::State.todo + start = Time.now + + MemoryProfiler.report do + run(config, input, state) + end.pretty_print(to_file: "prof/memprof-tk-#{start.strftime("%Y%m%dT%H%M%S")}.txt", + color_output: false, retained_strings: 100, + allocated_strings: 100, detailed_report: true, + scale_bytes: true) +end From f5614d96cfd7ede27a2e0fe2019f6a88d6d072ea Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 24 Jun 2019 02:03:25 -0500 Subject: [PATCH 16/38] Restore one allocation to improve benchmark time to 6.2s --- lib/stupidedi/reader/pointer.rb | 3 +-- lib/stupidedi/reader/tokenizer.rb | 30 ++++++++++-------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index bb80def1e..4e61de433 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -80,8 +80,7 @@ def reify(always_allocate = false) #stderr.puts "reify: no allocation" @storage else - $stderr.puts "reify: allocate[#@offset, #@length]" - raise + # $stderr.puts "reify: allocate[#@offset, #@length]" @storage[@offset, @length] end end diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index cea5f2fab..b9c53f075 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -366,10 +366,10 @@ def _next_segment_id(input, state) # # So it's better to make one copy here instead of them being created # implicitly twice below. - segment_id = buffer #.to_s + segment_id = buffer.to_s - # return expected("segment identifier, found %s" % segment_id.inspect, :position) \ - # if segment_id.match?(Tokenizer::NOT_SEGMENT_ID, 0, true) + return expected("segment identifier, found %s" % segment_id.inspect, :position) \ + if segment_id.match?(Tokenizer::NOT_SEGMENT_ID) offset = _skip_control_characters(input, offset) return done([segment_id, :position], input.drop(offset)) @@ -393,17 +393,7 @@ def next_segment(input, state) return unexpected("eof after %s" % segment_id, :position) \ if result.rest.empty? - # This is the only String allocation we cannot get around. The `match?` - # call either has a pattern with \A..\z, or the length of segment_id - # is very small compared to the `@storage` after it, so `match?` will - # allocate the short substring. Next, we need to convert segment_id to - # a symbol, and this also requires reifying the string to call `to_sym` - # - # So it's better to make one copy here instead of them being created - # implicitly twice below. - # segment_id = segment_id.to_sym - - if segment_id == "ISA" # :ISA + if segment_id == :ISA # We encountered a new ISA segment without having seen the previous # ISA's matching IEA segment. result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } @@ -418,17 +408,17 @@ def next_segment(input, state) result.rest.head.inspect, segment_id], :position) end - # if state.segment_dict.defined_at?(segment_id) - # element_uses = state.segment_dict.at(segment_id).element_uses - # else - element_uses = [] - # end + if state.segment_dict.defined_at?(segment_id) + element_uses = state.segment_dict.at(segment_id).element_uses + else + element_uses = [] + end result = _read_elements(result.rest, state, segment_id, element_uses) return result if result.fail? # We've parsed an IEA segment, so reset and look for an ISA next time - state.separators = nil if segment_id == "IEA" # :IEA + state.separators = nil if segment_id == :IEA done(SegmentTok.build(segment_id, result.value, :position), result.rest) end From 5dac65cc07bd10a6a9abcc0454dffe211eb0fc68 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 24 Jun 2019 02:11:41 -0500 Subject: [PATCH 17/38] Leave segment_id as a String, not symbol. Benchmark at 6.8s --- lib/stupidedi/reader/tokenizer.rb | 13 ++++++------- prof/memprof-tk.rb | 8 +++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index b9c53f075..32db48784 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -361,15 +361,14 @@ def _next_segment_id(input, state) # This is the only String allocation we cannot get around. The `match?` # call either has a pattern with \A..\z, or the length of segment_id # is very small compared to the `@storage` after it, so `match?` will - # allocate the short substring. Next, we need to convert segment_id to - # a symbol, and this also requires reifying the string to call `to_sym` + # allocate the short substring. # - # So it's better to make one copy here instead of them being created - # implicitly twice below. + # Later, the segment_id will be subject to many equality checks, so it + # is faster overall to make the substring copy here. segment_id = buffer.to_s return expected("segment identifier, found %s" % segment_id.inspect, :position) \ - if segment_id.match?(Tokenizer::NOT_SEGMENT_ID) + unless segment_id.match?(Tokenizer::SEGMENT_ID) offset = _skip_control_characters(input, offset) return done([segment_id, :position], input.drop(offset)) @@ -393,7 +392,7 @@ def next_segment(input, state) return unexpected("eof after %s" % segment_id, :position) \ if result.rest.empty? - if segment_id == :ISA + if segment_id == "ISA" #:ISA # We encountered a new ISA segment without having seen the previous # ISA's matching IEA segment. result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } @@ -418,7 +417,7 @@ def next_segment(input, state) return result if result.fail? # We've parsed an IEA segment, so reset and look for an ISA next time - state.separators = nil if segment_id == :IEA + state.separators = nil if segment_id == "IEA"#:IEA done(SegmentTok.build(segment_id, result.value, :position), result.rest) end diff --git a/prof/memprof-tk.rb b/prof/memprof-tk.rb index c3e9661b5..5e083b597 100755 --- a/prof/memprof-tk.rb +++ b/prof/memprof-tk.rb @@ -23,18 +23,17 @@ def run(config, input, state) until result.fail? segment_tok = result.value - #p segment_tok.segment_id + puts segment_tok.segment_id - if false case segment_tok.segment_id when "GS" # GS08: Version / Release / Industry Identifier Code version = segment_tok.element_toks.at(7).try(:value).try(:to_s) gscode = version.try(:slice, 0, 6) - + # GS01: Functional Identifier Code fgcode = segment_tok.element_toks.at(0).try(:value) - + if config.functional_group.defined_at?(gscode) envelope_def = config.functional_group.at(gscode) envelope_val = envelope_def.empty @@ -47,7 +46,6 @@ def run(config, input, state) state.segment_dict = segment_dict end end - end result = Stupidedi::Reader::Tokenizer.next_segment(result.rest, state) end From 69a77df7a14907b1203da274f812a4a8a7b8665e Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 24 Jun 2019 02:14:11 -0500 Subject: [PATCH 18/38] Put it back, segment_id is a Symbol. No significant change in benchmark time --- lib/stupidedi/reader/tokenizer.rb | 6 +++--- prof/memprof-tk.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index 32db48784..c01339284 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -371,7 +371,7 @@ def _next_segment_id(input, state) unless segment_id.match?(Tokenizer::SEGMENT_ID) offset = _skip_control_characters(input, offset) - return done([segment_id, :position], input.drop(offset)) + return done([segment_id.to_sym, :position], input.drop(offset)) end # @return [SegmentTok] @@ -392,7 +392,7 @@ def next_segment(input, state) return unexpected("eof after %s" % segment_id, :position) \ if result.rest.empty? - if segment_id == "ISA" #:ISA + if segment_id == :ISA # We encountered a new ISA segment without having seen the previous # ISA's matching IEA segment. result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } @@ -417,7 +417,7 @@ def next_segment(input, state) return result if result.fail? # We've parsed an IEA segment, so reset and look for an ISA next time - state.separators = nil if segment_id == "IEA"#:IEA + state.separators = nil if segment_id == :IEA done(SegmentTok.build(segment_id, result.value, :position), result.rest) end diff --git a/prof/memprof-tk.rb b/prof/memprof-tk.rb index 5e083b597..b61e59948 100755 --- a/prof/memprof-tk.rb +++ b/prof/memprof-tk.rb @@ -26,7 +26,7 @@ def run(config, input, state) puts segment_tok.segment_id case segment_tok.segment_id - when "GS" + when :GS # GS08: Version / Release / Industry Identifier Code version = segment_tok.element_toks.at(7).try(:value).try(:to_s) gscode = version.try(:slice, 0, 6) @@ -40,7 +40,7 @@ def run(config, input, state) segment_dict = state.segment_dict.push(envelope_val.segment_dict) state.segment_dict = segment_dict end - when "GE" + when :GE unless state.segment_dict.empty? segment_dict = state.segment_dict.pop state.segment_dict = segment_dict From de3ae6d5f9bce0c56a6858e12042dbe719eea1d0 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 24 Jun 2019 14:01:05 -0500 Subject: [PATCH 19/38] Housekeeping --- lib/stupidedi/reader.rb | 12 +++--- lib/stupidedi/reader/pointer.rb | 21 ++++++---- lib/stupidedi/reader/position.rb | 67 ++++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index 1f2992106..ac04697fd 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -24,11 +24,11 @@ module Reader # @private # @return [Regexp] - R_BASIC = /[A-Z0-9!"&'()*+,.\/:;?= -]/ + R_BASIC = /[A-Z0-9!"&'()*+,.\/:;?= -]/.freeze # @private # @return [Regexp] - R_EXTENDED = /[a-z%@\[\]_{}\\|<>~^`#\$ÀÃÂÄàáâäÈÉÊèéêëÌÃÎìíîïÒÓÔÖòóôöÙÚÛÜùúûüÇçÑñ¿¡]/ + R_EXTENDED = /[a-z%@\[\]_{}\\|<>~^`#\$ÀÃÂÄàáâäÈÉÊèéêëÌÃÎìíîïÒÓÔÖòóôöÙÚÛÜùúûüÇçÑñ¿¡]/.freeze # @private # @return [Regexp] @@ -36,19 +36,19 @@ module Reader # @private # @return [String] - C_BYTES = (0..255).inject(""){|string, c| string + [c].pack('U') } + C_BYTES = (0..255).inject(""){|string, c| string + [c].pack('U') }.freeze # @private # @return [Hash] - H_BASIC = C_BYTES.scan(R_BASIC).inject({}){|h,c| h[c] = nil; h } + H_BASIC = C_BYTES.scan(R_BASIC).inject({}){|h,c| h[c] = nil; h }.freeze # @private # @return [Hash] - H_EXTENDED = C_BYTES.scan(R_EXTENDED).inject({}){|h,c| h[c] = nil; h } + H_EXTENDED = C_BYTES.scan(R_EXTENDED).inject({}){|h,c| h[c] = nil; h }.freeze # @private # @return [Hash] - H_EITHER = C_BYTES.scan(R_EITHER).inject({}){|h,c| h[c] = nil; h } + H_EITHER = C_BYTES.scan(R_EITHER).inject({}){|h,c| h[c] = nil; h }.freeze # @private # @return [Regexp] diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 4e61de433..77fb73f8b 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -57,9 +57,9 @@ def initialize(storage, offset=0, length=storage.length) # @return [String] def inspect - "#<%s%s@storage=0x%s @offset=%d @length=%d>" % + "#<%s %s@storage=0x%s@offset=%d @length=%d>" % [self.class.name.split("::").last, - @storage.frozen? ? "+" : "-", + @storage.frozen? ? "-" : "+", (@storage.object_id << 1).to_s(16), @offset, @length] end @@ -327,9 +327,11 @@ def to_str reify end - # an unescaped \A, \a, or ^, \Z, \z, $ - ANCHORS_A = /(? 1024 + # TODO: Run some experiments to test when reify is faster than not return reify.match?(pattern, offset) end @@ -387,6 +390,8 @@ def match?(pattern, offset=0, anchorless=false) # @return [MatchData, Integer] def match_(pattern, offset, anchorless=false) if @length < 1024 and @storage.length - @offset > 1024 + # TODO: Run some experiments to test when reify is faster than not + # # We are pointing to a short segment, but there is still a very # long string that follows in @storage. There's no way to tell # the regexp engine to stop after a given offset, so it will start @@ -633,12 +638,14 @@ def ==(other) if @storage.eql?(other.storage) @offset == other.offset and @length == other.length else - # length == other.length and reify == other.reify + # TODO: When can String#index on a long String be slower than an + # allocation and String#== ? length == other.length and \ @storage.index(other.reify, @offset) == @offset end elsif other.is_a?(String) - # length == other.length and reify == other + # TODO: When can String#index on a long String be slower than an + # allocation and String#== ? length == other.length and \ @storage.index(other, @offset) == @offset end diff --git a/lib/stupidedi/reader/position.rb b/lib/stupidedi/reader/position.rb index abfb01e32..2a18e213b 100644 --- a/lib/stupidedi/reader/position.rb +++ b/lib/stupidedi/reader/position.rb @@ -5,7 +5,7 @@ module Stupidedi module Reader # - # This module is intended to be used with a user-defined Struct. This + # This mixin is intended to be used with a user-defined Struct. This # scheme allows customization of what position information is tracked via # the tokenizer and passed along into the parse tree. # @@ -31,7 +31,7 @@ module Reader # beneficial to track the minimum. # # Here's how the memory footprint works out: - # path: roughly 20 bytes + length of string in bytes, but minimum is 40 + # path: roughly 20 bytes + length of string in bytes, but minimum is 40b # line: represented directly, so no overhead besides the VALUE struct # column: same # offset: same @@ -54,7 +54,6 @@ module Reader # The default NoPosition implementation is provided which still consumes # 40 bytes, but only one instance is created. # - module Position def self.included(base) base.__send__(:extend, ClassMethods) @@ -62,6 +61,10 @@ def self.included(base) end module ClassMethods + def build(path) + new.reset(path, 1, 0, 0) + end + def caller(offset = 1) path, line, = Stupidedi.caller(offset + 1) new.reset(path, line, nil, nil) @@ -69,12 +72,8 @@ def caller(offset = 1) end module InstanceMethods - def to_s - inspect - end - # @return [String] - def inspect + def to_s parts = [] parts << "path #{path}" if respond_to?(:path) parts << "line #{line}" if respond_to?(:line) @@ -83,6 +82,30 @@ def inspect parts.join(", ") end + # Calculate the new position if we started on the current position and + # then read the given input. + # + # @parma input [#length, #count, #rindex] + # @return [self.class] + def advance(input) + length_ = input.length + lines_ = input.count("\n") + column_ = unless lines_.zero? + # Column numbering starts at 1 + length_ - input.rindex("\n") - 1 + else + _column = respond_to?(:column) ? column : 0 + length_ + _column + end + + clone.reset \ + respond_to?(:path) ? path : nil, + respond_to?(:line) ? line + lines_ : nil, + respond_to?(:column) ? column_ : nil, + respond_to?(:offset) ? offset + length_ : nil + end + + # @return self def reset(path, line, column, offset) self[:path] = path if respond_to?(:path) self[:line] = line if respond_to?(:line) @@ -93,26 +116,30 @@ def reset(path, line, column, offset) end end + # This provides a stub that acts like a Position but doesn't compute + # or retain any information. Because it has no state, `NoPosition.new` + # returns the class itself, which implements `#to_s` and `#advance`. class NoPosition - def to_s - inspect - end - - def inspect - "no position info" - end - - def reset(path, line, column, offset) - end end class << NoPosition def caller(offset = 1) - @instance ||= NoPosition.allocate + self end def new(*args) - @instance ||= NoPosition.allocate + self + end + + # Singleton instance methods + ######################################################################### + + def to_s + "no position info" + end + + def advance(input) + self end end end From f9a44aa1ab8a9375a7bb33dbffec5110bac00b4f Mon Sep 17 00:00:00 2001 From: kputnam Date: Wed, 17 Jul 2019 15:21:14 -0500 Subject: [PATCH 20/38] Hook the new tokenizer up to the existing parser - Reader::Tokenizer returns fatal error when zero ISA's found or when an error occurs inside of an ISA..IEA envelope; other errors are not Reader::Result#fatal? - Create Reader::Input to track the position of tokenized elements - Fix subtle bugs in Reader::Pointer and/or Reader::StringPtr - Parser::StateMachine#insert now destructively updates tokenizer state (separators and segment_dict) - Parser::BuilderDsl uses new Reader::StacktracePosition - Parser::DslReader has been merged into Parser::BuilderDsl - Now ElementVal::AN and ElementVal::ID use a StringPtr where possible, but ElementVal::TM, ::DT, ::Nn, ::R all need to "parse" the string so it requires allocating the substring first (TODO) --- lib/stupidedi.rb | 4 +- lib/stupidedi/editor/result.rb | 2 +- lib/stupidedi/parser/builder_dsl.rb | 83 +- lib/stupidedi/parser/constraint_table.rb | 20 +- lib/stupidedi/parser/generation.rb | 67 +- .../parser/states/functional_group_state.rb | 2 +- .../parser/states/interchange_state.rb | 3 +- .../parser/states/transaction_set_state.rb | 4 + lib/stupidedi/parser/tokenization.rb | 18 +- lib/stupidedi/reader.rb | 13 +- lib/stupidedi/reader/input.rb | 72 ++ lib/stupidedi/reader/pointer.rb | 73 +- lib/stupidedi/reader/position.rb | 93 +-- lib/stupidedi/reader/position/no_position.rb | 44 + .../reader/position/offset_position.rb | 26 + .../reader/position/stacktrace_position.rb | 48 ++ lib/stupidedi/reader/separators.rb | 15 + lib/stupidedi/reader/tokenizer.rb | 762 ++++++++++-------- lib/stupidedi/reader/tokens/segment_tok.rb | 20 +- .../transaction_sets/validation/ambiguity.rb | 5 +- lib/stupidedi/values/invalid_envelope_val.rb | 8 +- .../versions/common/element_types/an.rb | 1 + .../versions/common/element_types/dt.rb | 4 +- .../versions/common/element_types/id.rb | 1 + .../versions/common/element_types/nn.rb | 3 +- .../versions/common/element_types/r.rb | 1 + .../versions/common/element_types/tm.rb | 16 +- .../element_types/separator_spec.rb | 2 +- spec/lib/stupidedi/interchanges_spec.rb | 3 +- .../versions/common/element_types/an_spec.rb | 2 +- .../versions/common/element_types/dt_spec.rb | 2 +- .../versions/common/element_types/id_spec.rb | 2 +- .../versions/common/element_types/nn_spec.rb | 2 +- .../versions/common/element_types/r_spec.rb | 2 +- .../versions/common/element_types/tm_spec.rb | 2 +- spec/support/fixtures.rb | 7 +- 36 files changed, 859 insertions(+), 573 deletions(-) create mode 100644 lib/stupidedi/reader/input.rb create mode 100644 lib/stupidedi/reader/position/no_position.rb create mode 100644 lib/stupidedi/reader/position/offset_position.rb create mode 100644 lib/stupidedi/reader/position/stacktrace_position.rb diff --git a/lib/stupidedi.rb b/lib/stupidedi.rb index 869fe247d..8995766b2 100644 --- a/lib/stupidedi.rb +++ b/lib/stupidedi.rb @@ -50,8 +50,8 @@ module Stupidedi autoload :Versions, "stupidedi/versions" autoload :VERSION, "stupidedi/version" - def self.caller(depth = 2) - if k = ::Kernel.caller.at(depth - 1) + def self.caller(offset = 2) + if k = ::Kernel.caller(offset, 1).first k.split(":") end end diff --git a/lib/stupidedi/editor/result.rb b/lib/stupidedi/editor/result.rb index f458c9a35..cc6fd1101 100644 --- a/lib/stupidedi/editor/result.rb +++ b/lib/stupidedi/editor/result.rb @@ -62,7 +62,7 @@ def initialize(zipper, action, code, reason) # @return [String] def inspect name = self.class.name.split("::").last - "#{name}(#{zipper.node.position.inspect}, #{@reason}, #{@zipper.node.inspect})" + "#{name}(#{zipper.node.position}, #{@reason}, #{@zipper.node.inspect})" end def error? diff --git a/lib/stupidedi/parser/builder_dsl.rb b/lib/stupidedi/parser/builder_dsl.rb index 37794db1b..c110e1af1 100644 --- a/lib/stupidedi/parser/builder_dsl.rb +++ b/lib/stupidedi/parser/builder_dsl.rb @@ -16,16 +16,21 @@ class BuilderDsl # @return [Boolean] attr_writer :strict - # @return [DslReader] - attr_reader :reader + # @return [Reader::Separators] + attr_accessor :separators + + # @return [Reader::SegmentDict] + attr_accessor :segment_dict - def_delegators :@machine, :pretty_print, :segment, :element, :zipper, :successors, :empty?, :first?, :last?, :deterministic? + def_delegators :@machine, :pretty_print, :segment, :element, :zipper, :successors, + :empty?, :first?, :last?, :deterministic? def initialize(machine, strict = true) - @machine = machine - @strict = strict - @reader = DslReader.new(Reader::Separators.empty, - Reader::SegmentDict.empty) + @position = Reader::NoPosition + @machine = machine + @strict = strict + @separators = Reader::Separators.empty + @segment_dict = Reader::SegmentDict.empty end def respond_to_missing?(name, include_private = false) @@ -38,8 +43,8 @@ def strict? # @return [BuilderDsl] def segment!(name, position, *elements) - segment_tok = mksegment_tok(@reader.segment_dict, name, elements, position) - machine, reader = @machine.insert(segment_tok, @strict, @reader) + segment_tok = mksegment_tok(@segment_dict, name, elements, position) + machine = @machine.insert(segment_tok, @strict, self) if @strict unless machine.deterministic? @@ -59,7 +64,6 @@ def segment!(name, position, *elements) # starting a new interchange will end all previously "open" syntax # nodes. So we compare the state before adding `segment_tok` to the # corresponding state after we've added `segment_tok`. - machine.prev.tap do |prev| prev.active.zip(machine.active) do |p, q| # If the new state `q` is a descendent of `p`, we know that `p` @@ -91,7 +95,6 @@ def segment!(name, position, *elements) end @machine = machine - @reader = reader self end @@ -100,7 +103,7 @@ def segment!(name, position, *elements) def method_missing(name, *args) if SEGMENT_ID.match?(name.to_s) - segment!(name, Reader::Position.caller(2), *args) + segment!(name, @position.build, *args) else super end @@ -113,24 +116,24 @@ def critique(zipper, recursive = false, position = false) if zipper.node.simple? or zipper.node.component? if zipper.node.invalid? raise Exceptions::ParseError, - "invalid #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "invalid #{zipper.node.descriptor} at #{zipper.node.position}" elsif zipper.node.blank? if zipper.node.usage.required? raise Exceptions::ParseError, - "required #{zipper.node.descriptor} is blank at #{zipper.node.position.inspect}" + "required #{zipper.node.descriptor} is blank at #{zipper.node.position}" end elsif zipper.node.usage.forbidden? raise Exceptions::ParseError, - "forbidden #{zipper.node.descriptor} is present at #{zipper.node.position.inspect}" + "forbidden #{zipper.node.descriptor} is present at #{zipper.node.position}" elsif not zipper.node.allowed? raise Exceptions::ParseError, - "value #{zipper.node.to_s} is not allowed in #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "value #{zipper.node.to_s} is not allowed in #{zipper.node.descriptor} at #{zipper.node.position}" elsif zipper.node.too_long? raise Exceptions::ParseError, - "value is too long in #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "value is too long in #{zipper.node.descriptor} at #{zipper.node.position}" elsif zipper.node.too_short? raise Exceptions::ParseError, - "value is too short in #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "value is too short in #{zipper.node.descriptor} at #{zipper.node.position}" end elsif zipper.node.composite? @@ -140,11 +143,11 @@ def critique(zipper, recursive = false, position = false) # position of it's first child; but an empty composit element # doesn't have children. So the closest position is of the parent raise Exceptions::ParseError, - "required #{zipper.node.descriptor} is blank at #{zipper.parent.node.position.inspect}" + "required #{zipper.node.descriptor} is blank at #{zipper.parent.node.position}" end elsif zipper.node.usage.forbidden? raise Exceptions::ParseError, - "forbidden #{zipper.node.descriptor} is present at #{zipper.node.position.inspect}" + "forbidden #{zipper.node.descriptor} is present at #{zipper.node.position}" else if zipper.node.present? zipper.children.each_with_index do |z, i| @@ -155,7 +158,7 @@ def critique(zipper, recursive = false, position = false) d.syntax_notes.each do |s| unless s.satisfied?(zipper) raise Exceptions::ParseError, - "for #{zipper.node.descriptor}, #{s.reason(zipper)} at #{zipper.node.position.inspect}" + "for #{zipper.node.descriptor}, #{s.reason(zipper)} at #{zipper.node.position}" end end end @@ -164,7 +167,7 @@ def critique(zipper, recursive = false, position = false) elsif zipper.node.repeated? unless zipper.node.usage.repeat_count.include?(zipper.node.children.length) raise Exceptions::ParseError, - "repeating #{zipper.node.descriptor} occurs too many times at #{zipper.node.position.inspect}" + "repeating #{zipper.node.descriptor} occurs too many times at #{zipper.node.position}" end zipper.children.each{|z| critique(z) } @@ -174,10 +177,10 @@ def critique(zipper, recursive = false, position = false) if zipper.up.node.invalid? # parent is an InvalidEnvelopeVal raise Exceptions::ParseError, - "#{zipper.first.node.reason} at #{zipper.first.node.position.inspect}" + "#{zipper.first.node.reason} at #{zipper.first.node.position}" else raise Exceptions::ParseError, - "#{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "#{zipper.node.descriptor} at #{zipper.node.position}" end else zipper.children.each_with_index do |z, i| @@ -187,7 +190,7 @@ def critique(zipper, recursive = false, position = false) d = zipper.node.definition d.syntax_notes.each do |s| raise Exceptions::ParseError, - "for #{zipper.node.descriptor}, #{s.reason(zipper)} at #{zipper.node.position.inspect}" \ + "for #{zipper.node.descriptor}, #{s.reason(zipper)} at #{zipper.node.position}" \ unless s.satisfied?(zipper) end end @@ -224,7 +227,7 @@ def critique_occurences(zipper, recursive) occurences[child.node.usage] += 1 elsif child.node.invalid? raise Exceptions::ParseError, - "#{child.node.descriptor} at #{child.node.position.inspect}" + "#{child.node.descriptor} at #{child.node.position}" else occurences[child.node.definition] += 1 end @@ -240,10 +243,10 @@ def critique_occurences(zipper, recursive) if count.zero? and child.required? raise Exceptions::ParseError, - "required #{child.descriptor} is missing from #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "required #{child.descriptor} is missing from #{zipper.node.descriptor} at #{zipper.node.position}" elsif bound < count raise Exceptions::ParseError, - "#{child.descriptor} occurs too many times in #{zipper.node.descriptor} at #{zipper.node.position.inspect}" + "#{child.descriptor} occurs too many times in #{zipper.node.descriptor} at #{zipper.node.position}" end end end @@ -264,29 +267,5 @@ def build(config, strict = true) # @endgroup ######################################################################### end - - # @private - class DslReader - # @return [Reader::Separators] - attr_reader :separators - - # @return [Reader::SegmentDict] - attr_reader :segment_dict - - def initialize(separators, segment_dict) - @separators, @segment_dict = separators, segment_dict - end - - # @return [DslReader] - def copy(changes = {}) - @separators = changes.fetch(:separators, @separators) - @segment_dict = changes.fetch(:segment_dict, @segment_dict) - self - end - - def stream? - false - end - end end end diff --git a/lib/stupidedi/parser/constraint_table.rb b/lib/stupidedi/parser/constraint_table.rb index c78052592..f6bcf4c6d 100644 --- a/lib/stupidedi/parser/constraint_table.rb +++ b/lib/stupidedi/parser/constraint_table.rb @@ -89,24 +89,6 @@ def matches(segment_tok, strict, mode) end end - # Chooses the {Instruction} that pops the greatest number of states. - # - class Deepest < ConstraintTable - # def initialize(instructions) - # @instructions = instructions - # end - - # # @return [Array] - # def matches(segment_tok, strict, mode) - # @__matches ||= begin - # deepest = @instructions.map(&:pop_count).max - # @instructions.select{|i| i.pop_count == deepest }.tap do |xs| - # critique(segment_tok, xs.map(&:segment_use)) if strict - # end - # end - # end - end - # Chooses the subset of {Instruction} values based on the distinguishing # values allowed by each {Schema::SegmentUse}. For instance, there are # often several loops that begin with `NM1`, which are distinguished by @@ -134,7 +116,7 @@ def matches(segment_tok, strict, mode) when nil, :not_used, :default # value wasn't present in segment_tok, can't use it to decide else - singleton = map.at(value) + singleton = map.at(value.to_s) present = true unless singleton.nil? diff --git a/lib/stupidedi/parser/generation.rb b/lib/stupidedi/parser/generation.rb index 87206f104..ed4c48725 100644 --- a/lib/stupidedi/parser/generation.rb +++ b/lib/stupidedi/parser/generation.rb @@ -2,6 +2,8 @@ module Stupidedi using Refinements + # TODO: Rename this + # module Parser module Generation # Consumes all input from `reader` and returns the updated @@ -21,52 +23,28 @@ module Generation # been consumed. The extra parse trees are returned (in memory) # via the {StateMachine} to aide diagnosis. # - # @return [(StateMachine, Reader::Result)] + # @param tokenizer [Reader::Tokenizer] + # @param options + # + # @yield [Reader::IgnoredTok] + # @return [(StateMachine, Reader::Tokenizer::Result)] def read(tokenizer, options = {}) - drain(tokenizer) - end - - # @return [StateMachine] - def drain(tokenizer) - machine_ = machine.dup + limit = options.fetch(:nondeterminism, 1) + machine = self.dup - tokenizer.each do |token| + return machine, tokenizer.each do |token| case token - when ErrorTok - if block_given? - yield token # TODO: Should user be able to signal something to us? - else - # - end - - when IgnoreTok - if block_given? - yield token # TODO: Should user be able to signal something to us? - else - # - end - - when SegmentTok - machine_.insert!(token, false, tokenizer) - - if machine_.active.length > limit - matches = machine_.active.map do |m| - if segment_use = m.node.zipper.node.usage - "SegmentUse(%s, %s, %s, %s)" % [segment_use.position, - segment_use.id, - segment_use.requirement.inspect, - segment_use.repeat_count.inspect] - else - m.node.zipper.node.inspect - end - end.join(", ") - - raise ... - end + when Reader::SegmentTok + machine.insert!(token, false, tokenizer) + when Reader::IgnoredTok + yield token if block_given? end end end + # NOTE: This may destructively update the `state` by reassigning its + # `segment_dict` or `separators` attributes. + # # @return [StateMachine] def insert(segment_tok, strict, tokenizer) StateMachine.new(@config, insert_(segment_tok, strict, tokenizer)) @@ -80,6 +58,9 @@ def insert!(segment_tok, strict, tokenizer) private + # NOTE: This may destructively update the `state` by reassigning its + # `segment_dict` or `separators` attributes. + # # @return [Array>] def insert_(segment_tok, strict, tokenizer) @active.flat_map do |zipper| @@ -94,7 +75,7 @@ def insert_(segment_tok, strict, tokenizer) # We might be moving up or down past the interchange or functional # group envelope, which determine the separators and segment_dict - unless op.push.nil? and (op.pop_count.zero? or tokenizer.stream?) + unless op.push.nil? and (op.pop_count.zero? or tokenizer.separators.blank?) tokenizer.separators = successor.node.separators tokenizer.segment_dict = successor.node.segment_dict end @@ -105,6 +86,8 @@ def insert_(segment_tok, strict, tokenizer) end end + public + # Three things change together when executing an {Instruction}: # # 1. The stack of instruction tables that indicates where a segment @@ -148,8 +131,8 @@ def execute(op, zipper, tokenizer, segment_tok) parent = state.node.copy \ :zipper => value, :children => [], - :separators => tokenizer.try(&:separators), - :segment_dict => tokenizer.try(&:segment_dict), + :separators => tokenizer.try{|x| x.separators }, + :segment_dict => tokenizer.try{|x| x.segment_dict }, :instructions => table.pop(op.pop_count).drop(op.drop_count) # Note, `state` is a cursor pointing at a state, while `parent` diff --git a/lib/stupidedi/parser/states/functional_group_state.rb b/lib/stupidedi/parser/states/functional_group_state.rb index bf5df1fd7..0e25cd03a 100644 --- a/lib/stupidedi/parser/states/functional_group_state.rb +++ b/lib/stupidedi/parser/states/functional_group_state.rb @@ -53,7 +53,7 @@ class << FunctionalGroupState def push(zipper, parent, segment_tok, segment_use, config) # GS08: Version / Release / Industry Identifier Code gs08 = segment_tok.element_toks.at(7).try(:value) - gs08_version = gs08.try(:slice, 0, 6) + gs08_version = gs08.try(:slice, 0, 6).to_s # GS01: Functional Identifier Code gs01 = segment_tok.element_toks.at(0).try(:value) diff --git a/lib/stupidedi/parser/states/interchange_state.rb b/lib/stupidedi/parser/states/interchange_state.rb index b519f11c4..a829543c0 100644 --- a/lib/stupidedi/parser/states/interchange_state.rb +++ b/lib/stupidedi/parser/states/interchange_state.rb @@ -42,7 +42,8 @@ class << InterchangeState # @return [Zipper::AbstractCursor] def push(zipper, parent, segment_tok, segment_use, config) # ISA12: Interchange Control Version Number - version = segment_tok.element_toks.at(11).try(:value) + version = segment_tok.element_toks.at(11) + version = version.value.to_s if version unless config.interchange.defined_at?(version) return FailureState.push( diff --git a/lib/stupidedi/parser/states/transaction_set_state.rb b/lib/stupidedi/parser/states/transaction_set_state.rb index 1156fb7d8..c4f6a7939 100644 --- a/lib/stupidedi/parser/states/transaction_set_state.rb +++ b/lib/stupidedi/parser/states/transaction_set_state.rb @@ -69,6 +69,10 @@ def push(zipper, parent, segment_tok, segment_use, config) # end # end + st03 = st03.to_s + gs01 = gs01.to_s + st01 = st01.to_s + unless config.transaction_set.defined_at?(st03, gs01, st01) context = "#{st03.inspect} #{gs01.inspect} #{st01.inspect}" diff --git a/lib/stupidedi/parser/tokenization.rb b/lib/stupidedi/parser/tokenization.rb index 343b4cf4e..ed2f54c71 100644 --- a/lib/stupidedi/parser/tokenization.rb +++ b/lib/stupidedi/parser/tokenization.rb @@ -11,14 +11,14 @@ module Tokenization # # @return [void] def repeated(*elements) - [:repeated, elements, Reader::Position.caller(2)] + [:repeated, elements, Reader::StacktracePosition.build] end # Generates a composite element # # @return [void] def composite(*components) - [:composite, components, Reader::Position.caller(2)] + [:composite, components, Reader::StacktracePosition.build] end ######################################################################### @@ -28,7 +28,7 @@ def composite(*components) # # @return [void] def blank - [:blank, nil, Reader::Position.caller(2)] + [:blank, nil, Reader::StacktracePosition.build] end # Generates a blank element and asserts that the element's usage @@ -38,7 +38,7 @@ def blank # # @return [void] def not_used - [:not_used, nil, Reader::Position.caller(2)] + [:not_used, nil, Reader::StacktracePosition.build] end # Generates the only possible value an element may have, which may @@ -50,7 +50,7 @@ def not_used # # @return [void] def default - [:default, nil, Reader::Position.caller(2)] + [:default, nil, Reader::StacktracePosition.build] end # @endgroup @@ -124,7 +124,7 @@ def mksegment_tok(segment_dict, id, elements, position) end end - Reader::SegmentTok.new(id, element_toks, position, nil) + Reader::SegmentTok.build(id, element_toks, position) end # @return [Reader::RepeatedElementTok] @@ -175,17 +175,17 @@ def mkcomposite_tok(components, composite_use, designator, position) component_toks << mkcomponent_tok(c_tag, c_position || position) end - Reader::CompositeElementTok.build(component_toks, position, nil) + Reader::CompositeElementTok.build(component_toks, position) end # @return [Reader::ComponentElementTok] def mkcomponent_tok(value, position) - Reader::ComponentElementTok.build(value, position, nil) + Reader::ComponentElementTok.build(value, position) end # @return [Reader::SimpleElementTok] def mksimple_tok(value, position) - Reader::SimpleElementTok.build(value, position, nil) + Reader::SimpleElementTok.build(value, position) end # @endgroup diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index ac04697fd..f09f95107 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -14,10 +14,13 @@ module Reader autoload :CompositeElementTok, "stupidedi/reader/tokens/composite_element_tok" autoload :RepeatedElementTok, "stupidedi/reader/tokens/repeated_element_tok" - autoload :Position, "stupidedi/reader/position" - autoload :NoPosition, "stupidedi/reader/position" - autoload :Tokenizer, "stupidedi/reader/tokenizer" + autoload :Input, "stupidedi/reader/input" + autoload :Position, "stupidedi/reader/position" + autoload :NoPosition, "stupidedi/reader/position/no_position" + autoload :OffsetPosition, "stupidedi/reader/position/offset_position" + autoload :StacktracePosition, "stupidedi/reader/position/stacktrace_position" + autoload :Tokenizer, "stupidedi/reader/tokenizer" autoload :Pointer, "stupidedi/reader/pointer" autoload :ArrayPtr, "stupidedi/reader/pointer" autoload :StringPtr, "stupidedi/reader/pointer" @@ -59,8 +62,8 @@ class << self ######################################################################### # @return [StreamReader] - def build(input) - StreamReader.new(Input.build(input)) + def build(input, position = NoPosition) + Tokenizer.build(Input.build(input, position)) end # @endgroup diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb new file mode 100644 index 000000000..83d915773 --- /dev/null +++ b/lib/stupidedi/reader/input.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + class Input + include Inspect + + # @return [StringPtr] + attr_reader :pointer + + # @return [Position] + attr_reader :position + + def_delegators :@pointer, :defined_at?, :empty?, :count, :head, :take, + :match?, :slice, :index, :rindex, :reify, :last, :length, :=~, :[] + + def initialize(pointer, position) + @pointer, @position = + pointer, position + end + + # @NOTE: This allocates 3 objects: Input, Position, and StringPtr + def tail + drop(1) + end + + # @NOTE: This allocates 3 objects: Input, Position, and StringPtr, so + # if you've written x.drop(10).position.. x.position_at(10) is cheaper + def drop(n) + self.class.new(@pointer.drop(n), position_at(n)) + end + + # NOTE: This allocates 2 objects: StringPtr and Position + def position_at(n) + if @position.eql?(NoPosition) + @position + elsif @position.is_a?(Integer) + @position + n + else + @position.advance(@pointer.take(n)) + end + end + end + + class << Input + def build(value, position = NoPosition) + if value.is_a?(String) + string(value, position) + elsif defined?(Pathname) and value.is_a?(Pathname) + file(value, position) + elsif value.is_a?(Input) + value + elsif value.respond_to?(:read) + path = value.path if value.respond_to?(:path) + new(Pointer.build(value.read), position.build(path)) + else + raise TypeError, + "value must be a String, Pathname, or respond to #read" + end + end + + def file(path, position = NoPosition) + new(Pointer.build(File.read(path)), position.build(path)) + end + + def string(value, position = NoPosition) + new(Pointer.build(value), position.build(nil)) + end + end + end +end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 77fb73f8b..2b743d2cd 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -57,7 +57,7 @@ def initialize(storage, offset=0, length=storage.length) # @return [String] def inspect - "#<%s %s@storage=0x%s@offset=%d @length=%d>" % + "#<%s %s@storage=0x%s @offset=%d @length=%d>" % [self.class.name.split("::").last, @storage.frozen? ? "-" : "+", (@storage.object_id << 1).to_s(16), @offset, @length] @@ -102,6 +102,14 @@ def empty? @length <= 0 end + def blank? + empty? + end + + def present? + not blank? + end + # Return the first element. If {empty?}, `nil` will be returned. # # @return [E] @@ -120,11 +128,7 @@ def tail # # @return [E] def last - @storage[@offset + @length] if @length > 0 - end - - def end - self.class.new(@storage.freeze, @length, 0) + @storage[@offset + @length - 1] if @length > 0 end # True if `#at(n)` is defined. @@ -155,28 +159,36 @@ def at(n) # @return [Pointer | E] def [](offset, length=nil) if length.present? - raise ArgumentError, "offset must be non-negative" if 0 > offset raise ArgumentError, "length must be non-negative" if 0 > length - return nil if offset >= @length + return nil if offset >= @length or offset < -@length - if length > @length - offset - length = @length - offset - end + offset += @length if offset < 0 + length = @length - offset if length > @length - offset self.class.new(@storage.freeze, @offset + offset, length) elsif offset.kind_of?(Range) - unless offset.last - length = @storage.length - offset.first + a = offset.begin + b = offset.end + + a += @length if a < 0 + b += @length if b and b < 0 + + return nil if a < 0 or a >= @length + return nil if b and (b < 0 or b >= @length) + + if b.nil? + length = @length - a else - length = offset.last - offset.first + length = b - a length += 1 unless offset.exclude_end? end - self[offset.first, length] + + self[a, length] else - raise ArgumentError, "argument must be non-negative" if 0 > offset - @storage[@offset + offset] if @length > offset + offset += @length if offset < 0 + @storage[@offset + offset] if @length > offset and offset >= 0 end end @@ -188,6 +200,7 @@ def [](offset, length=nil) def drop(n) raise ArgumentError, "argument must be non-negative" if n < 0 n = @length if n > @length + return self if n.zero? self.class.new(@storage.freeze, @offset + n, @length - n) end @@ -241,6 +254,22 @@ def take!(n) prefix end + # This method is equivalent to x.drop(n).take(m), but it allocates + # one less object because the intermediate Pointer value isn't needed + # + # @return [Pointer] + def drop_take(drop, take) + raise ArgumentError, "drop must be non-negative" if drop < 0 + raise ArgumentError, "take must be non-negative" if take < 0 + + drop = @length if drop > @length + offset = @offset + drop + length = @length - drop + + take = length if take > length + self.class.new(@storage.freeze, offset, take) + end + # Split the Pointer in two at the given position by creating two new # Flyweights. # @@ -314,7 +343,7 @@ def build(object) class StringPtr < Pointer # TODO: More of these - def_delegators :reify, :to_sym, :intern + def_delegators :reify, :to_sym, :intern, :to_i, :to_d # This is called implicitly when we are used in String interpolation, # eg `"abc #{pointer} xyz"` or `"abc %s xyz" % pointer`. @@ -464,13 +493,13 @@ def index(other, offset=0, anchorless=false) # the given `offset`. If not found, then `nil` is returned. # # @return [Integer] - def rindex(other, offset=@length) + def rindex(other, offset=@length-1) raise ArgumentError, "offset must be non-negative" if offset < 0 - offset = @length if offset > @length + offset = @length - 1 if offset >= @length if other.is_a?(Regexp) if n = @storage.rindex(other, @offset + offset) - if n + $&.length <= @offset + @length + if n + other.length <= @offset + @length n - @offset else # The match starts within bounds but ends outside, so we need to @@ -497,7 +526,7 @@ def count(char) while true offset = @storage.index(char, offset) - offset and offset <= @offset + @length or break + offset and offset < @offset + @length or break offset += 1 count += 1 end diff --git a/lib/stupidedi/reader/position.rb b/lib/stupidedi/reader/position.rb index 2a18e213b..345f6ea0f 100644 --- a/lib/stupidedi/reader/position.rb +++ b/lib/stupidedi/reader/position.rb @@ -14,10 +14,10 @@ module Reader # TinyPosition = Struct.new(:offset) # TinyPosition.include(Stupidedi::Reader::Position) # - # anonClass = Struct.new(:path, :line) + # anonClass = Struct.new(:name, :line) # anonClass.include(Stupidedi::Reader::Position) # - # class BigPosition < Struct.new(:path, :line, :column, :offset) + # class BigPosition < Struct.new(:name, :line, :column, :offset) # include Stupidedi::Reader::Position # # # Return 50 chars before and after this position @@ -31,7 +31,7 @@ module Reader # beneficial to track the minimum. # # Here's how the memory footprint works out: - # path: roughly 20 bytes + length of string in bytes, but minimum is 40b + # name: roughly 20 bytes + length of string in bytes, but minimum is 40b # line: represented directly, so no overhead besides the VALUE struct # column: same # offset: same @@ -41,14 +41,14 @@ module Reader # consumed. # # So tracking three or fewer numeric-only fields consumes 40 bytes. But - # adding the fourth field increases that to 100 bytes + length of path. - # Tracking the path and two or less integer fields consumes 60 + length of - # the path string. + # adding the fourth field increases that to 100 bytes + length of name. + # Tracking the name and two or less integer fields consumes 60 + length of + # the name string. # # Because a position is attached to each individual part of syntax (the # start of a segment, the start of each individual element), this can add # up to a lot of space. In different situations, the user may independently - # know the file path and not need it stored here. Or they may not care + # know the file name and not need it stored here. Or they may not care # about the offset and manage with only line and column numbers. # # The default NoPosition implementation is provided which still consumes @@ -61,13 +61,13 @@ def self.included(base) end module ClassMethods - def build(path) - new.reset(path, 1, 0, 0) + def build(name) + new.reset(name, 1, 1, 0) end def caller(offset = 1) - path, line, = Stupidedi.caller(offset + 1) - new.reset(path, line, nil, nil) + name, line, = Stupidedi.caller(offset + 1) + new.reset(name, line, nil, nil) end end @@ -75,7 +75,7 @@ module InstanceMethods # @return [String] def to_s parts = [] - parts << "path #{path}" if respond_to?(:path) + parts << name if respond_to?(:name) and name.present? parts << "line #{line}" if respond_to?(:line) parts << "column #{column}" if respond_to?(:column) parts << "offset #{offset}" if respond_to?(:offset) @@ -88,26 +88,36 @@ def to_s # @parma input [#length, #count, #rindex] # @return [self.class] def advance(input) - length_ = input.length - lines_ = input.count("\n") - column_ = unless lines_.zero? - # Column numbering starts at 1 - length_ - input.rindex("\n") - 1 - else - _column = respond_to?(:column) ? column : 0 - length_ + _column - end + length_ = input.length + r_name = respond_to?(:name) + r_line = respond_to?(:line) + r_column = respond_to?(:column) + r_offset = respond_to?(:offset) + if r_line or r_column + lines_ = input.count("\n") + end + + if r_column + column_ = if lines_.zero? + length_ + column + else + length_ - input.rindex("\n") + end + end + + # Use `clone` because it's already implemented for Struct and any other + # class. Otherwise we would prefer our own `#copy(changes)` convention. clone.reset \ - respond_to?(:path) ? path : nil, - respond_to?(:line) ? line + lines_ : nil, - respond_to?(:column) ? column_ : nil, - respond_to?(:offset) ? offset + length_ : nil + r_name ? name : nil, + r_line ? line + lines_ : nil, + r_column ? column_ : nil, + r_offset ? offset + length_ : nil end # @return self - def reset(path, line, column, offset) - self[:path] = path if respond_to?(:path) + def reset(name, line, column, offset) + self[:name] = name if respond_to?(:name) self[:line] = line if respond_to?(:line) self[:column] = column if respond_to?(:column) self[:offset] = offset if respond_to?(:offset) @@ -115,32 +125,5 @@ def reset(path, line, column, offset) end end end - - # This provides a stub that acts like a Position but doesn't compute - # or retain any information. Because it has no state, `NoPosition.new` - # returns the class itself, which implements `#to_s` and `#advance`. - class NoPosition - end - - class << NoPosition - def caller(offset = 1) - self - end - - def new(*args) - self - end - - # Singleton instance methods - ######################################################################### - - def to_s - "no position info" - end - - def advance(input) - self - end - end end -end +end \ No newline at end of file diff --git a/lib/stupidedi/reader/position/no_position.rb b/lib/stupidedi/reader/position/no_position.rb new file mode 100644 index 000000000..98c57acfe --- /dev/null +++ b/lib/stupidedi/reader/position/no_position.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + # This provides a stub that acts like a Position but doesn't compute + # or retain any information. Because it has no state, `NoPosition.new` + # returns the class itself, which implements `#to_s` and `#advance`. + # + class NoPosition + end + + class << NoPosition + def caller(offset = 1) + self + end + + def new(*args) + self + end + + def build(*args) + new + end + end + + class << NoPosition + # Singleton "instance" methods + ######################################################################### + + def to_s + "no position info" + end + + def inspect + "NoPosition" + end + + def advance(input) + self + end + end + end +end \ No newline at end of file diff --git a/lib/stupidedi/reader/position/offset_position.rb b/lib/stupidedi/reader/position/offset_position.rb new file mode 100644 index 000000000..49db6fbdf --- /dev/null +++ b/lib/stupidedi/reader/position/offset_position.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + # Tracks only the number of characters from the start of the input + # + # NOTE: There aren't any instances of this class, because `build` returns + # an Integer rather than an instance of OffsetPosition. We don't need to + # implement `advance` because there is a special case in `Input#position_at` + # that does the work when its @position is an Integer. + # + class OffsetPosition + end + + class << OffsetPosition + def new(*args) + raise NoMethodError, "OffsetPosition isn't meant to be instantiated" + end + + def build(*args) + 0 + end + end + end +end \ No newline at end of file diff --git a/lib/stupidedi/reader/position/stacktrace_position.rb b/lib/stupidedi/reader/position/stacktrace_position.rb new file mode 100644 index 000000000..9be9b94b7 --- /dev/null +++ b/lib/stupidedi/reader/position/stacktrace_position.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Reader + # Tracks filename, method name, and line number of a frame in the stack. + # Usually the stack frame just above the stupidedi boundary is chosen, so + # users will have the location in their source where a value was generated. + # + # TODO: Build a way to store more than a single stack frame's worth of data + # along with a way to configure how many stack frames should be saved. + # + class StacktracePosition + + # @return [Thread::Backtrace::Location] + attr_reader :frame + + def_delegators :@frame, :absolute_path, :base_label, :inspect, :to_s, + :label, :lineno, :path + + def initialize(frame) + @frame = frame + end + + def name + @frame.path + end + + def line + @frame.lineno + end + + def advance(input) + raise NoMethodError, "StacktracePosition does not implement #advance" + end + end + + class << StacktracePosition + def caller(offset = 1) + new(Kernel.caller_locations(offset + 1, 1).first) + end + + def build(*args) + caller(2) + end + end + end +end \ No newline at end of file diff --git a/lib/stupidedi/reader/separators.rb b/lib/stupidedi/reader/separators.rb index 783832c9c..4e573aba5 100644 --- a/lib/stupidedi/reader/separators.rb +++ b/lib/stupidedi/reader/separators.rb @@ -27,6 +27,19 @@ def initialize(component, repetition, element, segment) component, repetition, element, segment end + # True if all separators are `#blank?` + def blank? + @component.blank? and + @repetition.blank? and + @element.blank? and + @segment.blank? + end + + # True if any one separator is not `#blank?` + def present? + not blank? + end + # @return [Separators] def copy(changes = {}) Separators.new \ @@ -38,6 +51,8 @@ def copy(changes = {}) # Creates a new value that has the separators from `other`, when they # are not nil, and will use separators from `self` otherwise. + # + # @return [Separators] def merge(other) Separators.new \ other.component || @component, diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index c01339284..cf54dbdd7 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -5,145 +5,143 @@ module Stupidedi module Reader # TODO: - # - fix shared builder from caller to callee - # - review/improve errors - # - determine what is yield'd, what is return'd - # - determine error recovery # - mark correct positions - # - switch char-by-char concat with regexps that grab whole elements + # - switch char-by-char with regexps that grab contiguous blocks + # - fatal errors: + # - tokenizer error inside ISA/IEA envelope + # - no ISA found in entire input + # + # TODONE: + # - fix shared builder from caller to callee: composite->component class Tokenizer - SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/ - NOT_SEGMENT_ID = /[^A-Z0-9]/ + include Inspect - def initialize(input) - @input = input + SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/.freeze + BAD_SEPARATOR = /[a-zA-Z0-9 ]/.freeze + + # @return [Separators] + attr_accessor :separators + + # @return [SegmentDict] + attr_accessor :segment_dict + + # @param input [Input] + def initialize(input, separators, segment_dict, switcher) + @input, @separators, @segment_dict, @switcher = + input, separators, segment_dict, switcher end + # @yield [SegmentTok | IgnoredTok] + # @return [Result] def each - if block_given? - Tokenizer.each(@input){|t| yield t } - else - Tokenizer.each(@input) - end - end + return enum_for(:each) unless block_given? + count = 0 + input = @input - def each_isa - if block_given? - Tokenizer.each_isa(@input){|t| yield t } - else - Tokenizer.each_isa(@input) - end - end + while true + result = next_segment(input) do |token| + yield token # IgnoredTok + end - # - # Track the configuration info, which controls how the tokenizer reads - # input. The `separators` field is straightforward, but `segment_dict` - # is used by `read_element` to determine which kind of elements should - # be parsed, according to the segment definition. - # - class State - include Inspect + break if result.fail? + yield result.value + input = result.rest + count += 1 + end - # @return [Separators] - attr_accessor :separators + # The loop only terminates on failure, but we do expect an EOF + # at some point; however, not before seeing an ISA, and not in + # the middle of an interchange. + result.fatal = count.zero? || @separators.present? + result + end - # @return [SegmentDict] - attr_accessor :segment_dict + # @yield [SegmentTok | IgnoredTok] + # @return [Result] + def each_isa + return enum_for(:each) unless block_given? + count = 0 + input = @input - # @return [ElementTokSwitch] - attr_accessor :builder + while true + result = next_isa_segment do |token| + yield token # IgnoredTok + end - def initialize(separators, segment_dict, builder) - @separators, @segment_dict, @builder = - separators, segment_dict, builder + break if result.fail? + yield result.value + input = result.rest + count += 1 end - def self.todo - new(Separators.new(":", "^", "*", "~"), SegmentDict.empty, ElementTokSwitch.new) - end + result.fatal = count.zero? || @separators.present? + result end - # - # Tokenizer operations return three pieces of information: either an - # error value or a success value (one piece is which one, the other - # piece is the value), and the remaining unconsumed input. - # class Result - def done? - not fail? - end - end + include Inspect - class Fail < Result - # @return [String] - attr_reader :error + # @return [Boolean] + abstract :fail? # @return [Position] - attr_reader :position - - def initialize(error, position) - @error, @position = error, position - end - - def fail? - true - end - end + abstract :position - class Done < Result - # @return [Object] - attr_reader :value + class Fail < Result + # @return [String] + attr_reader :error - # @return [StringPtr] - attr_reader :rest + # @return [Position] + attr_reader :position - def initialize(value, rest) - @value, @rest = value, rest - end + # @return [Boolean] + attr_accessor :fatal - def fail? - false - end - end - - module ElementTokBuilder - class Repeatable - def initialize(position) - @position, @element_toks = position, [] + def initialize(error, position, fatal = false) + @error, @position, @fatal = + error, position, fatal end - def add(element_tok) - @element_toks.push(element_tok) + def fail? + true end - def build - RepeatedElementTok.build(@element_toks, @element_toks.head.position) + def fatal? + @fatal end - def reset!(position) - @position = position - @element_toks = element_toks - self + def explain + yield @error end end - class NonRepeatable - def initialize(position) - @position = position - end - - def add(element_tok) - @element_tok = element_tok + class Done < Result + # @return [Object] + attr_reader :value + + # The remaining, unconsumed input + # + # @return [Input] + attr_reader :rest + + # The position within the input attributed to `value`. In many cases + # in the tokenizer, the `value` itself also carries a position, but + # this is useful for cases where the value does not. + # + # @return [Position] + attr_reader :position + + def initialize(value, position, rest) + @value, @position, @rest = + value, position, rest end - def build - @element_tok + def fail? + false end - def reset!(position) - @position = position - @element_tok = nil + def explain self end end @@ -152,13 +150,12 @@ def reset!(position) # # This is a bit clunky, but we can save quite a lot of memory allocation # by reusing our ElementTokBuilders. This is done by keeping a pair and - # reseting them before giving to a caller. We thread this switch through - # various parts of the Tokenizer storing it in State. - # - # Otherwise, one of these would be allocated for every time a simple or - # composite is tokenized. + # reseting them before giving to a caller. Otherwise, one of these would + # be allocated each time a simple or composite is tokenized. # class ElementTokSwitch + include Inspect + def_delegators :@active, :add, :build, :reset! def initialize @@ -170,60 +167,166 @@ def switch(repeatable, position) @active = repeatable ? @repeatable : @nonrepeatable @active.reset!(position) end - end - end - class << Tokenizer - # @yield [SegmentTok | IgnoredTok | ErrorTok] - def each(input) - return enum_for(:each, input) unless block_given? - state = Tokenizer::State.new(nil, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) + class ElementTokBuilder + include Inspect - while true - result = next_segment(input, state) do |token| - yield token + # @return self + abstract :add + + # @return [ElementTok] + abstract :build + + # @return self + abstract :reset! + + # @return [Position] + abstract :position + + class Repeatable < ElementTokBuilder + # @return [Position] + attr_reader :position + + def initialize(position) + @position, @element_toks = + position, [] + end + + def add(element_tok) + @element_toks.push(element_tok) + self + end + + def build + RepeatedElementTok.build(@element_toks, @position) + end + + def reset!(position) + @position = position + @element_toks = [] + self + end end - break if result.fail? - yield result.value - input = result.rest + class NonRepeatable < ElementTokBuilder + # @return [Position] + attr_reader :position + + def initialize(position) + @position = position + end + + def add(element_tok) + @element_tok = element_tok + self + end + + def build + @element_tok + end + + def reset!(position) + @position = position + @element_tok = nil + self + end + end end + end - result + # Skips over any text, X12 or not, until an interchange segment is + # found. Then @separators are reset. This is more efficient than + # repeatedly calling `next_segment` and checking if the segment_id + # is :ISA. + # + # @yield [IgnoredTok] the input that was ignored before "ISA" + # @return [Result] the ISA segment + def next_isa_segment(input) + segment_id = _next_isa_segment_id(input) do |t| + # It's not unusual for IgnoredTok to have no content, because "ISA" + # was found at the first character of the input. + yield t if block_given? and not t.value.blank? + end + return segment_id if segment_id.fail? + + elements = _read_isa_elements(segment_id.rest) + return elements if elements.fail? + + done(SegmentTok.build(:ISA, elements.value, segment_id.position), + segment_id.position, elements.rest) end - # This method will skip over without tokenizing input, until an ISA - # segment is found. + # Returns the next segment. `@separators` must have at least + # the `element` and `segment` fields initialized. + # + # If the next segment is an ISA, then it resets the element and + # segment separators in `@separators` according to the ISA segment. # - # @yield [SegmentTok | IgnoredTok | ErrorTok] - def each_isa(input) - return enum_for(:each, input) unless block_given? - state = Tokenizer::State.new(nil, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) + # @return [Result] + def next_segment(input) + if @separators.blank? + # We haven't yet found an ISA segment, which requires a different + # method than `next_segment`. + return next_isa_segment(input){|t| yield t if block_given? } + end - while true - result = next_isa_segment(input, state) do |token| - yield token # IgnoreTok - end + segment_id = _next_segment_id(input) + return segment_id if segment_id.fail? - break if result.fail? - yield result.value - input = result.rest + # Note, _next_segment_id does not guarantee result.rest isn't eof + return unexpected("eof after %s" % segment_id.value, input.position) \ + if segment_id.rest.empty? + + if segment_id.value == :ISA + # We encountered a new ISA segment without having seen the previous + # ISA's matching IEA segment. + elements = _read_isa_elements(segment_id.rest){|t| yield t if block_given? } + return elements if elements.fail? + + segment_tok = SegmentTok.build(:ISA, elements.value, segment_id.position) + return done(segment_tok, segment_id.position, + elements.rest.drop(_skip_control_characters(elements.rest))) end - result + unless segment_id.rest.head == @separators.element \ + or segment_id.rest.head == @separators.segment + return unexpected("%s after segment identifier %s" % [ + segment_id.rest.head.inspect, segment_id.value], segment_id.rest.position) + end + + if @segment_dict.defined_at?(segment_id.value) + element_uses = @segment_dict.at(segment_id.value).element_uses + else + element_uses = [] + end + + elements = _read_elements(segment_id.rest, segment_id.value, element_uses) + return elements if elements.fail? + + # We've parsed an IEA segment, so reset and look for an ISA next time + @separators = Separators.empty if segment_id.value == :IEA + + done(SegmentTok.build(segment_id.value, elements.value, segment_id.position), + segment_id.position, elements.rest) end - BAD_SEPARATOR = /[a-zA-Z0-9 ]/ + private # Consume next occurrence of "ISA" and any control characters that - # immediately follow. Validation is done to skip over "ISA" where - # it is less likely to be X12 than part of a word. + # immediately follow. Some validation is done to skip over cases + # where it is less likely to be X12 than part of a word. # - # Returns the input that was consumed as an IgnoredTok. If no "ISA" + # Yields the input that was consumed as an IgnoredTok. If no "ISA" # was found, then the entire input will be consumed. # - # @yield [IgnoreTok] - # @return [Result] + # NOTE: In the best case, when "ISA" is at the start of the input, + # this method will allocate: + # - position_at StringPtr, Position # find I + # - drop Input, Position, StringPtr # skip ISA + # - position_at + # + # @yield [IgnoredTok] the input that was consumed before "ISA" + # @return [Result] :ISA def _next_isa_segment_id(input) offset = 0 @@ -233,7 +336,7 @@ def _next_isa_segment_id(input) i = input.index("I", offset) # There's no I in the rest of the input, so it's all ignored - return eof("ISA", :position) if i.nil? + return eof("ISA", input.position_at(offset)) if i.nil? # In the next iteration, search for "I" begins right after this one offset = i + 1 @@ -241,7 +344,7 @@ def _next_isa_segment_id(input) s = input.index("S", i + 1) # There's no S in the rest of the input, so it's all ignored - return eof("ISA", :position) if s.nil? + return eof("ISA", input.position_at(i)) if s.nil? # There's something between I..S but it's not a control character next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER, 0, true) @@ -249,7 +352,7 @@ def _next_isa_segment_id(input) a = input.index("A", offset) # There's no A in the rest of the input, so it's all ignored - return eof("ISA", :position) if a.nil? + return eof("ISA", input.position_at(i)) if a.nil? # There's something between S..A but it's not a control character next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER, 0, true) @@ -263,68 +366,52 @@ def _next_isa_segment_id(input) next if not input.defined_at?(a+1) or input[a+1].match?(BAD_SEPARATOR) # Success, ignore everything before "I", resume parsing after "A". - yield IgnoredTok.new(input.take(i), :position) - return done(:position, input.drop(a+1)) + yield IgnoredTok.new(input.take(i), input.position) + return done(:ISA, input.position_at(i + 1), input.drop(a + 1)) end - return eof("ISA", :position) + return eof("ISA", input.position) end - # Read ISA segment and update element and segment separators in `state`. - # - # This should be called when `input.head` is pointing at the first - # element separator (commonly "*"). There is no validation done here - # so take care, the results can be really strange otherwise. + # Read ISA elements and update @separators accordingly. # # @return [Result>] - def _read_isa_elements(input, state) + # + # NOTE: This method allocates at minimum: + # - separators.new Separators + # - ... + # - input.position_at(offset) StringPtr, Position + # - input.drop(offset + 2) StringPtr, Position, Input + # + def _read_isa_elements(input) # The next character is a declaration of the element separator - separators = Separators.new(nil, nil, input.head, nil) - # NOTE: Doing this destructive update here would complicate backtracking - state.separators = separators + @separators = Separators.new(nil, nil, input.head, nil) # Read the first 15 simple elements into an array element_toks = 15.times.map do |element_idx| - result = _read_simple_element(input, state, false, :ISA, element_idx+1) - return result if result.fail? + element = _read_simple_element(input, false, :ISA, element_idx+1) + return element if element.fail? - input = result.rest - result.value + input = element.rest + element.value end # We have to assume the last (16th) element is fixed-length because # it is not terminated by an element separator. First we will skip # past control characters, then read the next character. offset = _skip_control_characters(input, 1) - return eof("ISA16", :position) unless input.defined_at?(offset) - - element_toks << SimpleElementTok.build(input[offset], :position) + return eof("ISA16", input.position) unless input.defined_at?(offset) + element_toks << SimpleElementTok.build(input[offset], input.position_at(offset)) # The character immediately after ISA16 is defined to be the - # segment terminator. Here we do not skip past control characters, - # so the separator could be a control character - return eof("segment terminator for ISA", :position) \ + # segment terminator. The separator could be a control character, + # e.g. \n, because we do not skip past them here. + return eof("segment terminator for ISA", input.position_at(offset)) \ unless input.defined_at?(offset + 1) - state.separators.segment = input[offset + 1] - done(element_toks, input.drop(offset + 2)) - end - - # @yield [IgnoreTok] - # @return [Result] - def next_isa_segment(input, state) - position = _next_isa_segment_id(input) do |t| - # It's not unusual for IgnoreTok to have no content, because "ISA" - # was found at the first character of the input. - yield t if block_given? and not t.value.blank? - end - return position if position.fail? - - result = _read_isa_elements(position.rest, state) - return result if result.fail? - - done(SegmentTok.build(:ISA, result.value, position.value), result.rest) + @separators.segment = input[offset + 1] + done(element_toks, nil, input.drop(offset + 2)) end # Works similarly to `_next_isa_segment_id`, except the result is the @@ -336,19 +423,19 @@ def next_isa_segment(input, state) # no validation done on the next character, even though it should be a # segment or element separator. # - # @return [Array(Symbol, Position)] - def _next_segment_id(input, state) + # @return Symbol + def _next_segment_id(input) offset = _skip_control_characters(input) - buffer = input.drop(offset).take(0) - separators = state.separators + buffer = input.pointer.drop_take(offset, 0) + start_pos = input.position_at(offset) while true - return eof("segment identifier", :position) \ + return eof("segment identifier", input.position) \ unless input.defined_at?(offset) char = input[offset] - break if separators.element == char - break if separators.segment == char + break if char == @separators.element + break if char == @separators.segment offset += 1 next if Reader.is_control_character?(char) @@ -367,83 +454,33 @@ def _next_segment_id(input, state) # is faster overall to make the substring copy here. segment_id = buffer.to_s - return expected("segment identifier, found %s" % segment_id.inspect, :position) \ + return expected("segment identifier, found %s" % segment_id.inspect, start_pos) \ unless segment_id.match?(Tokenizer::SEGMENT_ID) offset = _skip_control_characters(input, offset) - return done([segment_id.to_sym, :position], input.drop(offset)) - end - - # @return [SegmentTok] - def next_segment(input, state) - separators = state.separators - - if separators.nil? - # We haven't yet found an ISA segment, which requires a different - # method than `next_segment`. - return next_isa_segment(input, state){|t| yield t if block_given? } - end - - result = _next_segment_id(input, state) - return result if result.fail? - segment_id, position = result.value - - # Note, _next_segment_id does not guarantee result.rest isn't eof - return unexpected("eof after %s" % segment_id, :position) \ - if result.rest.empty? - - if segment_id == :ISA - # We encountered a new ISA segment without having seen the previous - # ISA's matching IEA segment. - result = _read_isa_elements(result.rest, state){|t| yield t if block_given? } - return result if result.fail? - - return done(SegmentTok.build(:ISA, result.value, position), result.rest) - end - - unless result.rest.head == separators.element \ - or result.rest.head == separators.segment - return unexpected("%s after segment identifier %s" % [ - result.rest.head.inspect, segment_id], :position) - end - - if state.segment_dict.defined_at?(segment_id) - element_uses = state.segment_dict.at(segment_id).element_uses - else - element_uses = [] - end - - result = _read_elements(result.rest, state, segment_id, element_uses) - return result if result.fail? - - # We've parsed an IEA segment, so reset and look for an ISA next time - state.separators = nil if segment_id == :IEA - - done(SegmentTok.build(segment_id, result.value, :position), result.rest) + return done(segment_id.to_sym, start_pos, input.drop(offset)) end - # @params element_uses Indicates which elements are composite and/or - # repeatable + # @param input should be positioned on an element separator: "NM1[*].." + # @param segment_id used to report errors + # @param element_uses determines which elements are composite or repeatable # # @return [Array] - # - # @note Input should be positioned on an element separator: "NM1[*]..*..*..~" - def _read_elements(input, state, segment_id, element_uses=[]) + def _read_elements(input, segment_id, element_uses=[]) element_toks = [] element_idx = 1 - separators = state.separators # We are placed on an element separator at the start of each iteration - while not input.empty? and input.head == separators.element + while not input.empty? and input.head == @separators.element result = if element_uses.defined_at?(element_toks.length) element_use = element_uses[element_toks.length] repeatable = element_use.repeatable? if element_use.composite? - _read_composite_element(input, state, repeatable, segment_id, element_idx) + _read_composite_element(input, repeatable, segment_id, element_idx) else - _read_simple_element(input, state, repeatable, segment_id, element_idx) + _read_simple_element(input, repeatable, segment_id, element_idx) end else # We either don't have a corresponding SegmentDef or it has @@ -452,7 +489,7 @@ def _read_elements(input, state, segment_id, element_uses=[]) # # If the input contains a component or repetition separator, # they will be interpreted as ordinary characters. - _read_simple_element(input, state, repeatable, segment_id, element_idx) + _read_simple_element(input, false, segment_id, element_idx) end return result if result.fail? @@ -461,89 +498,120 @@ def _read_elements(input, state, segment_id, element_uses=[]) element_idx += 1 end - return eof("segment terminator for %s" % segment_id, :position) \ + return eof("segment terminator for %s" % segment_id, input.position) \ if input.empty? return expected("segment terminator for %s, found %s" % - [segment_id, input.head.inspect], :position) \ - if input.head != separators.segment + [segment_id, input.head.inspect], input.position) \ + if input.head != @separators.segment # Skip past the segment separator - done(element_toks, input.tail) + done(element_toks, nil, input.tail) end - # @param repeatable When false, repetition separator is treated as data + # @param input should be positioned at an element separator, + # "NM1*X[*]:A:B.." + # @param repeatable whether repetition separators is data or syntax + # @param segment_id used to report errors + # @param element_idx used to report errors # - # @return [CompositeElementTok] - def _read_composite_element(input, state, repeatable, segment_id, element_idx) - separators = state.separators + # @return [CompositeElementTok | RepeatedElementTok] + def _read_composite_element(input, repeatable, segment_id, element_idx) + return eof("element separator before %s%02d" % [ + segment_id, element_idx], input.position) if input.empty? - return eof("element separator before %s%02d" % [segment_id, element_idx], :position) \ - if input.empty? - - return expected("element separator before %s%02d, found %s" % - [segment_id, element_idx, input.head.inspect], :position) \ - unless input.head == separators.element + return expected("element separator before %s%02d, found %s" % [ + segment_id, element_idx, input.head.inspect], input.position) \ + unless input.head == @separators.element - builder = state.builder.switch(repeatable, :position) - component_toks = [] + builder = @switcher.switch(repeatable, input.position_at(1)) + repeat_pos = builder.position component_idx = 1 + component_toks = [] + + # Make a separate switch for _read_component_element so our isn't switched; + # because this composite might or might not repeat independently from the + # its components, which also might or might not repeat. + switcher = Tokenizer::ElementTokSwitch.new until input.empty? - result = _read_component_element(input, state, false, segment_id, element_idx, component_idx) + result = _read_component_element(input, switcher, false, segment_id, element_idx, component_idx) return result if result.fail? input = result.rest component_toks << result.value - if repeatable and input.head == separators.repetition - # TODO: We could return unexpected("repetition separator for ...") - builder.add(CompositeElementTok.build(component_toks, :position)) + if input.head == @separators.element \ + or input.head == @separators.segment + builder.add(CompositeElementTok.build(component_toks, repeat_pos)) + return done(builder.build, builder.position, input) + + elsif repeatable and input.head == @separators.repetition + builder.add(CompositeElementTok.build(component_toks, repeat_pos)) + repeat_pos = input.position_at(1) component_toks.clear - elsif input.head == separators.element \ - or input.head == separators.segment - builder.add(CompositeElementTok.build(component_toks, :position)) - return done(builder.build, input) + elsif input.head == @separators.repetition + # We aren't repetable and neither was the component element we just + # read, or it would have consumed the repetition separator. We can + # either pretend we didn't see it and carry on, or abort. + # + # Whether or not the sender inadvertently wrote the separator (e.g. + # from not cleaning user input before spitting it out as X12), the + # next character is probably ordinary data and we have nowhere to + # put it, because ordinary data wouldn't have occured here anyway. + # + # So there's not much we can do but bail. + return unexpected("repetition separator after %s%02d" % [ + segment_id, element_idx], input.head) end - descriptor_.succ! + component_idx += 1 end - eof("element or segment separator after %s%02d" % [segment_id, element_idx], input) + eof("element or segment separator after %s%02d" % [ + segment_id, element_idx], builder.position) end - # @param repeatable So far, X12 does not allow components to repeat + # @param input should be positioned at an element separator, + # "NM1[*]..", or component separator "FOO*A[:]B.." + # @param repeatable currently X12 does not allow components to repeat + # @param segment_id used to report errors + # @param element_idx used to report errors + # @param composite_idx used to report errors # - # @return [ComponentElementTok] - def _read_component_element(input, state, repeatable, segment_id, element_idx, component_idx) - separators = state.separators - + # @return [ComponentElementTok | RepeatedElementTok] + def _read_component_element(input, switcher, repeatable, segment_id, element_idx, component_idx) return eof("element or component separator before %s%02d-%02d" % [ - segment_id, element_idx, component_idx], :position) if input.empty? + segment_id, element_idx, component_idx], input.position) if input.empty? return expected("element or component separator before %s%02d-%02d, found %s" % - [segment_id, element_idx, component_idx, input.head.inspect], :position) \ - unless input.head == separators.element \ - or input.head == separators.component + [segment_id, element_idx, component_idx, input.head.inspect], input.position) \ + unless input.head == @separators.element \ + or input.head == @separators.component - offset = _skip_control_characters(input, 1) - buffer = input.drop(offset).take(0) - builder = state.builder.switch(repeatable, :position) + offset = _skip_control_characters(input, 1) + buffer = input.pointer.drop_take(offset, 0) + repeat_pos = input.position + builder = switcher.switch(repeatable, input.position) while input.defined_at?(offset) char = input[offset] - if repeatable and char == separators.repetition - # TODO: We could return unexpected("repetition separator for ...") - builder.add(SimpleElementTok.build(buffer, :position)) - offset += 1 - - elsif char == separators.segment \ - or char == separators.element \ - or char == separators.component - builder.add(ComponentElementTok.build(buffer, :position)) - return done(builder.build, input.drop(offset)) + if repeatable and char == @separators.repetition + builder.add(SimpleElementTok.build(buffer, repeat_pos)) + repeat_pos = input.position_at(offset + 1) + offset += 1 + + elsif char == @separators.segment \ + or char == @separators.element \ + or char == @separators.component \ + or char == @separators.repetition + # Because we're not repeatable, a repetition seperator could + # belong to the parent/composite element. If it's not repeatable + # either, an error can be returned. + builder.add(ComponentElementTok.build(buffer, repeat_pos)) + return done(builder.build, builder.position, input.drop(offset)) else # This is zero-copy as long as we haven't skipped any characters @@ -554,43 +622,58 @@ def _read_component_element(input, state, repeatable, segment_id, element_idx, c if repeatable eof("segment, element, component or repetition separator after %s%02d-%02d" % [ - segment_id, element_idx, component_idx], :position) + segment_id, element_idx, component_idx], builder.position) else eof("segment, element or repetition separator after %s%02d-%02d" % [ - segment_id, element_idx, component_idx], :position) + segment_id, element_idx, component_idx], builder.position) end end - # Input should be positioned on an element separator: "NM1[*]..*..*..~" + # @param input should be positioned at an element separator: "NM1[*].." + # @param repeatable whether repetition separators is data or syntax + # @param segment_id used to report errors + # @param element_idx used to report errors # - # @return [SimpleElementTok] - def _read_simple_element(input, state, repeatable, segment_id, element_idx) - separators = state.separators - + # @return [SimpleElementTok | RepeatedElementTok] + # + # NOTE: This method allocates at minimum: + # - TODO + def _read_simple_element(input, repeatable, segment_id, element_idx) return eof("element separator before %s%02d" % [segment_id, element_idx], - :position) if input.empty? + input.position) if input.empty? return expected("element separator before %s%02d, found %s" % [ - segment_id, element_idx, input.head.inspect], :position) \ - unless input.head == separators.element + segment_id, element_idx, input.head.inspect], input.position) \ + unless input.head == @separators.element - offset = _skip_control_characters(input, 1) - buffer = input.drop(offset).take(0) - builder = state.builder.switch(repeatable, :position) + offset = _skip_control_characters(input, 1) + buffer = input.pointer.drop_take(offset, 0) + start_pos = input.position + repeat_pos = input.position + builder = @switcher.switch(repeatable, input.position) while input.defined_at?(offset) char = input[offset] - if char == separators.element \ - or char == separators.segment - builder.add(SimpleElementTok.build(buffer, :position)) - return done(builder.build, input.drop(offset)) - - elsif repeatable and char == separators.repetition - # TODO: We could return unexpected("repetition separator for ...") - builder.add(SimpleElementTok.build(buffer, :position)) - offset += 1 - + if char == @separators.element \ + or char == @separators.segment + builder.add(SimpleElementTok.build(buffer, repeat_pos)) + return done(builder.build, start_pos, input.drop(offset)) + + elsif repeatable and char == @separators.repetition + builder.add(SimpleElementTok.build(buffer, repeat_pos)) + offset += 1 + buffer = input.pointer.drop_take(offset, 0) + repeat_pos = input.position_at(offset) + + # We won't do this because it would make tokenizing a valid file not + # possible. If it truly had repeatable elements, but we hadn't setup + # segment_dict to recognize which elements are repeatable, this would + # halt tokenization. Instead, we just read the separator as data, but + # this might later result in an invalid element in the parse tree. + # + # elsif char == @separators.repetition + # return unexpected("...", ...) else # This is zero-copy as long as we haven't skipped any characters buffer << char unless Reader.is_control_character?(char) @@ -599,7 +682,7 @@ def _read_simple_element(input, state, repeatable, segment_id, element_idx) end eof("segment or element separator after %s%02d" % [ - segment_id, element_idx], :position) + segment_id, element_idx], start_pos) end # @return [Integer] @@ -612,24 +695,45 @@ def _skip_control_characters(input, offset=0) offset end - def done(value, rest) - Tokenizer::Done.new(value, rest) + # @param value [Object] + # @param rest [Input] + def done(value, position, rest) + Tokenizer::Result::Done.new(value, position, rest) end + # @param value [String] + # @param position [Position] def fail(error, position) - Tokenizer::Fail.new(error, position) + Tokenizer::Result::Fail.new(error, position) end + # @param value [String] + # @param position [Position] def expected(error, position) - Tokenizer::Fail.new("expected #{error}", position) + Tokenizer::Result::Fail.new("expected #{error}", position) end + # @param value [String] + # @param position [Position] def unexpected(error, positiion) - Tokenizer::Fail.new("unexpected #{error}", position) + Tokenizer::Result::Fail.new("unexpected #{error}", position) + end + + # @param value [String] + # @param position [Position] + def eof(error, x) + case x + when Input + expected("#{error}, found eof", x.position_at(x.length)) + else + expected("#{error}, found eof", x) + end end + end - def eof(error, position) - expected("expected #{error}, found eof", position) + class << Tokenizer + def build(input) + new(input, Separators.empty, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) end end end diff --git a/lib/stupidedi/reader/tokens/segment_tok.rb b/lib/stupidedi/reader/tokens/segment_tok.rb index 2ef0ea92b..80fedead5 100644 --- a/lib/stupidedi/reader/tokens/segment_tok.rb +++ b/lib/stupidedi/reader/tokens/segment_tok.rb @@ -7,7 +7,7 @@ class SegmentTok include Inspect # @return [Symbol] - attr_reader :segment_id + attr_reader :id # @return [Array] attr_reader :element_toks @@ -15,22 +15,22 @@ class SegmentTok # @return [Position] attr_reader :position - def initialize(segment_id, element_toks, position) - @segment_id, @element_toks, @position = - segment_id, element_toks, position + def initialize(id, element_toks, position) + @id, @element_toks, @position = + id, element_toks, position end # @return [SegmentTok] def copy(changes = {}) SegmentTok.new \ - changes.fetch(:segment_id, @segment_id), + changes.fetch(:id, @id), changes.fetch(:element_toks, @element_toks), changes.fetch(:position, @position) end # :nocov: def pretty_print(q) - q.pp(:segment.cons(@segment_id.cons(@element_toks))) + q.pp(:segment.cons(@id.cons(@element_toks))) end # :nocov: @@ -44,12 +44,12 @@ def present? def to_x12(separators) if blank? - "#{segment_id}#{(separators.segment || "~").strip}" + "#{id}#{(separators.segment || "~").strip}" else es = @element_toks.map{|x| x.to_x12(separators) } sep = separators.element || "*" eos = separators.segment || "~" - segment_id.cons(es).join(sep).gsub(/#{Regexp.escape(sep)}+$/, "") + eos.strip + id.cons(es).join(sep).gsub(/#{Regexp.escape(sep)}+$/, "") + eos.strip end end end @@ -59,8 +59,8 @@ class << SegmentTok ######################################################################### # @return [SegmentTok] - def build(segment_id, element_toks, position) - new(segment_id, element_toks, position) + def build(id, element_toks, position) + new(id, element_toks, position) end # @endgroup diff --git a/lib/stupidedi/transaction_sets/validation/ambiguity.rb b/lib/stupidedi/transaction_sets/validation/ambiguity.rb index a1c560836..aac5a3403 100644 --- a/lib/stupidedi/transaction_sets/validation/ambiguity.rb +++ b/lib/stupidedi/transaction_sets/validation/ambiguity.rb @@ -78,8 +78,7 @@ def enqueue(zipper) def step(machine) machine.successors.head.constraints.each do |segment_id, table| case table - when Stupidedi::Parser::ConstraintTable::Shallowest, - Stupidedi::Parser::ConstraintTable::Deepest + when Stupidedi::Parser::ConstraintTable::Shallowest segment_tok = mksegment_tok(@reader.segment_dict, segment_id, @elements[segment_id], nil) @@ -365,7 +364,7 @@ def build(transaction_set_def, functional_group_def) builder.GS(*gs_elements) builder.ST(*st_elements) - new(builder.machine, builder.reader, isa_elements, gs_elements, st_elements) + new(builder.machine, builder, isa_elements, gs_elements, st_elements) end def mkconfig(definition, functional_group_def, isa11, gs01, gs08, st01) diff --git a/lib/stupidedi/values/invalid_envelope_val.rb b/lib/stupidedi/values/invalid_envelope_val.rb index aaca8a6c3..9f51f664e 100644 --- a/lib/stupidedi/values/invalid_envelope_val.rb +++ b/lib/stupidedi/values/invalid_envelope_val.rb @@ -36,9 +36,11 @@ def valid? def pretty_print(q) q.text(ansi.segment("InvalidEnvelopeVal")) q.group(2, "(", ")") do - q.breakable "" - q.text @children.first.reason - q.text "," + if @children.present? + q.breakable "" + q.text @children.first.reason + q.text "," + end q.breakable @children.each do |e| diff --git a/lib/stupidedi/versions/common/element_types/an.rb b/lib/stupidedi/versions/common/element_types/an.rb index 1713e0ef5..5a7d106cb 100644 --- a/lib/stupidedi/versions/common/element_types/an.rb +++ b/lib/stupidedi/versions/common/element_types/an.rb @@ -363,6 +363,7 @@ def value(object, usage, position) elsif object.kind_of?(Date) or object.kind_of?(Time) self::Invalid.new(object, usage, position) else + # STRINGPTR: to_s + rstrip + new self::NonEmpty.new(object.to_s.rstrip, usage, position) end rescue diff --git a/lib/stupidedi/versions/common/element_types/dt.rb b/lib/stupidedi/versions/common/element_types/dt.rb index fde8f0333..c223f91e6 100644 --- a/lib/stupidedi/versions/common/element_types/dt.rb +++ b/lib/stupidedi/versions/common/element_types/dt.rb @@ -516,8 +516,8 @@ def value(object, usage, position) object#.copy(:usage => usage, :position => position) elsif object.blank? self::Empty.new(usage, position) - elsif object.is_a?(String) or object.is_a?(StringVal) - string = object.to_s + elsif object.is_a?(String) or object.is_a?(StringVal) or object.is_a?(Reader::StringPtr) + string = object.to_s # STRINGPTR: to_s + 3x slice + 3x to_i + civil + new if string.length < 6 self::Invalid.new(object, usage, position) diff --git a/lib/stupidedi/versions/common/element_types/id.rb b/lib/stupidedi/versions/common/element_types/id.rb index 42115b664..708c950ff 100644 --- a/lib/stupidedi/versions/common/element_types/id.rb +++ b/lib/stupidedi/versions/common/element_types/id.rb @@ -287,6 +287,7 @@ def value(object, usage, position) elsif object.blank? self::Empty.new(usage, position) else + # STRINGPTR: to_s + rstrip + new self::NonEmpty.new(object.to_s.rstrip, usage, position) end rescue diff --git a/lib/stupidedi/versions/common/element_types/nn.rb b/lib/stupidedi/versions/common/element_types/nn.rb index 53b01b1f4..f54505410 100644 --- a/lib/stupidedi/versions/common/element_types/nn.rb +++ b/lib/stupidedi/versions/common/element_types/nn.rb @@ -286,8 +286,9 @@ def value(object, usage, position) object#.copy(:usage => usage, :position => position) elsif object.blank? self::Empty.new(usage, position) - elsif object.is_a?(String) + elsif object.is_a?(String) or object.is_a?(Reader::StringPtr) # The number of fractional digits is implied by usage.precision + # STRINGPTR: to_d factor = 10 ** usage.definition.precision self::NonEmpty.new(object.to_d / factor, usage, position) else diff --git a/lib/stupidedi/versions/common/element_types/r.rb b/lib/stupidedi/versions/common/element_types/r.rb index 1772c34b0..4d03ff60a 100644 --- a/lib/stupidedi/versions/common/element_types/r.rb +++ b/lib/stupidedi/versions/common/element_types/r.rb @@ -319,6 +319,7 @@ def value(object, usage, position) elsif object.blank? self::Empty.new(usage, position) else + # STRINGPTR: to_d self::NonEmpty.new(object.to_d, usage, position) end rescue ArgumentError diff --git a/lib/stupidedi/versions/common/element_types/tm.rb b/lib/stupidedi/versions/common/element_types/tm.rb index ba9a78fba..10d470b0e 100644 --- a/lib/stupidedi/versions/common/element_types/tm.rb +++ b/lib/stupidedi/versions/common/element_types/tm.rb @@ -288,7 +288,7 @@ def empty(usage, position) end # @private - FOUR_DIGITS = /^\d{4,}$/ + FOUR_DIGITS = /^\d{4,}$/.freeze # @return [TimeVal] def value(object, usage, position) @@ -296,15 +296,17 @@ def value(object, usage, position) object#.copy(:usage => usage, :position => position) elsif object.blank? self::Empty.new(usage, position) - elsif object.is_a?(String) or object.is_a?(StringVal) + elsif object.is_a?(String) or object.is_a?(StringVal) or object.is_a?(Reader::StringPtr) + # STRINGPTR: match? + 4x slice + to_d + object = object.to_s return self::Invalid.new(object, usage, position) \ - unless FOUR_DIGITS.match?(object) + unless object.match?(FOUR_DIGITS) - hour = object.to_s.slice(0, 2).to_i - minute = object.to_s.slice(2, 2).try{|mm| mm.to_i unless mm.blank? } - second = object.to_s.slice(4, 2).try{|ss| ss.to_d unless ss.blank? } + hour = object.slice(0, 2).to_i + minute = object.slice(2, 2).try{|mm| mm.to_i unless mm.blank? } + second = object.slice(4, 2).try{|ss| ss.to_d unless ss.blank? } - if decimal = object.to_s.slice(6..-1) + if decimal = object.slice(6..-1) decimal = 0 if decimal.empty? second += "0.#{decimal}".to_d end diff --git a/spec/lib/stupidedi/interchanges/element_types/separator_spec.rb b/spec/lib/stupidedi/interchanges/element_types/separator_spec.rb index d5071d446..02575bb1a 100644 --- a/spec/lib/stupidedi/interchanges/element_types/separator_spec.rb +++ b/spec/lib/stupidedi/interchanges/element_types/separator_spec.rb @@ -8,7 +8,7 @@ t::Separator.new(:DE1, "String Element", 4, 10).simple_use(r::Mandatory, d.bounded(1)) end - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } def value(x) element_use.value(x, position) diff --git a/spec/lib/stupidedi/interchanges_spec.rb b/spec/lib/stupidedi/interchanges_spec.rb index 557933969..52a8a79fd 100644 --- a/spec/lib/stupidedi/interchanges_spec.rb +++ b/spec/lib/stupidedi/interchanges_spec.rb @@ -5,10 +5,11 @@ let(:version_id) { version_id } let(:separators) { Stupidedi::Reader::Separators.default } + let(:position) { Stupidedi::Reader::NoPosition } def mksegment_tok(id, *elements) Stupidedi::Reader::SegmentTok.build(id, - elements.map{|e| Stupidedi::Reader::SimpleElementTok.build(e, nil, "")}, nil, "") + elements.map{|e| Stupidedi::Reader::SimpleElementTok.build(e, position)}, position) end let(:segment_val) do diff --git a/spec/lib/stupidedi/versions/common/element_types/an_spec.rb b/spec/lib/stupidedi/versions/common/element_types/an_spec.rb index 4653d67f4..a054403d8 100644 --- a/spec/lib/stupidedi/versions/common/element_types/an_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/an_spec.rb @@ -10,7 +10,7 @@ t::AN.new(:DE1, "String Element", 4, 10).simple_use(r::Mandatory, d.bounded(1)) end - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } let(:invalid_str) do "i'm angry!!".tap do |invalid| diff --git a/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb b/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb index 3f6e5e793..1d84abfeb 100644 --- a/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb @@ -16,7 +16,7 @@ t::DT.new(:DE1, "Date Element", 6, 6).simple_use(r::Mandatory, d.bounded(1)) end - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } def value_8(x) element_use_8.value(x, position) diff --git a/spec/lib/stupidedi/versions/common/element_types/id_spec.rb b/spec/lib/stupidedi/versions/common/element_types/id_spec.rb index 3dc22c30e..4eaba8188 100644 --- a/spec/lib/stupidedi/versions/common/element_types/id_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/id_spec.rb @@ -9,7 +9,7 @@ t::ID.new(:DE1, "Quailfier Element", 2, 4).simple_use(r::Mandatory, d.bounded(1)) end - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } let(:invalid_str) do "i'm angry!!".tap do |invalid| diff --git a/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb b/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb index c5add2a6e..38659d568 100644 --- a/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb @@ -10,7 +10,7 @@ end # Dummy file position - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } def value(x) element_use.value(x, position) diff --git a/spec/lib/stupidedi/versions/common/element_types/r_spec.rb b/spec/lib/stupidedi/versions/common/element_types/r_spec.rb index 9636d57ba..0b3d89141 100644 --- a/spec/lib/stupidedi/versions/common/element_types/r_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/r_spec.rb @@ -10,7 +10,7 @@ end # Dummy file position - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } def value(x) element_use.value(x, position) diff --git a/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb b/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb index 35225fde1..b7866d27a 100644 --- a/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb @@ -23,7 +23,7 @@ t::TM.new(:DE1, "Time Element", 8, 8).simple_use(r::Mandatory, d.bounded(1)) end - let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:position) { Stupidedi::Reader::NoPosition } def value_4(x) element_use_4.value(x, position) diff --git a/spec/support/fixtures.rb b/spec/support/fixtures.rb index 456f19edc..455576eae 100644 --- a/spec/support/fixtures.rb +++ b/spec/support/fixtures.rb @@ -2,6 +2,10 @@ using Stupidedi::Refinements Fixtures = Class.new do + + Position = Struct.new(:name, :line, :column, :offset) + Position.include(Stupidedi::Reader::Position) + def versions { "006020" => "SixtyTwenty", "005010" => "FiftyTen", @@ -51,7 +55,8 @@ def parse(path, config = nil) _, config, _ = mkconfig(*parts(path)) end - Stupidedi::Parser.build(config).read(Stupidedi::Reader.build(read(path))) + tokenizer = Stupidedi::Reader.build(@root.join(path), Position) + Stupidedi::Parser.build(config).read(tokenizer) end # @return [Stupidedi::Parser::StateMachine, Stupidedi::Reader::Result] From d06579ee7704debf4ede9f78ae353286e3c30ffc Mon Sep 17 00:00:00 2001 From: kputnam Date: Fri, 19 Jul 2019 01:59:00 -0500 Subject: [PATCH 21/38] Eliminate ~540MB of allocations from benchmark - Don't use def_delegators on methods called frequently, it allocates *args - Build native extension String.strncmp to compare two substrings, which results in faster StringPtr#== and StringPtr#start_with? --- .gitignore | 4 + .travis.yml | 2 + Gemfile | 8 +- Gemfile.lock | 11 +- Rakefile | 18 +- ext/strncmp/extconf.rb | 6 + ext/strncmp/strncmp.c | 34 ++++ lib/ruby/string.rb | 11 ++ lib/strncmp.rb | 1 + lib/stupidedi.rb | 4 + lib/stupidedi/parser/builder_dsl.rb | 4 +- lib/stupidedi/parser/generation.rb | 2 +- lib/stupidedi/parser/states/initial_state.rb | 2 +- lib/stupidedi/reader/input.rb | 45 ++++- lib/stupidedi/reader/input/abstract_input.rb | 134 -------------- lib/stupidedi/reader/input/delegated_input.rb | 110 ------------ lib/stupidedi/reader/input/file_input.rb | 157 ----------------- lib/stupidedi/reader/pointer.rb | 60 +++++-- lib/stupidedi/reader/separators.rb | 2 +- lib/stupidedi/reader/tokenizer.rb | 163 ++++++++---------- prof/memprof-tk.rb | 68 +++++--- spec/lib/stupidedi/parser/navigation_spec.rb | 2 +- spec/lib/stupidedi/reader/separators_spec.rb | 34 ++-- stupidedi.gemspec | 5 +- 24 files changed, 326 insertions(+), 561 deletions(-) create mode 100644 ext/strncmp/extconf.rb create mode 100644 ext/strncmp/strncmp.c create mode 100644 lib/strncmp.rb delete mode 100644 lib/stupidedi/reader/input/abstract_input.rb delete mode 100644 lib/stupidedi/reader/input/delegated_input.rb delete mode 100644 lib/stupidedi/reader/input/file_input.rb diff --git a/.gitignore b/.gitignore index 4250a737c..dcfb25c1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ build/generated .bundle .ruby-version +lib/strncmp +ext/strncmp/Makefile +ext/strncmp/strncmp.h +ext/strncmp/strncmp.o diff --git a/.travis.yml b/.travis.yml index 9041e0932..4956113fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ before_install: bundle update --bundler; fi +before_script: bundle exec rake compile + rvm: # 2.0.0 - 2.1.0 diff --git a/Gemfile b/Gemfile index ecc1d0aa5..1e98bd170 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gem "cantor", "~> 1.2.1" group :development do gem "rake" + gem "rake-compiler" gem "term-ansicolor" gem "rspec", "3.8.0" @@ -16,12 +17,13 @@ group :development do gem "simplecov", :platforms => [:ruby_24, :ruby_25] gem "simplecov-inline-html", :platforms => [:ruby_24, :ruby_25] - # gem "stackprof" + gem "stackprof" # gem "fasterer" - # gem "benchmark-ips" - # gem "memory_profiler" + gem "benchmark-ips" + gem "memory_profiler", :platform => [:ruby_23, :ruby_24, :ruby_25] # gem "allocation_stats" # gem "heapy" + # gem "derailed_benchmarks" # We're using a patched version installed in yard/ until the # maintainer improves the plugin. The patch has been submitted diff --git a/Gemfile.lock b/Gemfile.lock index 5f52743a6..3c724bb57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,11 +1,15 @@ GEM remote: http://rubygems.org/ specs: + benchmark-ips (2.7.2) cantor (1.2.1) diff-lcs (1.3) docile (1.3.1) json (2.1.0) + memory_profiler (0.9.13) rake (12.3.2) + rake-compiler (1.0.7) + rake rspec (3.8.0) rspec-core (~> 3.8.0) rspec-expectations (~> 3.8.0) @@ -27,6 +31,7 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.2) simplecov-inline-html (0.0.1) + stackprof (0.2.12) term-ansicolor (1.7.1) tins (~> 1.0) tins (1.20.2) @@ -37,14 +42,18 @@ PLATFORMS ruby DEPENDENCIES + benchmark-ips cantor (~> 1.2.1) + memory_profiler rake + rake-compiler rspec (= 3.8.0) rspec-collection_matchers simplecov simplecov-inline-html + stackprof term-ansicolor yard (~> 0.9.12) BUNDLED WITH - 1.17.2 + 1.17.3 diff --git a/Rakefile b/Rakefile index e694d3411..f921f75c0 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,5 @@ -require "pathname" -abspath = Pathname.new(File.dirname(__FILE__)).expand_path -relpath = abspath.relative_path_from(Pathname.pwd) +require "bundler" +Bundler::GemHelper.install_tasks task :default => :spec @@ -18,8 +17,12 @@ end # Note options are loaded from .yardopts require "yard" +require "pathname" +abspath = Pathname.new(File.dirname(__FILE__)).expand_path +relpath = abspath.relative_path_from(Pathname.pwd) YARD::Rake::YardocTask.new(:yard => :clobber_yard) task :clobber_yard do + rm_rf "#{relpath}/build/generated/doc" mkdir_p "#{relpath}/build/generated/doc/images" end @@ -27,3 +30,12 @@ end task :console do exec(*%w(irb -I lib -r stupidedi)) end + +if RUBY_PLATFORM !~ /java/ + require "rake/extensiontask" + gemspec = Gem::Specification.load("stupidedi.gemspec") + + Rake::ExtensionTask.new("strncmp") do |x| + x.lib_dir = "lib/strncmp" + end +end diff --git a/ext/strncmp/extconf.rb b/ext/strncmp/extconf.rb new file mode 100644 index 000000000..48fe19fe8 --- /dev/null +++ b/ext/strncmp/extconf.rb @@ -0,0 +1,6 @@ +require "mkmf" +extension_name = "strncmp/strncmp" + +create_header +dir_config extension_name +create_makefile extension_name diff --git a/ext/strncmp/strncmp.c b/ext/strncmp/strncmp.c new file mode 100644 index 000000000..656ad8bc2 --- /dev/null +++ b/ext/strncmp/strncmp.c @@ -0,0 +1,34 @@ +#include "ruby.h" +#include "extconf.h" + +VALUE rb_strncmp(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2, VALUE length) { + // rb_raise(rb_eArgError, "message %s", "argument"); + + if (!FIXNUM_P(length)) rb_raise(rb_eTypeError, "length must be an Integer"); + if (!FIXNUM_P(offset1)) rb_raise(rb_eTypeError, "offset1 must be an Integer"); + if (!FIXNUM_P(offset2)) rb_raise(rb_eTypeError, "offset1 must be an Integer"); + + long len = NUM2LONG(length); + long beg1 = NUM2LONG(offset1); + long beg2 = NUM2LONG(offset2); + + if (len < 0) rb_raise(rb_eArgError, "length cannot be negative"); + if (beg1 < 0) rb_raise(rb_eArgError, "offset1 cannot be negative"); + if (beg2 < 0) rb_raise(rb_eArgError, "offset2 cannot be negative"); + + if (beg1 + len > rb_str_strlen(str1)) return Qnil; + if (beg2 + len > rb_str_strlen(str2)) return Qnil; + + char *ptr1 = rb_str_subpos(str1, beg1, &len); + char *ptr2 = rb_str_subpos(str2, beg2, &len); + + if (ptr1 == 0 || ptr2 == 0) return Qnil; + + return (memcmp(ptr1, ptr2, len) == 0) ? Qtrue : Qfalse; +} + +void Init_strncmp() +{ + // VALUE rb_cString = rb_define_class("String", rb_cObject); + rb_define_singleton_method(rb_cString, "strncmp", rb_strncmp, 5); +} diff --git a/lib/ruby/string.rb b/lib/ruby/string.rb index e0ed9a6bc..ac590f42f 100644 --- a/lib/ruby/string.rb +++ b/lib/ruby/string.rb @@ -84,6 +84,17 @@ def position def join gsub(/\n[ \t]+/, " ") end + + unless "".respond_to?(:match?) + def match?(pattern, pos=nil) + if pos.nil? + !!(self =~ pattern) + else + !!match(pattern, pos) + end + end + end + end end end diff --git a/lib/strncmp.rb b/lib/strncmp.rb new file mode 100644 index 000000000..38bcd4f9b --- /dev/null +++ b/lib/strncmp.rb @@ -0,0 +1 @@ +require "strncmp/strncmp" diff --git a/lib/stupidedi.rb b/lib/stupidedi.rb index 8995766b2..5cedcdc1c 100644 --- a/lib/stupidedi.rb +++ b/lib/stupidedi.rb @@ -12,6 +12,10 @@ $:.unshift(File.expand_path("..", __FILE__)) +if RUBY_PLATFORM !~ /java/ + require "strncmp" +end + require "ruby/regexp" require "ruby/array" require "ruby/blank" diff --git a/lib/stupidedi/parser/builder_dsl.rb b/lib/stupidedi/parser/builder_dsl.rb index c110e1af1..aee641e25 100644 --- a/lib/stupidedi/parser/builder_dsl.rb +++ b/lib/stupidedi/parser/builder_dsl.rb @@ -26,10 +26,10 @@ class BuilderDsl :empty?, :first?, :last?, :deterministic? def initialize(machine, strict = true) - @position = Reader::NoPosition + @position = Reader::StacktracePosition @machine = machine @strict = strict - @separators = Reader::Separators.empty + @separators = Reader::Separators.blank @segment_dict = Reader::SegmentDict.empty end diff --git a/lib/stupidedi/parser/generation.rb b/lib/stupidedi/parser/generation.rb index ed4c48725..b056d70dd 100644 --- a/lib/stupidedi/parser/generation.rb +++ b/lib/stupidedi/parser/generation.rb @@ -29,7 +29,7 @@ module Generation # @yield [Reader::IgnoredTok] # @return [(StateMachine, Reader::Tokenizer::Result)] def read(tokenizer, options = {}) - limit = options.fetch(:nondeterminism, 1) + #imit = options.fetch(:nondeterminism, 1) machine = self.dup return machine, tokenizer.each do |token| diff --git a/lib/stupidedi/parser/states/initial_state.rb b/lib/stupidedi/parser/states/initial_state.rb index 4911e0ac5..b136eb7a3 100644 --- a/lib/stupidedi/parser/states/initial_state.rb +++ b/lib/stupidedi/parser/states/initial_state.rb @@ -39,7 +39,7 @@ class << InitialState # @return [InitialState] def build(zipper) new( - Reader::Separators.empty, + Reader::Separators.blank, Reader::SegmentDict.empty, InstructionTable.build( diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb index 83d915773..437f4bef9 100644 --- a/lib/stupidedi/reader/input.rb +++ b/lib/stupidedi/reader/input.rb @@ -12,14 +12,36 @@ class Input # @return [Position] attr_reader :position - def_delegators :@pointer, :defined_at?, :empty?, :count, :head, :take, - :match?, :slice, :index, :rindex, :reify, :last, :length, :=~, :[] + def_delegators :@pointer, :count, :take, :match?, :slice, + :index, :rindex, :reify, :last, :length, :=~, :[] def initialize(pointer, position) @pointer, @position = pointer, position end + # NOTE: This could be implemented using def_delegators, but unfortunately + # that allocates an Array to pass the arguments along. Since this method + # is called frequently, we can reduce allocations by defining it this way. + def head + @pointer.head + end + + # NOTE: This could be implemented using def_delegators, but unfortunately + # that allocates an Array to pass the arguments along. Since this method + # is called frequently, we can reduce allocations by defining it this way. + def defined_at?(n) + @pointer.defined_at?(n) + end + + # NOTE: This could be implemented using def_delegators, but unfortunately + # that allocates an Array to pass the arguments along. Since this method + # is called frequently, we can reduce allocations by defining it this way. + # NOTE: + def empty? + @pointer.empty? + end + # @NOTE: This allocates 3 objects: Input, Position, and StringPtr def tail drop(1) @@ -28,7 +50,11 @@ def tail # @NOTE: This allocates 3 objects: Input, Position, and StringPtr, so # if you've written x.drop(10).position.. x.position_at(10) is cheaper def drop(n) - self.class.new(@pointer.drop(n), position_at(n)) + if n.zero? + self + else + self.class.new(@pointer.drop(n), position_at(n)) + end end # NOTE: This allocates 2 objects: StringPtr and Position @@ -41,6 +67,19 @@ def position_at(n) @position.advance(@pointer.take(n)) end end + + def start_with?(other) + other and @pointer.start_with?(other) + end + + def skip_control_characters(offset = 0) + while @pointer.defined_at?(offset) \ + and Reader.is_control_character?(@pointer[offset]) + offset += 1 + end + + drop(offset) + end end class << Input diff --git a/lib/stupidedi/reader/input/abstract_input.rb b/lib/stupidedi/reader/input/abstract_input.rb deleted file mode 100644 index 7f03e37af..000000000 --- a/lib/stupidedi/reader/input/abstract_input.rb +++ /dev/null @@ -1,134 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - # - # Provides an abstract interface for a positioned cursor within an - # element-based input stream. The main operations are implemented by the - # {#take} and {#drop} methods. - # - # The {DelegatedInput} subclass wraps values that already implement the - # interface, like `String` and `Array`. The {FileInput} subclass wraps - # opened `IO` streams like `File`, and possibly others. - # - # @example Reading the input - # input = Input.build("abc") - # input.at(0) #=> "a" - # input.take(3) #=> "abc" - # - # input = input.drop(1) #=> # - # input.at(0) #=> "b" - # input.take(3) #=> "bc" - # - # input = input.drop(1) #=> # - # input.at(0) #=> "c" - # input.take(3) #=> "c" - # - # @example Querying the position - # input = Input.build("abc\ndef\nghi") - # input.offset #=> 0 - # input.line #=> 1 - # input.column #=> 1 - # - # input.drop(3).bind do |in| - # in.offset #=> 3 - # in.line #=> 1 - # in.column #=> 4 - # end - # - # input.drop(4).bind do |in| - # in.offset #=> 4 - # in.line #=> 2 - # in.column #=> 1 - # end - # - # @note The `String` and `Array` refinements in `lib/ruby` provide compatible - # implementations of the abstract methods, so code written to target - # this interface is backward-compatable with plain unwrapped `String` - # and `Array` values. - # - class AbstractInput - include Inspect - - # @group Querying the Position - ######################################################################## - - # The {Position} value that describes the position of the input stream - # - # @return [Position] - abstract :position - - # The current position as the number of elements previously read - # - # @return [Integer] - def_delegators :position, :offset - - # The line of the current position - # - # @return [Integer] - def_delegators :position, :line - - # The column of the current position. The column resets to `1` each time - # a newline is read - # - # @return [Integer] - def_delegators :position, :column - - # The file name, URI, etc that identifies the input stream - # - # @return [String] - def_delegators :position, :path - - # @group Reading the Input - ######################################################################## - - # Read the first `n` elements - # - # @param [Integer] n number of elements to read (`n >= 0`) - abstract :take, :args => %w(n) - - # Read a single element at the given index. Result is undefined unless - # the input contains enough elements, which can be tested with - # {#defined_at?} - # - # @param [Integer] n the index of the element to read (`n >= 0`) - abstract :at, :args => %w(n) - - # Returns the smallest `n`, where {#at}`(n)` == `element` - # - # @param [Object] element the element to find in the input - # - # @return [Integer] - # @return nil if `element` is not present in the input - abstract :index, :args => %w(element) - - # @group Advancing the Cursor - ######################################################################## - - # Advance the cursor forward `n` elements - # - # @param [Integer] n the number of elements to advance (`n >= 0`) - # - # @return [AbstractInput] new object with the remaining input - abstract :drop, :args => %w(n) - - # @group Testing the Input - ######################################################################## - - # True if the input contains enough elements such that {#at}`(n)` is - # defined - # - # @param [Integer] n the index to test (`n >= 0`) - abstract :defined_at?, :args => %w(n) - - # True if no elements remain in the input - abstract :empty? - - # True if `other` equals the remaining input - # - # @return [Boolean] - abstract :==, :args => %w(other) - end - end -end diff --git a/lib/stupidedi/reader/input/delegated_input.rb b/lib/stupidedi/reader/input/delegated_input.rb deleted file mode 100644 index 105ccc5e6..000000000 --- a/lib/stupidedi/reader/input/delegated_input.rb +++ /dev/null @@ -1,110 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - class DelegatedInput < AbstractInput - def initialize(delegate, offset = 0, line = 1, column = 1) - @delegate, @line, @column = - delegate, line, column - end - - # @group Querying the Position - ######################################################################## - - # (see AbstractInput#offset) - def offset - nil - end - - # (see AbstractInput#line) - attr_reader :line - - # (see AbstractInput#column) - attr_reader :column - - # (see AbstractInput#position) - def position - Position.new(nil, @line, @column, nil) - end - - # @group Reading the Input - ######################################################################## - - # (see AbstractInput#take) - def_delegators :@delegate, :take - - # (see AbstractInput#at) - def_delegators :@delegate, :at - - # (see AbstractInput#index) - def_delegators :@delegate, :index - - # @group Advancing the Cursor - ######################################################################## - - # (see AbstractInput#drop) - def drop(n) - raise ArgumentError, "n must be positive" unless n >= 0 - - prefix = @delegate.take(n) - length = prefix.length - count = prefix.count("\n") - - column = unless count.zero? - length - prefix.rindex("\n") - else - @column + length - end - - copy(:delegate => @delegate.drop(n), - :line => @line + count, - :column => column) - end - - # @group Testing the Input - ######################################################################## - - # (see AbstractInput#defined_at?) - def_delegators :@delegate, :defined_at? - - # (see AbstractInput#empty?) - def_delegators :@delegate, :empty? - - # (see AbstractInput#==) - def_delegators :@delegate, :== - - # @endgroup - ######################################################################## - - # @return [void] - def pretty_print(q) - q.text("DelegateInput") - q.group(2, "(", ")") do - preview = @delegate.take(4) - preview = if preview.empty? - "EOF" - elsif preview.length <= 3 - preview.inspect - else - (preview.take(3) + "...").inspect - end - - q.text preview - q.text " at line #{@line}, column #{@column}, offset #{nil}" - end - end - - private - - # @return [DelegatedInput] - def copy(changes = {}) - DelegatedInput.new \ - changes.fetch(:delegate, @delegate), - nil, - changes.fetch(:line, @line), - changes.fetch(:column, @column) - end - end - end -end diff --git a/lib/stupidedi/reader/input/file_input.rb b/lib/stupidedi/reader/input/file_input.rb deleted file mode 100644 index b551594a3..000000000 --- a/lib/stupidedi/reader/input/file_input.rb +++ /dev/null @@ -1,157 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Reader - # - # @note This class is not thread-safe. If more than one `Thread` has access - # to the same instance, and they simultaneously call methods on that - # instance, the methods may produce incorrect results and the object might - # be left in an inconsistent state. - # - class FileInput < AbstractInput - # @return [IO] - attr_reader :io - - def initialize(io, offset = 0, line = 1, column = 1, size = io.stat.size) - @io, @offset, @line, @column, @size = - io, offset, line, column, size - end - - # @group Querying the Position - ######################################################################## - - # (see AbstractInput#offset) - attr_reader :offset - - # @return [Integer] - # (see AbstractInput#line) - attr_reader :line - - # (see AbstractInput#column) - attr_reader :column - - # (see AbstractInput#path) - attr_reader :path - - # (see AbstractInput#position) - def position - Position.new(@offset, @line, @column, @io.path) - end - - # @group Reading the Input - ######################################################################## - - # (see AbstractInput#take) - # @return [String] - def take(n) - @io.seek(@offset) - - # Calling @io.read with more than the number of available bytes will - # return nil, so we have to calculate how many bytes remain - @io.read((n <= @size) ? n : @size) - end - - # (see AbstractInput#at) - # @return [String] - def at(n) - raise ArgumentError, "n must be positive" unless n >= 0 - - @io.seek(@offset + n) - @io.read(1) - end - - # (see AbstractInput#index) - def index(element) - @io.seek(@offset) - length = element.length - - # We need to start with element != buffer, and this is a reasonable guess - buffer = "\377" * length - - until @io.eof? - buffer.slice!(0) - buffer = buffer + @io.read(1) - - if buffer == element - return @io.tell - @offset - length - end - end - end - - # @group Advancing the Cursor - ######################################################################## - - # (see AbstractInput#drop) - def drop(n) - raise ArgumentError, "n must be positive" unless n >= 0 - - @io.seek(@offset) - prefix = @io.read(n) - # suffix = @io - - length = prefix.length - count = prefix.count("\n") - - column = unless count.zero? - length - prefix.rindex("\n") - else - @column + length - end - - copy(:offset => @offset + length, - :line => @line + count, - :column => column, - :size => @size - length) - end - - # @group Testing the Input - ######################################################################## - - # (see AbstractInput#defined_at?) - def defined_at?(n) - n < @size - end - - # (see AbstractInput#empty?) - def empty? - @io.eof? - end - - # @endgroup - ######################################################################## - - # @return [void] - # :nocov: - def pretty_print(q) - q.text("FileInput") - q.group(2, "(", ")") do - preview = take(4) - preview = if preview.empty? - "EOF" - elsif preview.length <= 3 - preview.inspect - else - (preview.take(3) + "...").inspect - end - - q.text preview - q.text " at line #{@line}, column #{@column}, offset #{@offset}, file #{File.basename(@io.path)}" - end - end - # :nocov: - - private - - # @return [FileInput] - def copy(changes = {}) - FileInput.new \ - changes.fetch(:io, @io), - changes.fetch(:offset, @offset), - changes.fetch(:line, @line), - changes.fetch(:column, @column), - changes.fetch(:size, @size) - end - end - end -end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 2b743d2cd..2f5e933c2 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -357,14 +357,15 @@ def to_str end # Match an unescaped anchor \A, \a, or ^, \Z, \z, $ - # NOTE: These will match [^$], which aren't anchors, but the regexp to - # exclude those is very gnarly and very noticeably slow. + # + # NOTE: These will match character class [^$], which aren't anchors, but + # the regexp to exclude those is very gnarly and very noticeably slow. ANCHORS_A = /(? @length + false + else + strncmp(@storage, @offset, other, 0, other.length) + end + when Regexp + reify.start_with?(other) + else + raise TypeError, "expected String or Regexp" + end + end + + if String.respond_to?(:strncmp) + def strncmp(s1, n1, s2, n2, len) + String.strncmp(s1, n1, s2, n2, len) + end + else + def strncmp(s1, n1, s2, n2, len) + if s1.length <= n1 + len or s2.length <= n2 + len + false + else + s1 = s1[n1, len] unless n1.zero? and n1.length == len + s2 = s2[n2, len] unless n2.zero? and n2.length == len + s1 == s2 + end end end end diff --git a/lib/stupidedi/reader/separators.rb b/lib/stupidedi/reader/separators.rb index 4e573aba5..273591c3d 100644 --- a/lib/stupidedi/reader/separators.rb +++ b/lib/stupidedi/reader/separators.rb @@ -90,7 +90,7 @@ class << Separators ######################################################################### # @return [Separators] - def empty + def blank new(nil, nil, nil, nil) end diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index cf54dbdd7..9b8f3f9c5 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -4,15 +4,9 @@ module Stupidedi module Reader - # TODO: - # - mark correct positions - # - switch char-by-char with regexps that grab contiguous blocks - # - fatal errors: - # - tokenizer error inside ISA/IEA envelope - # - no ISA found in entire input - # - # TODONE: - # - fix shared builder from caller to callee: composite->component + # TODO: One of the top garbage producers is reading a single character like + # input[offset]. It might be more efficient to replace char-at-a-time reads + # with Regexps that consume multiple characters at once. class Tokenizer include Inspect @@ -27,9 +21,11 @@ class Tokenizer attr_accessor :segment_dict # @param input [Input] - def initialize(input, separators, segment_dict, switcher) - @input, @separators, @segment_dict, @switcher = - input, separators, segment_dict, switcher + def initialize(input, separators, segment_dict, switcher, switcher_) + # Make a separate switch for _read_component_element so building a + # composite element won't interfere with building its components. + @input, @separators, @segment_dict, @switcher, @switcher_ = + input, separators, segment_dict, switcher, switcher_ end # @yield [SegmentTok | IgnoredTok] @@ -50,9 +46,6 @@ def each count += 1 end - # The loop only terminates on failure, but we do expect an EOF - # at some point; however, not before seeing an ISA, and not in - # the middle of an interchange. result.fatal = count.zero? || @separators.present? result end @@ -65,7 +58,7 @@ def each_isa input = @input while true - result = next_isa_segment do |token| + result = next_isa_segment(input) do |token| yield token # IgnoredTok end @@ -75,6 +68,9 @@ def each_isa count += 1 end + # The loop always terminates on failure, but we normall expect an EOF + # at some point; however, it's not expected to happen before reading + # an ISA or while tokenizing things inside ISA..IEA result.fatal = count.zero? || @separators.present? result end @@ -111,6 +107,7 @@ def fatal? @fatal end + # @yield [String] def explain yield @error end @@ -127,7 +124,7 @@ class Done < Result # The position within the input attributed to `value`. In many cases # in the tokenizer, the `value` itself also carries a position, but - # this is useful for cases where the value does not. + # this is useful for cases where the value does not (_read_segment_id) # # @return [Position] attr_reader :position @@ -141,6 +138,7 @@ def fail? false end + # @return self def explain self end @@ -151,23 +149,37 @@ def explain # This is a bit clunky, but we can save quite a lot of memory allocation # by reusing our ElementTokBuilders. This is done by keeping a pair and # reseting them before giving to a caller. Otherwise, one of these would - # be allocated each time a simple or composite is tokenized. + # be allocated each time a simple or composite is tokenized, then thrown + # out once it's finished. # class ElementTokSwitch include Inspect - def_delegators :@active, :add, :build, :reset! - def initialize @repeatable = ElementTokBuilder::Repeatable.new(nil) @nonrepeatable = ElementTokBuilder::NonRepeatable.new(nil) end + # Toggles the behavior of `#add`. When repeatable, it accumulates an + # array of elements; otherwise it only stores the most recent. def switch(repeatable, position) @active = repeatable ? @repeatable : @nonrepeatable @active.reset!(position) + @active + end + + # @return self + def add(element_tok) + @active.add(element_tok) + self end + # @return [SimpleElementTok | CompositeElementTok | ComponentElementTok] + def build + @active.build + end + + # @private class ElementTokBuilder include Inspect @@ -183,6 +195,7 @@ class ElementTokBuilder # @return [Position] abstract :position + # @private class Repeatable < ElementTokBuilder # @return [Position] attr_reader :position @@ -194,7 +207,6 @@ def initialize(position) def add(element_tok) @element_toks.push(element_tok) - self end def build @@ -204,10 +216,10 @@ def build def reset!(position) @position = position @element_toks = [] - self end end + # @private class NonRepeatable < ElementTokBuilder # @return [Position] attr_reader :position @@ -218,7 +230,6 @@ def initialize(position) def add(element_tok) @element_tok = element_tok - self end def build @@ -228,7 +239,6 @@ def build def reset!(position) @position = position @element_tok = nil - self end end end @@ -256,11 +266,7 @@ def next_isa_segment(input) segment_id.position, elements.rest) end - # Returns the next segment. `@separators` must have at least - # the `element` and `segment` fields initialized. - # - # If the next segment is an ISA, then it resets the element and - # segment separators in `@separators` according to the ISA segment. + # Returns the next segment. # # @return [Result] def next_segment(input) @@ -273,25 +279,27 @@ def next_segment(input) segment_id = _next_segment_id(input) return segment_id if segment_id.fail? - # Note, _next_segment_id does not guarantee result.rest isn't eof + # _next_segment_id does not guarantee result.rest isn't eof return unexpected("eof after %s" % segment_id.value, input.position) \ if segment_id.rest.empty? if segment_id.value == :ISA # We encountered a new ISA segment without having seen the previous - # ISA's matching IEA segment. - elements = _read_isa_elements(segment_id.rest){|t| yield t if block_given? } + # ISA's matching IEA segment. This is definitely a syntax error, but + # we can still continue tokenizing and let the caller decide how to + # handle it. + elements = _read_isa_elements(segment_id.rest) return elements if elements.fail? segment_tok = SegmentTok.build(:ISA, elements.value, segment_id.position) - return done(segment_tok, segment_id.position, - elements.rest.drop(_skip_control_characters(elements.rest))) + return done(segment_tok, segment_id.position, elements.rest.skip_control_characters) end - unless segment_id.rest.head == @separators.element \ - or segment_id.rest.head == @separators.segment + # _next_segment_id will skip any trailing control characters + unless segment_id.rest.start_with?(@separators.element) \ + or segment_id.rest.start_with?(@separators.segment) return unexpected("%s after segment identifier %s" % [ - segment_id.rest.head.inspect, segment_id.value], segment_id.rest.position) + char.head.inspect, segment_id.value], segment_id.rest.position) end if @segment_dict.defined_at?(segment_id.value) @@ -304,7 +312,7 @@ def next_segment(input) return elements if elements.fail? # We've parsed an IEA segment, so reset and look for an ISA next time - @separators = Separators.empty if segment_id.value == :IEA + @separators = Separators.blank if segment_id.value == :IEA done(SegmentTok.build(segment_id.value, elements.value, segment_id.position), segment_id.position, elements.rest) @@ -314,16 +322,10 @@ def next_segment(input) # Consume next occurrence of "ISA" and any control characters that # immediately follow. Some validation is done to skip over cases - # where it is less likely to be X12 than part of a word. + # where it is more likely to be part of a word (eg, LISA) than X12. # # Yields the input that was consumed as an IgnoredTok. If no "ISA" - # was found, then the entire input will be consumed. - # - # NOTE: In the best case, when "ISA" is at the start of the input, - # this method will allocate: - # - position_at StringPtr, Position # find I - # - drop Input, Position, StringPtr # skip ISA - # - position_at + # was found, nothing will be yielded. # # @yield [IgnoredTok] the input that was consumed before "ISA" # @return [Result] :ISA @@ -366,8 +368,9 @@ def _next_isa_segment_id(input) next if not input.defined_at?(a+1) or input[a+1].match?(BAD_SEPARATOR) # Success, ignore everything before "I", resume parsing after "A". - yield IgnoredTok.new(input.take(i), input.position) - return done(:ISA, input.position_at(i + 1), input.drop(a + 1)) + yield IgnoredTok.new(input.take(i), input.position) if block_given? + + return done(:ISA, input.position_at(i), input.drop(a + 1)) end return eof("ISA", input.position) @@ -376,17 +379,9 @@ def _next_isa_segment_id(input) # Read ISA elements and update @separators accordingly. # # @return [Result>] - # - # NOTE: This method allocates at minimum: - # - separators.new Separators - # - ... - # - input.position_at(offset) StringPtr, Position - # - input.drop(offset + 2) StringPtr, Position, Input - # def _read_isa_elements(input) # The next character is a declaration of the element separator - # NOTE: Doing this destructive update here would complicate backtracking - @separators = Separators.new(nil, nil, input.head, nil) + @separators.element = input.head # Read the first 15 simple elements into an array element_toks = 15.times.map do |element_idx| @@ -457,8 +452,7 @@ def _next_segment_id(input) return expected("segment identifier, found %s" % segment_id.inspect, start_pos) \ unless segment_id.match?(Tokenizer::SEGMENT_ID) - offset = _skip_control_characters(input, offset) - return done(segment_id.to_sym, start_pos, input.drop(offset)) + return done(segment_id.to_sym, start_pos, input.skip_control_characters(offset)) end # @param input should be positioned on an element separator: "NM1[*].." @@ -471,10 +465,10 @@ def _read_elements(input, segment_id, element_uses=[]) element_idx = 1 # We are placed on an element separator at the start of each iteration - while not input.empty? and input.head == @separators.element + while not input.empty? and input.start_with?(@separators.element) result = - if element_uses.defined_at?(element_toks.length) - element_use = element_uses[element_toks.length] + if element_uses.defined_at?(element_idx - 1) + element_use = element_uses[element_idx - 1] repeatable = element_use.repeatable? if element_use.composite? @@ -483,12 +477,12 @@ def _read_elements(input, segment_id, element_uses=[]) _read_simple_element(input, repeatable, segment_id, element_idx) end else - # We either don't have a corresponding SegmentDef or it has - # fewer elements than are present in the input. We'll make - # the assumption that it's a simple non-repeatable element. + # WARNING: We either don't have a corresponding SegmentDef or it + # has fewer elements than are present in the input. We'll make the + # assumption that it's a simple non-repeatable element. # - # If the input contains a component or repetition separator, - # they will be interpreted as ordinary characters. + # If the input contains a component or repetition separator, they + # will be interpreted as ordinary characters. _read_simple_element(input, false, segment_id, element_idx) end @@ -522,36 +516,31 @@ def _read_composite_element(input, repeatable, segment_id, element_idx) return expected("element separator before %s%02d, found %s" % [ segment_id, element_idx, input.head.inspect], input.position) \ - unless input.head == @separators.element + unless input.start_with?(@separators.element) builder = @switcher.switch(repeatable, input.position_at(1)) repeat_pos = builder.position component_idx = 1 component_toks = [] - # Make a separate switch for _read_component_element so our isn't switched; - # because this composite might or might not repeat independently from the - # its components, which also might or might not repeat. - switcher = Tokenizer::ElementTokSwitch.new - until input.empty? - result = _read_component_element(input, switcher, false, segment_id, element_idx, component_idx) + result = _read_component_element(input, false, segment_id, element_idx, component_idx) return result if result.fail? input = result.rest component_toks << result.value - if input.head == @separators.element \ - or input.head == @separators.segment + if input.start_with?(@separators.element) \ + or input.start_with?(@separators.segment) builder.add(CompositeElementTok.build(component_toks, repeat_pos)) return done(builder.build, builder.position, input) - elsif repeatable and input.head == @separators.repetition + elsif repeatable and input.start_with?(@separators.repetition) builder.add(CompositeElementTok.build(component_toks, repeat_pos)) repeat_pos = input.position_at(1) component_toks.clear - elsif input.head == @separators.repetition + elsif input.start_with?(@separators.repetition) # We aren't repetable and neither was the component element we just # read, or it would have consumed the repetition separator. We can # either pretend we didn't see it and carry on, or abort. @@ -581,19 +570,19 @@ def _read_composite_element(input, repeatable, segment_id, element_idx) # @param composite_idx used to report errors # # @return [ComponentElementTok | RepeatedElementTok] - def _read_component_element(input, switcher, repeatable, segment_id, element_idx, component_idx) + def _read_component_element(input, repeatable, segment_id, element_idx, component_idx) return eof("element or component separator before %s%02d-%02d" % [ segment_id, element_idx, component_idx], input.position) if input.empty? return expected("element or component separator before %s%02d-%02d, found %s" % [segment_id, element_idx, component_idx, input.head.inspect], input.position) \ - unless input.head == @separators.element \ - or input.head == @separators.component + unless input.start_with?(@separators.element) \ + or input.start_with?(@separators.component) offset = _skip_control_characters(input, 1) buffer = input.pointer.drop_take(offset, 0) repeat_pos = input.position - builder = switcher.switch(repeatable, input.position) + builder = @switcher_.switch(repeatable, input.position) while input.defined_at?(offset) char = input[offset] @@ -635,16 +624,13 @@ def _read_component_element(input, switcher, repeatable, segment_id, element_idx # @param element_idx used to report errors # # @return [SimpleElementTok | RepeatedElementTok] - # - # NOTE: This method allocates at minimum: - # - TODO def _read_simple_element(input, repeatable, segment_id, element_idx) return eof("element separator before %s%02d" % [segment_id, element_idx], input.position) if input.empty? return expected("element separator before %s%02d, found %s" % [ segment_id, element_idx, input.head.inspect], input.position) \ - unless input.head == @separators.element + unless input.start_with?(@separators.element) offset = _skip_control_characters(input, 1) buffer = input.pointer.drop_take(offset, 0) @@ -666,7 +652,7 @@ def _read_simple_element(input, repeatable, segment_id, element_idx) buffer = input.pointer.drop_take(offset, 0) repeat_pos = input.position_at(offset) - # We won't do this because it would make tokenizing a valid file not + # We won't do this because it could make tokenizing a valid file not # possible. If it truly had repeatable elements, but we hadn't setup # segment_dict to recognize which elements are repeatable, this would # halt tokenization. Instead, we just read the separator as data, but @@ -733,7 +719,8 @@ def eof(error, x) class << Tokenizer def build(input) - new(input, Separators.empty, SegmentDict.empty, Tokenizer::ElementTokSwitch.new) + new(input, Separators.blank, SegmentDict.empty, + Tokenizer::ElementTokSwitch.new, Tokenizer::ElementTokSwitch.new) end end end diff --git a/prof/memprof-tk.rb b/prof/memprof-tk.rb index b61e59948..cf8d12f86 100755 --- a/prof/memprof-tk.rb +++ b/prof/memprof-tk.rb @@ -10,22 +10,24 @@ exit end -def run(config, input, state) +def run(config, tokenizer) mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 $stderr.puts "Pre-init: %0.2d MiB" % mem - start = Time.now + start = Time.now mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 $stderr.puts "Post-init: %0.2d MiB" % mem - result = Stupidedi::Reader::Tokenizer.next_isa_segment(input, state) + result = tokenizer.each do |segment_tok| + if segment_tok.is_a?(Stupidedi::Reader::IgnoredTok) + pp segment_tok + next + end - until result.fail? - segment_tok = result.value - puts segment_tok.segment_id + puts segment_tok.id - case segment_tok.segment_id + case segment_tok.id when :GS # GS08: Version / Release / Industry Identifier Code version = segment_tok.element_toks.at(7).try(:value).try(:to_s) @@ -37,21 +39,17 @@ def run(config, input, state) if config.functional_group.defined_at?(gscode) envelope_def = config.functional_group.at(gscode) envelope_val = envelope_def.empty - segment_dict = state.segment_dict.push(envelope_val.segment_dict) - state.segment_dict = segment_dict + tokenizer.segment_dict = + tokenizer.segment_dict.push(envelope_val.segment_dict) end when :GE - unless state.segment_dict.empty? - segment_dict = state.segment_dict.pop - state.segment_dict = segment_dict + unless tokenizer.segment_dict.empty? + tokenizer.segment_dict = tokenizer.segment_dict.pop end end - - result = Stupidedi::Reader::Tokenizer.next_segment(result.rest, state) end - # pp result - # puts + pp result mem = Integer(`ps -o rss= -p #{Process.pid}`) * 0.001 $stderr.puts "Finish: %0.2d MiB" % mem @@ -60,19 +58,39 @@ def run(config, input, state) $stderr.puts "%0.3f seconds" % (stop - start) end +position = + if ARGV.delete('--0-pos') + Stupidedi::Reader::NoPosition + + elsif ARGV.delete('--1-pos') + Stupidedi::Reader::OffsetPosition + + elsif ARGV.delete('--2-pos') + Struct.new(:line, :column).include(Stupidedi::Reader::Position) + + elsif ARGV.delete('--3-pos') + Struct.new(:name, :line, :column).include(Stupidedi::Reader::Position) + + elsif ARGV.delete('--4-pos') + Struct.new(:name, :line, :column, :offset).include(Stupidedi::Reader::Position) + + else + Stupidedi::Reader::NoPosition + end + if ARGV.delete('--fast') - config = Stupidedi::Config.contrib - input = Stupidedi::Reader::Pointer.build(File.read(ARGV[0])) - state = Stupidedi::Reader::Tokenizer::State.todo - run(config, input, state) + config = Stupidedi::Config.contrib + input = Stupidedi::Reader::Input.file(ARGV[0], position) + tokenizer = Stupidedi::Reader::Tokenizer.build(input) + run(config, tokenizer) else - config = Stupidedi::Config.contrib - input = Stupidedi::Reader::Pointer.build(File.read(ARGV[0])) - state = Stupidedi::Reader::Tokenizer::State.todo - start = Time.now + start = Time.now + config = Stupidedi::Config.contrib + input = Stupidedi::Reader::Input.file(ARGV[0], position) + tokenizer = Stupidedi::Reader::Tokenizer.build(input) MemoryProfiler.report do - run(config, input, state) + run(config, tokenizer) end.pretty_print(to_file: "prof/memprof-tk-#{start.strftime("%Y%m%dT%H%M%S")}.txt", color_output: false, retained_strings: 100, allocated_strings: 100, detailed_report: true, diff --git a/spec/lib/stupidedi/parser/navigation_spec.rb b/spec/lib/stupidedi/parser/navigation_spec.rb index acb648918..440b56e47 100644 --- a/spec/lib/stupidedi/parser/navigation_spec.rb +++ b/spec/lib/stupidedi/parser/navigation_spec.rb @@ -868,7 +868,7 @@ def config(details) context "when mth element does not occur" do it "returns a failure" do - result = b.COM.machine.tap{|x| pp x }.elementn(1, 2) + result = b.COM.machine.elementn(1, 2) expect(result).to be_failure(/COM01 is empty/) end end diff --git a/spec/lib/stupidedi/reader/separators_spec.rb b/spec/lib/stupidedi/reader/separators_spec.rb index 43b70e080..f7c4869b2 100644 --- a/spec/lib/stupidedi/reader/separators_spec.rb +++ b/spec/lib/stupidedi/reader/separators_spec.rb @@ -1,18 +1,22 @@ describe Stupidedi::Reader::Separators do - let(:empty) { Stupidedi::Reader::Separators.empty } + let(:blank) { Stupidedi::Reader::Separators.blank } + + describe ".blank" do + it "returns #blank?" do + expect(blank).to be_blank + end - describe ".empty" do it "returns Separators(nil, nil, nil, nil)" do - expect(empty.element).to be_nil - expect(empty.segment).to be_nil - expect(empty.component).to be_nil - expect(empty.repetition).to be_nil + expect(blank.element).to be_nil + expect(blank.segment).to be_nil + expect(blank.component).to be_nil + expect(blank.repetition).to be_nil end end describe "#merge(other)" do let(:first) do - empty.copy(:element => "a", + blank.copy(:element => "a", :segment => "b", :component => "c", :repetition => "d") @@ -20,49 +24,49 @@ context "if other.element.nil?" do it "then a.merge(b).element == a.element" do - expect(first.merge(empty).element).to be == first.element + expect(first.merge(blank).element).to be == first.element end end context "if not other.element.nil?" do it "then a.merge(b).element == b.element" do - expect(first.merge(empty.copy(:element => "A")).element).to be == "A" + expect(first.merge(blank.copy(:element => "A")).element).to be == "A" end end context "if other.segment.nil?" do it "then a.merge(b).segment == a.segment" do - expect(first.merge(empty).segment).to be == first.segment + expect(first.merge(blank).segment).to be == first.segment end end context "if not other.segment.nil?" do it "then a.merge(b).segment == b.segment" do - expect(first.merge(empty.copy(:segment => "A")).segment).to be == "A" + expect(first.merge(blank.copy(:segment => "A")).segment).to be == "A" end end context "if other.component.nil?" do it "then a.merge(b).component == a.component" do - expect(first.merge(empty).component).to be == first.component + expect(first.merge(blank).component).to be == first.component end end context "if not other.component.nil?" do it "then a.merge(b).component == b.component" do - expect(first.merge(empty.copy(:component => "A")).component).to be == "A" + expect(first.merge(blank.copy(:component => "A")).component).to be == "A" end end context "if other.repetition.nil?" do it "then a.merge(b).repetition == a.repetition" do - expect(first.merge(empty).repetition).to be == first.repetition + expect(first.merge(blank).repetition).to be == first.repetition end end context "if not other.repetition.nil?" do it "then a.merge(b).repetition == b.repetition" do - expect(first.merge(empty.copy(:repetition => "A")).repetition).to be == "A" + expect(first.merge(blank.copy(:repetition => "A")).repetition).to be == "A" end end end diff --git a/stupidedi.gemspec b/stupidedi.gemspec index 9d9095687..08d002474 100644 --- a/stupidedi.gemspec +++ b/stupidedi.gemspec @@ -15,7 +15,6 @@ Gem::Specification.new do |s| "bin/*", "lib/**/*", "doc/**/*.md"].map {|glob| Dir[glob] }.flatten - s.has_rdoc = false s.bindir = "bin" s.executables = ["edi-pp", "edi-ed"] s.require_path = "lib" @@ -23,4 +22,8 @@ Gem::Specification.new do |s| s.add_dependency "term-ansicolor", "~> 1.3" s.add_dependency "cantor", "~> 1.2.1" # s.metadata["yard.run"] = "yard doc" + + if RUBY_PLATFORM !~ /java/ + s.extensions << "ext/strncmp/extconf.rb" + end end From 2a6186463d7d84376f9ef7f9e076d3d55904c1f8 Mon Sep 17 00:00:00 2001 From: kputnam Date: Sun, 21 Jul 2019 10:18:32 -0500 Subject: [PATCH 22/38] Rename native extension to reader/native_ext, strncmp to substr_eq?; implement zero-allocation lstrip, rstrip, is_control_character_at?(offset), and lstrip_control_characters_offset(offset) --- .gitignore | 7 +- Rakefile | 4 +- ext/strncmp/strncmp.c | 34 --- .../reader/native_ext}/extconf.rb | 2 +- ext/stupidedi/reader/native_ext/native_ext.c | 231 +++++++++++++++ lib/strncmp.rb | 1 - lib/stupidedi.rb | 4 - lib/stupidedi/reader.rb | 32 +++ lib/stupidedi/reader/input.rb | 25 +- lib/stupidedi/reader/pointer.rb | 262 ++++++++++++------ lib/stupidedi/reader/tokenizer.rb | 135 ++++----- stupidedi.gemspec | 2 +- 12 files changed, 531 insertions(+), 208 deletions(-) delete mode 100644 ext/strncmp/strncmp.c rename ext/{strncmp => stupidedi/reader/native_ext}/extconf.rb (72%) create mode 100644 ext/stupidedi/reader/native_ext/native_ext.c delete mode 100644 lib/strncmp.rb diff --git a/.gitignore b/.gitignore index dcfb25c1c..9b33e709c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ build/generated .bundle .ruby-version -lib/strncmp -ext/strncmp/Makefile -ext/strncmp/strncmp.h -ext/strncmp/strncmp.o +lib/**/*.bundle +ext/**/Makefile +ext/**/*.o diff --git a/Rakefile b/Rakefile index f921f75c0..bbabb38ac 100644 --- a/Rakefile +++ b/Rakefile @@ -35,7 +35,7 @@ if RUBY_PLATFORM !~ /java/ require "rake/extensiontask" gemspec = Gem::Specification.load("stupidedi.gemspec") - Rake::ExtensionTask.new("strncmp") do |x| - x.lib_dir = "lib/strncmp" + Rake::ExtensionTask.new("stupidedi/reader/native_ext") do |x| + x.lib_dir = "lib" end end diff --git a/ext/strncmp/strncmp.c b/ext/strncmp/strncmp.c deleted file mode 100644 index 656ad8bc2..000000000 --- a/ext/strncmp/strncmp.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "ruby.h" -#include "extconf.h" - -VALUE rb_strncmp(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2, VALUE length) { - // rb_raise(rb_eArgError, "message %s", "argument"); - - if (!FIXNUM_P(length)) rb_raise(rb_eTypeError, "length must be an Integer"); - if (!FIXNUM_P(offset1)) rb_raise(rb_eTypeError, "offset1 must be an Integer"); - if (!FIXNUM_P(offset2)) rb_raise(rb_eTypeError, "offset1 must be an Integer"); - - long len = NUM2LONG(length); - long beg1 = NUM2LONG(offset1); - long beg2 = NUM2LONG(offset2); - - if (len < 0) rb_raise(rb_eArgError, "length cannot be negative"); - if (beg1 < 0) rb_raise(rb_eArgError, "offset1 cannot be negative"); - if (beg2 < 0) rb_raise(rb_eArgError, "offset2 cannot be negative"); - - if (beg1 + len > rb_str_strlen(str1)) return Qnil; - if (beg2 + len > rb_str_strlen(str2)) return Qnil; - - char *ptr1 = rb_str_subpos(str1, beg1, &len); - char *ptr2 = rb_str_subpos(str2, beg2, &len); - - if (ptr1 == 0 || ptr2 == 0) return Qnil; - - return (memcmp(ptr1, ptr2, len) == 0) ? Qtrue : Qfalse; -} - -void Init_strncmp() -{ - // VALUE rb_cString = rb_define_class("String", rb_cObject); - rb_define_singleton_method(rb_cString, "strncmp", rb_strncmp, 5); -} diff --git a/ext/strncmp/extconf.rb b/ext/stupidedi/reader/native_ext/extconf.rb similarity index 72% rename from ext/strncmp/extconf.rb rename to ext/stupidedi/reader/native_ext/extconf.rb index 48fe19fe8..53f14fce2 100644 --- a/ext/strncmp/extconf.rb +++ b/ext/stupidedi/reader/native_ext/extconf.rb @@ -1,5 +1,5 @@ require "mkmf" -extension_name = "strncmp/strncmp" +extension_name = "native_ext" create_header dir_config extension_name diff --git a/ext/stupidedi/reader/native_ext/native_ext.c b/ext/stupidedi/reader/native_ext/native_ext.c new file mode 100644 index 000000000..eb4681cbc --- /dev/null +++ b/ext/stupidedi/reader/native_ext/native_ext.c @@ -0,0 +1,231 @@ +#include "extconf.h" +#include "ruby.h" +#include "ruby/encoding.h" + +static inline int +single_byte_optimizable(VALUE str, rb_encoding *enc) { + if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT) + return 1; + + if (rb_enc_mbmaxlen(enc) == 1) + return 1; + + return 0; +} + +/* + * call-seq: + * Stupidedi::Reader.substr_eql?("abc ", 0, "xyz abc", 4, 2) #=> true + * Stupidedi::Reader.substr_eql?(" abc", 1, "xyz abc", 3, 2) #=> true + * Stupidedi::Reader.substr_eql?(" abc", 1, "xyz a_c", 3, 2) #=> false + * + * Returns true if the substring of s1[n1, length] is equal to the + * substring s2[n2, length]. This is more efficient than doing + * `s1[...] == s2[...]` because it doesn't allocate a new String for + * each substring before performing the comparison. + */ +static VALUE +rb_substr_eql_p(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2, VALUE length) { + Check_Type(str1, T_STRING); + Check_Type(str2, T_STRING); + Check_Type(offset1, T_FIXNUM); + Check_Type(offset2, T_FIXNUM); + Check_Type(length, T_FIXNUM); + + long len = NUM2LONG(length); + long beg1 = NUM2LONG(offset1); + long beg2 = NUM2LONG(offset2); + + if (len < 0) rb_raise(rb_eArgError, "length cannot be negative"); + if (beg1 < 0) rb_raise(rb_eArgError, "offset1 cannot be negative"); + if (beg2 < 0) rb_raise(rb_eArgError, "offset2 cannot be negative"); + + if (beg1 + len > rb_str_strlen(str1)) return Qnil; + if (beg2 + len > rb_str_strlen(str2)) return Qnil; + + /* Number of bytes in str1[offset, length], calculated by rb_str_subpos */ + long len1 = len; + long len2 = len; + + const char *ptr1 = rb_str_subpos(str1, beg1, &len1); + const char *ptr2 = rb_str_subpos(str2, beg2, &len2); + + if (ptr1 == NULL || ptr2 == NULL) return Qnil; + if (len1 != len2 || len1 < len) return Qnil; + return (memcmp(ptr1, ptr2, len) == 0) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * Stupidedi::Reader.lstrip_offset("abc xyz www", 0) #=> 0 + * Stupidedi::Reader.lstrip_offset("abc xyz www", 3) #=> 6 + * Stupidedi::Reader.lstrip_offset("abc xyz www", 6) #=> 6 + */ +static VALUE +rb_lstrip_offset(VALUE self, VALUE str, VALUE offset) { + Check_Type(str, T_STRING); + Check_Type(offset, T_FIXNUM); + + char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); + rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + + if (single_byte_optimizable(str, enc)) { + ptr = start + FIX2LONG(offset); + + if (ptr >= end) + return LONG2NUM(RSTRING_LEN(str)); + + while (ptr < end && rb_isspace(*ptr)) + ptr ++; + + return LONG2NUM(ptr - start); + } + + long len_ = 1, count = 0; + unsigned int c; int len; + ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); + + if (!ptr || ptr >= end || ptr < start) + return LONG2NUM(RSTRING_LEN(str)); + + while (ptr < end && (c = rb_enc_codepoint_len(ptr, end, &len, enc)) != 0 && rb_isspace(c)) { + ptr += len; + count ++; + } + + return LONG2NUM(count); +} + +/* + * call-seq: + * Stupidedi::Reader.rstrip_offset("", 0) #=> 0 + * Stupidedi::Reader.rstrip_offset("", 3) #=> 6 + * Stupidedi::Reader.rstrip_offset("", 6) #=> 6 + */ +static VALUE +rb_rstrip_offset(VALUE self, VALUE str, VALUE offset) { + Check_Type(str, T_STRING); + Check_Type(offset, T_FIXNUM); + + char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); + rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + + if (single_byte_optimizable(str, enc)) { + ptr = RSTRING_PTR(str) + FIX2LONG(offset); + + if (!start || ptr >= end || ptr < start) + return LONG2NUM(0); + + while (start < ptr && rb_isspace(*ptr)) + ptr --; + + return LONG2NUM(ptr - start); + } + + long len = 1; + end = RSTRING_END(str); + ptr = rb_str_subpos(str, FIX2LONG(offset), &len); + + if (!ptr || ptr >= end || ptr < start) + return LONG2NUM(RSTRING_LEN(str)); + + long count = 0; + unsigned int c; + + while (ptr >= start && (c = rb_enc_codepoint(ptr, end, enc)) != 0 && rb_isspace(c)) { + char *ptr_ = rb_enc_prev_char(start, ptr, end, enc); + if (ptr_ == NULL) break; + ptr = ptr_; + count ++; + } + + return LONG2NUM(FIX2LONG(offset) - count); +} + +static int +is_ctrl_char(const unsigned int c) { + if ((c >= 32 && c <= 64 ) // !\"\#$%&'()*+,-./0123456789:;<=>?@ + || (c >= 65 && c <= 97 ) // ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_` + || (c >= 97 && c <= 126) // abcdefghijklmnopqrstuvwxyz{|}~ + || (c >= 161 && c <= 191) // ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ + || (c >= 192 && c <= 255) // ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ + || (c > 255)) + return 0; + + return 1; +} + +static VALUE +rb_lstrip_control_characters_offset(VALUE self, VALUE str, VALUE offset) { + Check_Type(str, T_STRING); + Check_Type(offset, T_FIXNUM); + + char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); + rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + + if (single_byte_optimizable(str, enc)) { + ptr = start + FIX2LONG(offset); + + if (ptr >= end) + return LONG2NUM(RSTRING_LEN(str)); + + while (ptr < end && is_ctrl_char((unsigned int) *ptr)) + ptr ++; + + return LONG2NUM(ptr - start); + } + + long len_ = 1, count = 0; + ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); + + if (!ptr || ptr >= end || ptr < start) + return Qnil; + + while (ptr < end) { + int len; + const unsigned int c = rb_enc_codepoint_len(ptr, end, &len, enc); + if (c != 0 && !is_ctrl_char(c)) break; + ptr += len; + count ++; + } + + return LONG2NUM(FIX2LONG(offset) + count); +} + +/* + * call-seq: + * Stupidedi::Reader.is_control_character_at?("abc\n", 0) #=> false + * Stupidedi::Reader.is_control_character_at?("abc\n", 3) #=> true + * + * Returns true if the character at the given offset is a control character + * according to the X12 specification. This is equivalent to checking if + * `str[offset]` is a control character, but more efficient because it doesn't + * allocate a new string for the single char at `str[offset]` before testing. + */ +static VALUE +rb_is_control_character_at_p(VALUE self, VALUE str, VALUE offset) { + Check_Type(str, T_STRING); + Check_Type(offset, T_FIXNUM); + + long len = 1; + char *ptr = rb_str_subpos(str, FIX2LONG(offset), &len); + + if (!ptr || !len) return Qnil; + if (len > 1) return Qfalse; // There be a multi-byte character here + + if (is_ctrl_char(*ptr)) + return Qtrue; + + return Qfalse; +} + +void Init_native_ext(void) { + VALUE rb_mStupidedi = rb_define_module("Stupidedi"); + VALUE rb_mReader = rb_define_module_under(rb_mStupidedi, "Reader"); + + rb_define_singleton_method(rb_mReader, "substr_eql?", rb_substr_eql_p, 5); + rb_define_singleton_method(rb_mReader, "lstrip_offset", rb_lstrip_offset, 2); + rb_define_singleton_method(rb_mReader, "rstrip_offset", rb_rstrip_offset, 2); + rb_define_singleton_method(rb_mReader, "lstrip_control_characters_offset", rb_lstrip_control_characters_offset, 2); + rb_define_singleton_method(rb_mReader, "is_control_character_at?", rb_is_control_character_at_p, 2); +} diff --git a/lib/strncmp.rb b/lib/strncmp.rb deleted file mode 100644 index 38bcd4f9b..000000000 --- a/lib/strncmp.rb +++ /dev/null @@ -1 +0,0 @@ -require "strncmp/strncmp" diff --git a/lib/stupidedi.rb b/lib/stupidedi.rb index 5cedcdc1c..8995766b2 100644 --- a/lib/stupidedi.rb +++ b/lib/stupidedi.rb @@ -12,10 +12,6 @@ $:.unshift(File.expand_path("..", __FILE__)) -if RUBY_PLATFORM !~ /java/ - require "strncmp" -end - require "ruby/regexp" require "ruby/array" require "ruby/blank" diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index f09f95107..c778f11b8 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -3,6 +3,10 @@ module Stupidedi using Refinements + if RUBY_PLATFORM !~ /java/ + require "stupidedi/reader/native_ext" + end + module Reader autoload :Separators, "stupidedi/reader/separators" autoload :SegmentDict, "stupidedi/reader/segment_dict" @@ -89,6 +93,34 @@ def is_extended_character?(character) H_EXTENDED.include?(character) end + unless Reader.respond_to?(:is_control_character_at?) + # @private + # @see X222.pdf B.1.1.2.2 Extended Characters + def is_control_character_at?(string, offset) + is_control_character?(string[offset]) + end + end + + unless Reader.respond_to?(:lstrip_control_characters_offset) + def lstrip_control_characters_offset(string, offset) + while string.defined_at?(offset) + break offset unless is_control_character_at?(string, offset) + offset += 1 + end + end + end + + unless Reader.respond_to?(:substr_eql?) + # @private + def substr_eql?(s1, n1, s2, n2, length) + if s1.length >= n1 + length and s2.length >= n2 + length + s1 = s1[n1, length] unless n1.zero? and length == s1.length + s2 = s2[n2, length] unless n2.zero? and length == s2.length + s1 == s2 + end + end + end + if R_EXTENDED.respond_to?(:match?) # @private def has_extended_characters?(string) diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb index 437f4bef9..24303990c 100644 --- a/lib/stupidedi/reader/input.rb +++ b/lib/stupidedi/reader/input.rb @@ -72,13 +72,26 @@ def start_with?(other) other and @pointer.start_with?(other) end - def skip_control_characters(offset = 0) - while @pointer.defined_at?(offset) \ - and Reader.is_control_character?(@pointer[offset]) - offset += 1 - end + def substr_at?(offset, other) + other and @pointer.substr_at?(offset, other) + end + + def is_control_character_at?(offset) + @pointer.is_control_character_at?(offset) + end + + # Returns a new Input with the leading control characters removed + # + # @return [Input] + def lstrip_control_characters(offset = 0) + drop(lstrip_control_characters_offset(offset)) + end - drop(offset) + # Returns the offset of the next non-control character at or after offset + # + # @return [Integer] + def lstrip_control_characters_offset(offset = 0) + @pointer.lstrip_control_characters_offset(offset) end end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 2f5e933c2..40d22792f 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -85,31 +85,40 @@ def reify(always_allocate = false) end end - # @return [Pointer] - def reset - self.class.new(@storage.freeze, 0, @storage.length) - end - - # @return [self] - def reset! - @offset = 0 - @length = @storage.length - self - end + # @group Querying + ######################################################################### # @return [Boolean] def empty? @length <= 0 end + # @return [Boolean] def blank? empty? end + # @return [Boolean] def present? not blank? end + # @return [Boolean] + def ==(other) + if self.class == other.class + if @storage.eql?(other.storage) + @offset == other.offset and @length == other.length + else + length == other.length and reify == other.reify + end + else + length == other.length and reify == other + end + end + + # @group Single Element Selection + ######################################################################### + # Return the first element. If {empty?}, `nil` will be returned. # # @return [E] @@ -117,13 +126,6 @@ def head @storage[@offset] if @length > 0 end - # Return a new pointer with the first item removed. - # - # @return [Pointer] - def tail - drop(1) - end - # Return the last element. If {empty?}, `nil` will be returned. # # @return [E] @@ -136,7 +138,7 @@ def last # @return [Boolean] def defined_at?(n) raise ArgumentError, "argument must be non-negative" if 0 > n - n <= @length + n < @length end # Return the nth element. @@ -147,6 +149,16 @@ def at(n) @storage[@offset + n] if @length > n end + # @group Slicing + ######################################################################### + + # Return a new pointer with the first item removed. + # + # @return [Pointer] + def tail + drop(1) + end + # When given a range or a start index and length, returns a new pointer # that spans the given indices. When given a single index, returns the # single element at that index. @@ -280,6 +292,9 @@ def split_at(n) [take(n), drop(n)] end + # @group Concatenation + ######################################################################### + # Concatenate two flyweights to form a third. # # When the two flyweights are backed by the same storage object, and the @@ -305,21 +320,15 @@ def +(other) end end - # @return [Boolean] - def ==(other) - if self.class == other.class - if @storage.eql?(other.storage) - @offset == other.offset and @length == other.length - else - length == other.length and reify == other.reify - end - else - length == other.length and reify == other - end - end + # @endgroup + ######################################################################### end class << Pointer + + # @group Constructors + ######################################################################### + def build(object) case object when String @@ -338,11 +347,16 @@ def build(object) Pointer.new(object) end end + + # @endgroup + ######################################################################### end class StringPtr < Pointer - # TODO: More of these + # @group Conversion Methods + ######################################################################### + def_delegators :reify, :to_sym, :intern, :to_i, :to_d # This is called implicitly when we are used in String interpolation, @@ -356,6 +370,9 @@ def to_str reify end + # @group Matching + ######################################################################### + # Match an unescaped anchor \A, \a, or ^, \Z, \z, $ # # NOTE: These will match character class [^$], which aren't anchors, but @@ -387,11 +404,10 @@ def match?(pattern, offset=0, anchorless=false) end offset = @length if offset > length - offset = @offset + offset # Unfortunately we can't use pattern.match?, which doesn't allocate # a MatchData object, because we need to check bounds on the match - m = pattern.match(@storage, offset) + m = pattern.match(@storage, offset + @offset) if m and m.begin(0) <= @offset + @length if m.end(0) <= @offset + @length @@ -412,10 +428,18 @@ def match?(pattern, offset=0, anchorless=false) # the offset to let the caller make adjustments when needed. # # NOTE: The offset argument controls where the regex engine begins, but - # it doesn't change which part of the string anchors match like ^ $ \A - # \Z and \z. For example, match(/^/, n) with n > 0, will never succeed - # because ^ is at offset 0 of the substring. This behavior is the same - # as String#match. + # it doesn't change which part anchors will match, like ^ $ \A \Z and + # \z. For example, match(/^/, n) with n > 0, will never succeed because + # ^ is at offset 0 of the substring. This behavior is the same as + # String#match. + # + # NOTE: Some of the optimizations in this method require knowing whether + # or not the given Regexp contains an anchor like ^ $ \A \Z and \z. Only + # a quick test is performed, which can falsely detect an anchor; when an + # anchor is detected, the substring must be allocated. If you know from + # the call site that your Regexp does not have an anchor, call with + # `anchorless = true` to skip the detection, thus avoiding an extra String + # allocation. # # @return [MatchData, Integer] def match_(pattern, offset, anchorless=false) @@ -465,6 +489,73 @@ def match_(pattern, offset, anchorless=false) end end + # Optimized to only allocate one string when two pointers don't share + # the same storage. Otherwise no allocations are performed. + # + # @return [Boolean] + def ==(other) + if self.class == other.class + if @storage.eql?(other.storage) \ + and @offset == other.offset \ + and @length == other.length + return true + end + + @length == other.length and \ + Reader.substr_eql?(@storage, @offset, other.storage, other.offset, other.length) + elsif other.is_a?(String) + length == other.length and \ + Reader.substr_eql?(@storage, @offset, other, 0, other.length) + end + end + + # Returns true if this StringPtr begins with the given prefix. No + # allocations are performed. + # + # @return [Boolean] + def start_with?(other) + substr_eql?(0, other, 0) + end + + # Returns true if this StringPtr, starting at the given offset, starts + # with the other string (starting at it's respective offset). This is + # essentially `self[offset, length] == other[offset_, length]`, but no + # Strings are allocated. + # + # Pointer.build("abcdef").substr_at?(0, "abc", 0) #=> true + # Pointer.build("abcdef").substr_at?(0, "abc", 1) #=> false + # Pointer.build("abcdef").substr_at?(0, "xab", 1) #=> true + # Pointer.build("abcdef").substr_at?(1, "abc", 0) #=> false + # Pointer.build("abcdef").substr_at?(1, "bcd", 0) #=> true + # + # @return [Boolean] + def substr_eql?(offset, other, offset_=0, length = other.length) + raise ArgumentError "offset must be be non-negative" if offset < 0 + raise ArgumentError "offset must be be non-negative" if offset_ < 0 + raise ArgumentError "length must be be non-negative" if length < 0 + + case other + when String + if offset + length <= @length and length <= other.length + Reader.substr_eql?(@storage, @offset + offset, other, offset_, length) + end + when StringPtr + if offset + length <= @length and other.offset + length <= other.length + if @storage.eql?(other.storage) and @offset + offset == other.offset + offset_ + # Other pointer starts at the same index and points to the same storage + @length >= length + else + Reader.substr_eql?(@storage, @offset + offset, other.storage, other.offset + offset_, length) + end + end + else + raise TypeError, "expected String or StringPtr, got #{other.inspect}" + end + end + + # @group Indexing + ######################################################################### + # Return offset of the first match, or `nil` if no match occurs. # # @return [Integer] @@ -516,6 +607,9 @@ def rindex(other, offset=@length-1) end end + # @group Search + ######################################################################### + # Return number of occurrences of given character. # # NOTE: This only supports a subset of functionality of String#count. @@ -535,6 +629,9 @@ def count(char) count end + # @group Concatenation + ######################################################################### + # If two flyweights share the same storage and are contiguous (one ends # where the other starts), then string concatenation can be optimized. # @@ -659,56 +756,65 @@ def +(other) end end - # Optimized to only allocate one string, when two pointers don't share - # the same storage. Otherwise zero allocations are performed. + # @group Formatting + ######################################################################### + + # Returns a new StringPtr with trailing spaces removed; "\000", "\t", + # "\n", "\v", "\f", "\r", " " # - # @return [Boolean] - def ==(other) - if self.class == other.class - if @storage.eql?(other.storage) \ - and @offset == other.offset \ - and @length == other.length - return true - end + # @return [StringPtr] + def rstrip(offset = @length - 1) + raise ArgumentError, "offset must be non-negative" if offset < 0 + offset = @length if offset_ > @length + offset_ = Reader.rstrip_offset(@storage, @offset + offset) - @length == other.length and \ - strncmp(@storage, @offset, other.storage, other.offset, other.length) - elsif other.is_a?(String) - length == other.length and \ - strncmp(@storage, @offset, other, 0, other.length) + if offset_ < @offset + self + else + take(offset_ - @offset) end end - def start_with?(other) - case other - when String - if other.length > @length - false - else - strncmp(@storage, @offset, other, 0, other.length) - end - when Regexp - reify.start_with?(other) + # Returns a new StringPtr with leading spaces removed; "\000", "\t", + # "\n", "\v", "\f", "\r", " " + # + # @return [StringPtr] + def lstrip(offset = 0) + raise ArgumentError, "offset must be non-negative" if offset < 0 + offset = @length if offset > @length + offset_ = Reader.lstrip_offset(@storage, @offset + offset) + + if offset_ > @offset + @length + self else - raise TypeError, "expected String or Regexp" + drop(offset_ - @offset) end end - if String.respond_to?(:strncmp) - def strncmp(s1, n1, s2, n2, len) - String.strncmp(s1, n1, s2, n2, len) - end - else - def strncmp(s1, n1, s2, n2, len) - if s1.length <= n1 + len or s2.length <= n2 + len - false - else - s1 = s1[n1, len] unless n1.zero? and n1.length == len - s2 = s2[n2, len] unless n2.zero? and n2.length == len - s1 == s2 - end - end + # @group Miscellaneous + ######################################################################### + + # Returns the offset of the next non-control character at or after offset + # + # @return [Integer] + def lstrip_control_characters_offset(offset = 0) + raise ArgumentError, "offset must be non-negative" if offset < 0 + offset = @length if offset > @length + + n = Reader.lstrip_control_characters_offset(@storage, @offset + offset) + n = @offset + @length if n > @offset + @length + n - @offset + end + + # Returns true if the character at the given offset is a control + # character (defined by X222.pdf B.1.1.2.4 Control Characters) + def is_control_character_at?(offset) + raise ArgumentError "offset must be be non-negative" if offset < 0 + Reader.is_control_character_at?(@storage, @offset + offset) end + + # @endgroup + ######################################################################### end class ArrayPtr < Pointer diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index 9b8f3f9c5..62b4b2c84 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -292,7 +292,7 @@ def next_segment(input) return elements if elements.fail? segment_tok = SegmentTok.build(:ISA, elements.value, segment_id.position) - return done(segment_tok, segment_id.position, elements.rest.skip_control_characters) + return done(segment_tok, segment_id.position, elements.rest.lstrip_control_characters) end # _next_segment_id will skip any trailing control characters @@ -351,7 +351,7 @@ def _next_isa_segment_id(input) # There's something between I..S but it's not a control character next if s > i + 1 and input[i+1, s-i-1].match?(Reader::R_EITHER, 0, true) - a = input.index("A", offset) + a = input.index("A", s + 1) # There's no A in the rest of the input, so it's all ignored return eof("ISA", input.position_at(i)) if a.nil? @@ -360,7 +360,7 @@ def _next_isa_segment_id(input) next if a > s + 1 and input[s+1, a-s-1].match?(Reader::R_EITHER, 0, true) # Needed to perform the extra validation below - a = _skip_control_characters(input, a) + a = input.lstrip_control_characters_offset(a) # The next character determines the element separator. If it's an # alphanumeric or space, we assume this is not the start of an ISA @@ -395,7 +395,7 @@ def _read_isa_elements(input) # We have to assume the last (16th) element is fixed-length because # it is not terminated by an element separator. First we will skip # past control characters, then read the next character. - offset = _skip_control_characters(input, 1) + offset = input.lstrip_control_characters_offset(1) return eof("ISA16", input.position) unless input.defined_at?(offset) element_toks << SimpleElementTok.build(input[offset], input.position_at(offset)) @@ -420,7 +420,7 @@ def _read_isa_elements(input) # # @return Symbol def _next_segment_id(input) - offset = _skip_control_characters(input) + offset = input.lstrip_control_characters_offset buffer = input.pointer.drop_take(offset, 0) start_pos = input.position_at(offset) @@ -432,11 +432,10 @@ def _next_segment_id(input) break if char == @separators.element break if char == @separators.segment + # Zero-copy as long as we've not skipped over any characters yet + buffer << char unless input.is_control_character_at?(offset) offset += 1 - next if Reader.is_control_character?(char) - # Zero-copy as long as we've not skipped over any characters yet - buffer << char break if buffer.length >= 3 end @@ -449,10 +448,10 @@ def _next_segment_id(input) # is faster overall to make the substring copy here. segment_id = buffer.to_s - return expected("segment identifier, found %s" % segment_id.inspect, start_pos) \ - unless segment_id.match?(Tokenizer::SEGMENT_ID) + return expected("segment identifier, found %s" % segment_id.inspect, + start_pos) unless segment_id.match?(Tokenizer::SEGMENT_ID) - return done(segment_id.to_sym, start_pos, input.skip_control_characters(offset)) + return done(segment_id.to_sym, start_pos, input.lstrip_control_characters(offset)) end # @param input should be positioned on an element separator: "NM1[*].." @@ -492,12 +491,12 @@ def _read_elements(input, segment_id, element_uses=[]) element_idx += 1 end - return eof("segment terminator for %s" % segment_id, input.position) \ - if input.empty? + return eof("segment terminator %s for %s" % [@separators.segment.inspect, + segment_id], input.position) if input.empty? - return expected("segment terminator for %s, found %s" % - [segment_id, input.head.inspect], input.position) \ - if input.head != @separators.segment + return expected("segment terminator %s for %s, found %s" % [ + @separators.segment.inspect, segment_id, input.head.inspect], + input.position) if input.head != @separators.segment # Skip past the segment separator done(element_toks, nil, input.tail) @@ -511,12 +510,13 @@ def _read_elements(input, segment_id, element_uses=[]) # # @return [CompositeElementTok | RepeatedElementTok] def _read_composite_element(input, repeatable, segment_id, element_idx) - return eof("element separator before %s%02d" % [ - segment_id, element_idx], input.position) if input.empty? + return eof("element separator %s before %s%02d" % [ + @separators.element.inspect, segment_id, element_idx], + input.position) if input.empty? - return expected("element separator before %s%02d, found %s" % [ - segment_id, element_idx, input.head.inspect], input.position) \ - unless input.start_with?(@separators.element) + return expected("element separator %s before %s%02d, found %s" % [ + @separators.element.inspect, segment_id, element_idx, input.head.inspect], + input.position) unless input.start_with?(@separators.element) builder = @switcher.switch(repeatable, input.position_at(1)) repeat_pos = builder.position @@ -528,19 +528,20 @@ def _read_composite_element(input, repeatable, segment_id, element_idx) return result if result.fail? input = result.rest + char = input.head component_toks << result.value - if input.start_with?(@separators.element) \ - or input.start_with?(@separators.segment) + if char == @separators.element \ + or char == @separators.segment builder.add(CompositeElementTok.build(component_toks, repeat_pos)) return done(builder.build, builder.position, input) - elsif repeatable and input.start_with?(@separators.repetition) + elsif repeatable and char == @separators.repetition builder.add(CompositeElementTok.build(component_toks, repeat_pos)) repeat_pos = input.position_at(1) component_toks.clear - elsif input.start_with?(@separators.repetition) + elsif char == @separators.repetition # We aren't repetable and neither was the component element we just # read, or it would have consumed the repetition separator. We can # either pretend we didn't see it and carry on, or abort. @@ -551,15 +552,16 @@ def _read_composite_element(input, repeatable, segment_id, element_idx) # put it, because ordinary data wouldn't have occured here anyway. # # So there's not much we can do but bail. - return unexpected("repetition separator after %s%02d" % [ - segment_id, element_idx], input.head) + return unexpected("repetition separator %s after %s%02d" % [ + @separators.repetition.inspect, segment_id, element_idx], input.head) end component_idx += 1 end - eof("element or segment separator after %s%02d" % [ - segment_id, element_idx], builder.position) + eof("element %s or segment separator %s after %s%02d" % [ + @separator.element.inspect, @separator.segment.inspect, segment_id, + element_idx], builder.position) end # @param input should be positioned at an element separator, @@ -571,15 +573,17 @@ def _read_composite_element(input, repeatable, segment_id, element_idx) # # @return [ComponentElementTok | RepeatedElementTok] def _read_component_element(input, repeatable, segment_id, element_idx, component_idx) - return eof("element or component separator before %s%02d-%02d" % [ - segment_id, element_idx, component_idx], input.position) if input.empty? + return eof("element %s or component separator %s before %s%02d-%02d" % [ + @separators.element.inspect, @separators.component.inspect, segment_id, + element_idx, component_idx], input.position) if input.empty? - return expected("element or component separator before %s%02d-%02d, found %s" % - [segment_id, element_idx, component_idx, input.head.inspect], input.position) \ + return expected("element %s or component separator %s before %s%02d-%02d, found %s" % [ + @separators.element.inspect, @separators.component.inspect, segment_id, + element_idx, component_idx, input.head.inspect], input.position) \ unless input.start_with?(@separators.element) \ or input.start_with?(@separators.component) - offset = _skip_control_characters(input, 1) + offset = input.lstrip_control_characters_offset(1) buffer = input.pointer.drop_take(offset, 0) repeat_pos = input.position builder = @switcher_.switch(repeatable, input.position) @@ -604,17 +608,21 @@ def _read_component_element(input, repeatable, segment_id, element_idx, componen else # This is zero-copy as long as we haven't skipped any characters - buffer << char unless Reader.is_control_character?(char) + buffer << char unless input.is_control_character_at?(offset) offset += 1 end end if repeatable - eof("segment, element, component or repetition separator after %s%02d-%02d" % [ + eof("segment %s, element %s, component %s or repetition separator %s after %s%02d-%02d" % [ + @separators.segment.inspect, @separators.element.inspect, + @separators.component.inspect, @separators.repetition.inspect, segment_id, element_idx, component_idx], builder.position) else - eof("segment, element or repetition separator after %s%02d-%02d" % [ - segment_id, element_idx, component_idx], builder.position) + eof("segment %s, element %s or component separator %s after %s%02d-%02d" % [ + @separators.segment.inspect, @separators.element.inspect, + @separators.component.inspect, segment_id, element_idx, + component_idx], builder.position) end end @@ -625,14 +633,15 @@ def _read_component_element(input, repeatable, segment_id, element_idx, componen # # @return [SimpleElementTok | RepeatedElementTok] def _read_simple_element(input, repeatable, segment_id, element_idx) - return eof("element separator before %s%02d" % [segment_id, element_idx], + return eof("element separator %s before %s%02d" % [ + @separators.element.inspect, segment_id, element_idx], input.position) if input.empty? - return expected("element separator before %s%02d, found %s" % [ - segment_id, element_idx, input.head.inspect], input.position) \ - unless input.start_with?(@separators.element) + return expected("element separator %s before %s%02d, found %s" % [ + @separators.element.inspect, segment_id, element_idx, input.head.inspect], + input.position) unless input.start_with?(@separators.element) - offset = _skip_control_characters(input, 1) + offset = input.lstrip_control_characters_offset(1) buffer = input.pointer.drop_take(offset, 0) start_pos = input.position repeat_pos = input.position @@ -652,33 +661,16 @@ def _read_simple_element(input, repeatable, segment_id, element_idx) buffer = input.pointer.drop_take(offset, 0) repeat_pos = input.position_at(offset) - # We won't do this because it could make tokenizing a valid file not - # possible. If it truly had repeatable elements, but we hadn't setup - # segment_dict to recognize which elements are repeatable, this would - # halt tokenization. Instead, we just read the separator as data, but - # this might later result in an invalid element in the parse tree. - # - # elsif char == @separators.repetition - # return unexpected("...", ...) else # This is zero-copy as long as we haven't skipped any characters - buffer << char unless Reader.is_control_character?(char) + buffer << char unless input.is_control_character_at?(offset) offset += 1 end end - eof("segment or element separator after %s%02d" % [ - segment_id, element_idx], start_pos) - end - - # @return [Integer] - def _skip_control_characters(input, offset=0) - while input.defined_at?(offset) \ - and Reader.is_control_character?(input[offset]) - offset += 1 - end - - offset + eof("segment %s or element separator %s after %s%02d" % [ + @separators.segment.inspect, @separators.element.inspect, segment_id, + element_idx], start_pos) end # @param value [Object] @@ -687,12 +679,6 @@ def done(value, position, rest) Tokenizer::Result::Done.new(value, position, rest) end - # @param value [String] - # @param position [Position] - def fail(error, position) - Tokenizer::Result::Fail.new(error, position) - end - # @param value [String] # @param position [Position] def expected(error, position) @@ -707,13 +693,8 @@ def unexpected(error, positiion) # @param value [String] # @param position [Position] - def eof(error, x) - case x - when Input - expected("#{error}, found eof", x.position_at(x.length)) - else - expected("#{error}, found eof", x) - end + def eof(error, position) + expected("#{error}, found eof", position) end end diff --git a/stupidedi.gemspec b/stupidedi.gemspec index 08d002474..9be80b097 100644 --- a/stupidedi.gemspec +++ b/stupidedi.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |s| # s.metadata["yard.run"] = "yard doc" if RUBY_PLATFORM !~ /java/ - s.extensions << "ext/strncmp/extconf.rb" + s.extensions << "ext/stupidedi/reader_ext" end end From edd0f6e5676b000a4ad09f277e9f96787d3b9a8e Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 22 Jul 2019 00:13:08 -0500 Subject: [PATCH 23/38] Further performance improvements! Use reflection to specialize the delegator methods so we won't allocate *args when not needed. Benchmark now runs in 7.0s and 549MB max RSS (from 8.2s and 977MB in 2a61864) --- .gitignore | 1 + lib/ruby/module.rb | 148 ++++++++++++++++++++++++++++-- lib/stupidedi/reader/input.rb | 122 ++++++++++++++++-------- lib/stupidedi/reader/pointer.rb | 46 +++++----- lib/stupidedi/reader/tokenizer.rb | 26 +++--- 5 files changed, 257 insertions(+), 86 deletions(-) diff --git a/.gitignore b/.gitignore index 9b33e709c..bf6f5bcdf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/generated lib/**/*.bundle ext/**/Makefile ext/**/*.o +tmp diff --git a/lib/ruby/module.rb b/lib/ruby/module.rb index 3c4e7cad3..25d84833a 100644 --- a/lib/ruby/module.rb +++ b/lib/ruby/module.rb @@ -1,6 +1,19 @@ # frozen_string_literal: true module Stupidedi module Refinements + + # When `def_delegators` is given `debug: true`, this hash map will + # have [class, method] => number of invocations. + CALL_COUNT = Hash.new(0) + + # Kernel.at_exit do + # pp CALL_COUNT + # end + + # Used as a default value for optional arguments; when this is received + # by a delegator method we know the user didn't provide a value. + class DEFAULT < BasicObject; end + refine Module do # Creates an abstract method # @@ -36,25 +49,142 @@ def #{name}(*args) end end - def def_delegators(target, *methods) + # If we simply forward all arguments using `def foo(*args, &block)`, then + # we allocate an extra Array to store the parameters. This can really add + # up for methods that are called many times. + # + # When given a `type: Class` or `instance: Class.allocate`, we can inspect + # the Method/UnboundMethod and see the parameter list. Then we can usually + # declare a method that takes exactly the same parameters as the receiving + # method. Then invoking the delegating method won't cause anything to be + # allocated. Sometimes we have to fallback on *args though. + # + # class X + # def_delegators_ :push, :length, to: "@ary", type: Array + # def_delegators_ :[], :[]=, to: "@map", instance: {} + # + # def initialize + # @ary, @map = [], {} + # end + # end + # + def def_delegators(receiver, *names, type:DEFAULT, instance:DEFAULT, debug:nil) file, line, = Stupidedi.caller - for m in methods - if m.to_s.end_with?("=") - class_eval(<<-RUBY, file, line.to_i - 1) - def #{m}(value) - #{target}.#{m}(value) + if not DEFAULT.eql?(instance) + names.each do |name| + def_delegator(receiver, name, file, line, instance.method(name), debug) + end + elsif not DEFAULT.eql?(type) + names.each do |name| + def_delegator(receiver, name, file, line, type.instance_method(name), debug) + end + else + names.each do |name| + def_delegator(receiver, name, file, line, nil, debug) + end + end + end + + private + + def def_delegator(receiver, name, file, line, method, debug) + if method.nil? + if name.to_s.end_with?("=") + class_eval(<<-RUBY.tap{|s| puts s if debug }, file, line.to_i - 1) + def #{name}(value) + #{emit_instrumentation(name) if debug} + #{receiver}.#{name}(value) end RUBY else - class_eval(<<-RUBY, file, line.to_i - 1) - def #{m}(*args, &block) - #{target}.#{m}(*args, &block) + class_eval(<<-RUBY.tap{|s| puts s if debug }, file, line.to_i - 1) + def #{name}(*args, &block) + #{emit_instrumentation(name) if debug} + #{receiver}.#{name}(*args, &block) end RUBY end + else + new_names = String.new("__`") + has_block = false + optionals = [] + + triples = method.parameters.map do |k, n| + n ||= new_names.succ! # Get a fresh name if needed + case k + when :req then [n, n, n] # def foo(x) + when :keyreq then [n, n, "#{n}:#{n}"] # def foo(x:) + when :rest then [n, "*#{n}", "*#{n}"] # def foo(*x) + when :opt then # def foo(x = nil) + optionals << n + [n, "#{n}=#{DEFAULT}", n] + when :key # def foo(x: nil) + optionals << n + [n, "::#{n}:#{DEFAULT}", "#{n}:#{n}"] + when :block # def foo(&x) + has_block = true + [n, "&#{n}", "&#{n}"] + end + end + + unless has_block + # We can't know if the receiver method uses an *implicit* block + # parameter, so we will pass along any block we are given. + triples << [new_names.succ!, "&#{new_names}", "&#{new_names}"] + end + + arg_names = triples.map{|n, _, _| n } + arguments = Hash[triples.map{|n, _, a| [n, a] }] # How to pass + parameters = triples.map{|_, p, _| p }.join(", ") # How to recv + + class_eval(<<-RUBY.tap{|x| puts x if debug }, file, line.to_i - 1) + def #{name}(#{parameters}) + #{emit_instrumentation(name) if debug} + #{emit_branches(optionals, 7) do |state| + emit_call(receiver, name, arg_names, arguments, optionals, state) + end.lstrip} + end + RUBY + end + end + + def emit_instrumentation(name) + "Stupidedi::Refinements::CALL_COUNT[['#{self.name}', '#{name}']] += 1" + end + + def emit_branches(optionals, predent, state=0, depth=0, &block) + indent = " " * (predent + depth) + + if optionals.empty? + "#{indent}#{yield state}" + else + head, *tail = optionals + end_state = optionals.length.times.inject(state) do |s,n| + s | (1 << (depth + n)) + end + + ["#{indent}if #{DEFAULT}.eql?(#{head})", + emit_branches([], predent, end_state, depth+1, &block), + "#{indent}else", + emit_branches(tail, predent, state, depth+1, &block), + "#{indent}end"].join("\n") end end + + def emit_call(receiver, name, arg_names, arguments, optionals, state) + "#{receiver}.#{name}(#{emit_args(arg_names, arguments, optionals, state)})" + end + + def emit_args(arg_names, arguments, optionals, state) + arg_names.inject([]) do |args, name| + if k = optionals.index(name) + args << arguments[name] if state[k].zero? + else + args << arguments[name] + end; args + end.join(", ") + end end end end diff --git a/lib/stupidedi/reader/input.rb b/lib/stupidedi/reader/input.rb index 24303990c..74f51a421 100644 --- a/lib/stupidedi/reader/input.rb +++ b/lib/stupidedi/reader/input.rb @@ -12,43 +12,21 @@ class Input # @return [Position] attr_reader :position - def_delegators :@pointer, :count, :take, :match?, :slice, - :index, :rindex, :reify, :last, :length, :=~, :[] + def_delegators "@pointer", :head, :defined_at?, :empty?, :[], :length, + :count, :take, :match?, :slice, :index, :rindex, :reify, :last, :=~, + :at, type: StringPtr def initialize(pointer, position) @pointer, @position = pointer, position end - # NOTE: This could be implemented using def_delegators, but unfortunately - # that allocates an Array to pass the arguments along. Since this method - # is called frequently, we can reduce allocations by defining it this way. - def head - @pointer.head - end - - # NOTE: This could be implemented using def_delegators, but unfortunately - # that allocates an Array to pass the arguments along. Since this method - # is called frequently, we can reduce allocations by defining it this way. - def defined_at?(n) - @pointer.defined_at?(n) - end - - # NOTE: This could be implemented using def_delegators, but unfortunately - # that allocates an Array to pass the arguments along. Since this method - # is called frequently, we can reduce allocations by defining it this way. - # NOTE: - def empty? - @pointer.empty? - end - - # @NOTE: This allocates 3 objects: Input, Position, and StringPtr - def tail - drop(1) - end + # @endgroup + ######################################################################### - # @NOTE: This allocates 3 objects: Input, Position, and StringPtr, so - # if you've written x.drop(10).position.. x.position_at(10) is cheaper + # Returns the input with the first `n` characters removed + # + # @return [Input] def drop(n) if n.zero? self @@ -57,7 +35,30 @@ def drop(n) end end - # NOTE: This allocates 2 objects: StringPtr and Position + # Destructively updates this Input to start `n` characters after the + # first. + # + # @return [Input] + def drop!(n) + unless n.zero? + if @position.is_a?(Integer) + @pointer.drop!(n) + @position += n + else + unless @position.eql?(NoPosition) + @position = @position.advance(@pointer.take(n)) + end + + @pointer.drop!(n) + end + end + + self + end + + # Calculates the position at the given offset. + # + # @return [Position] def position_at(n) if @position.eql?(NoPosition) @position @@ -68,14 +69,20 @@ def position_at(n) end end + # Returns true if the input begins with the other value (String, StringPtr) def start_with?(other) other and @pointer.start_with?(other) end + # Returns true if the input contains the other value, starting at the + # `nth` character. + # def substr_at?(offset, other) other and @pointer.substr_at?(offset, other) end + # Returns true if the character at the given offset is a control + # character (defined by X222.pdf B.1.1.2.4 Control Characters) def is_control_character_at?(offset) @pointer.is_control_character_at?(offset) end @@ -96,29 +103,66 @@ def lstrip_control_characters_offset(offset = 0) end class << Input - def build(value, position = NoPosition) + # @group Constructors + ######################################################################### + + # @examples + # Input.build(File.open("sample.edi"), mode: "rb") + # Input.build(File.read("sample.edi", encoding: "UTF-8")) + # Input.build(Pathname.new("sample.edi"), position: OffsetPosition) + # + def build(value, *args) if value.is_a?(String) - string(value, position) + string(value, *args) + elsif defined?(Pathname) and value.is_a?(Pathname) - file(value, position) + file(value, *args) + elsif value.is_a?(Input) value + elsif value.respond_to?(:read) - path = value.path if value.respond_to?(:path) - new(Pointer.build(value.read), position.build(path)) + position = args.last.delete(:position) if args.last.is_a?(Hash) + path = value.path if value.respond_to?(:path) + new(Pointer.build(value.read), (position || NoPosition).build(path)) + else raise TypeError, "value must be a String, Pathname, or respond to #read" end end - def file(path, position = NoPosition) - new(Pointer.build(File.read(path)), position.build(path)) + # + # @examples + # Input.file("example.edi", length, offset) + # Input.file("example.edi", mode: "rb") + # Input.file("example.edi", encoding: "US-ASCII") + # Input.file("example.edi", position: Stupidedi::Reader::NoPosition) + # + def file(path, *args) + position = + if args.last.is_a?(Hash) + args.last.delete(:position) + end || NoPosition + + new(Pointer.build(File.read(path, *args)), position.build(path)) end - def string(value, position = NoPosition) + # @examples + # Input.string(io.read) + # Input.string("...", position: Stupidedi::Reader::OffsetPosition) + # + def string(value, *args) + position = + if args.last.is_a?(Hash) + args.last.delete(:position) + end || NoPosition + new(Pointer.build(value), position.build(nil)) end + + # @endgroup + ######################################################################### end end end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index 40d22792f..db0c9faed 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -208,6 +208,10 @@ def [](offset, length=nil) # Return a new pointer, skipping the first n items. # + # x = Pointer.new("eyeball") + # x.drop(5) #=> "ll" + # x #=> "eyeball" + # # @return [Pointer] def drop(n) raise ArgumentError, "argument must be non-negative" if n < 0 @@ -216,29 +220,27 @@ def drop(n) self.class.new(@storage.freeze, @offset + n, @length - n) end - # Return a new pointer, skipping the first n items, and destructively - # update this pointer to end at the nth element. + # Destructively update this pointer to start at the (n+1)th element. # # x = Pointer.new("eyeball") - # x.drop!(5) == "ll" - # x == "eyeba" + # x.drop!(5) #=> "eyeba" + # x #=> "ll" # # @return [Pointer] def drop!(n) raise ArgumentError, "argument must be non-negative" if n < 0 - n = @length if n > @length - offset = @offset + n - length = @length - n - suffix = self.class.new(@storage.freeze, offset, length) - - # We become the prefix that ends where suffix starts - @length = n - - suffix + n = @length if n > @length + @offset += n + @length -= n + self end # Return a new pointer spanning only the first n items. # + # x = Pointer.new("eyeball") + # x.take(5) #=> "eyeba" + # x #=> "eyeball" + # # @return [Pointer] def take(n) raise ArgumentError, "argument must be non-negative" if n < 0 @@ -246,24 +248,18 @@ def take(n) self.class.new(@storage.freeze, @offset, n) end - # Return a new pointer spanning only the first n items, and destructively - # update this pointer to start at the (n+1)th element. + # Destructively update this pointer to end at the nth element. # # x = Pointer.new("eyeball") - # x.take!(5) == "eyeba" - # x == "ll" + # x.take!(5) #=> "eyeba" + # x #=> "eyeba" # # @return [Pointer] def take!(n) raise ArgumentError, "argument must be non-negative" if n < 0 - n = @length if n > @length - prefix = self.class.new(@storage.freeze, @offset, n) - - # We become the suffix starts where prefix ends - @offset += n - @length -= n - - prefix + n = @length if n > @length + @length = n + self end # This method is equivalent to x.drop(n).take(m), but it allocates diff --git a/lib/stupidedi/reader/tokenizer.rb b/lib/stupidedi/reader/tokenizer.rb index 62b4b2c84..47de671d5 100644 --- a/lib/stupidedi/reader/tokenizer.rb +++ b/lib/stupidedi/reader/tokenizer.rb @@ -5,7 +5,7 @@ module Stupidedi module Reader # TODO: One of the top garbage producers is reading a single character like - # input[offset]. It might be more efficient to replace char-at-a-time reads + # input.at(offset). It might be more efficient to replace char-at-a-time reads # with Regexps that consume multiple characters at once. class Tokenizer @@ -299,7 +299,7 @@ def next_segment(input) unless segment_id.rest.start_with?(@separators.element) \ or segment_id.rest.start_with?(@separators.segment) return unexpected("%s after segment identifier %s" % [ - char.head.inspect, segment_id.value], segment_id.rest.position) + segment_id.rest.head.inspect, segment_id.value], segment_id.rest.position) end if @segment_dict.defined_at?(segment_id.value) @@ -365,12 +365,12 @@ def _next_isa_segment_id(input) # The next character determines the element separator. If it's an # alphanumeric or space, we assume this is not the start of an ISA # segment. Perhaps a word like "L[ISA] " or "D[ISA]RRAY" - next if not input.defined_at?(a+1) or input[a+1].match?(BAD_SEPARATOR) + next if not input.defined_at?(a+1) or input.at(a+1).match?(BAD_SEPARATOR) # Success, ignore everything before "I", resume parsing after "A". yield IgnoredTok.new(input.take(i), input.position) if block_given? - return done(:ISA, input.position_at(i), input.drop(a + 1)) + return done(:ISA, input.position_at(i), input.drop!(a + 1)) end return eof("ISA", input.position) @@ -397,7 +397,7 @@ def _read_isa_elements(input) # past control characters, then read the next character. offset = input.lstrip_control_characters_offset(1) return eof("ISA16", input.position) unless input.defined_at?(offset) - element_toks << SimpleElementTok.build(input[offset], input.position_at(offset)) + element_toks << SimpleElementTok.build(input.at(offset), input.position_at(offset)) # The character immediately after ISA16 is defined to be the # segment terminator. The separator could be a control character, @@ -405,8 +405,8 @@ def _read_isa_elements(input) return eof("segment terminator for ISA", input.position_at(offset)) \ unless input.defined_at?(offset + 1) - @separators.segment = input[offset + 1] - done(element_toks, nil, input.drop(offset + 2)) + @separators.segment = input.at(offset + 1) + done(element_toks, nil, input.drop!(offset + 2)) end # Works similarly to `_next_isa_segment_id`, except the result is the @@ -428,7 +428,7 @@ def _next_segment_id(input) return eof("segment identifier", input.position) \ unless input.defined_at?(offset) - char = input[offset] + char = input.at(offset) break if char == @separators.element break if char == @separators.segment @@ -499,7 +499,7 @@ def _read_elements(input, segment_id, element_uses=[]) input.position) if input.head != @separators.segment # Skip past the segment separator - done(element_toks, nil, input.tail) + done(element_toks, nil, input.drop!(1)) end # @param input should be positioned at an element separator, @@ -589,7 +589,7 @@ def _read_component_element(input, repeatable, segment_id, element_idx, componen builder = @switcher_.switch(repeatable, input.position) while input.defined_at?(offset) - char = input[offset] + char = input.at(offset) if repeatable and char == @separators.repetition builder.add(SimpleElementTok.build(buffer, repeat_pos)) @@ -604,7 +604,7 @@ def _read_component_element(input, repeatable, segment_id, element_idx, componen # belong to the parent/composite element. If it's not repeatable # either, an error can be returned. builder.add(ComponentElementTok.build(buffer, repeat_pos)) - return done(builder.build, builder.position, input.drop(offset)) + return done(builder.build, builder.position, input.drop!(offset)) else # This is zero-copy as long as we haven't skipped any characters @@ -648,12 +648,12 @@ def _read_simple_element(input, repeatable, segment_id, element_idx) builder = @switcher.switch(repeatable, input.position) while input.defined_at?(offset) - char = input[offset] + char = input.at(offset) if char == @separators.element \ or char == @separators.segment builder.add(SimpleElementTok.build(buffer, repeat_pos)) - return done(builder.build, start_pos, input.drop(offset)) + return done(builder.build, start_pos, input.drop!(offset)) elsif repeatable and char == @separators.repetition builder.add(SimpleElementTok.build(buffer, repeat_pos)) From a908f9db81fe47ca8768baf1a2ff4ffd1a6ee7a2 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 22 Jul 2019 00:21:22 -0500 Subject: [PATCH 24/38] Fix C90 compilation warnings about declarations and assignments --- ext/stupidedi/reader/native_ext/native_ext.c | 73 ++++++++++++++------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/ext/stupidedi/reader/native_ext/native_ext.c b/ext/stupidedi/reader/native_ext/native_ext.c index eb4681cbc..a4cc0f10c 100644 --- a/ext/stupidedi/reader/native_ext/native_ext.c +++ b/ext/stupidedi/reader/native_ext/native_ext.c @@ -32,9 +32,10 @@ rb_substr_eql_p(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2 Check_Type(offset2, T_FIXNUM); Check_Type(length, T_FIXNUM); - long len = NUM2LONG(length); - long beg1 = NUM2LONG(offset1); - long beg2 = NUM2LONG(offset2); + long len, beg1, beg2; + len = NUM2LONG(length); + beg1 = NUM2LONG(offset1); + beg2 = NUM2LONG(offset2); if (len < 0) rb_raise(rb_eArgError, "length cannot be negative"); if (beg1 < 0) rb_raise(rb_eArgError, "offset1 cannot be negative"); @@ -44,11 +45,13 @@ rb_substr_eql_p(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2 if (beg2 + len > rb_str_strlen(str2)) return Qnil; /* Number of bytes in str1[offset, length], calculated by rb_str_subpos */ - long len1 = len; - long len2 = len; + long len1, len2; + len1 = len; + len2 = len; - const char *ptr1 = rb_str_subpos(str1, beg1, &len1); - const char *ptr2 = rb_str_subpos(str2, beg2, &len2); + const char *ptr1, *ptr2; + ptr1 = rb_str_subpos(str1, beg1, &len1); + ptr2 = rb_str_subpos(str2, beg2, &len2); if (ptr1 == NULL || ptr2 == NULL) return Qnil; if (len1 != len2 || len1 < len) return Qnil; @@ -66,8 +69,12 @@ rb_lstrip_offset(VALUE self, VALUE str, VALUE offset) { Check_Type(str, T_STRING); Check_Type(offset, T_FIXNUM); - char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + char *ptr, *start, *end; + rb_encoding *enc; + + start = RSTRING_PTR(str); + end = RSTRING_END(str); + enc = rb_enc_from_index(ENCODING_GET(str)); if (single_byte_optimizable(str, enc)) { ptr = start + FIX2LONG(offset); @@ -81,9 +88,13 @@ rb_lstrip_offset(VALUE self, VALUE str, VALUE offset) { return LONG2NUM(ptr - start); } - long len_ = 1, count = 0; - unsigned int c; int len; - ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); + int len; + long len_, count; + unsigned int c; + + len_ = 1; + count = 0; + ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); if (!ptr || ptr >= end || ptr < start) return LONG2NUM(RSTRING_LEN(str)); @@ -107,8 +118,12 @@ rb_rstrip_offset(VALUE self, VALUE str, VALUE offset) { Check_Type(str, T_STRING); Check_Type(offset, T_FIXNUM); - char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + char *ptr, *start, *end; + rb_encoding *enc; + + start = RSTRING_PTR(str); + end = RSTRING_END(str); + enc = rb_enc_from_index(ENCODING_GET(str)); if (single_byte_optimizable(str, enc)) { ptr = RSTRING_PTR(str) + FIX2LONG(offset); @@ -122,19 +137,21 @@ rb_rstrip_offset(VALUE self, VALUE str, VALUE offset) { return LONG2NUM(ptr - start); } - long len = 1; + long len; + len = 1; end = RSTRING_END(str); ptr = rb_str_subpos(str, FIX2LONG(offset), &len); if (!ptr || ptr >= end || ptr < start) return LONG2NUM(RSTRING_LEN(str)); - long count = 0; unsigned int c; + long count; + count = 0; while (ptr >= start && (c = rb_enc_codepoint(ptr, end, enc)) != 0 && rb_isspace(c)) { - char *ptr_ = rb_enc_prev_char(start, ptr, end, enc); - if (ptr_ == NULL) break; + char *ptr_; + if ((ptr_ = rb_enc_prev_char(start, ptr, end, enc)) == NULL) break; ptr = ptr_; count ++; } @@ -160,8 +177,12 @@ rb_lstrip_control_characters_offset(VALUE self, VALUE str, VALUE offset) { Check_Type(str, T_STRING); Check_Type(offset, T_FIXNUM); - char *ptr, *start = RSTRING_PTR(str), *end = RSTRING_END(str); - rb_encoding *enc = rb_enc_from_index(ENCODING_GET(str)); + char *ptr, *start, *end; + rb_encoding *enc; + + start = RSTRING_PTR(str); + end = RSTRING_END(str); + enc = rb_enc_from_index(ENCODING_GET(str)); if (single_byte_optimizable(str, enc)) { ptr = start + FIX2LONG(offset); @@ -175,7 +196,9 @@ rb_lstrip_control_characters_offset(VALUE self, VALUE str, VALUE offset) { return LONG2NUM(ptr - start); } - long len_ = 1, count = 0; + long len_, count; + len_ = 1; + count = 0; ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); if (!ptr || ptr >= end || ptr < start) @@ -184,6 +207,7 @@ rb_lstrip_control_characters_offset(VALUE self, VALUE str, VALUE offset) { while (ptr < end) { int len; const unsigned int c = rb_enc_codepoint_len(ptr, end, &len, enc); + if (c != 0 && !is_ctrl_char(c)) break; ptr += len; count ++; @@ -207,8 +231,11 @@ rb_is_control_character_at_p(VALUE self, VALUE str, VALUE offset) { Check_Type(str, T_STRING); Check_Type(offset, T_FIXNUM); - long len = 1; - char *ptr = rb_str_subpos(str, FIX2LONG(offset), &len); + long len; + char *ptr; + + len = 1; + ptr = rb_str_subpos(str, FIX2LONG(offset), &len); if (!ptr || !len) return Qnil; if (len > 1) return Qfalse; // There be a multi-byte character here From 6dea80890807c38126a91d0163bec1acd3e458a5 Mon Sep 17 00:00:00 2001 From: kputnam Date: Mon, 22 Jul 2019 00:52:49 -0500 Subject: [PATCH 25/38] Fix build error --- lib/ruby/module.rb | 11 ++++++----- lib/stupidedi/reader/pointer.rb | 3 ++- spec/support/fixtures.rb | 9 +++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/ruby/module.rb b/lib/ruby/module.rb index 25d84833a..c3a74282a 100644 --- a/lib/ruby/module.rb +++ b/lib/ruby/module.rb @@ -111,7 +111,8 @@ def #{name}(*args, &block) optionals = [] triples = method.parameters.map do |k, n| - n ||= new_names.succ! # Get a fresh name if needed + n ||= (new_names.succ!).dup # Get a fresh name if needed + case k when :req then [n, n, n] # def foo(x) when :keyreq then [n, n, "#{n}:#{n}"] # def foo(x:) @@ -177,12 +178,12 @@ def emit_call(receiver, name, arg_names, arguments, optionals, state) end def emit_args(arg_names, arguments, optionals, state) - arg_names.inject([]) do |args, name| + arg_names.inject([]) do |xs, name| if k = optionals.index(name) - args << arguments[name] if state[k].zero? + xs << arguments[name] if state[k].zero? else - args << arguments[name] - end; args + xs << arguments[name] + end; xs end.join(", ") end end diff --git a/lib/stupidedi/reader/pointer.rb b/lib/stupidedi/reader/pointer.rb index db0c9faed..b40e74433 100644 --- a/lib/stupidedi/reader/pointer.rb +++ b/lib/stupidedi/reader/pointer.rb @@ -353,7 +353,8 @@ class StringPtr < Pointer # @group Conversion Methods ######################################################################### - def_delegators :reify, :to_sym, :intern, :to_i, :to_d + def_delegators :reify, :to_sym, :intern, :to_i, type: String + def_delegators :reify, :to_d # This is called implicitly when we are used in String interpolation, # eg `"abc #{pointer} xyz"` or `"abc %s xyz" % pointer`. diff --git a/spec/support/fixtures.rb b/spec/support/fixtures.rb index 455576eae..02ada1d78 100644 --- a/spec/support/fixtures.rb +++ b/spec/support/fixtures.rb @@ -3,9 +3,6 @@ Fixtures = Class.new do - Position = Struct.new(:name, :line, :column, :offset) - Position.include(Stupidedi::Reader::Position) - def versions { "006020" => "SixtyTwenty", "005010" => "FiftyTen", @@ -45,6 +42,10 @@ def read(path) File.open(File.join(@root, path), "rb", &:read) end + def position + @position ||= Struct.new(:name, :line, :column, :offset).include(Stupidedi::Reader::Position) + end + # @return [Stupidedi::Parser::StateMachine, Stupidedi::Reader::Result] def parse(path, config = nil) if path.is_a?(String) @@ -55,7 +56,7 @@ def parse(path, config = nil) _, config, _ = mkconfig(*parts(path)) end - tokenizer = Stupidedi::Reader.build(@root.join(path), Position) + tokenizer = Stupidedi::Reader.build(@root.join(path), position: position) Stupidedi::Parser.build(config).read(tokenizer) end From 99bfef97a565bf09292970172db93983672be3f7 Mon Sep 17 00:00:00 2001 From: kputnam Date: Tue, 30 Jul 2019 16:10:34 -0500 Subject: [PATCH 26/38] Commit summary forthcoming --- .gitignore | 5 +- .simplecov | 5 +- .travis.yml | 66 +- .yardopts | 14 +- Gemfile | 34 +- Gemfile.lock | 59 -- LICENSE => LICENSE.md | 0 Rakefile | 42 +- bin/edi-ed | 5 - bin/edi-obfuscate | 6 +- bin/edi-pp | 3 +- build/doc/lib/meta-plugin/lib/yard-meta.rb | 4 +- build/gemspec/stupidedi-core.gemspec | 48 ++ build/gemspec/stupidedi-defs.gemspec | 37 + build/gemspec/stupidedi-exts.gemspec | 42 ++ build/gemspec/stupidedi.gemspec | 32 + build/tasks/clean.rake | 10 + build/tasks/ext.rake | 24 + build/tasks/gem.rake | 38 + build/tasks/irb.rake | 4 + build/tasks/release.task | 10 + build/tasks/rspec.rake | 13 + build/tasks/yard.rake | 19 + doc/Navigating.md | 48 +- doc/Parsing.md | 0 doc/Tokenizing.md | 0 doc/Validating.md | 0 doc/design/Parser.md | 0 doc/design/Reader.md | 0 examples/generate.rb | 2 +- examples/tokenizer.rb | 80 ++- ext/c/stupidedi/reader/codepoints.h | 29 + .../stupidedi/reader}/extconf.rb | 5 +- ext/c/stupidedi/reader/reader_ext.c | 660 ++++++++++++++++++ ext/stupidedi/reader/native_ext/native_ext.c | 258 ------- lib/stupidedi.rb | 28 +- lib/stupidedi/editor.rb | 38 +- lib/stupidedi/exceptions.rb | 13 +- lib/stupidedi/exceptions/tokenize_error.rb | 7 - lib/stupidedi/interchanges.rb | 2 +- lib/stupidedi/interchanges/00200.rb | 3 +- .../interchanges/00200/element_defs.rb | 2 +- lib/stupidedi/interchanges/00300.rb | 3 +- .../interchanges/00300/element_defs.rb | 2 +- lib/stupidedi/interchanges/00400.rb | 3 +- .../interchanges/00400/element_defs.rb | 2 +- lib/stupidedi/interchanges/00401.rb | 3 +- .../interchanges/00401/element_defs.rb | 2 +- lib/stupidedi/interchanges/00501.rb | 3 +- .../interchanges/00501/element_defs.rb | 2 +- lib/stupidedi/interchanges/common.rb | 8 + .../interchanges/common/element_types.rb | 13 + .../common/element_types/separator.rb | 81 +++ .../common/element_types/special_an.rb | 50 ++ lib/stupidedi/interchanges/element_types.rb | 11 - .../element_types/separator_val.rb | 79 --- .../interchanges/element_types/special_val.rb | 48 -- lib/stupidedi/parser/builder_dsl.rb | 2 +- lib/stupidedi/parser/constraint_table.rb | 4 +- lib/stupidedi/parser/generation.rb | 23 +- lib/stupidedi/parser/state_machine.rb | 2 + lib/stupidedi/parser/tokenization.rb | 40 +- lib/stupidedi/position.rb | 130 ++++ .../{reader => }/position/no_position.rb | 4 +- .../{reader => }/position/offset_position.rb | 5 +- .../position/stacktrace_position.rb | 5 +- lib/stupidedi/reader.rb | 359 ++++++---- lib/stupidedi/reader/input.rb | 39 +- lib/stupidedi/reader/pointer.rb | 512 +------------- lib/stupidedi/reader/position.rb | 129 ---- lib/stupidedi/reader/segment_dict.rb | 2 + lib/stupidedi/reader/separators.rb | 3 +- lib/stupidedi/reader/string_ptr.rb | 470 +++++++++++++ lib/stupidedi/reader/tokenizer.rb | 301 ++------ .../reader/tokenizer/element_tok_switch.rb | 106 +++ lib/stupidedi/reader/tokenizer/result.rb | 78 +++ lib/{ => stupidedi}/ruby/array.rb | 0 lib/{ => stupidedi}/ruby/blank.rb | 2 +- lib/{ => stupidedi}/ruby/exception.rb | 0 lib/{ => stupidedi}/ruby/hash.rb | 0 lib/{ => stupidedi}/ruby/module.rb | 23 +- lib/{ => stupidedi}/ruby/object.rb | 0 lib/{ => stupidedi}/ruby/regexp.rb | 0 lib/{ => stupidedi}/ruby/string.rb | 0 lib/{ => stupidedi}/ruby/to_d.rb | 2 +- lib/{ => stupidedi}/ruby/to_date.rb | 0 lib/{ => stupidedi}/ruby/to_time.rb | 0 lib/{ => stupidedi}/ruby/try.rb | 0 lib/stupidedi/schema/component_element_use.rb | 2 + lib/stupidedi/schema/composite_element_def.rb | 2 + lib/stupidedi/schema/composite_element_use.rb | 2 + lib/stupidedi/schema/functional_group_def.rb | 2 + lib/stupidedi/schema/interchange_def.rb | 2 + lib/stupidedi/schema/loop_def.rb | 2 + lib/stupidedi/schema/segment_def.rb | 2 + lib/stupidedi/schema/segment_use.rb | 2 + lib/stupidedi/schema/simple_element_use.rb | 2 + lib/stupidedi/schema/table_def.rb | 2 + lib/stupidedi/schema/transaction_set_def.rb | 2 + lib/stupidedi/tokens.rb | 13 + .../tokens/component_element_tok.rb | 4 +- .../tokens/composite_element_tok.rb | 15 +- .../{reader => }/tokens/ignored_tok.rb | 4 +- .../tokens/repeated_element_tok.rb | 4 +- .../{reader => }/tokens/segment_tok.rb | 23 +- .../{reader => }/tokens/simple_element_tok.rb | 4 +- lib/stupidedi/transaction_sets.rb | 3 +- lib/stupidedi/transaction_sets/builder.rb | 4 +- lib/stupidedi/values/abstract_val.rb | 4 +- lib/stupidedi/values/composite_element_val.rb | 4 +- lib/stupidedi/values/functional_group_val.rb | 4 + lib/stupidedi/values/interchange_val.rb | 2 + lib/stupidedi/values/invalid_envelope_val.rb | 2 + lib/stupidedi/values/invalid_segment_val.rb | 4 + lib/stupidedi/values/loop_val.rb | 4 + lib/stupidedi/values/table_val.rb | 4 + lib/stupidedi/values/transaction_set_val.rb | 2 + lib/stupidedi/values/transmission_val.rb | 2 + lib/stupidedi/writer/default.rb | 6 +- spec/coverage_spec.rb | 172 +++++ spec/fixtures/tokenizer/each_composite.edi | 53 ++ spec/fixtures/tokenizer/each_isa.edi | 7 + spec/lib/ruby/count_spec.rb | 66 -- .../element_types/separator_spec.rb | 6 +- spec/lib/stupidedi/interchanges_spec.rb | 6 +- spec/lib/stupidedi/reader/input_spec.rb | 2 + spec/lib/stupidedi/reader/native_ext_spec.rb | 202 ++++++ spec/lib/stupidedi/reader/pointer_spec.rb | 286 ++++++++ spec/lib/stupidedi/reader/string_ptr_spec.rb | 345 +++++++++ spec/lib/stupidedi/reader/tokenizer_spec.rb | 205 ++++++ spec/lib/stupidedi/reader_spec.rb | 164 ----- spec/lib/{ => stupidedi}/ruby/array_spec.rb | 65 ++ spec/lib/{ => stupidedi}/ruby/blank_spec.rb | 0 .../{ => stupidedi}/ruby/exception_spec.rb | 0 spec/lib/stupidedi/ruby/module_spec.rb | 122 ++++ spec/lib/{ => stupidedi}/ruby/object_spec.rb | 0 spec/lib/{ => stupidedi}/ruby/string_spec.rb | 0 spec/lib/{ => stupidedi}/ruby/to_d_spec.rb | 0 spec/lib/{ => stupidedi}/ruby/to_time_spec.rb | 0 spec/lib/{ => stupidedi}/ruby/try_spec.rb | 0 .../X091A1-HP835_spec.rb | 0 .../{ => implementations}/X212-HR276_spec.rb | 0 .../{ => implementations}/X221-HP835_spec.rb | 0 .../X222A1-HC837_spec.rb | 0 .../versions/common/element_types/an_spec.rb | 2 +- .../versions/common/element_types/dt_spec.rb | 2 +- .../versions/common/element_types/id_spec.rb | 2 +- .../versions/common/element_types/nn_spec.rb | 2 +- .../versions/common/element_types/r_spec.rb | 2 +- .../versions/common/element_types/tm_spec.rb | 2 +- .../stupidedi/versions/element_defs_spec.rb | 6 +- ..._def_spec.rb => functional_groups_spec.rb} | 0 spec/spec_helper.rb | 28 +- spec/support/coverage.rb | 4 +- spec/support/fixtures.rb | 12 +- spec/support/matchers/reader_ext_matchers.rb | 96 +++ stupidedi.gemspec | 29 - 157 files changed, 4188 insertions(+), 2080 deletions(-) delete mode 100644 Gemfile.lock rename LICENSE => LICENSE.md (100%) create mode 100644 build/gemspec/stupidedi-core.gemspec create mode 100644 build/gemspec/stupidedi-defs.gemspec create mode 100644 build/gemspec/stupidedi-exts.gemspec create mode 100644 build/gemspec/stupidedi.gemspec create mode 100644 build/tasks/clean.rake create mode 100644 build/tasks/ext.rake create mode 100644 build/tasks/gem.rake create mode 100644 build/tasks/irb.rake create mode 100644 build/tasks/release.task create mode 100644 build/tasks/rspec.rake create mode 100644 build/tasks/yard.rake delete mode 100644 doc/Parsing.md delete mode 100644 doc/Tokenizing.md delete mode 100644 doc/Validating.md delete mode 100644 doc/design/Parser.md delete mode 100644 doc/design/Reader.md create mode 100644 ext/c/stupidedi/reader/codepoints.h rename ext/{stupidedi/reader/native_ext => c/stupidedi/reader}/extconf.rb (65%) create mode 100644 ext/c/stupidedi/reader/reader_ext.c delete mode 100644 ext/stupidedi/reader/native_ext/native_ext.c delete mode 100644 lib/stupidedi/exceptions/tokenize_error.rb create mode 100644 lib/stupidedi/interchanges/common.rb create mode 100644 lib/stupidedi/interchanges/common/element_types.rb create mode 100644 lib/stupidedi/interchanges/common/element_types/separator.rb create mode 100644 lib/stupidedi/interchanges/common/element_types/special_an.rb delete mode 100644 lib/stupidedi/interchanges/element_types.rb delete mode 100644 lib/stupidedi/interchanges/element_types/separator_val.rb delete mode 100644 lib/stupidedi/interchanges/element_types/special_val.rb create mode 100644 lib/stupidedi/position.rb rename lib/stupidedi/{reader => }/position/no_position.rb (97%) rename lib/stupidedi/{reader => }/position/offset_position.rb (96%) rename lib/stupidedi/{reader => }/position/stacktrace_position.rb (97%) delete mode 100644 lib/stupidedi/reader/position.rb create mode 100644 lib/stupidedi/reader/string_ptr.rb create mode 100644 lib/stupidedi/reader/tokenizer/element_tok_switch.rb create mode 100644 lib/stupidedi/reader/tokenizer/result.rb rename lib/{ => stupidedi}/ruby/array.rb (100%) rename lib/{ => stupidedi}/ruby/blank.rb (97%) rename lib/{ => stupidedi}/ruby/exception.rb (100%) rename lib/{ => stupidedi}/ruby/hash.rb (100%) rename lib/{ => stupidedi}/ruby/module.rb (88%) rename lib/{ => stupidedi}/ruby/object.rb (100%) rename lib/{ => stupidedi}/ruby/regexp.rb (100%) rename lib/{ => stupidedi}/ruby/string.rb (100%) rename lib/{ => stupidedi}/ruby/to_d.rb (98%) rename lib/{ => stupidedi}/ruby/to_date.rb (100%) rename lib/{ => stupidedi}/ruby/to_time.rb (100%) rename lib/{ => stupidedi}/ruby/try.rb (100%) create mode 100644 lib/stupidedi/tokens.rb rename lib/stupidedi/{reader => }/tokens/component_element_tok.rb (98%) rename lib/stupidedi/{reader => }/tokens/composite_element_tok.rb (87%) rename lib/stupidedi/{reader => }/tokens/ignored_tok.rb (98%) rename lib/stupidedi/{reader => }/tokens/repeated_element_tok.rb (98%) rename lib/stupidedi/{reader => }/tokens/segment_tok.rb (74%) rename lib/stupidedi/{reader => }/tokens/simple_element_tok.rb (98%) create mode 100644 spec/coverage_spec.rb create mode 100644 spec/fixtures/tokenizer/each_composite.edi create mode 100644 spec/fixtures/tokenizer/each_isa.edi delete mode 100644 spec/lib/ruby/count_spec.rb rename spec/lib/stupidedi/interchanges/{ => common}/element_types/separator_spec.rb (93%) create mode 100644 spec/lib/stupidedi/reader/input_spec.rb create mode 100644 spec/lib/stupidedi/reader/native_ext_spec.rb create mode 100644 spec/lib/stupidedi/reader/pointer_spec.rb create mode 100644 spec/lib/stupidedi/reader/string_ptr_spec.rb create mode 100644 spec/lib/stupidedi/reader/tokenizer_spec.rb rename spec/lib/{ => stupidedi}/ruby/array_spec.rb (86%) rename spec/lib/{ => stupidedi}/ruby/blank_spec.rb (100%) rename spec/lib/{ => stupidedi}/ruby/exception_spec.rb (100%) create mode 100644 spec/lib/stupidedi/ruby/module_spec.rb rename spec/lib/{ => stupidedi}/ruby/object_spec.rb (100%) rename spec/lib/{ => stupidedi}/ruby/string_spec.rb (100%) rename spec/lib/{ => stupidedi}/ruby/to_d_spec.rb (100%) rename spec/lib/{ => stupidedi}/ruby/to_time_spec.rb (100%) rename spec/lib/{ => stupidedi}/ruby/try_spec.rb (100%) rename spec/lib/stupidedi/transaction_sets/004010/{ => implementations}/X091A1-HP835_spec.rb (100%) rename spec/lib/stupidedi/transaction_sets/005010/{ => implementations}/X212-HR276_spec.rb (100%) rename spec/lib/stupidedi/transaction_sets/005010/{ => implementations}/X221-HP835_spec.rb (100%) rename spec/lib/stupidedi/transaction_sets/005010/{ => implementations}/X222A1-HC837_spec.rb (100%) rename spec/lib/stupidedi/versions/{functional_group_def_spec.rb => functional_groups_spec.rb} (100%) create mode 100644 spec/support/matchers/reader_ext_matchers.rb delete mode 100644 stupidedi.gemspec diff --git a/.gitignore b/.gitignore index bf6f5bcdf..22636717f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ +Gemfile.lock build/generated -.bundle -.ruby-version lib/**/*.bundle ext/**/Makefile ext/**/*.o -tmp +ext/**/*.class diff --git a/.simplecov b/.simplecov index 8456678a5..5c83d9307 100644 --- a/.simplecov +++ b/.simplecov @@ -11,13 +11,15 @@ SimpleCov.start do add_filter %r{/transaction_sets/[0-9]+/[A-Z0-9-]+\.rb} add_filter %r{/stupidedi/editor} - add_group "Refinements", "lib/ruby" + add_group "Refinements", "lib/stupidedi/ruby" add_group "Config", "lib/stupidedi/config" add_group "Editor", "lib/stupidedi/editor" add_group "Exceptions", "lib/stupidedi/exceptions" add_group "Interchanges", "lib/stupidedi/interchanges" add_group "Parser", "lib/stupidedi/parser" add_group "Reader", "lib/stupidedi/reader" + add_group "Tokens", "lib/stupidedi/tokens" + add_group "Position", "lib/stupidedi/position" add_group "Schema", "lib/stupidedi/schema" add_group "TransactionSets", "lib/stupidedi/transaction_sets" add_group "Values", "lib/stupidedi/values" @@ -131,7 +133,6 @@ class SimpleCov::Formatter::CustomHtmlFormatter < SimpleCov::Formatter::InlineHT .ui-icon { width:0px !important; height:0px !important; } th { font-size:.7rem; overflow:hidden; white-space:normal !important; } EOF - css else super(path) end diff --git a/.travis.yml b/.travis.yml index 4956113fd..0e8dbcb86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: ruby +cache: bundler + before_install: + # https://docs.travis-ci.com/user/languages/ruby/#bundler-20 - if [[ $(ruby -v) < "ruby 2.3" ]]; then gem install bundler -v "< 2"; @@ -9,31 +12,42 @@ before_install: bundle update --bundler; fi -before_script: bundle exec rake compile +os: + - osx + - linux + - windows rvm: -# 2.0.0 -- 2.1.0 -- 2.1.1 -# 2.1.2 -# 2.1.3 -# 2.1.4 -- 2.1.5 -- 2.1.6 -- 2.1.7 -- 2.1.8 -- 2.2.0 -- 2.2.1 -- 2.2.2 -- 2.2.3 -- 2.2.4 -- 2.3.0 -- 2.3.1 -- 2.3.2 -- 2.3.3 -- 2.4.1 -- 2.4.3 -- 2.5.0 -- 2.5.1 -- 2.5.3 -- 2.6.3 + # 2.0.0 + # 2.1.0 + # 2.1.1 + # 2.1.2 + # 2.1.3 + # 2.1.4 + # 2.1.5 + # 2.1.6 + # 2.1.7 + - 2.1.8 + # 2.2.0 + # 2.2.1 + # 2.2.2 + # 2.2.3 + - 2.2.4 + # 2.3.0 + # 2.3.1 + # 2.3.2 + - 2.3.3 + # 2.4.1 + - 2.4.3 + # 2.5.0 + # 2.5.1 + - 2.5.3 + - 2.6.3 + +# NOTE: Travis CI is limited to 200 entries in the build matrix +# matrix: +# exclude: +# - os: linux +# rvm: 2.1.8 +# - os: windows +# rvm: 2.6.3 diff --git a/.yardopts b/.yardopts index 013088f03..aa21607b4 100644 --- a/.yardopts +++ b/.yardopts @@ -11,9 +11,15 @@ --load ./build/doc/lib/meta-plugin/lib/yard-meta.rb --asset doc/images:images --files doc/**/*.md ---exclude 'element_defs\.rb' ---exclude 'segment_defs\.rb' ---exclude 'segment_defs\/' + +--exclude '/editor' +--exclude '/guides.rb' +--exclude '/contrib.rb' +--exclude '/versions/functional_groups.rb' + +--exclude '/versions/[0-9]' +--exclude '/interchanges/[0-9]' +--exclude '/transaction_sets/[0-9]' lib/**/*.rb -spec/examples/**/*_spec.rb +spec/**/*_spec.rb diff --git a/Gemfile b/Gemfile index 1e98bd170..34170ff1a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,33 +1,11 @@ source "http://rubygems.org" -gem "cantor", "~> 1.2.1" +gemspec(path: "build/gemspec", name: "stupidedi") +gemspec(path: "build/gemspec", name: "stupidedi-core") +gemspec(path: "build/gemspec", name: "stupidedi-defs") +gemspec(path: "build/gemspec", name: "stupidedi-exts") group :development do - gem "rake" - gem "rake-compiler" - gem "term-ansicolor" - - gem "rspec", "3.8.0" - gem "rspec-collection_matchers" - - gem "yard","~> 0.9.12" -# gem "redcarpet","~> 3.4.0", :platforms => [:mri] - - # https://github.com/colszowka/simplecov#ruby-version-compatibility - gem "simplecov", :platforms => [:ruby_24, :ruby_25] - gem "simplecov-inline-html", :platforms => [:ruby_24, :ruby_25] - - gem "stackprof" - # gem "fasterer" - gem "benchmark-ips" - gem "memory_profiler", :platform => [:ruby_23, :ruby_24, :ruby_25] - # gem "allocation_stats" - # gem "heapy" - # gem "derailed_benchmarks" - - # We're using a patched version installed in yard/ until the - # maintainer improves the plugin. The patch has been submitted - # to the author. - # - # gem "yard-rspec", "~> 0.1" + gem "yard", "= 0.9.16" + gem "rdiscount", "~> 2.2" end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 3c724bb57..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,59 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - benchmark-ips (2.7.2) - cantor (1.2.1) - diff-lcs (1.3) - docile (1.3.1) - json (2.1.0) - memory_profiler (0.9.13) - rake (12.3.2) - rake-compiler (1.0.7) - rake - rspec (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-collection_matchers (1.1.3) - rspec-expectations (>= 2.99.0.beta1) - rspec-core (3.8.0) - rspec-support (~> 3.8.0) - rspec-expectations (3.8.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-mocks (3.8.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-support (3.8.0) - simplecov (0.16.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) - simplecov-inline-html (0.0.1) - stackprof (0.2.12) - term-ansicolor (1.7.1) - tins (~> 1.0) - tins (1.20.2) - yard (0.9.16) - -PLATFORMS - java - ruby - -DEPENDENCIES - benchmark-ips - cantor (~> 1.2.1) - memory_profiler - rake - rake-compiler - rspec (= 3.8.0) - rspec-collection_matchers - simplecov - simplecov-inline-html - stackprof - term-ansicolor - yard (~> 0.9.12) - -BUNDLED WITH - 1.17.3 diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/Rakefile b/Rakefile index bbabb38ac..fe2cfd62c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,41 +1,5 @@ -require "bundler" -Bundler::GemHelper.install_tasks +Bundler.setup(:default, :development) -task :default => :spec +Dir["build/tasks/*.rake"].sort.each{|r| load r } -require "rspec/core/rake_task" -RSpec::Core::RakeTask.new do |t| - t.verbose = false - t.rspec_opts = %w(-w -rspec_helper) - - if ENV.include?("CI") or ENV.include?("TRAVIS") - t.rspec_opts += %w(--format progress) - else - t.rspec_opts += %w(--format documentation) - end -end - -# Note options are loaded from .yardopts -require "yard" -require "pathname" -abspath = Pathname.new(File.dirname(__FILE__)).expand_path -relpath = abspath.relative_path_from(Pathname.pwd) -YARD::Rake::YardocTask.new(:yard => :clobber_yard) -task :clobber_yard do - - rm_rf "#{relpath}/build/generated/doc" - mkdir_p "#{relpath}/build/generated/doc/images" -end - -task :console do - exec(*%w(irb -I lib -r stupidedi)) -end - -if RUBY_PLATFORM !~ /java/ - require "rake/extensiontask" - gemspec = Gem::Specification.load("stupidedi.gemspec") - - Rake::ExtensionTask.new("stupidedi/reader/native_ext") do |x| - x.lib_dir = "lib" - end -end +task(default: "spec") diff --git a/bin/edi-ed b/bin/edi-ed index ee1938cd6..959a4729a 100755 --- a/bin/edi-ed +++ b/bin/edi-ed @@ -1,7 +1,4 @@ #!/usr/bin/env ruby -require "pathname" -$:.unshift(File.expand_path("../../lib", Pathname.new(__FILE__).realpath)) - require "stupidedi" require "pp" @@ -12,10 +9,8 @@ config = Stupidedi::Config.hipaa config.editor.tap do |c| c.register(Stupidedi::Interchanges::FourOhOne::InterchangeDef) { Stupidedi::Editor::FiveOhOneEd } c.register(Stupidedi::Interchanges::FiveOhOne::InterchangeDef) { Stupidedi::Editor::FiveOhOneEd } - c.register(Stupidedi::Versions::FortyTen::FunctionalGroupDef) { Stupidedi::Editor::FiftyTenEd } c.register(Stupidedi::Versions::FiftyTen::FunctionalGroupDef) { Stupidedi::Editor::FiftyTenEd } - c.register(Stupidedi::TransactionSets::FiftyTen::Implementations::X222A1::HC837) { Stupidedi::Editor::X222 } end diff --git a/bin/edi-obfuscate b/bin/edi-obfuscate index 73f632516..e1496970a 100755 --- a/bin/edi-obfuscate +++ b/bin/edi-obfuscate @@ -1,4 +1,6 @@ #!/usr/bin/env ruby +require "stupidedi" +require "pp" # # This is a utility that will strip all free-form text (elements of type AN), # dates, and times from an X12 file. Strings are replaced with underscores, @@ -10,10 +12,6 @@ # Otherwise, unrecognized segments or elements will cause an exception since it # is not possible to know if these should be obfuscated or not (they are invalid). # -require File.expand_path("../../lib/stupidedi", __FILE__) - -require "stupidedi" -require "pp" # Short-lived processes should win some peformance gains here GC.disable diff --git a/bin/edi-pp b/bin/edi-pp index 3c52719cd..8c13e171a 100755 --- a/bin/edi-pp +++ b/bin/edi-pp @@ -1,9 +1,10 @@ #!/usr/bin/env ruby -require File.expand_path("../../lib/stupidedi", __FILE__) +require "stupidedi" require "pp" # This will be auto-enabled when $stdout.tty?, but -C forces color output require "term/ansicolor" if ARGV.delete("-C") + if idx = ARGV.index("--format") ARGV.delete("--format") format = ARGV.delete_at(idx) diff --git a/build/doc/lib/meta-plugin/lib/yard-meta.rb b/build/doc/lib/meta-plugin/lib/yard-meta.rb index 201af76d0..ac937bc81 100644 --- a/build/doc/lib/meta-plugin/lib/yard-meta.rb +++ b/build/doc/lib/meta-plugin/lib/yard-meta.rb @@ -1,2 +1,2 @@ -require File.join(File.dirname(__FILE__), "yard-meta", "handler") if RUBY19 -require File.join(File.dirname(__FILE__), "yard-meta", "legacy") unless RUBY19 +require_relative "yard-meta/handler" +#equire_relative "yard-meta/legacy" diff --git a/build/gemspec/stupidedi-core.gemspec b/build/gemspec/stupidedi-core.gemspec new file mode 100644 index 000000000..d2266da1a --- /dev/null +++ b/build/gemspec/stupidedi-core.gemspec @@ -0,0 +1,48 @@ +require_relative "../../lib/stupidedi/version" + +Gem::Specification.new do |s| + # Required attributes + s.name = "stupidedi-core" + s.summary = "Parse, generate, validate ASC X12 EDI" + s.authors = ["Kyle Putnam", "Isi Robayna"] + s.version = Stupidedi::VERSION + s.files = Dir[*%w(*.md lib/**/*.rb)] + s.files += [__FILE__] + s.files.reject!{|x| x =~ %r(lib/stupidedi/versions/\d) } + s.files.reject!{|x| x =~ %r(lib/stupidedi/interchanges/\d) } + s.files.reject!{|x| x =~ %r(lib/stupidedi/transaction_sets/\d) } + s.files.reject!{|x| x =~ %r(lib/stupidedi/functional_groups/\d) } + + # Recommended attributes + s.platform = Gem::Platform::RUBY + s.description = "Ruby API for parsing and generating ASC X12 EDI transactions" + s.email = "irobayna@gmail.com" + s.homepage = "https://github.com/irobayna/stupidedi" + s.license = "BSD-3-Clause" + s.metadata = Hash[ + "bug_tracker_uri" => "https://github.com/irobayna/stupidedi/issues", + "changelog_uri" => "https://github.com/irobayna/stupidedi/blob/v#{Stupidedi::VERSION}/CHANGELOG.md", + "homepage_uri" => "https://github.com/irobayna/stupidedi", + "source_code_uri" => "https://github.com/irobayna/stupidedi", + "documentation_uri" => "http://rubydoc.info/gems/stupidedi-core/#{Stupidedi::VERSION}"] + + s.add_runtime_dependency "cantor", "~> 1.2" + s.add_runtime_dependency "term-ansicolor", "~> 1.3" + + # Optional attributes + s.executables = %w(edi-pp edi-ed edi-obfuscate) + s.required_ruby_version = ">= 2.0.0" + s.required_rubygems_version = ">= 2.5.0" + s.requirements # << "" + + # Development dependencies + s.add_development_dependency "irb"# "~> 1.0" + s.add_development_dependency "rake", "~> 12.3" + s.add_development_dependency "rspec", "~> 3.8" + s.add_development_dependency "rspec-collection_matchers"# " " + s.add_development_dependency "simplecov"# "" + s.add_development_dependency "simplecov-inline-html"# "" # requires ruby 2.4+ + s.add_development_dependency "stackprof", "~> 0.2" + s.add_development_dependency "benchmark-ips"# "" + s.add_development_dependency "memory_profiler"# "" # requires ruby 2.3+ +end diff --git a/build/gemspec/stupidedi-defs.gemspec b/build/gemspec/stupidedi-defs.gemspec new file mode 100644 index 000000000..e94b11ebc --- /dev/null +++ b/build/gemspec/stupidedi-defs.gemspec @@ -0,0 +1,37 @@ +require_relative "../../lib/stupidedi/version" + +Gem::Specification.new do |s| + # Required attributes + s.name = "stupidedi-defs" + s.summary = "Parse, generate, validate ASC X12 EDI" + s.authors = ["Kyle Putnam", "Isi Robayna"] + s.version = Stupidedi::VERSION + s.files += Dir[*%w(*.md lib/**/*.rb)] + s.files += [__FILE__] + s.files.select! do |x| + x =~ %r(lib/stupidedi/versions/\d) or + x =~ %r(lib/stupidedi/interchanges/\d) or + x =~ %r(lib/stupidedi/transaction_sets/\d) or + x =~ %r(lib/stupidedi/functional_groups/\d) + end + + # Recommended attributes + s.platform = Gem::Platform::RUBY + s.description = "Ruby API for parsing and generating ASC X12 EDI transactions" + s.email = "irobayna@gmail.com" + s.homepage = "https://github.com/irobayna/stupidedi" + s.license = "BSD-3-Clause" + s.metadata = Hash[ + "bug_tracker_uri" => "https://github.com/irobayna/stupidedi/issues", + "changelog_uri" => "https://github.com/irobayna/stupidedi/blob/v#{Stupidedi::VERSION}/CHANGELOG.md", + "homepage_uri" => "https://github.com/irobayna/stupidedi", + "source_code_uri" => "https://github.com/irobayna/stupidedi", + "documentation_uri" => "http://rubydoc.info/gems/stupidedi-defs/#{Stupidedi::VERSION}"] + + # Optional attributes + s.add_runtime_dependency "stupidedi-core", "= #{Stupidedi::VERSION}" + + s.required_ruby_version = ">= 2.0.0" + s.required_rubygems_version = ">= 2.5.0" + s.requirements # << "" +end diff --git a/build/gemspec/stupidedi-exts.gemspec b/build/gemspec/stupidedi-exts.gemspec new file mode 100644 index 000000000..02465010d --- /dev/null +++ b/build/gemspec/stupidedi-exts.gemspec @@ -0,0 +1,42 @@ +require_relative "../../lib/stupidedi/version" + +Gem::Specification.new do |s| + # Required attributes + s.name = "stupidedi-exts" + s.summary = "Native extensions to accelerate stupidedi" + s.authors = ["Kyle Putnam", "Isi Robayna"] + s.version = Stupidedi::VERSION + s.files = [__FILE__] + + # Recommended attributes + if Gem.win_platform? + s.platform = Gem::Platform::CURRENT + s.files += Dir[*%w(*.md lib/**/*.dll)] + else + s.platform = Gem::Platform::RUBY + s.files += Dir[*%w(*.md ext/**/*.{c,h,rb})] + s.extensions += Dir[*%w(ext/c/**/extconf.rb)] + end + + s.description = "Ruby API for parsing and generating ASC X12 EDI transactions" + s.email = "irobayna@gmail.com" + s.homepage = "https://github.com/irobayna/stupidedi" + s.license = "BSD-3-Clause" + s.metadata = Hash[ + "bug_tracker_uri" => "https://github.com/irobayna/stupidedi/issues", + "changelog_uri" => "https://github.com/irobayna/stupidedi/blob/v#{Stupidedi::VERSION}/CHANGELOG.md", + "homepage_uri" => "https://github.com/irobayna/stupidedi", + "source_code_uri" => "https://github.com/irobayna/stupidedi", + "documentation_uri" => "http://rubydoc.info/gems/stupidedi-exts/#{Stupidedi::VERSION}"] + + s.add_runtime_dependency "stupidedi-core", "= #{Stupidedi::VERSION}" + + # Optional attributes + s.required_ruby_version = ">= 2.0.0" + s.required_rubygems_version = ">= 2.5.0" + s.requirements # << "" + + # Development dependencies + s.add_development_dependency "rake-compiler", "~> 1.0" + s.add_development_dependency "rake-compiler-dock", "~> 0.7" +end diff --git a/build/gemspec/stupidedi.gemspec b/build/gemspec/stupidedi.gemspec new file mode 100644 index 000000000..a9181a854 --- /dev/null +++ b/build/gemspec/stupidedi.gemspec @@ -0,0 +1,32 @@ +require_relative "../../lib/stupidedi/version" + +Gem::Specification.new do |s| + # Required attributes + s.name = "stupidedi" + s.summary = "Parse, generate, validate ASC X12 EDI" + s.authors = ["Kyle Putnam", "Isi Robayna"] + s.version = Stupidedi::VERSION + s.files = Dir[*%w(*.md)] + s.files += [__FILE__] + + # Recommended attributes + s.platform = Gem::Platform::RUBY + s.description = "Ruby API for parsing and generating ASC X12 EDI transactions" + s.email = "irobayna@gmail.com" + s.homepage = "https://github.com/irobayna/stupidedi" + s.license = "BSD-3-Clause" + s.metadata = Hash[ + "bug_tracker_uri" => "https://github.com/irobayna/stupidedi/issues", + "changelog_uri" => "https://github.com/irobayna/stupidedi/blob/v#{Stupidedi::VERSION}/CHANGELOG.md", + "homepage_uri" => "https://github.com/irobayna/stupidedi", + "source_code_uri" => "https://github.com/irobayna/stupidedi", + "documentation_uri" => "http://rubydoc.info/gems/stupidedi-core/#{Stupidedi::VERSION}"] + + s.add_runtime_dependency "stupidedi-core", "= #{Stupidedi::VERSION}" + s.add_runtime_dependency "stupidedi-defs", "= #{Stupidedi::VERSION}" + + # Optional attributes + s.required_ruby_version = ">= 2.0.0" + s.required_rubygems_version = ">= 2.5.0" + s.requirements # << "" +end diff --git a/build/tasks/clean.rake b/build/tasks/clean.rake new file mode 100644 index 000000000..50d91b804 --- /dev/null +++ b/build/tasks/clean.rake @@ -0,0 +1,10 @@ +require "rake/clean" + +# rake clean -- remove temporary files, but leave final build artifacts +CLEAN.include("build/generated/coverage") +CLEAN.include("tmp/#{RUBY_PLATFORM}") + +# rake clobber -- remove build artifacts + everything in CLEAN +CLOBBER.include("build/generated") +CLOBBER.include("pkg") +CLOBBER.include("Gemfile.lock") diff --git a/build/tasks/ext.rake b/build/tasks/ext.rake new file mode 100644 index 000000000..d24a28e6d --- /dev/null +++ b/build/tasks/ext.rake @@ -0,0 +1,24 @@ +require "rake/extensiontask" +require "rake/extensioncompiler" + +spec_ext = Gem::Specification.load("build/gemspec/stupidedi-exts.gemspec") + +# rake compile +Rake::ExtensionTask.new("native_ext", spec_ext) do |t| + t.ext_dir = "ext/c/stupidedi/reader" # Where source is located + t.lib_dir = "lib/stupidedi/reader" # Where binaries will go + t.tmp_dir = "build/generated/ext" + + if (Rake::ExtensionCompiler.mingw_host rescue nil) + t.cross_compile = true + t.cross_platform = %w(x86-mingw32 x64-mingw32 x86-linux x86_64-linux) + t.cross_config_options << "--enable-cross-build" + + t.cross_compiling do |s| + # Modify gemspec for Windows binary + s.files -= Dir[*%w(ext/c/**/*)] + s.files += Dir[*%w(lib/**.dll)] + s.extensions = [] + end + end +end diff --git a/build/tasks/gem.rake b/build/tasks/gem.rake new file mode 100644 index 000000000..68594d44d --- /dev/null +++ b/build/tasks/gem.rake @@ -0,0 +1,38 @@ +require "rubygems/package_task" + +# rake gem +# rake package == gem +# rake repackage == clobber_package + package +# rake clobber_package +Dir["build/gemspec/*.gemspec"].each do |x| + gem_spec = Gem::Specification.load(x) + Gem::PackageTask.new(gem_spec) do |t| + t.package_dir = "build/generated/pkg" + end + + # Make gem also depend on gemspec + gem_file = File.basename(gem_spec.cache_file) + gem_path = File.join("build/generated/pkg", gem_file) + file gem_file => x +end + +# Build extension first with `rake compile` +Rake::Task["gem"].prerequisites << "compile" + +# Hide this from the rake -T, since it's an intermediate task of `rake package` +Rake::Task["gem"].clear_comments + +# The normal `clobber_package` task does `rm -rf build/generated/pkg`, rather +# than removing specific gem files and directories. Here we only remove the +# current package. +Rake::Task["clobber_package"].clear +Dir["build/gemspec/*.gemspec"].each do |x| + gem_spec = Gem::Specification.load(x) + gem_file = File.basename(gem_spec.cache_file) + gem_path = File.join("build/generated/pkg", gem_file) + + task "clobber_package" do + rm_f gem_path + rm_rf gem_path[/^(.+?)\.gem/, 1] + end +end diff --git a/build/tasks/irb.rake b/build/tasks/irb.rake new file mode 100644 index 000000000..6afd62eed --- /dev/null +++ b/build/tasks/irb.rake @@ -0,0 +1,4 @@ +# rake console +task(irb: "compile") do + exec(*%w(bundle exec irb -Ilib -rstupidedi)) +end diff --git a/build/tasks/release.task b/build/tasks/release.task new file mode 100644 index 000000000..1b6134866 --- /dev/null +++ b/build/tasks/release.task @@ -0,0 +1,10 @@ +require_relative "../../lib/stupidedi/version" + +# Want: updating/pushing a new tag will build and release gem +# https://github.com/simp/simp-core/blob/master/.travis.yml +# https://simp.readthedocs.io/en/master/contributors_guide/maintenance/Tagging_and_Releasing_Components.html + +# Tags pointing to current commit +# `git tag -l --points-at HEAD` + +# diff --git a/build/tasks/rspec.rake b/build/tasks/rspec.rake new file mode 100644 index 000000000..c2bf0d18b --- /dev/null +++ b/build/tasks/rspec.rake @@ -0,0 +1,13 @@ +require "rspec/core/rake_task" + +# rake spec +RSpec::Core::RakeTask.new(spec: "compile") do |t| + t.verbose = false + t.rspec_opts = %w(-w -rspec_helper) + + if ENV.include?("CI") or ENV.include?("TRAVIS") + t.rspec_opts += %w(--format progress) + else + t.rspec_opts += %w(--format documentation) + end +end diff --git a/build/tasks/yard.rake b/build/tasks/yard.rake new file mode 100644 index 000000000..dee15c794 --- /dev/null +++ b/build/tasks/yard.rake @@ -0,0 +1,19 @@ +require "yard" + +# rake yard +YARD::Rake::YardocTask.new(yard: %w(build/generated/doc/images)) do |t| + # NOTE: Options are loaded from .yardopts + t.after = lambda { $stderr.puts "See build/generated/doc/index.html" } +end + +task "build/generated/doc/images" do + mkdir_p "build/generated/doc/images" +end + +task "yard:clobber" do + rm_rf "build/generated/doc" + rm_rf "build/generated/.yardoc" +end + +CLOBBER.include(*%w(build/generated/doc)) +CLOBBER.include(*%w(build/generated/.yardoc)) diff --git a/doc/Navigating.md b/doc/Navigating.md index a69944b7f..a675d8ef5 100644 --- a/doc/Navigating.md +++ b/doc/Navigating.md @@ -207,8 +207,9 @@ is not reachable will cause [`#find`][14] to raise an exception. To be clear, #### Sibling Segments - -View diagram + +![](images/837P-siblings.png) Segments connected directly by a horizontal dashed black line are siblings and are reachable using [`#find`][14]. For instance, from the third `NM1`, the `N3`, @@ -249,8 +250,9 @@ there are no segments in Loop 2000A that follow the child loops. #### Nephew Segments - -View diagram + +![](images/837P-nephews.png) Segments that occur as the _first_ direct child of a sibling node are nephews. The siblings that _follow_ the first child are not directly reachable, but they @@ -282,8 +284,9 @@ problems. #### Cousin Segments - -View diagram + +![](images/837P-cousins.png) Segments that occurr as the _first_ child of a sibling of the parent node are cousins of the current segment. Similar to the restriction on nephew segments, @@ -307,8 +310,9 @@ See [Element Constraints](#Element_Constraints) for information on how to find a #### Parent Segments - -View diagram + +![](images/837P-parents.png) Internal knowledge of the underlying tree structure makes it possible to *rewind* to the first segment of a parent structure, using the [`#parent`][15] @@ -621,29 +625,29 @@ all have the same element values, but have a different meaning. ### Resolution - [1]: Stupidedi/Builder/StateMachine.html + [1]: Stupidedi/Parser/StateMachine.html [2]: Stupidedi/Values/LoopVal.html [3]: Stupidedi/Values/TableVal.html - [4]: Stupidedi/Builder/BuilderDsl.html#machine-instance_method + [4]: Stupidedi/Parser/BuilderDsl.html#machine-instance_method [5]: Stupidedi/Values/SegmentVal.html - [6]: Stupidedi/Builder/Navigation.html#segment-instance_method + [6]: Stupidedi/Parser/Navigation.html#segment-instance_method [7]: Stupidedi/Zipper/AbstractCursor.html [8]: Stupidedi/Either.html - [9]: Stupidedi/Builder/Navigation.html#next-instance_method - [10]: Stupidedi/Builder/Navigation.html#prev-instance_method - [11]: Stupidedi/Builder/Navigation.html#element-instance_method - [12]: Stupidedi/Builder/Navigation.html#first-instance_method - [13]: Stupidedi/Builder/Navigation.html#last-instance_method - [14]: Stupidedi/Builder/Navigation.html#find-instance_method - [15]: Stupidedi/Builder/Navigation.html#parent-instance_method + [9]: Stupidedi/Parser/Navigation.html#next-instance_method + [10]: Stupidedi/Parser/Navigation.html#prev-instance_method + [11]: Stupidedi/Parser/Navigation.html#element-instance_method + [12]: Stupidedi/Parser/Navigation.html#first-instance_method + [13]: Stupidedi/Parser/Navigation.html#last-instance_method + [14]: Stupidedi/Parser/Navigation.html#find-instance_method + [15]: Stupidedi/Parser/Navigation.html#parent-instance_method [16]: Stupidedi/Either.html#map-instance_method [17]: Stupidedi/Either.html#flatmap-instance_method [18]: Stupidedi/Either.html#or-instance_method [19]: Stupidedi/Either.html#tap-instance_method [20]: Object.html#try-instance_method [21]: Object.html#tap-instance_method - [22]: Stupidedi/Builder/InstructionTable.html - [23]: Stupidedi/Builder/StateMachine.html#successors-instance_method - [24]: Stupidedi/Builder/StateMachine.html#deterministic%3F-instance_method - [25]: Stupidedi/Builder/Navigation.html#iterate-instance_method + [22]: Stupidedi/Parser/InstructionTable.html + [23]: Stupidedi/Parser/StateMachine.html#successors-instance_method + [24]: Stupidedi/Parser/StateMachine.html#deterministic%3F-instance_method + [25]: Stupidedi/Parser/Navigation.html#iterate-instance_method diff --git a/doc/Parsing.md b/doc/Parsing.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/doc/Tokenizing.md b/doc/Tokenizing.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/doc/Validating.md b/doc/Validating.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/doc/design/Parser.md b/doc/design/Parser.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/doc/design/Reader.md b/doc/design/Reader.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/generate.rb b/examples/generate.rb index 8158d0fa2..69ef7c109 100755 --- a/examples/generate.rb +++ b/examples/generate.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -require File.expand_path("../../lib/stupidedi", __FILE__) +require "stupidedi" require "pp" # See https://github.com/irobayna/stupidedi/issues/160 diff --git a/examples/tokenizer.rb b/examples/tokenizer.rb index 7a217478a..c506ab10e 100755 --- a/examples/tokenizer.rb +++ b/examples/tokenizer.rb @@ -1,5 +1,5 @@ -#!/usr/bin/env ruby -require File.expand_path("../../lib/stupidedi", __FILE__) +#!/usr/bin/env ruby -Ilib +require "stupidedi" require "pp" using Stupidedi::Refinements @@ -9,37 +9,63 @@ exit end -config = Stupidedi::Config.hipaa -reader = Stupidedi::Reader.build(File.read(ARGV[0])) +def run(config, path, position) + input = Stupidedi::Reader::Input.file(path, position: position) + tokenizer = Stupidedi::Reader::Tokenizer.build(input) -while (result = reader.read_segment).defined? - segment_tok = result.fetch - reader = result.remainder + result = tokenizer.each do |segment_tok| + if segment_tok.is_a?(Stupidedi::Tokens::IgnoredTok) + pp segment_tok + next + end - pp segment_tok + pp segment_tok - # @todo: Perhaps the code below should be built-into the tokenizer? Normally - # segment_dict is updated in the parser (in {FunctionalGroupState}), but if - # users want to use the tokenizer directly, like we're doing here, then they - # need to re-implement this. - case segment_tok.id - when :GS - reader = result.remainder + case segment_tok.id + when :ISA + version = segment_tok.element_toks.at(11) + version = version.value.to_s if version - # GS08: Version / Release / Industry Identifier Code - version = segment_tok.element_toks.at(7).try(:value) - gscode = version.try(:slice, 0, 6) + if config.interchange.defined_at?(version) + # Configure separators that depend on the ISA version + envelope_def = config.interchange.at(version) + ver_separators = envelope_def.separators(segment_tok) + tokenizer.separators = tokenizer.separators.merge(ver_separators) + end + when :GS + # GS08: Version / Release / Industry Identifier Code + version = segment_tok.element_toks.at(7).try(:value).try(:to_s) + gscode = version.try(:slice, 0, 6) - # GS01: Functional Identifier Code - fgcode = segment_tok.element_toks.at(0).try(:value) + # GS01: Functional Identifier Code + fgcode = segment_tok.element_toks.at(0).try(:value) - if config.functional_group.defined_at?(gscode) - envelope_def = config.functional_group.at(gscode) - envelope_val = envelope_def.empty - segment_dict = reader.segment_dict.push(envelope_val.segment_dict) - reader = reader.copy(:segment_dict => segment_dict) + if config.functional_group.defined_at?(gscode) + envelope_def = config.functional_group.at(gscode) + envelope_val = envelope_def.empty + tokenizer.segment_dict = + tokenizer.segment_dict.push(envelope_val.segment_dict) + end + when :GE + unless tokenizer.segment_dict.empty? + tokenizer.segment_dict = tokenizer.segment_dict.pop + end end - when :GE - reader = reader.copy(:segment_dict => reader.segment_dict.pop) + end + + if result.fatal? + raise "#{result.error} at #{result.position}" + else + puts "#{result.error} at #{result.position}" end end + +position = + Stupidedi::Position::NoPosition + # Stupidedi::Position::OffsetPosition + # Struct.new(:line, :column).include(Stupidedi::Position) + # Struct.new(:name, :line, :column).include(Stupidedi::Position) + # Struct.new(:name, :line, :column, :offset).include(Stupidedi::Position) + +config = Stupidedi::Config.contrib(Stupidedi::Config.hipaa) +run(config, ARGV[0], position) diff --git a/ext/c/stupidedi/reader/codepoints.h b/ext/c/stupidedi/reader/codepoints.h new file mode 100644 index 000000000..9ff7ad197 --- /dev/null +++ b/ext/c/stupidedi/reader/codepoints.h @@ -0,0 +1,29 @@ +/* Bitmap indicating ISO-8859-x graphic characters starting from 0xa0..0xff */ +unsigned char iso_8859_graphic[16][12] = { + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-1 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-2 + {0xff,0xfe,0xff,0xf7,0xff,0xfe,0xff,0xf7,0xbf,0xff,0xbf,0xdf}, // iso-8859-3 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-4 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-5 + {0x00,0x07,0xff,0xff,0x07,0xff,0xff,0xfe,0x88,0x00,0x30,0x11}, // iso-8859-6 + {0x7f,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xbf,0xff}, // iso-8859-7 + {0x67,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x7f,0xff,0xff,0xfd}, // iso-8859-8 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-9 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-10 + {0x0f,0xff,0xff,0xff,0x87,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-11 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // iso-8859-12 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-13 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-14 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-15 + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, // iso-8859-16 +}; + +/* List of intervals of indicating graphic Unicode codepoints */ +unsigned int ucs_codepoints_graphic_count = 667; +unsigned int ucs_codepoints_graphic_min[] = {0x20,0xa0,0xae,0x37a,0x384,0x38c,0x38e,0x3a3,0x531,0x559,0x58d,0x591,0x5d0,0x5ef,0x606,0x61e,0x6de,0x710,0x74d,0x7c0,0x7fd,0x830,0x840,0x85e,0x860,0x8a0,0x8b6,0x8d3,0x8e3,0x985,0x98f,0x993,0x9aa,0x9b2,0x9b6,0x9bc,0x9c7,0x9cb,0x9d7,0x9dc,0x9df,0x9e6,0xa01,0xa05,0xa0f,0xa13,0xa2a,0xa32,0xa35,0xa38,0xa3c,0xa3e,0xa47,0xa4b,0xa51,0xa59,0xa5e,0xa66,0xa81,0xa85,0xa8f,0xa93,0xaaa,0xab2,0xab5,0xabc,0xac7,0xacb,0xad0,0xae0,0xae6,0xaf9,0xb01,0xb05,0xb0f,0xb13,0xb2a,0xb32,0xb35,0xb3c,0xb47,0xb4b,0xb56,0xb5c,0xb5f,0xb66,0xb82,0xb85,0xb8e,0xb92,0xb99,0xb9c,0xb9e,0xba3,0xba8,0xbae,0xbbe,0xbc6,0xbca,0xbd0,0xbd7,0xbe6,0xc00,0xc0e,0xc12,0xc2a,0xc3d,0xc46,0xc4a,0xc55,0xc58,0xc60,0xc66,0xc77,0xc8e,0xc92,0xcaa,0xcb5,0xcbc,0xcc6,0xcca,0xcd5,0xcde,0xce0,0xce6,0xcf1,0xd00,0xd05,0xd0e,0xd12,0xd46,0xd4a,0xd54,0xd66,0xd82,0xd85,0xd9a,0xdb3,0xdbd,0xdc0,0xdca,0xdcf,0xdd6,0xdd8,0xde6,0xdf2,0xe01,0xe3f,0xe81,0xe84,0xe86,0xe8c,0xea5,0xea7,0xec0,0xec6,0xec8,0xed0,0xedc,0xf00,0xf49,0xf71,0xf99,0xfbe,0xfce,0x1000,0x10c7,0x10cd,0x10d0,0x124a,0x1250,0x1258,0x125a,0x1260,0x128a,0x1290,0x12b2,0x12b8,0x12c0,0x12c2,0x12c8,0x12d8,0x1312,0x1318,0x135d,0x1380,0x13a0,0x13f8,0x1400,0x16a0,0x1700,0x170e,0x1720,0x1740,0x1760,0x176e,0x1772,0x1780,0x17e0,0x17f0,0x1800,0x1810,0x1820,0x1880,0x18b0,0x1900,0x1920,0x1930,0x1940,0x1944,0x1970,0x1980,0x19b0,0x19d0,0x19de,0x1a1e,0x1a60,0x1a7f,0x1a90,0x1aa0,0x1ab0,0x1b00,0x1b50,0x1b80,0x1bfc,0x1c3b,0x1c4d,0x1c90,0x1cbd,0x1cd0,0x1d00,0x1dfb,0x1f18,0x1f20,0x1f48,0x1f50,0x1f59,0x1f5b,0x1f5d,0x1f5f,0x1f80,0x1fb6,0x1fc6,0x1fd6,0x1fdd,0x1ff2,0x1ff6,0x2000,0x2010,0x202f,0x2070,0x2074,0x2090,0x20a0,0x20d0,0x2100,0x2190,0x2440,0x2460,0x2b76,0x2b98,0x2c30,0x2c60,0x2cf9,0x2d27,0x2d2d,0x2d30,0x2d6f,0x2d7f,0x2da0,0x2da8,0x2db0,0x2db8,0x2dc0,0x2dc8,0x2dd0,0x2dd8,0x2de0,0x2e80,0x2e9b,0x2f00,0x2ff0,0x3000,0x3041,0x3099,0x3105,0x3131,0x3190,0x31c0,0x31f0,0x3220,0x4dc0,0xa000,0xa490,0xa4d0,0xa640,0xa700,0xa7c2,0xa7f7,0xa830,0xa840,0xa880,0xa8ce,0xa8e0,0xa95f,0xa980,0xa9cf,0xa9de,0xaa00,0xaa40,0xaa50,0xaa5c,0xaadb,0xab01,0xab09,0xab11,0xab20,0xab28,0xab30,0xab70,0xabf0,0xac00,0xd7b0,0xd7cb,0xf900,0xfa70,0xfb00,0xfb13,0xfb1d,0xfb38,0xfb3e,0xfb40,0xfb43,0xfb46,0xfbd3,0xfd50,0xfd92,0xfdf0,0xfe00,0xfe20,0xfe54,0xfe68,0xfe70,0xfe76,0xff01,0xffc2,0xffca,0xffd2,0xffda,0xffe0,0xffe8,0xfffc,0x10000,0x1000d,0x10028,0x1003c,0x1003f,0x10050,0x10080,0x10100,0x10107,0x10137,0x10190,0x101a0,0x101d0,0x10280,0x102a0,0x102e0,0x10300,0x1032d,0x10350,0x10380,0x1039f,0x103c8,0x10400,0x104a0,0x104b0,0x104d8,0x10500,0x10530,0x1056f,0x10600,0x10740,0x10760,0x10800,0x10808,0x1080a,0x10837,0x1083c,0x1083f,0x10857,0x108a7,0x108e0,0x108f4,0x108fb,0x1091f,0x1093f,0x10980,0x109bc,0x109d2,0x10a05,0x10a0c,0x10a15,0x10a19,0x10a38,0x10a3f,0x10a50,0x10a60,0x10ac0,0x10aeb,0x10b00,0x10b39,0x10b58,0x10b78,0x10b99,0x10ba9,0x10c00,0x10c80,0x10cc0,0x10cfa,0x10d30,0x10e60,0x10f00,0x10f30,0x10fe0,0x11000,0x11052,0x1107f,0x110be,0x110d0,0x110f0,0x11100,0x11136,0x11150,0x11180,0x111d0,0x111e1,0x11200,0x11213,0x11280,0x11288,0x1128a,0x1128f,0x1129f,0x112b0,0x112f0,0x11300,0x11305,0x1130f,0x11313,0x1132a,0x11332,0x11335,0x1133b,0x11347,0x1134b,0x11350,0x11357,0x1135d,0x11366,0x11370,0x11400,0x1145b,0x1145d,0x11480,0x114d0,0x11580,0x115b8,0x11600,0x11650,0x11660,0x11680,0x116c0,0x11700,0x1171d,0x11730,0x11800,0x118a0,0x118ff,0x119a0,0x119aa,0x119da,0x11a00,0x11a50,0x11ac0,0x11c00,0x11c0a,0x11c38,0x11c50,0x11c70,0x11c92,0x11ca9,0x11d00,0x11d08,0x11d0b,0x11d3a,0x11d3c,0x11d3f,0x11d50,0x11d60,0x11d67,0x11d6a,0x11d90,0x11d93,0x11da0,0x11ee0,0x11fc0,0x11fff,0x12400,0x12470,0x12480,0x13000,0x14400,0x16800,0x16a40,0x16a60,0x16a6e,0x16ad0,0x16af0,0x16b00,0x16b50,0x16b5b,0x16b63,0x16b7d,0x16e40,0x16f00,0x16f4f,0x16f8f,0x16fe0,0x17000,0x18800,0x1b000,0x1b150,0x1b164,0x1b170,0x1bc00,0x1bc70,0x1bc80,0x1bc90,0x1bc9c,0x1d000,0x1d100,0x1d129,0x1d17b,0x1d200,0x1d2e0,0x1d300,0x1d360,0x1d400,0x1d456,0x1d49e,0x1d4a2,0x1d4a5,0x1d4a9,0x1d4ae,0x1d4bb,0x1d4bd,0x1d4c5,0x1d507,0x1d50d,0x1d516,0x1d51e,0x1d53b,0x1d540,0x1d546,0x1d54a,0x1d552,0x1d6a8,0x1d7ce,0x1da9b,0x1daa1,0x1e000,0x1e008,0x1e01b,0x1e023,0x1e026,0x1e100,0x1e130,0x1e140,0x1e14e,0x1e2c0,0x1e2ff,0x1e800,0x1e8c7,0x1e900,0x1e950,0x1e95e,0x1ec71,0x1ed01,0x1ee00,0x1ee05,0x1ee21,0x1ee24,0x1ee27,0x1ee29,0x1ee34,0x1ee39,0x1ee3b,0x1ee42,0x1ee47,0x1ee49,0x1ee4b,0x1ee4d,0x1ee51,0x1ee54,0x1ee57,0x1ee59,0x1ee5b,0x1ee5d,0x1ee5f,0x1ee61,0x1ee64,0x1ee67,0x1ee6c,0x1ee74,0x1ee79,0x1ee7e,0x1ee80,0x1ee8b,0x1eea1,0x1eea5,0x1eeab,0x1eef0,0x1f000,0x1f030,0x1f0a0,0x1f0b1,0x1f0c1,0x1f0d1,0x1f100,0x1f110,0x1f170,0x1f1e6,0x1f210,0x1f240,0x1f250,0x1f260,0x1f300,0x1f6e0,0x1f6f0,0x1f700,0x1f780,0x1f7e0,0x1f800,0x1f810,0x1f850,0x1f860,0x1f890,0x1f900,0x1f90d,0x1f973,0x1f97a,0x1f9a5,0x1f9ae,0x1f9cd,0x1fa60,0x1fa70,0x1fa78,0x1fa80,0x1fa90,0x20000,0x2a700,0x2b740,0x2b820,0x2ceb0,0x2f800,0xe0100}; +unsigned int ucs_codepoints_graphic_max[] = {0x7e,0xac,0x377,0x37f,0x38a,0x38c,0x3a1,0x52f,0x556,0x58a,0x58f,0x5c7,0x5ea,0x5f4,0x61b,0x6dc,0x70d,0x74a,0x7b1,0x7fa,0x82d,0x83e,0x85b,0x85e,0x86a,0x8b4,0x8bd,0x8e1,0x983,0x98c,0x990,0x9a8,0x9b0,0x9b2,0x9b9,0x9c4,0x9c8,0x9ce,0x9d7,0x9dd,0x9e3,0x9fe,0xa03,0xa0a,0xa10,0xa28,0xa30,0xa33,0xa36,0xa39,0xa3c,0xa42,0xa48,0xa4d,0xa51,0xa5c,0xa5e,0xa76,0xa83,0xa8d,0xa91,0xaa8,0xab0,0xab3,0xab9,0xac5,0xac9,0xacd,0xad0,0xae3,0xaf1,0xaff,0xb03,0xb0c,0xb10,0xb28,0xb30,0xb33,0xb39,0xb44,0xb48,0xb4d,0xb57,0xb5d,0xb63,0xb77,0xb83,0xb8a,0xb90,0xb95,0xb9a,0xb9c,0xb9f,0xba4,0xbaa,0xbb9,0xbc2,0xbc8,0xbcd,0xbd0,0xbd7,0xbfa,0xc0c,0xc10,0xc28,0xc39,0xc44,0xc48,0xc4d,0xc56,0xc5a,0xc63,0xc6f,0xc8c,0xc90,0xca8,0xcb3,0xcb9,0xcc4,0xcc8,0xccd,0xcd6,0xcde,0xce3,0xcef,0xcf2,0xd03,0xd0c,0xd10,0xd44,0xd48,0xd4f,0xd63,0xd7f,0xd83,0xd96,0xdb1,0xdbb,0xdbd,0xdc6,0xdca,0xdd4,0xdd6,0xddf,0xdef,0xdf4,0xe3a,0xe5b,0xe82,0xe84,0xe8a,0xea3,0xea5,0xebd,0xec4,0xec6,0xecd,0xed9,0xedf,0xf47,0xf6c,0xf97,0xfbc,0xfcc,0xfda,0x10c5,0x10c7,0x10cd,0x1248,0x124d,0x1256,0x1258,0x125d,0x1288,0x128d,0x12b0,0x12b5,0x12be,0x12c0,0x12c5,0x12d6,0x1310,0x1315,0x135a,0x137c,0x1399,0x13f5,0x13fd,0x169c,0x16f8,0x170c,0x1714,0x1736,0x1753,0x176c,0x1770,0x1773,0x17dd,0x17e9,0x17f9,0x180d,0x1819,0x1878,0x18aa,0x18f5,0x191e,0x192b,0x193b,0x1940,0x196d,0x1974,0x19ab,0x19c9,0x19da,0x1a1b,0x1a5e,0x1a7c,0x1a89,0x1a99,0x1aad,0x1abe,0x1b4b,0x1b7c,0x1bf3,0x1c37,0x1c49,0x1c88,0x1cba,0x1cc7,0x1cfa,0x1df9,0x1f15,0x1f1d,0x1f45,0x1f4d,0x1f57,0x1f59,0x1f5b,0x1f5d,0x1f7d,0x1fb4,0x1fc4,0x1fd3,0x1fdb,0x1fef,0x1ff4,0x1ffe,0x200a,0x2029,0x205f,0x2071,0x208e,0x209c,0x20bf,0x20f0,0x218b,0x2426,0x244a,0x2b73,0x2b95,0x2c2e,0x2c5e,0x2cf3,0x2d25,0x2d27,0x2d2d,0x2d67,0x2d70,0x2d96,0x2da6,0x2dae,0x2db6,0x2dbe,0x2dc6,0x2dce,0x2dd6,0x2dde,0x2e4f,0x2e99,0x2ef3,0x2fd5,0x2ffb,0x303f,0x3096,0x30ff,0x312f,0x318e,0x31ba,0x31e3,0x321e,0x4db5,0x9fef,0xa48c,0xa4c6,0xa62b,0xa6f7,0xa7bf,0xa7c6,0xa82b,0xa839,0xa877,0xa8c5,0xa8d9,0xa953,0xa97c,0xa9cd,0xa9d9,0xa9fe,0xaa36,0xaa4d,0xaa59,0xaac2,0xaaf6,0xab06,0xab0e,0xab16,0xab26,0xab2e,0xab67,0xabed,0xabf9,0xd7a3,0xd7c6,0xd7fb,0xfa6d,0xfad9,0xfb06,0xfb17,0xfb36,0xfb3c,0xfb3e,0xfb41,0xfb44,0xfbc1,0xfd3f,0xfd8f,0xfdc7,0xfdfd,0xfe19,0xfe52,0xfe66,0xfe6b,0xfe74,0xfefc,0xffbe,0xffc7,0xffcf,0xffd7,0xffdc,0xffe6,0xffee,0xfffd,0x1000b,0x10026,0x1003a,0x1003d,0x1004d,0x1005d,0x100fa,0x10102,0x10133,0x1018e,0x1019b,0x101a0,0x101fd,0x1029c,0x102d0,0x102fb,0x10323,0x1034a,0x1037a,0x1039d,0x103c3,0x103d5,0x1049d,0x104a9,0x104d3,0x104fb,0x10527,0x10563,0x1056f,0x10736,0x10755,0x10767,0x10805,0x10808,0x10835,0x10838,0x1083c,0x10855,0x1089e,0x108af,0x108f2,0x108f5,0x1091b,0x10939,0x1093f,0x109b7,0x109cf,0x10a03,0x10a06,0x10a13,0x10a17,0x10a35,0x10a3a,0x10a48,0x10a58,0x10a9f,0x10ae6,0x10af6,0x10b35,0x10b55,0x10b72,0x10b91,0x10b9c,0x10baf,0x10c48,0x10cb2,0x10cf2,0x10d27,0x10d39,0x10e7e,0x10f27,0x10f59,0x10ff6,0x1104d,0x1106f,0x110bc,0x110c1,0x110e8,0x110f9,0x11134,0x11146,0x11176,0x111cd,0x111df,0x111f4,0x11211,0x1123e,0x11286,0x11288,0x1128d,0x1129d,0x112a9,0x112ea,0x112f9,0x11303,0x1130c,0x11310,0x11328,0x11330,0x11333,0x11339,0x11344,0x11348,0x1134d,0x11350,0x11357,0x11363,0x1136c,0x11374,0x11459,0x1145b,0x1145f,0x114c7,0x114d9,0x115b5,0x115dd,0x11644,0x11659,0x1166c,0x116b8,0x116c9,0x1171a,0x1172b,0x1173f,0x1183b,0x118f2,0x118ff,0x119a7,0x119d7,0x119e4,0x11a47,0x11aa2,0x11af8,0x11c08,0x11c36,0x11c45,0x11c6c,0x11c8f,0x11ca7,0x11cb6,0x11d06,0x11d09,0x11d36,0x11d3a,0x11d3d,0x11d47,0x11d59,0x11d65,0x11d68,0x11d8e,0x11d91,0x11d98,0x11da9,0x11ef8,0x11ff1,0x12399,0x1246e,0x12474,0x12543,0x1342e,0x14646,0x16a38,0x16a5e,0x16a69,0x16a6f,0x16aed,0x16af5,0x16b45,0x16b59,0x16b61,0x16b77,0x16b8f,0x16e9a,0x16f4a,0x16f87,0x16f9f,0x16fe3,0x187f7,0x18af2,0x1b11e,0x1b152,0x1b167,0x1b2fb,0x1bc6a,0x1bc7c,0x1bc88,0x1bc99,0x1bc9f,0x1d0f5,0x1d126,0x1d172,0x1d1e8,0x1d245,0x1d2f3,0x1d356,0x1d378,0x1d454,0x1d49c,0x1d49f,0x1d4a2,0x1d4a6,0x1d4ac,0x1d4b9,0x1d4bb,0x1d4c3,0x1d505,0x1d50a,0x1d514,0x1d51c,0x1d539,0x1d53e,0x1d544,0x1d546,0x1d550,0x1d6a5,0x1d7cb,0x1da8b,0x1da9f,0x1daaf,0x1e006,0x1e018,0x1e021,0x1e024,0x1e02a,0x1e12c,0x1e13d,0x1e149,0x1e14f,0x1e2f9,0x1e2ff,0x1e8c4,0x1e8d6,0x1e94b,0x1e959,0x1e95f,0x1ecb4,0x1ed3d,0x1ee03,0x1ee1f,0x1ee22,0x1ee24,0x1ee27,0x1ee32,0x1ee37,0x1ee39,0x1ee3b,0x1ee42,0x1ee47,0x1ee49,0x1ee4b,0x1ee4f,0x1ee52,0x1ee54,0x1ee57,0x1ee59,0x1ee5b,0x1ee5d,0x1ee5f,0x1ee62,0x1ee64,0x1ee6a,0x1ee72,0x1ee77,0x1ee7c,0x1ee7e,0x1ee89,0x1ee9b,0x1eea3,0x1eea9,0x1eebb,0x1eef1,0x1f02b,0x1f093,0x1f0ae,0x1f0bf,0x1f0cf,0x1f0f5,0x1f10c,0x1f16c,0x1f1ac,0x1f202,0x1f23b,0x1f248,0x1f251,0x1f265,0x1f6d5,0x1f6ec,0x1f6fa,0x1f773,0x1f7d8,0x1f7eb,0x1f80b,0x1f847,0x1f859,0x1f887,0x1f8ad,0x1f90b,0x1f971,0x1f976,0x1f9a2,0x1f9aa,0x1f9ca,0x1fa53,0x1fa6d,0x1fa73,0x1fa7a,0x1fa82,0x1fa95,0x2a6d6,0x2b734,0x2b81d,0x2cea1,0x2ebe0,0x2fa1d,0xe01ef}; + +/* List of intervals of indicating whitespace Unicode codepoints */ +unsigned int ucs_codepoints_whitespace_count = 10; +unsigned int ucs_codepoints_whitespace_min[] = {0x09,0x20,0x85,0xa0,0x1680,0x2000,0x2028,0x202f,0x205f,0x3000}; +unsigned int ucs_codepoints_whitespace_max[] = {0x0d,0x20,0x85,0xa0,0x1680,0x200a,0x2029,0x202f,0x205f,0x3000}; diff --git a/ext/stupidedi/reader/native_ext/extconf.rb b/ext/c/stupidedi/reader/extconf.rb similarity index 65% rename from ext/stupidedi/reader/native_ext/extconf.rb rename to ext/c/stupidedi/reader/extconf.rb index 53f14fce2..55efc7543 100644 --- a/ext/stupidedi/reader/native_ext/extconf.rb +++ b/ext/c/stupidedi/reader/extconf.rb @@ -1,6 +1,7 @@ require "mkmf" -extension_name = "native_ext" -create_header +extension_name = "stupidedi/reader/native_ext" + dir_config extension_name create_makefile extension_name +create_header diff --git a/ext/c/stupidedi/reader/reader_ext.c b/ext/c/stupidedi/reader/reader_ext.c new file mode 100644 index 000000000..e3fe46bfa --- /dev/null +++ b/ext/c/stupidedi/reader/reader_ext.c @@ -0,0 +1,660 @@ +#include "extconf.h" +#include "ruby.h" +#include "ruby/encoding.h" +#include "codepoints.h" +#include + +extern VALUE rb_str_length(VALUE str); + +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) + +#ifdef __GNUC__ +#if __GNUC__ >= 3 +#undef LIKELY +#undef UNLIKELY +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#define UNLIKELY(x) (__builtin_expect(!!(x), 0)) +#endif +#endif + +static int ENCIDX_CP850 = -1; +static int ENCIDX_CP852 = -1; +static int ENCIDX_CP855 = -1; +static int ENCIDX_IBM037 = -1; +static int ENCIDX_IBM437 = -1; +static int ENCIDX_IBM737 = -1; +static int ENCIDX_IBM775 = -1; +static int ENCIDX_IBM852 = -1; +static int ENCIDX_IBM855 = -1; +static int ENCIDX_IBM857 = -1; +static int ENCIDX_IBM860 = -1; +static int ENCIDX_IBM861 = -1; +static int ENCIDX_IBM862 = -1; +static int ENCIDX_IBM863 = -1; +static int ENCIDX_IBM864 = -1; +static int ENCIDX_IBM865 = -1; +static int ENCIDX_IBM866 = -1; +static int ENCIDX_IBM869 = -1; +static int ENCIDX_ISO_8859_1 = -1; +static int ENCIDX_ISO_8859_10 = -1; +static int ENCIDX_ISO_8859_11 = -1; +static int ENCIDX_ISO_8859_13 = -1; +static int ENCIDX_ISO_8859_14 = -1; +static int ENCIDX_ISO_8859_15 = -1; +static int ENCIDX_ISO_8859_16 = -1; +static int ENCIDX_ISO_8859_2 = -1; +static int ENCIDX_ISO_8859_3 = -1; +static int ENCIDX_ISO_8859_4 = -1; +static int ENCIDX_ISO_8859_5 = -1; +static int ENCIDX_ISO_8859_6 = -1; +static int ENCIDX_ISO_8859_7 = -1; +static int ENCIDX_ISO_8859_8 = -1; +static int ENCIDX_ISO_8859_9 = -1; +static int ENCIDX_TIS_620 = -1; +static int ENCIDX_US_ASCII = -1; +static int ENCIDX_UTF_16 = -1; +static int ENCIDX_UTF_16BE = -1; +static int ENCIDX_UTF_16LE = -1; +static int ENCIDX_UTF_32 = -1; +static int ENCIDX_UTF_32BE = -1; +static int ENCIDX_UTF_32LE = -1; +static int ENCIDX_UTF_8 = -1; +static int ENCIDX_Windows_1250 = -1; +static int ENCIDX_Windows_1251 = -1; +static int ENCIDX_Windows_1252 = -1; +static int ENCIDX_Windows_1253 = -1; +static int ENCIDX_Windows_1254 = -1; +static int ENCIDX_Windows_1255 = -1; +static int ENCIDX_Windows_1256 = -1; +static int ENCIDX_Windows_1257 = -1; +static int ENCIDX_Windows_1258 = -1; + +static unsigned char encset[32]; + +static inline bool +bitmask_test(unsigned char *bitmask, int bitidx, int bitmask_size) { + if (bitidx < 0 || bitidx >= 8*bitmask_size) + return false; + + int n = bitmask_size - 1 - bitidx / 8; + int m = bitidx % 8; + char c = *(bitmask + n); + return (c >> m) & 0x1; +} + +static void +bitmask_set(unsigned char *bitmask, int bitidx, int bitmask_size) { + if (bitidx < 0 || bitidx >= 8*bitmask_size) + return; + + int n = bitmask_size - 1 - bitidx / 8; + int m = bitidx % 8; + bitmask[n] |= (1 << m); +} + +static bool +update_encidx(int encidx) { + const char *encname; + + /* We've already assigned the encidx to an ENCIDX_xx global */ + if (bitmask_test(encset, encidx, 32)) + return false; + + /* Otherwise, match the "NAME" to the ENCIDX_NAME constant */ + encname = rb_enc_name(rb_enc_from_index(encidx)); + +#define TESTENC(name, id) if (0 == strncmp(name,encname,64)) {\ + ENCIDX_##id = encidx;\ + bitmask_set(encset, encidx, 32); \ + return true; \ +} + + TESTENC("CP850", CP850) // US-ASCII+ https://en.wikipedia.org/wiki/Code_page_850 + TESTENC("CP852", CP852) // CP850+ https://en.wikipedia.org/wiki/Code_page_852 + TESTENC("CP855", CP855) // CP850+ https://en.wikipedia.org/wiki/Code_page_855 + + TESTENC("IBM037", IBM037) // EBCDIC https://en.wikipedia.org/wiki/Code_page_037 + TESTENC("IBM437", IBM437) // ASCII+ https://en.wikipedia.org/wiki/Code_page_437 + TESTENC("IBM737", IBM737) // IBM437+ https://en.wikipedia.org/wiki/Code_page_737 + TESTENC("IBM775", IBM775) // IBM437+ https://en.wikipedia.org/wiki/Code_page_775 + TESTENC("IBM852", IBM852) // IBM437+ https://en.wikipedia.org/wiki/Code_page_852 + TESTENC("IBM855", IBM855) // IBM437+ https://en.wikipedia.org/wiki/Code_page_855 + TESTENC("IBM857", IBM857) // IBM437+ https://en.wikipedia.org/wiki/Code_page_857 + TESTENC("IBM860", IBM860) // IBM437+ https://en.wikipedia.org/wiki/Code_page_860 + TESTENC("IBM861", IBM861) // IBM437+ https://en.wikipedia.org/wiki/Code_page_861 + TESTENC("IBM862", IBM862) // IBM437+ https://en.wikipedia.org/wiki/Code_page_862 + TESTENC("IBM863", IBM863) // IBM437+ https://en.wikipedia.org/wiki/Code_page_863 + TESTENC("IBM864", IBM864) // IBM437+? https://en.wikipedia.org/wiki/Code_page_864 + TESTENC("IBM865", IBM865) // IBM437+ https://en.wikipedia.org/wiki/Code_page_865 + TESTENC("IBM866", IBM866) // IBM437+ https://en.wikipedia.org/wiki/Code_page_866 + TESTENC("IBM869", IBM869) // IBM437+ https://en.wikipedia.org/wiki/Code_page_869 + + TESTENC("ISO-8859-1", ISO_8859_1) + TESTENC("ISO-8859-2", ISO_8859_2) + TESTENC("ISO-8859-3", ISO_8859_3) + TESTENC("ISO-8859-4", ISO_8859_4) + TESTENC("ISO-8859-5", ISO_8859_5) + TESTENC("ISO-8859-6", ISO_8859_6) + TESTENC("ISO-8859-7", ISO_8859_7) + TESTENC("ISO-8859-8", ISO_8859_8) + TESTENC("ISO-8859-9", ISO_8859_9) + TESTENC("ISO-8859-10", ISO_8859_10) + TESTENC("ISO-8859-11", ISO_8859_11) + TESTENC("ISO-8859-13", ISO_8859_13) + TESTENC("ISO-8859-14", ISO_8859_14) + TESTENC("ISO-8859-15", ISO_8859_15) + TESTENC("ISO-8859-16", ISO_8859_16) + TESTENC("TIS-620", TIS_620) // https://en.wikipedia.org/wiki/ISO/IEC_8859-11 + + TESTENC("US-ASCII", US_ASCII) + + TESTENC("UTF-8", UTF_8) + TESTENC("UTF-16", UTF_16) + TESTENC("UTF-16BE", UTF_16BE) + TESTENC("UTF-16LE", UTF_16LE) + TESTENC("UTF-32", UTF_32) + TESTENC("UTF-32BE", UTF_32BE) + TESTENC("UTF-32LE", UTF_32LE) + + TESTENC("Windows-1250", Windows_1250) + TESTENC("Windows-1251", Windows_1251) + TESTENC("Windows-1252", Windows_1252) + TESTENC("Windows-1253", Windows_1253) + TESTENC("Windows-1254", Windows_1254) + TESTENC("Windows-1255", Windows_1255) + TESTENC("Windows-1256", Windows_1256) + TESTENC("Windows-1257", Windows_1257) + TESTENC("Windows-1258", Windows_1258) + + return false; +} + + +/* + * This performs a binary search on sorted disjoint intervals (sorted by each + * interval's min). First it finds the largest min that's no larger than the + * point; then it checks that point doesn't exceed that interval's max. + * + * Most queries will require descending all the way to a leaf, even if the + * matching interval is further up the tree. That is because once a `min` is + * found that is less than the point, we need to find the next smallest `min`. + * Doing so amounts to descending to the leftmost leaf of the right subtree + * of the current `min`. + * + * This means for around 700 intervals (the number of codepoint ranges that + * cover Unicode graphical characters), about 10 iterations are required. + * + * TODO: Using an optimal binary tree might reduce the number of iterations, + * but would increase the complexity -- using a contiguous region of memory + * like an array provides good data locality, but some scheme would be needed + * to represent a non-complete binary tree. The best approach might be to + * allocate a contiguous block of memory and then use a linked representation. + * But reducing most queries from 10 iterations to 1-2 might not improve much? + */ +static inline int +has_matching_interval(const unsigned int point, + const unsigned int *min, + const unsigned int *max, + const unsigned int size) +{ + int k, l, r, z; + l = 0; + r = size - 1; + z = -1; + + for (l = 0, r = size - 1, z = -1; + k = (l + r) / 2, l <= r;) { + if (UNLIKELY(point > min[k])) + l = (z = k) + 1; // descend right + else if (point < min[k]) + r = k - 1; // descend left + else + break; + } + + if (point < min[k]) + k = z; + + if (k >= 0 && point <= max[k]) + return true; + + return false; +} + +// https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt + +static inline bool +is_whitespace(const unsigned int c, const int encidx) +{ + if (encidx == ENCIDX_US_ASCII) + return (c >= 0x08 && c <= 0x0d) || c == 0x20; + else if (encidx == ENCIDX_UTF_8) + return has_matching_interval(c, + ucs_codepoints_whitespace_min, + ucs_codepoints_whitespace_max, + ucs_codepoints_whitespace_count); + else if (encidx == ENCIDX_ISO_8859_1 || + encidx == ENCIDX_ISO_8859_2 || + encidx == ENCIDX_ISO_8859_3 || + encidx == ENCIDX_ISO_8859_4 || + encidx == ENCIDX_ISO_8859_5 || + encidx == ENCIDX_ISO_8859_6 || + encidx == ENCIDX_ISO_8859_7 || + encidx == ENCIDX_ISO_8859_8 || + encidx == ENCIDX_ISO_8859_9 || + encidx == ENCIDX_ISO_8859_10 || + encidx == ENCIDX_ISO_8859_11 || + encidx == ENCIDX_ISO_8859_13 || + encidx == ENCIDX_ISO_8859_14 || + encidx == ENCIDX_ISO_8859_15 || + encidx == ENCIDX_ISO_8859_16) + return (c >= 0x08 && c <= 0x0d) || c == 0x20 || c == 0xa0; + + /* If nothing matched, it could be the first time we've seen this encoding + * and we haven't assigned ENCIDX_XX yet. If so, update and retry */ + if (update_encidx(encidx)) + return is_whitespace(c, encidx); + + rb_raise(rb_eEncCompatError, "unsupported encoding: %s", + rb_enc_name(rb_enc_from_index(encidx))); +} + +/* + * Letters, punctuation, symbols, ... have a visual representation + * + * Not control character (e.g., <= 0x1f in US-ASCII) + * Not undefined character (e.g., > 0x7f in US-ASCII) + */ +static bool +is_graphic(const unsigned int c, const int encidx) +{ + // https://en.wikipedia.org/wiki/Graphic_character + // https://en.wikipedia.org/wiki/ISO/IEC_8859#Table + + if (encidx == ENCIDX_UTF_8) + return has_matching_interval(c, + ucs_codepoints_graphic_min, + ucs_codepoints_graphic_max, + ucs_codepoints_graphic_count); + + if (encidx == ENCIDX_US_ASCII) + return (c >= 0x20 && c <= 0x7f); + + if (encidx == ENCIDX_ISO_8859_1 || + encidx == ENCIDX_ISO_8859_2 || + encidx == ENCIDX_ISO_8859_3 || + encidx == ENCIDX_ISO_8859_4 || + encidx == ENCIDX_ISO_8859_5 || + encidx == ENCIDX_ISO_8859_6 || + encidx == ENCIDX_ISO_8859_7 || + encidx == ENCIDX_ISO_8859_8 || + encidx == ENCIDX_ISO_8859_9 || + encidx == ENCIDX_ISO_8859_10 || + encidx == ENCIDX_ISO_8859_11 || + encidx == ENCIDX_ISO_8859_13 || + encidx == ENCIDX_ISO_8859_14 || + encidx == ENCIDX_ISO_8859_15 || + encidx == ENCIDX_ISO_8859_16) + if (c >= 0x20 && c <= 0x7f) + return true; + + if (encidx == ENCIDX_ISO_8859_1) return bitmask_test(iso_8859_graphic[0], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_2) return bitmask_test(iso_8859_graphic[1], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_3) return bitmask_test(iso_8859_graphic[2], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_4) return bitmask_test(iso_8859_graphic[3], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_5) return bitmask_test(iso_8859_graphic[4], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_6) return bitmask_test(iso_8859_graphic[5], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_7) return bitmask_test(iso_8859_graphic[6], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_8) return bitmask_test(iso_8859_graphic[7], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_9) return bitmask_test(iso_8859_graphic[8], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_10) return bitmask_test(iso_8859_graphic[9], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_11) return bitmask_test(iso_8859_graphic[10], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_13) return bitmask_test(iso_8859_graphic[12], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_14) return bitmask_test(iso_8859_graphic[13], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_15) return bitmask_test(iso_8859_graphic[14], c-0xa0, 12); + else if (encidx == ENCIDX_ISO_8859_16) return bitmask_test(iso_8859_graphic[15], c-0xa0, 12); + + /* If nothing matched, it could be the first time we've seen this encoding + * and we haven't assigned ENCIDX_XX yet. If so, update and retry */ + if (update_encidx(encidx)) + return is_graphic(c, encidx); + + rb_raise(rb_eEncCompatError, "unsupported encoding: %s", + rb_enc_name(rb_enc_from_index(encidx))); +} + +static inline bool +single_byte_optimizable(VALUE str, rb_encoding *enc) +{ + if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT) + return true; + + if (rb_enc_mbmaxlen(enc) == 1) + return true; + + return false; +} + +/* + * call-seq: + * substr_eq?(s1, n1, s2, n2, length) -> bool + * + * Returns true if the substring of s1[n1, length] is equal to the + * substring s2[n2, length]. This is more efficient than doing + * `s1[...] == s2[...]` because it doesn't allocate a new String for + * each substring before performing the comparison. + * + * substr_eq?("abc ", 0, "xyz abc", 4, 2) #=> true + * substr_eq?(" abc", 1, "xyz abc", 3, 2) #=> true + * substr_eq?(" abc", 1, "xyz a_c", 3, 2) #=> false + */ +static VALUE +rb_substr_eq_p(VALUE self, VALUE str1, VALUE _index1, VALUE str2, VALUE _index2, VALUE _length) { + Check_Type(str1, T_STRING); + Check_Type(str2, T_STRING); + Check_Type(_index1, T_FIXNUM); + Check_Type(_index2, T_FIXNUM); + Check_Type(_length, T_FIXNUM); + + long len, idx1, idx2; + len = NUM2LONG(_length); + idx1 = NUM2LONG(_index1); + idx2 = NUM2LONG(_index2); + + if (UNLIKELY(len < 0)) rb_raise(rb_eArgError, "length cannot be negative"); + if (UNLIKELY(idx1 < 0)) rb_raise(rb_eArgError, "index1 cannot be negative"); + if (UNLIKELY(idx2 < 0)) rb_raise(rb_eArgError, "index2 cannot be negative"); + + if (UNLIKELY(ENCODING_GET(str1) != ENCODING_GET(str2))) + rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s", + rb_enc_name(rb_enc_get(str1)), + rb_enc_name(rb_enc_get(str2))); + + if (idx1 + len > rb_str_strlen(str1)) return Qnil; + if (idx2 + len > rb_str_strlen(str2)) return Qnil; + + /* Number of bytes in str[idx, len], calculated by rb_str_subpos */ + long len1, len2; + len1 = len; + len2 = len; + + const char *ptr1, *ptr2; + ptr1 = rb_str_subpos(str1, idx1, &len1); + ptr2 = rb_str_subpos(str2, idx2, &len2); + + if (ptr1 == NULL || ptr2 == NULL) return Qnil; + if (len1 != len2 || len1 < len) return Qnil; + return (memcmp(ptr1, ptr2, len) == 0) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * graphic_character?(s, index) -> bool or nil + * + * Returns true if the character s[index] is a graphic character + * + * graphic_character?("x\t", 0) #=> true + * graphic_character?("x\t", 1) #=> false + */ +static VALUE +rb_graphic_p(int argc, const VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, 2); + + Check_Type(argv[0], T_STRING); + if (argc == 2) + Check_Type(argv[1], T_FIXNUM); + + VALUE str; + int encidx, len_; + long len, idx; + char *ptr, *end; + rb_encoding *enc; + unsigned int chr; + + str = argv[0]; + ptr = RSTRING_PTR(str); + end = RSTRING_END(str); + encidx = ENCODING_GET(str); + idx = (argc == 1) ? 0 : FIX2LONG(argv[1]); + len = 1; + + // Skip ahead to idx + ptr = rb_str_subpos(str, idx, &len); + + if (!ptr || !len || ptr >= end) + return Qnil; + + enc = rb_enc_from_index(encidx); + len_ = 1; + chr = rb_enc_codepoint_len(ptr, end, &len_, enc); + + if (!len_) + return Qnil; + + return is_graphic(chr, encidx) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * whitespace?(s, index) -> bool or nil + * + * Returns true if the character s[index] is a graphic character + * + * whitespace?("x\t", 0) #=> true + * whitespace?("x\t", 1) #=> false + */ +static VALUE +rb_whitespace_p(int argc, const VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, 2); + + Check_Type(argv[0], T_STRING); + if (argc == 2) + Check_Type(argv[1], T_FIXNUM); + + VALUE str; + int encidx, len_; + long len, idx; + char *ptr, *end; + rb_encoding *enc; + unsigned int chr; + + str = argv[0]; + ptr = RSTRING_PTR(str); + end = RSTRING_END(str); + encidx = ENCODING_GET(str); + idx = (argc == 1) ? 0 : FIX2LONG(argv[1]); + len = 1; + + // Skip ahead to idx + ptr = rb_str_subpos(str, idx, &len); + + if (!ptr || !len || ptr >= end) + return Qnil; + + enc = rb_enc_from_index(encidx); + len_ = 1; + chr = rb_enc_codepoint_len(ptr, end, &len_, enc); + + if (!len_) + return Qnil; + + return is_whitespace(chr, encidx) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * min_graphic_index(string, index=0) -> int + * + * Description + * + * min_graphic_index("\r\nabc ") #=> 2 + * min_graphic_index("\r\nabc ", 2) #=> 2 + * min_graphic_index("\r\nabc ", 5) #=> 5 + */ +static VALUE +rb_min_graphic_index(int argc, const VALUE *argv, VALUE self) { + rb_check_arity(argc, 1, 2); + + Check_Type(argv[0], T_STRING); + if (argc == 2) + Check_Type(argv[1], T_FIXNUM); + + VALUE str; + int encidx; + long idx; + char *ptr, *end; + rb_encoding *enc; + + str = argv[0]; + end = RSTRING_END(str); + ptr = RSTRING_PTR(str); + idx = (argc == 1) ? 0 : FIX2LONG(argv[1]); + + encidx = ENCODING_GET(str); + enc = rb_enc_from_index(encidx); + + if (idx < 0) rb_raise(rb_eArgError, "index cannot be negative"); + if (!ptr) return INT2FIX(0); + + if (single_byte_optimizable(str, enc)) { + ptr += idx; + + if (ptr >= end) + return LONG2NUM(RSTRING_LEN(str)); + + while (ptr < end && !is_graphic(*ptr, encidx)) + ptr ++; + + return LONG2NUM(ptr - RSTRING_PTR(str)); + } + + int len; + long len_, count; + unsigned int c; + + len_ = 1; + count = 0; + ptr = rb_str_subpos(str, idx, &len_); + + if (!ptr || ptr >= end) + return rb_str_length(str); + + while (ptr < end + && (c = rb_enc_codepoint_len(ptr, end, &len, enc)) != 0 + && !is_graphic(c, encidx)) { + ptr += len; + count ++; + } + + return LONG2NUM(count); +} + +/* + * call-seq: + * min_nonspace_index(string, index=0) -> int + * + * Description + * + * min_nonspace_index(" abc ") #=> 1 + * min_nonspace_index(" abc ", 2) #=> 2 + * min_nonspace_index(" abc ", 4) #=> 5 + * + * s[min_nonspace_index(s)..-1] == s.lstrip + * s[min_nonspace_index(s, n)..-1] == s[n..-1].lstrip + */ +static VALUE +rb_min_nonspace_index(int argc, const VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, 2); + + Check_Type(argv[0], T_STRING); + if (argc == 2) + Check_Type(argv[1], T_FIXNUM); + + VALUE str; + int encidx; + long idx; + char *ptr, *end; + rb_encoding *enc; + + str = argv[0]; + end = RSTRING_END(str); + ptr = RSTRING_PTR(str); + idx = (argc == 1) ? 0 : FIX2LONG(argv[1]); + + encidx = ENCODING_GET(str); + enc = rb_enc_from_index(encidx); + + if (idx < 0) rb_raise(rb_eArgError, "index cannot be negative"); + if (!ptr) return INT2FIX(0); + + if (single_byte_optimizable(str, enc)) { + ptr += idx; + + if (ptr >= end) + return LONG2NUM(RSTRING_LEN(str)); + + while (ptr < end && is_whitespace(*ptr, encidx)) + ptr ++; + + return LONG2NUM(ptr - RSTRING_PTR(str)); + } + + int len; + long len_, count; + unsigned int c; + + len_ = 1; + count = 0; + ptr = rb_str_subpos(str, idx, &len_); + + if (!ptr || ptr >= end) + return rb_str_length(str); + + while (ptr < end + && (c = rb_enc_codepoint_len(ptr, end, &len, enc)) != 0 + && is_whitespace(c, encidx)) { + ptr += len; + count ++; + } + + return LONG2NUM(count); +} + +/* + * call-seq: + * max_nonspace_index(string, index=0) -> int + * + * Description + * + * max_nonspace_index(" abc ") #=> 3 + * max_nonspace_index(" abc ", 2) #=> 2 + * max_nonspace_index(" abc ", 0) #=> 0 + * + * s[0, max_nonspace_index(s)] == s.rstrip + * s[0, max_nonspace_index(s, n)] == s[0..n].rstrip + */ +static VALUE +rb_max_nonspace_index() { + return Qnil; +} + + +void Init_native_ext(void) { + for (int n = 0; n < 32; n++) + encset[n] = 0; + + VALUE rb_mStupidedi = rb_define_module("Stupidedi"); + VALUE rb_mReader = rb_define_module_under(rb_mStupidedi, "Reader"); + VALUE rb_m = rb_define_module_under(rb_mReader, "NativeExt"); + + rb_define_singleton_method(rb_m, "substr_eq?", rb_substr_eq_p, 5); + rb_define_singleton_method(rb_m, "graphic?", rb_graphic_p, -1); + rb_define_singleton_method(rb_m, "whitespace?", rb_whitespace_p, -1); + rb_define_singleton_method(rb_m, "min_graphic_index", rb_min_graphic_index, -1); + rb_define_singleton_method(rb_m, "min_nonspace_index", rb_min_nonspace_index, -1); + rb_define_singleton_method(rb_m, "max_nonspace_index", rb_max_nonspace_index, -1); +} diff --git a/ext/stupidedi/reader/native_ext/native_ext.c b/ext/stupidedi/reader/native_ext/native_ext.c deleted file mode 100644 index a4cc0f10c..000000000 --- a/ext/stupidedi/reader/native_ext/native_ext.c +++ /dev/null @@ -1,258 +0,0 @@ -#include "extconf.h" -#include "ruby.h" -#include "ruby/encoding.h" - -static inline int -single_byte_optimizable(VALUE str, rb_encoding *enc) { - if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT) - return 1; - - if (rb_enc_mbmaxlen(enc) == 1) - return 1; - - return 0; -} - -/* - * call-seq: - * Stupidedi::Reader.substr_eql?("abc ", 0, "xyz abc", 4, 2) #=> true - * Stupidedi::Reader.substr_eql?(" abc", 1, "xyz abc", 3, 2) #=> true - * Stupidedi::Reader.substr_eql?(" abc", 1, "xyz a_c", 3, 2) #=> false - * - * Returns true if the substring of s1[n1, length] is equal to the - * substring s2[n2, length]. This is more efficient than doing - * `s1[...] == s2[...]` because it doesn't allocate a new String for - * each substring before performing the comparison. - */ -static VALUE -rb_substr_eql_p(VALUE self, VALUE str1, VALUE offset1, VALUE str2, VALUE offset2, VALUE length) { - Check_Type(str1, T_STRING); - Check_Type(str2, T_STRING); - Check_Type(offset1, T_FIXNUM); - Check_Type(offset2, T_FIXNUM); - Check_Type(length, T_FIXNUM); - - long len, beg1, beg2; - len = NUM2LONG(length); - beg1 = NUM2LONG(offset1); - beg2 = NUM2LONG(offset2); - - if (len < 0) rb_raise(rb_eArgError, "length cannot be negative"); - if (beg1 < 0) rb_raise(rb_eArgError, "offset1 cannot be negative"); - if (beg2 < 0) rb_raise(rb_eArgError, "offset2 cannot be negative"); - - if (beg1 + len > rb_str_strlen(str1)) return Qnil; - if (beg2 + len > rb_str_strlen(str2)) return Qnil; - - /* Number of bytes in str1[offset, length], calculated by rb_str_subpos */ - long len1, len2; - len1 = len; - len2 = len; - - const char *ptr1, *ptr2; - ptr1 = rb_str_subpos(str1, beg1, &len1); - ptr2 = rb_str_subpos(str2, beg2, &len2); - - if (ptr1 == NULL || ptr2 == NULL) return Qnil; - if (len1 != len2 || len1 < len) return Qnil; - return (memcmp(ptr1, ptr2, len) == 0) ? Qtrue : Qfalse; -} - -/* - * call-seq: - * Stupidedi::Reader.lstrip_offset("abc xyz www", 0) #=> 0 - * Stupidedi::Reader.lstrip_offset("abc xyz www", 3) #=> 6 - * Stupidedi::Reader.lstrip_offset("abc xyz www", 6) #=> 6 - */ -static VALUE -rb_lstrip_offset(VALUE self, VALUE str, VALUE offset) { - Check_Type(str, T_STRING); - Check_Type(offset, T_FIXNUM); - - char *ptr, *start, *end; - rb_encoding *enc; - - start = RSTRING_PTR(str); - end = RSTRING_END(str); - enc = rb_enc_from_index(ENCODING_GET(str)); - - if (single_byte_optimizable(str, enc)) { - ptr = start + FIX2LONG(offset); - - if (ptr >= end) - return LONG2NUM(RSTRING_LEN(str)); - - while (ptr < end && rb_isspace(*ptr)) - ptr ++; - - return LONG2NUM(ptr - start); - } - - int len; - long len_, count; - unsigned int c; - - len_ = 1; - count = 0; - ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); - - if (!ptr || ptr >= end || ptr < start) - return LONG2NUM(RSTRING_LEN(str)); - - while (ptr < end && (c = rb_enc_codepoint_len(ptr, end, &len, enc)) != 0 && rb_isspace(c)) { - ptr += len; - count ++; - } - - return LONG2NUM(count); -} - -/* - * call-seq: - * Stupidedi::Reader.rstrip_offset("", 0) #=> 0 - * Stupidedi::Reader.rstrip_offset("", 3) #=> 6 - * Stupidedi::Reader.rstrip_offset("", 6) #=> 6 - */ -static VALUE -rb_rstrip_offset(VALUE self, VALUE str, VALUE offset) { - Check_Type(str, T_STRING); - Check_Type(offset, T_FIXNUM); - - char *ptr, *start, *end; - rb_encoding *enc; - - start = RSTRING_PTR(str); - end = RSTRING_END(str); - enc = rb_enc_from_index(ENCODING_GET(str)); - - if (single_byte_optimizable(str, enc)) { - ptr = RSTRING_PTR(str) + FIX2LONG(offset); - - if (!start || ptr >= end || ptr < start) - return LONG2NUM(0); - - while (start < ptr && rb_isspace(*ptr)) - ptr --; - - return LONG2NUM(ptr - start); - } - - long len; - len = 1; - end = RSTRING_END(str); - ptr = rb_str_subpos(str, FIX2LONG(offset), &len); - - if (!ptr || ptr >= end || ptr < start) - return LONG2NUM(RSTRING_LEN(str)); - - unsigned int c; - long count; - count = 0; - - while (ptr >= start && (c = rb_enc_codepoint(ptr, end, enc)) != 0 && rb_isspace(c)) { - char *ptr_; - if ((ptr_ = rb_enc_prev_char(start, ptr, end, enc)) == NULL) break; - ptr = ptr_; - count ++; - } - - return LONG2NUM(FIX2LONG(offset) - count); -} - -static int -is_ctrl_char(const unsigned int c) { - if ((c >= 32 && c <= 64 ) // !\"\#$%&'()*+,-./0123456789:;<=>?@ - || (c >= 65 && c <= 97 ) // ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_` - || (c >= 97 && c <= 126) // abcdefghijklmnopqrstuvwxyz{|}~ - || (c >= 161 && c <= 191) // ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ - || (c >= 192 && c <= 255) // ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ - || (c > 255)) - return 0; - - return 1; -} - -static VALUE -rb_lstrip_control_characters_offset(VALUE self, VALUE str, VALUE offset) { - Check_Type(str, T_STRING); - Check_Type(offset, T_FIXNUM); - - char *ptr, *start, *end; - rb_encoding *enc; - - start = RSTRING_PTR(str); - end = RSTRING_END(str); - enc = rb_enc_from_index(ENCODING_GET(str)); - - if (single_byte_optimizable(str, enc)) { - ptr = start + FIX2LONG(offset); - - if (ptr >= end) - return LONG2NUM(RSTRING_LEN(str)); - - while (ptr < end && is_ctrl_char((unsigned int) *ptr)) - ptr ++; - - return LONG2NUM(ptr - start); - } - - long len_, count; - len_ = 1; - count = 0; - ptr = rb_str_subpos(str, FIX2LONG(offset), &len_); - - if (!ptr || ptr >= end || ptr < start) - return Qnil; - - while (ptr < end) { - int len; - const unsigned int c = rb_enc_codepoint_len(ptr, end, &len, enc); - - if (c != 0 && !is_ctrl_char(c)) break; - ptr += len; - count ++; - } - - return LONG2NUM(FIX2LONG(offset) + count); -} - -/* - * call-seq: - * Stupidedi::Reader.is_control_character_at?("abc\n", 0) #=> false - * Stupidedi::Reader.is_control_character_at?("abc\n", 3) #=> true - * - * Returns true if the character at the given offset is a control character - * according to the X12 specification. This is equivalent to checking if - * `str[offset]` is a control character, but more efficient because it doesn't - * allocate a new string for the single char at `str[offset]` before testing. - */ -static VALUE -rb_is_control_character_at_p(VALUE self, VALUE str, VALUE offset) { - Check_Type(str, T_STRING); - Check_Type(offset, T_FIXNUM); - - long len; - char *ptr; - - len = 1; - ptr = rb_str_subpos(str, FIX2LONG(offset), &len); - - if (!ptr || !len) return Qnil; - if (len > 1) return Qfalse; // There be a multi-byte character here - - if (is_ctrl_char(*ptr)) - return Qtrue; - - return Qfalse; -} - -void Init_native_ext(void) { - VALUE rb_mStupidedi = rb_define_module("Stupidedi"); - VALUE rb_mReader = rb_define_module_under(rb_mStupidedi, "Reader"); - - rb_define_singleton_method(rb_mReader, "substr_eql?", rb_substr_eql_p, 5); - rb_define_singleton_method(rb_mReader, "lstrip_offset", rb_lstrip_offset, 2); - rb_define_singleton_method(rb_mReader, "rstrip_offset", rb_rstrip_offset, 2); - rb_define_singleton_method(rb_mReader, "lstrip_control_characters_offset", rb_lstrip_control_characters_offset, 2); - rb_define_singleton_method(rb_mReader, "is_control_character_at?", rb_is_control_character_at_p, 2); -} diff --git a/lib/stupidedi.rb b/lib/stupidedi.rb index 8995766b2..f698100c7 100644 --- a/lib/stupidedi.rb +++ b/lib/stupidedi.rb @@ -10,20 +10,18 @@ warn "terminal color disabled. gem install term-ansicolor to enable" end -$:.unshift(File.expand_path("..", __FILE__)) - -require "ruby/regexp" -require "ruby/array" -require "ruby/blank" -require "ruby/exception" -require "ruby/hash" -require "ruby/module" -require "ruby/object" -require "ruby/string" -require "ruby/to_d" -require "ruby/to_date" -require "ruby/to_time" -require "ruby/try" +require_relative "stupidedi/ruby/regexp" +require_relative "stupidedi/ruby/array" +require_relative "stupidedi/ruby/blank" +require_relative "stupidedi/ruby/exception" +require_relative "stupidedi/ruby/hash" +require_relative "stupidedi/ruby/module" +require_relative "stupidedi/ruby/object" +require_relative "stupidedi/ruby/string" +require_relative "stupidedi/ruby/to_d" +require_relative "stupidedi/ruby/to_date" +require_relative "stupidedi/ruby/to_time" +require_relative "stupidedi/ruby/try" module Stupidedi # @todo deprecated @@ -39,9 +37,11 @@ module Stupidedi autoload :Inspect, "stupidedi/inspect" autoload :Interchanges, "stupidedi/interchanges" autoload :Parser, "stupidedi/parser" + autoload :Position, "stupidedi/position" autoload :Reader, "stupidedi/reader" autoload :Schema, "stupidedi/schema" autoload :Sets, "stupidedi/sets" + autoload :Tokens, "stupidedi/tokens" autoload :TransactionSets, "stupidedi/transaction_sets" autoload :Values, "stupidedi/values" autoload :Versions, "stupidedi/versions" diff --git a/lib/stupidedi/editor.rb b/lib/stupidedi/editor.rb index ca73c6319..03f0c2661 100644 --- a/lib/stupidedi/editor.rb +++ b/lib/stupidedi/editor.rb @@ -1,27 +1,27 @@ # frozen_string_literal: true module Stupidedi module Editor - autoload :Result, "stupidedi/editor/result" - autoload :TA105, "stupidedi/editor/result" - autoload :AK905, "stupidedi/editor/result" - autoload :IK304, "stupidedi/editor/result" - autoload :IK403, "stupidedi/editor/result" - autoload :IK502, "stupidedi/editor/result" - autoload :ClaimStatus, "stupidedi/editor/result" - autoload :Warning, "stupidedi/editor/result" - autoload :Error, "stupidedi/editor/result" - autoload :ResultSet, "stupidedi/editor/result_set" + autoload :Result, "stupidedi/editor/result" + autoload :TA105, "stupidedi/editor/result" + autoload :AK905, "stupidedi/editor/result" + autoload :IK304, "stupidedi/editor/result" + autoload :IK403, "stupidedi/editor/result" + autoload :IK502, "stupidedi/editor/result" + autoload :ClaimStatus, "stupidedi/editor/result" + autoload :Warning, "stupidedi/editor/result" + autoload :Error, "stupidedi/editor/result" + autoload :ResultSet, "stupidedi/editor/result_set" - autoload :AbstractEd, "stupidedi/editor/abstract_ed" - autoload :FourOhOneEd, "stupidedi/editor/00401" - autoload :FortyTenEd, "stupidedi/editor/004010" - autoload :FiveOhOneEd, "stupidedi/editor/00501" - autoload :FiftyTenEd, "stupidedi/editor/005010" - autoload :TransactionSetEd, "stupidedi/editor/transaction_set_ed" - autoload :TransmissionEd, "stupidedi/editor/transmission_ed" + autoload :AbstractEd, "stupidedi/editor/abstract_ed" + autoload :FourOhOneEd, "stupidedi/editor/00401" + autoload :FortyTenEd, "stupidedi/editor/004010" + autoload :FiveOhOneEd, "stupidedi/editor/00501" + autoload :FiftyTenEd, "stupidedi/editor/005010" + autoload :TransactionSetEd, "stupidedi/editor/transaction_set_ed" + autoload :TransmissionEd, "stupidedi/editor/transmission_ed" - autoload :X12, "stupidedi/editor/X12" - autoload :X222, "stupidedi/editor/X222-HC837" + autoload :X12, "stupidedi/editor/X12" + autoload :X222, "stupidedi/editor/X222-HC837" # TA1 autoload :InterchangeAck, diff --git a/lib/stupidedi/exceptions.rb b/lib/stupidedi/exceptions.rb index 72fb9bd89..495ed146e 100644 --- a/lib/stupidedi/exceptions.rb +++ b/lib/stupidedi/exceptions.rb @@ -1,12 +1,11 @@ # frozen_string_literal: true module Stupidedi module Exceptions - autoload :StupidediError, "stupidedi/exceptions/stupidedi_error" - autoload :InvalidElementError, "stupidedi/exceptions/invalid_element_error" - autoload :InvalidSchemaError, "stupidedi/exceptions/invalid_schema_error" - autoload :OutputError, "stupidedi/exceptions/output_error" - autoload :ParseError, "stupidedi/exceptions/parse_error" - autoload :TokenizeError, "stupidedi/exceptions/tokenize_error" - autoload :ZipperError, "stupidedi/exceptions/zipper_error" + autoload :StupidediError, "stupidedi/exceptions/stupidedi_error" + autoload :InvalidElementError, "stupidedi/exceptions/invalid_element_error" + autoload :InvalidSchemaError, "stupidedi/exceptions/invalid_schema_error" + autoload :OutputError, "stupidedi/exceptions/output_error" + autoload :ParseError, "stupidedi/exceptions/parse_error" + autoload :ZipperError, "stupidedi/exceptions/zipper_error" end end diff --git a/lib/stupidedi/exceptions/tokenize_error.rb b/lib/stupidedi/exceptions/tokenize_error.rb deleted file mode 100644 index 9b8e24b37..000000000 --- a/lib/stupidedi/exceptions/tokenize_error.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - module Exceptions - class TokenizeError < StupidediError - end - end -end diff --git a/lib/stupidedi/interchanges.rb b/lib/stupidedi/interchanges.rb index f6464321e..338f93d46 100644 --- a/lib/stupidedi/interchanges.rb +++ b/lib/stupidedi/interchanges.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true module Stupidedi module Interchanges + autoload :Common, "stupidedi/interchanges/common" autoload :TwoHundred, "stupidedi/interchanges/00200" autoload :ThreeHundred, "stupidedi/interchanges/00300" autoload :FourHundred, "stupidedi/interchanges/00400" autoload :FourOhOne, "stupidedi/interchanges/00401" autoload :FiveOhOne, "stupidedi/interchanges/00501" - autoload :ElementTypes, "stupidedi/interchanges/element_types" end end diff --git a/lib/stupidedi/interchanges/00200.rb b/lib/stupidedi/interchanges/00200.rb index 1f7f7f57c..9affd20ce 100644 --- a/lib/stupidedi/interchanges/00200.rb +++ b/lib/stupidedi/interchanges/00200.rb @@ -6,7 +6,8 @@ module TwoHundred autoload :SegmentDefs, "stupidedi/interchanges/00200/segment_defs" autoload :InterchangeDef, "stupidedi/interchanges/00200/interchange_def" - ElementReqs = Versions::Common::ElementReqs + ElementReqs = Versions::Common::ElementReqs + ElementTypes = Interchanges::Common::ElementTypes end end end diff --git a/lib/stupidedi/interchanges/00200/element_defs.rb b/lib/stupidedi/interchanges/00200/element_defs.rb index 1e7ab89b7..471866c37 100644 --- a/lib/stupidedi/interchanges/00200/element_defs.rb +++ b/lib/stupidedi/interchanges/00200/element_defs.rb @@ -4,7 +4,7 @@ module Interchanges module TwoHundred module ElementDefs t = Versions::FortyTen::ElementTypes - c = ElementTypes + c = Interchanges::Common::ElementTypes s = Schema I01 = t::ID.new(:I01, "Authorization Information Qualifier", 2, 2, diff --git a/lib/stupidedi/interchanges/00300.rb b/lib/stupidedi/interchanges/00300.rb index 8efe5aead..4fed00128 100644 --- a/lib/stupidedi/interchanges/00300.rb +++ b/lib/stupidedi/interchanges/00300.rb @@ -6,7 +6,8 @@ module ThreeHundred autoload :SegmentDefs, "stupidedi/interchanges/00300/segment_defs" autoload :InterchangeDef, "stupidedi/interchanges/00300/interchange_def" - ElementReqs = Versions::Common::ElementReqs + ElementReqs = Versions::Common::ElementReqs + ElementTypes = Interchanges::Common::ElementTypes end end end diff --git a/lib/stupidedi/interchanges/00300/element_defs.rb b/lib/stupidedi/interchanges/00300/element_defs.rb index 44bb9a42a..13f62ded5 100644 --- a/lib/stupidedi/interchanges/00300/element_defs.rb +++ b/lib/stupidedi/interchanges/00300/element_defs.rb @@ -4,7 +4,7 @@ module Interchanges module ThreeHundred module ElementDefs t = Versions::ThirtyTen::ElementTypes - c = ElementTypes + c = Interchanges::Common::ElementTypes s = Schema I01 = t::ID.new(:I01, "Authorization Information Qualifier", 2, 2, diff --git a/lib/stupidedi/interchanges/00400.rb b/lib/stupidedi/interchanges/00400.rb index 06327e5cd..252456273 100644 --- a/lib/stupidedi/interchanges/00400.rb +++ b/lib/stupidedi/interchanges/00400.rb @@ -6,7 +6,8 @@ module FourHundred autoload :SegmentDefs, "stupidedi/interchanges/00400/segment_defs" autoload :InterchangeDef, "stupidedi/interchanges/00400/interchange_def" - ElementReqs = Versions::Common::ElementReqs + ElementReqs = Versions::Common::ElementReqs + ElementTypes = Interchanges::Common::ElementTypes end end end diff --git a/lib/stupidedi/interchanges/00400/element_defs.rb b/lib/stupidedi/interchanges/00400/element_defs.rb index 9ecabb019..df6c88bd1 100644 --- a/lib/stupidedi/interchanges/00400/element_defs.rb +++ b/lib/stupidedi/interchanges/00400/element_defs.rb @@ -4,7 +4,7 @@ module Interchanges module FourHundred module ElementDefs t = Versions::FortyTen::ElementTypes - c = ElementTypes + c = Interchanges::Common::ElementTypes s = Schema I01 = t::ID.new(:I01, "Authorization Information Qualifier", 2, 2, diff --git a/lib/stupidedi/interchanges/00401.rb b/lib/stupidedi/interchanges/00401.rb index 416b5f2f5..d7d0c0477 100644 --- a/lib/stupidedi/interchanges/00401.rb +++ b/lib/stupidedi/interchanges/00401.rb @@ -6,7 +6,8 @@ module FourOhOne autoload :SegmentDefs, "stupidedi/interchanges/00401/segment_defs" autoload :InterchangeDef, "stupidedi/interchanges/00401/interchange_def" - ElementReqs = Versions::Common::ElementReqs + ElementReqs = Versions::Common::ElementReqs + ElementTypes = Interchanges::Common::ElementTypes end end end diff --git a/lib/stupidedi/interchanges/00401/element_defs.rb b/lib/stupidedi/interchanges/00401/element_defs.rb index e337fa25c..e65852be9 100644 --- a/lib/stupidedi/interchanges/00401/element_defs.rb +++ b/lib/stupidedi/interchanges/00401/element_defs.rb @@ -4,7 +4,7 @@ module Interchanges module FourOhOne module ElementDefs t = Versions::FortyTen::ElementTypes - c = ElementTypes + c = Interchanges::Common::ElementTypes s = Schema I01 = t::ID.new(:I01, "Authorization Information Qualifier", 2, 2, diff --git a/lib/stupidedi/interchanges/00501.rb b/lib/stupidedi/interchanges/00501.rb index 3723e0837..9b54abef4 100644 --- a/lib/stupidedi/interchanges/00501.rb +++ b/lib/stupidedi/interchanges/00501.rb @@ -6,7 +6,8 @@ module FiveOhOne autoload :SegmentDefs, "stupidedi/interchanges/00501/segment_defs" autoload :InterchangeDef, "stupidedi/interchanges/00501/interchange_def" - ElementReqs = Versions::Common::ElementReqs + ElementReqs = Versions::Common::ElementReqs + ElementTypes = Interchanges::Common::ElementTypes end end end diff --git a/lib/stupidedi/interchanges/00501/element_defs.rb b/lib/stupidedi/interchanges/00501/element_defs.rb index fbc26cb8e..b127c23ba 100644 --- a/lib/stupidedi/interchanges/00501/element_defs.rb +++ b/lib/stupidedi/interchanges/00501/element_defs.rb @@ -4,7 +4,7 @@ module Interchanges module FiveOhOne module ElementDefs t = Versions::FiftyTen::ElementTypes - c = ElementTypes + c = Interchanges::Common::ElementTypes s = Schema I01 = t::ID.new(:I01, "Authorization Information Qualifier", 2, 2, diff --git a/lib/stupidedi/interchanges/common.rb b/lib/stupidedi/interchanges/common.rb new file mode 100644 index 000000000..204234991 --- /dev/null +++ b/lib/stupidedi/interchanges/common.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module Stupidedi + module Interchanges + module Common + autoload :ElementTypes, "stupidedi/interchanges/common/element_types" + end + end +end diff --git a/lib/stupidedi/interchanges/common/element_types.rb b/lib/stupidedi/interchanges/common/element_types.rb new file mode 100644 index 000000000..23add7315 --- /dev/null +++ b/lib/stupidedi/interchanges/common/element_types.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +module Stupidedi + module Interchanges + module Common + module ElementTypes + autoload :SpecialAN, "stupidedi/interchanges/common/element_types/special_an" + autoload :SpecialVal, "stupidedi/interchanges/common/element_types/special_an" + autoload :Separator, "stupidedi/interchanges/common/element_types/separator" + autoload :SeparatorVal, "stupidedi/interchanges/common/element_types/separator" + end + end + end +end diff --git a/lib/stupidedi/interchanges/common/element_types/separator.rb b/lib/stupidedi/interchanges/common/element_types/separator.rb new file mode 100644 index 000000000..53dc1e979 --- /dev/null +++ b/lib/stupidedi/interchanges/common/element_types/separator.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Interchanges + module Common + module ElementTypes + class Separator < Versions::Common::ElementTypes::AN + def companion + SeparatorVal + end + end + + class SeparatorVal < Values::SimpleElementVal + def_delegators :@value, :to_s, :length, :== + + def initialize(value, usage, position) + @value = value + super(usage, position) + end + + # @return [SeparatorVal] + def copy(changes = {}) + SeparatorVal.new \ + changes.fetch(:value, @value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) + end + + def valid? + true + end + + def empty? + @value.blank? + end + + def too_short? + @value.length < 1 + end + + def too_long? + @value.length > 1 + end + + def separator? + true + end + + # @return [String] + def to_x12(truncate = true) + @value.to_s + end + + def inspect + id = definition.try{|d| ansi.bold("[#{d.id}]") } + ansi.element("SeparatorVal.value#{id}") + "(#{@value || "nil"})" + end + end + + class << SeparatorVal + # @group Constructors + ################################################################### + + # @raise NoMethodError + def empty(usage, position) + SeparatorVal.new(nil, usage, position) + end + + # @return [SeparatorVal] + def value(character, usage, position) + SeparatorVal.new(character, usage, position) + end + + # @endgroup + ################################################################### + end + end + end + end +end diff --git a/lib/stupidedi/interchanges/common/element_types/special_an.rb b/lib/stupidedi/interchanges/common/element_types/special_an.rb new file mode 100644 index 000000000..dc92c4917 --- /dev/null +++ b/lib/stupidedi/interchanges/common/element_types/special_an.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + module Interchanges + module Common + module ElementTypes + AN = Versions::Common::ElementTypes::AN + StringVal = Versions::Common::ElementTypes::StringVal + + class SpecialAN < AN + def companion + SpecialVal + end + end + + # + # The specifications declare ISA02 and ISA04 are required elements, but + # they only have "meaningful information" when ISA01 and ISA03 qualifiers + # aren't "00". + # + # Without specifications regarding what should be entered in these + # required elements, our best option is to defer to convention, which + # seems to be that these elements should have 10 spaces -- which means + # they're blank. So we can't really make these elements required! Even + # stupider, these are the only blank elements that should be written out + # space-padded to the min_length -- every other element should be + # collapsed to an empty string. + # + # So this "Special" class overrides to_x12 for empty values, but it + # otherwise looks and acts like a normal AN and StringVal + # + class SpecialVal < StringVal + class Empty < StringVal::Empty + # @return [String] + def to_x12 + " " * definition.min_length + end + end + + class NonEmpty < StringVal::NonEmpty + def too_short? + false + end + end + end + end + end + end +end diff --git a/lib/stupidedi/interchanges/element_types.rb b/lib/stupidedi/interchanges/element_types.rb deleted file mode 100644 index 2c2fa58bb..000000000 --- a/lib/stupidedi/interchanges/element_types.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - module Interchanges - module ElementTypes - autoload :SpecialAN, "stupidedi/interchanges/element_types/special_val" - autoload :SpecialVal, "stupidedi/interchanges/element_types/special_val" - autoload :Separator, "stupidedi/interchanges/element_types/separator_val" - autoload :SeparatorVal, "stupidedi/interchanges/element_types/separator_val" - end - end -end diff --git a/lib/stupidedi/interchanges/element_types/separator_val.rb b/lib/stupidedi/interchanges/element_types/separator_val.rb deleted file mode 100644 index 5b0c03a15..000000000 --- a/lib/stupidedi/interchanges/element_types/separator_val.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Interchanges - module ElementTypes - class Separator < Versions::Common::ElementTypes::AN - def companion - SeparatorVal - end - end - - class SeparatorVal < Values::SimpleElementVal - def_delegators :@value, :to_s, :length, :== - - def initialize(value, usage, position) - @value = value - super(usage, position) - end - - # @return [SeparatorVal] - def copy(changes = {}) - SeparatorVal.new \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - - def valid? - true - end - - def empty? - @value.blank? - end - - def too_short? - @value.length < 1 - end - - def too_long? - @value.length > 1 - end - - def separator? - true - end - - # @return [String] - def to_x12(truncate = true) - @value.to_s - end - - def inspect - id = definition.try{|d| ansi.bold("[#{d.id}]") } - ansi.element("SeparatorVal.value#{id}") + "(#{@value || "nil"})" - end - end - - class << SeparatorVal - # @group Constructors - ################################################################### - - # @raise NoMethodError - def empty(usage, position) - SeparatorVal.new(nil, usage, position) - end - - # @return [SeparatorVal] - def value(character, usage, position) - SeparatorVal.new(character, usage, position) - end - - # @endgroup - ################################################################### - end - end - end -end diff --git a/lib/stupidedi/interchanges/element_types/special_val.rb b/lib/stupidedi/interchanges/element_types/special_val.rb deleted file mode 100644 index deaf2099e..000000000 --- a/lib/stupidedi/interchanges/element_types/special_val.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -module Stupidedi - using Refinements - - module Interchanges - module ElementTypes - AN = Versions::Common::ElementTypes::AN - StringVal = Versions::Common::ElementTypes::StringVal - - # - # The specifications declare ISA02 and ISA04 are required elements, but - # they only have "meaningful information" when ISA01 and ISA03 qualifiers - # aren't "00". - # - # Without specifications regarding what should be entered in these - # required elements, our best option is to defer to convention, which - # seems to be that these elements should have 10 spaces -- which means - # they're blank. So we can't really make these elements required! Even - # stupider, these are the only blank elements that should be written out - # space-padded to the min_length -- every other element should be - # collapsed to an empty string. - # - # So this "Special" class overrides to_x12 for empty values, but it - # otherwise looks and acts like a normal AN and StringVal - # - class SpecialAN < AN - def companion - SpecialVal - end - end - - class SpecialVal < StringVal - class Empty < StringVal::Empty - # @return [String] - def to_x12 - " " * definition.min_length - end - end - - class NonEmpty < StringVal::NonEmpty - def too_short? - false - end - end - end - end - end -end diff --git a/lib/stupidedi/parser/builder_dsl.rb b/lib/stupidedi/parser/builder_dsl.rb index aee641e25..abaaec33f 100644 --- a/lib/stupidedi/parser/builder_dsl.rb +++ b/lib/stupidedi/parser/builder_dsl.rb @@ -26,7 +26,7 @@ class BuilderDsl :empty?, :first?, :last?, :deterministic? def initialize(machine, strict = true) - @position = Reader::StacktracePosition + @position = Position::StacktracePosition @machine = machine @strict = strict @separators = Reader::Separators.blank diff --git a/lib/stupidedi/parser/constraint_table.rb b/lib/stupidedi/parser/constraint_table.rb index f6bcf4c6d..55d48f08e 100644 --- a/lib/stupidedi/parser/constraint_table.rb +++ b/lib/stupidedi/parser/constraint_table.rb @@ -51,7 +51,7 @@ def critique(segment_tok, segment_uses) end # Performs no filtering of the {Instruction} list. This is used when there - # already is a single {Instruction} or when a {Reader::SegmentTok} doesn't + # already is a single {Instruction} or when a {Tokens::SegmentTok} doesn't # provide any more information to filter the list. # class Stub < ConstraintTable @@ -402,7 +402,7 @@ def build_distinct(total, n, m, instructions) # the value of the `n`-th component from the `n`-th element. When the # value is blank, the function returns `nil`. # - # @param [Array] element_toks + # @param [Array] element_toks # @param [Integer] m # @param [Integer, nil] n # diff --git a/lib/stupidedi/parser/generation.rb b/lib/stupidedi/parser/generation.rb index b056d70dd..d59cd5828 100644 --- a/lib/stupidedi/parser/generation.rb +++ b/lib/stupidedi/parser/generation.rb @@ -10,33 +10,20 @@ module Generation # {StateMachine} along with the result of the last attempt # to read a segment. # - # The `nondeterminism` argument specifies a limit on how many - # parse trees can be built simultaneously due to ambiguity in - # the input and/or specification. This prevents runaway memory - # CPU consumption (see GH-129), and will return a {Reader::Result.failure} - # once exceeded. - # - # The default value is 1, resulting in an error if any input - # is ambiguous. - # - # NOTE: The error is detected *after* the resources are already - # been consumed. The extra parse trees are returned (in memory) - # via the {StateMachine} to aide diagnosis. - # - # @param tokenizer [Reader::Tokenizer] + # @param tokenizer [Tokens::Tokenizer] # @param options # - # @yield [Reader::IgnoredTok] - # @return [(StateMachine, Reader::Tokenizer::Result)] + # @yield [Tokens::IgnoredTok] + # @return [(StateMachine, Tokens::Tokenizer::Result)] def read(tokenizer, options = {}) #imit = options.fetch(:nondeterminism, 1) machine = self.dup return machine, tokenizer.each do |token| case token - when Reader::SegmentTok + when Tokens::SegmentTok machine.insert!(token, false, tokenizer) - when Reader::IgnoredTok + when Tokens::IgnoredTok yield token if block_given? end end diff --git a/lib/stupidedi/parser/state_machine.rb b/lib/stupidedi/parser/state_machine.rb index a671bbd8d..5d6f7b49a 100644 --- a/lib/stupidedi/parser/state_machine.rb +++ b/lib/stupidedi/parser/state_machine.rb @@ -2,6 +2,8 @@ module Stupidedi using Refinements + # TODO: Remove support for ambiguous input / multiple parse trees + # module Parser class StateMachine include Inspect diff --git a/lib/stupidedi/parser/tokenization.rb b/lib/stupidedi/parser/tokenization.rb index ed2f54c71..209bec2e1 100644 --- a/lib/stupidedi/parser/tokenization.rb +++ b/lib/stupidedi/parser/tokenization.rb @@ -11,14 +11,14 @@ module Tokenization # # @return [void] def repeated(*elements) - [:repeated, elements, Reader::StacktracePosition.build] + [:repeated, elements, Position::StacktracePosition.build] end # Generates a composite element # # @return [void] def composite(*components) - [:composite, components, Reader::StacktracePosition.build] + [:composite, components, Position::StacktracePosition.build] end ######################################################################### @@ -28,7 +28,7 @@ def composite(*components) # # @return [void] def blank - [:blank, nil, Reader::StacktracePosition.build] + [:blank, nil, Position::StacktracePosition.build] end # Generates a blank element and asserts that the element's usage @@ -38,7 +38,7 @@ def blank # # @return [void] def not_used - [:not_used, nil, Reader::StacktracePosition.build] + [:not_used, nil, Position::StacktracePosition.build] end # Generates the only possible value an element may have, which may @@ -50,7 +50,7 @@ def not_used # # @return [void] def default - [:default, nil, Reader::StacktracePosition.build] + [:default, nil, Position::StacktracePosition.build] end # @endgroup @@ -58,7 +58,7 @@ def default private - # @return [Reader::SegmentTok] + # @return [Tokens::SegmentTok] def mksegment_tok(segment_dict, id, elements, position) id = id.to_sym element_toks = [] @@ -81,7 +81,7 @@ def mksegment_tok(segment_dict, id, elements, position) "#{id}#{element_idx} is assumed to be a simple element" end - element_toks << mksimple_tok(e_tag, e_position || position) + element_toks << Tokens::SimpleElementTok.build(e_tag, e_position || position) end else segment_def = segment_dict.at(id) @@ -119,15 +119,15 @@ def mksegment_tok(segment_dict, id, elements, position) "#{id}#{element_idx} is a non-repeatable simple element" end - element_toks << mksimple_tok(e_tag, e_position || position) + element_toks << Tokens::SimpleElementTok.build(e_tag, e_position || position) end end end - Reader::SegmentTok.build(id, element_toks, position) + Tokens::SegmentTok.build(id, element_toks, position) end - # @return [Reader::RepeatedElementTok] + # @return [Tokens::RepeatedElementTok] def mkrepeated_tok(elements, element_use, designator, position) element_toks = [] @@ -147,14 +147,14 @@ def mkrepeated_tok(elements, element_use, designator, position) "#{designator} is a simple element" end - element_toks << mksimple_tok(e_tag, e_position || position) + element_toks << Tokens::SimpleElementTok.build(e_tag, e_position || position) end end - Reader::RepeatedElementTok.build(element_toks, position) + Tokens::RepeatedElementTok.build(element_toks, position) end - # @return [Reader::CompositeElementTok] + # @return [Tokens::CompositeElementTok] def mkcomposite_tok(components, composite_use, designator, position) component_uses = composite_use.definition.component_uses @@ -172,20 +172,10 @@ def mkcomposite_tok(components, composite_use, designator, position) "#{designator}-#{component_idx} is a component element" end - component_toks << mkcomponent_tok(c_tag, c_position || position) + component_toks << Tokens::ComponentElementTok.build(c_tag, c_position || position) end - Reader::CompositeElementTok.build(component_toks, position) - end - - # @return [Reader::ComponentElementTok] - def mkcomponent_tok(value, position) - Reader::ComponentElementTok.build(value, position) - end - - # @return [Reader::SimpleElementTok] - def mksimple_tok(value, position) - Reader::SimpleElementTok.build(value, position) + Tokens::CompositeElementTok.build(component_toks, position) end # @endgroup diff --git a/lib/stupidedi/position.rb b/lib/stupidedi/position.rb new file mode 100644 index 000000000..2f4149833 --- /dev/null +++ b/lib/stupidedi/position.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true +module Stupidedi + using Refinements + + # + # This mixin is intended to be used with a user-defined Struct. This + # scheme allows customization of what position information is tracked via + # the tokenizer and passed along into the parse tree. + # + # For example, + # + # TinyPosition = Struct.new(:offset) + # TinyPosition.include(Stupidedi::Position) + # + # anonClass = Struct.new(:name, :line) + # anonClass.include(Stupidedi::Position) + # + # class BigPosition < Struct.new(:name, :line, :column, :offset) + # include Stupidedi::Position + # + # # Return 50 chars before and after this position + # def context(input) + # input[offset - 50, 50] + " >> " + input[offset, 50] + # end + # end + # + # Normally it would be fine to just track everything and let the user + # disregard what's not interesting. However, to conserve memory, it's + # beneficial to track the minimum. + # + # Here's how the memory footprint works out: + # name: roughly 20 bytes + length of string in bytes, but minimum is 40b + # line: represented directly, so no overhead besides the VALUE struct + # column: same + # offset: same + # + # The Position object itself also consumes 40 bytes, as long as it has three + # or fewer fields. Once a fourth field is added, another 40 bytes are + # consumed. + # + # So tracking three or fewer numeric-only fields consumes 40 bytes. But + # adding the fourth field increases that to 100 bytes + length of name. + # Tracking the name and two or less integer fields consumes 60 + length of + # the name string. + # + # Because a position is attached to each individual part of syntax (the + # start of a segment, the start of each individual element), this can add + # up to a lot of space. In different situations, the user may independently + # know the file name and not need it stored here. Or they may not care + # about the offset and manage with only line and column numbers. + # + # The default NoPosition implementation is provided which still consumes + # 40 bytes, but only one instance is created. + # + module Position + autoload :NoPosition, "stupidedi/position/no_position" + autoload :OffsetPosition, "stupidedi/position/offset_position" + autoload :StacktracePosition, "stupidedi/position/stacktrace_position" + + def self.included(base) + base.__send__(:extend, ClassMethods) + base.__send__(:include, InstanceMethods) + end + + module ClassMethods + def build(name) + new.reset(name, 1, 1, 0) + end + + def caller(offset = 1) + name, line, = Stupidedi.caller(offset + 1) + new.reset(name, line, nil, nil) + end + end + + module InstanceMethods + # @return [String] + def to_s + parts = [] + parts << name if respond_to?(:name) and name.present? + parts << "line #{line}" if respond_to?(:line) + parts << "column #{column}" if respond_to?(:column) + parts << "offset #{offset}" if respond_to?(:offset) + parts.join(", ") + end + + # Calculate the new position if we started on the current position and + # then read the given input. + # + # @param input [#length, #count, #rindex] + # @return [self.class] + def advance(input) + length_ = input.length + r_name = respond_to?(:name) + r_line = respond_to?(:line) + r_column = respond_to?(:column) + r_offset = respond_to?(:offset) + + if r_line or r_column + lines_ = input.count("\n") + end + + if r_column + column_ = if lines_.zero? + length_ + column + else + length_ - input.rindex("\n") + end + end + + # Use `clone` because it's already implemented for Struct and any other + # class. Otherwise we would prefer our own `#copy(changes)` convention. + clone.reset \ + r_name ? name : nil, + r_line ? line + lines_ : nil, + r_column ? column_ : nil, + r_offset ? offset + length_ : nil + end + + # @return self + def reset(name, line, column, offset) + self[:name] = name if respond_to?(:name) + self[:line] = line if respond_to?(:line) + self[:column] = column if respond_to?(:column) + self[:offset] = offset if respond_to?(:offset) + self + end + end + end +end diff --git a/lib/stupidedi/reader/position/no_position.rb b/lib/stupidedi/position/no_position.rb similarity index 97% rename from lib/stupidedi/reader/position/no_position.rb rename to lib/stupidedi/position/no_position.rb index 98c57acfe..de23fff4a 100644 --- a/lib/stupidedi/reader/position/no_position.rb +++ b/lib/stupidedi/position/no_position.rb @@ -2,7 +2,7 @@ module Stupidedi using Refinements - module Reader + module Position # This provides a stub that acts like a Position but doesn't compute # or retain any information. Because it has no state, `NoPosition.new` # returns the class itself, which implements `#to_s` and `#advance`. @@ -41,4 +41,4 @@ def advance(input) end end end -end \ No newline at end of file +end diff --git a/lib/stupidedi/reader/position/offset_position.rb b/lib/stupidedi/position/offset_position.rb similarity index 96% rename from lib/stupidedi/reader/position/offset_position.rb rename to lib/stupidedi/position/offset_position.rb index 49db6fbdf..7f3d46e9e 100644 --- a/lib/stupidedi/reader/position/offset_position.rb +++ b/lib/stupidedi/position/offset_position.rb @@ -2,7 +2,8 @@ module Stupidedi using Refinements - module Reader + module Position + # # Tracks only the number of characters from the start of the input # # NOTE: There aren't any instances of this class, because `build` returns @@ -23,4 +24,4 @@ def build(*args) end end end -end \ No newline at end of file +end diff --git a/lib/stupidedi/reader/position/stacktrace_position.rb b/lib/stupidedi/position/stacktrace_position.rb similarity index 97% rename from lib/stupidedi/reader/position/stacktrace_position.rb rename to lib/stupidedi/position/stacktrace_position.rb index 9be9b94b7..3dd2de592 100644 --- a/lib/stupidedi/reader/position/stacktrace_position.rb +++ b/lib/stupidedi/position/stacktrace_position.rb @@ -2,7 +2,8 @@ module Stupidedi using Refinements - module Reader + module Position + # # Tracks filename, method name, and line number of a frame in the stack. # Usually the stack frame just above the stupidedi boundary is chosen, so # users will have the location in their source where a value was generated. @@ -45,4 +46,4 @@ def build(*args) end end end -end \ No newline at end of file +end diff --git a/lib/stupidedi/reader.rb b/lib/stupidedi/reader.rb index c778f11b8..ba3422383 100644 --- a/lib/stupidedi/reader.rb +++ b/lib/stupidedi/reader.rb @@ -1,159 +1,236 @@ # frozen_string_literal: true -# encoding: ISO-8859-1 +# encoding: utf-8 + module Stupidedi using Refinements - if RUBY_PLATFORM !~ /java/ - require "stupidedi/reader/native_ext" - end - module Reader - autoload :Separators, "stupidedi/reader/separators" - autoload :SegmentDict, "stupidedi/reader/segment_dict" - - autoload :IgnoredTok, "stupidedi/reader/tokens/ignored_tok" - autoload :SegmentTok, "stupidedi/reader/tokens/segment_tok" - autoload :SimpleElementTok, "stupidedi/reader/tokens/simple_element_tok" - autoload :ComponentElementTok, "stupidedi/reader/tokens/component_element_tok" - autoload :CompositeElementTok, "stupidedi/reader/tokens/composite_element_tok" - autoload :RepeatedElementTok, "stupidedi/reader/tokens/repeated_element_tok" - - autoload :Input, "stupidedi/reader/input" - autoload :Position, "stupidedi/reader/position" - autoload :NoPosition, "stupidedi/reader/position/no_position" - autoload :OffsetPosition, "stupidedi/reader/position/offset_position" - autoload :StacktracePosition, "stupidedi/reader/position/stacktrace_position" - autoload :Tokenizer, "stupidedi/reader/tokenizer" + autoload :SegmentDict, "stupidedi/reader/segment_dict" + autoload :Separators, "stupidedi/reader/separators" + autoload :Input, "stupidedi/reader/input" autoload :Pointer, "stupidedi/reader/pointer" - autoload :ArrayPtr, "stupidedi/reader/pointer" - autoload :StringPtr, "stupidedi/reader/pointer" - - # @private - # @return [Regexp] - R_BASIC = /[A-Z0-9!"&'()*+,.\/:;?= -]/.freeze + autoload :StringPtr, "stupidedi/reader/string_ptr" - # @private - # @return [Regexp] - R_EXTENDED = /[a-z%@\[\]_{}\\|<>~^`#\$ÀÃÂÄàáâäÈÉÊèéêëÌÃÎìíîïÒÓÔÖòóôöÙÚÛÜùúûüÇçÑñ¿¡]/.freeze - - # @private - # @return [Regexp] - R_EITHER = Regexp.union(R_BASIC, R_EXTENDED) - - # @private - # @return [String] - C_BYTES = (0..255).inject(""){|string, c| string + [c].pack('U') }.freeze - - # @private - # @return [Hash] - H_BASIC = C_BYTES.scan(R_BASIC).inject({}){|h,c| h[c] = nil; h }.freeze - - # @private - # @return [Hash] - H_EXTENDED = C_BYTES.scan(R_EXTENDED).inject({}){|h,c| h[c] = nil; h }.freeze - - # @private - # @return [Hash] - H_EITHER = C_BYTES.scan(R_EITHER).inject({}){|h,c| h[c] = nil; h }.freeze + if RUBY_PLATFORM !~ /java/ + require "stupidedi/reader/native_ext" + end - # @private - # @return [Regexp] - #_CONTROL = Regexp.new("[^#{Regexp.quote(H_EITHER.keys.join)}]") + # RFI 2142 - component elements are not repeatable + # RFI 0001 - the X12.6 standard prohibits special characters in ID elements + # RFI 0028 - non-printable chars can be discarded (except delimiters and binary elements) + # RFI 0650 - control chars can be used for delimeters (element, sub-element etc) + # RFI 0669 - trading partner agreements lie outside X12 standard, they can do anything + # RFI 1207 - X12 is a character based standard, references to byte or bytes is incorrect + # RFI 1815 - none of the four delimeters can be used in data elements + # RFI 2026 - delimiters cannot appear as data except in binary elements + # RFI 2205 - space-only values are allowed in ISA02 and ISA04 to meet min length + # RFI 2207 - delimeters can be CR/LF, trading partner agreements are outside X12 scope + # RFI 2212 - unless TR3 explicitly states only chars from X12.6, chars are negotiable between patrners + # RFI 2264 - same as 2212 + + # http://members.x12.org/technical-assessment/x12-6-dm-042316.pdf + # https://en.wikipedia.org/wiki/ISO_8859#Table + # https://en.wikipedia.org/wiki/EBCDIC#Code_page_layout + # https://www.reddit.com/r/ruby/comments/5zhinx/ruby_24_has_optimized_lstrip_and_strip_methods/dezarc7/ + # https://blog.bigbinary.com/2017/03/14/ruby-2-4-has-optimized-lstrip-and-strip-methods.html + + # X12.6 3.3 Character Set + # + # NOTE: The X12 standards are graphic-character oriented, so any common + # character encoding schemes may be used as long as a common mapping is + # available. No collating sequence is to be assumed in any definition used + # in the standard since no single character code is specified and no other + # means of specifying a sequence is provided. + # + # 3.3.1 Basic Character Set + # + # Uppercase letters A to Z: + # "A" ... "Z" + # + # Digits 0 to 9: + # "0" ... "9" + # + # Special characters: + # "!" """ "&" "'" "(" ")" "*" "+" "," "-" "." "/" ":" ";" "?" "=" + # + # NOTE: Special characters are removed from this category when used as + # delimiters. + # + # Space character: + # " " + # + # 3.3.2 Extended Character Set + # + # Lowercase letters from a to z: + # "a" ... "z" + # + # Other special characters: + # "%" "@" "[" "]" "_" "{" "}" "\" "|" "<" ">" "~" "^" "`" + # + # NOTE: Special characters are removed from this category when used as + # delimiters. + # + # National characters: + # "#" "$" + # + # Select language characters: + # "À" "Ã" "Â" "Ä" "à" "á" "â" "ä" "È" "É" "Ê" "è" "é" "ê" "ë" + # "ÃŒ" "Ã" "ÃŽ" "ì" "í" "î" "ï" "Ã’" "Ó" "Ô" "Ö" "ò" "ó" "ô" "ö" + # "Ù" "Ú" "Û" "Ãœ" "ù" "ú" "û" "ü" "Ç" "ç" "Ñ" "ñ" "¿" "¡" + # + # NOTE: See ISO document 8859-1 (Latin-1 Alphabet) for an example of an + # encoding of select language characters. + # + # Other language characters: + # Any graphical character that is specified in any of the following ISO + # documents that is not defined in any of the previous BNF productions: + # + # * ISO 646 -- family of 7-bit encodings NOT SUPPORTED + # * ISO 8859-1 -- western european ✅ + # * ISO 8859-2 -- central/eastern european languages ✅ + # * ISO 8859-5 -- latin/cyrillic alphabet ✅ + # * ISO 8859-7 -- greek ✅ + # * ISO 8859-3 -- south european ✅ + # * ISO 8859-4 -- north european ✅ + # * ISO 8859-6 -- latin/arabic ✅ + # * ISO 8859-8 -- latin/hebrew ✅ + # * ISO 8859-9 -- turkish ✅ + # * ISO 8859-15 -- western european ✅ + # *[ISO 8859-10]-- nordic ✅ + # *[ISO 8859-11]-- thai ✅ + # *[ISO 8859-12]-- devanagari NOT SUPPORTED + # *[ISO 8859-13]-- baltic rim ✅ + # *[ISO 8859-14]-- celtic ✅ + # *[ISO 8859-15]-- ✅ + # *[ISO 8859-16]-- ✅ + # * ISO 2022:JP -- stateful variable width encoding ✅ + # * ISO 2022:JP2-- stateful variable width encoding ✅ + # * ISO 2375 -- NOT SUPPORTED + # * ISO 10646 -- unicode ✅ + # + # X12.5 3 Control Characters + # + # Two control character groups are specified which have only restricted + # usage. The common notation for these is also provided, together with the + # character coding in three common alphabets represented by their + # hexadecimal values. In the following table IA5 represents CCITT V.3 + # International Alphabet 5. + # + # 3.1 Terminal Control Set + # + # The terminal control set includes characters used to control terminal + # display characteristics. Thesecharacters usually will not have an effect + # on a transmission system. These are presented by the syntacticentity of + # . + # + # EBCDIC ASCII IA5 + # BEL 0x2f 0x07 0x07 + # HT 0x05 0x09 0x09 + # LF 0x25 0x0a 0x0a + # VT 0x0b 0x0b 0x0b + # FF 0x0c 0x0c 0x0c + # CR 0x0d 0x0d 0x0d + # NL 0x15 NOTE ---- + # FS 0x1c 0x1c 0x1c + # GS 0x1d 0x1d 0x1d + # RS 0x1e 0x1e 0x1e + # US 0x1f 0x1f 0x1f + # + # NOTE: The equivalent representation for the EBCDIC "new line" character + # is the character pair "carriage return" "line feed" in ASCII or IA5. If + # mapped in this manner, the superfluous “line feed†is ignored, and the + # "carriage return" is treated as the delimiter. For historical reasons, + # many systems support this mapping for use as the . + # + # 3.2 Communication Control Set + # + # The communication control set includes those that may have an effect on + # a transmission system. Theseare represented by the syntactic entity of + # . + # + # EBCDIC ASCII IA5 + # GS 0x1d 0x1d 0x1d + # RS 0x1e 0x1e 0x1e + # US 0x1f 0x1f 0x1f + # SOH 0x01 0x01 0x01 + # STX 0x02 0x02 0x02 + # ETX 0x03 0x03 0x03 + # EOT 0x37 0x04 0x04 + # ENQ 0x2d 0x05 0x05 + # ACK 0x2e 0x06 0x06 + # DC1 0x11 0x11 0x11 + # DC2 0x12 0x12 0x12 + # DC3 0x13 0x13 0x13 + # DC4 0x3c 0x14 0x14 + # NAK 0x3d 0x15 0x15 + # SYN 0x32 0x16 0x16 + # ETB 0x25 0x17 0x17 + + # Delimiters are specified in the interchange header segment, ISA.The ISA + # segment canbe considered in implementations compliant with this guide (see + # Appendix C, ISASegment Note 1) to be a 105 byte fixed length segment, + # followed by a segment terminator.The data element separator is byte number + # 4; the repetition separator is byte number83; the component element + # separator is byte number 105; and the segment terminatoris the byte that + # immediately follows the component element separator + # + # Once specified in the interchange header, the delimiters are not to be + # used in a dataelement value elsewhere in the interchange. For consistency, + # this implementation guideuses the delimiters shown in Table B.5 - + # Delimiters, in all examples of EDI transmissions. + + # 3.5.1.1 Numeric + # + # ::= [-] + # ::= {} + # + # 3.5.1.2 Decimal Number + # + # ::= [-] [] + # ::= E + # ::= [-] + # ::= | . | . {} + # + # 3.5.1.3 Identifier + # + # ::= {} {} + # ::= | + # + # 3.5.1.4 String + # + # ::= { | } { |} + # ::= | | | | | | + # + # 3.5.1.5 Date + # + # ::= | + # ::= + # ::= + # ::= "01" | "02" | ... | "12" + # ::= "01" | "02" | ... | "31" + # + # 3.5.1.6 Time + # + #