bychar 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4955440b9eff7331dd966ba6a381b032b24e8f1a
4
+ data.tar.gz: 345f1b882ce2aef85acf391dd55d8a8aa8db2a33
5
+ SHA512:
6
+ metadata.gz: 96879e770df2a03d227834db03c6c64ec7b501bff447c01e25ccd9ecfead0be28408ef5d0f92b6eacfa3380a16b72c8e8d9ef3142e277601b5ecbe2f26462f89
7
+ data.tar.gz: 89a78a2e211f15cc54a0fe8e21b4ef21338ece9662d16aa27ba5d228f581cdc84a576f2e35cd9151261cbc0ffe889cb81d00df723ba29f842c835e9b29b65c10
@@ -2,5 +2,7 @@ rvm:
2
2
  - 1.8.7
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.2
5
7
  - jruby
6
8
  - ruby-head
data/Gemfile CHANGED
@@ -1,13 +1,9 @@
1
1
  source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
2
 
6
- # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
8
3
  group :development do
4
+ gem "rake"
9
5
  gem "rdoc", "~> 3.12"
10
- gem "bundler"
11
- gem "jeweler", "~> 1.8.4"
6
+ gem 'test-unit'
7
+ gem "jeweler", '1.8.7'
12
8
  gem "flexmock", "~>0.8"
13
9
  end
@@ -0,0 +1,60 @@
1
+ # bychar
2
+
3
+ A simple IO wrapper for libraries that tend to read the IO in the following way:
4
+
5
+ * Only forward
6
+ * By a single character
7
+ * Without skipping
8
+
9
+ This is how most of recursive-descent parsers and stateful parsers would work. However,
10
+ reading a simple IO object byte by byte in Ruby is very slow. Orders slower in fact.
11
+ Therefore, this gem creates a simple wrapper that you can get like this:
12
+
13
+ wrapper = Bychar.wrap(io)
14
+ while c = wrapper.read_one_char
15
+ # Do your thang
16
+ end
17
+
18
+ The wrapper will cache some bytes from the passed IO object which will make parsing
19
+ faster when you advance your parser char by char.
20
+
21
+ ## Performance
22
+
23
+ I told you reading char by char is not the best strategy. On Ruby 1.9:
24
+
25
+ Bare IO using read(1): 3.400000 1.900000 5.300000 ( 5.300798)
26
+ Bychar using StringIO: 1.790000 0.010000 1.800000 ( 1.795277)
27
+ Bychar using a String buffer: 1.760000 0.000000 1.760000 ( 1.760440)
28
+ Platform-picked Bychar.wrap(io) Bychar::ReaderStrbuf: 1.770000 0.010000 1.780000 ( 1.776339)
29
+
30
+ while on Ruby 1.8 it is kinda sad:
31
+
32
+ Bare IO using read(1): 2.380000 0.000000 2.380000 ( 2.393260)
33
+ Bychar using StringIO: 2.270000 0.010000 2.280000 ( 2.275631)
34
+ Bychar using a String buffer: 2.920000 0.000000 2.920000 ( 2.924574)
35
+ Platform-picked Bychar.wrap(io) Bychar::ReaderBare: 2.380000 0.010000 2.390000 ( 2.384414)
36
+
37
+ And on JRuby it's different still:
38
+
39
+ Bare IO using read(1): 1.610000 0.040000 1.650000 ( 1.180000)
40
+ Bychar using StringIO: 0.730000 0.020000 0.750000 ( 0.603000)
41
+ Bychar using a String buffer: 1.040000 0.040000 1.080000 ( 0.790000)
42
+ Platform-picked Bychar.wrap(io) Bychar::ReaderIOBuf: 0.560000 0.030000 0.590000 ( 0.546000)
43
+
44
+ As you can see, Bychar will do some work to pick an implementation that makes sense for your Ruby platform.
45
+
46
+ ## Contributing to bychar
47
+
48
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
49
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
50
+ * Fork the project.
51
+ * Start a feature/bugfix branch.
52
+ * Commit and push until you are happy with your contribution.
53
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
54
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
55
+
56
+ ## Copyright
57
+
58
+ Copyright (c) 2013 Julik Tarkhanov. See LICENSE.txt for
59
+ further details.
60
+
@@ -2,26 +2,28 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: bychar 3.0.0 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = "bychar"
8
- s.version = "2.0.0"
9
+ s.version = "3.0.0"
9
10
 
10
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
11
13
  s.authors = ["Julik Tarkhanov"]
12
- s.date = "2013-03-23"
14
+ s.date = "2014-12-10"
13
15
  s.description = " Helps parsing IO char by char "
14
16
  s.email = "me@julik.nl"
15
17
  s.extra_rdoc_files = [
16
18
  "LICENSE.txt",
17
- "README.rdoc"
19
+ "README.md"
18
20
  ]
19
21
  s.files = [
20
22
  ".document",
21
23
  ".travis.yml",
22
24
  "Gemfile",
23
25
  "LICENSE.txt",
24
- "README.rdoc",
26
+ "README.md",
25
27
  "Rakefile",
26
28
  "bychar.gemspec",
27
29
  "lib/bychar.rb",
@@ -37,28 +39,30 @@ Gem::Specification.new do |s|
37
39
  ]
38
40
  s.homepage = "http://github.com/julik/bychar"
39
41
  s.licenses = ["MIT"]
40
- s.require_paths = ["lib"]
41
- s.rubygems_version = "1.8.24"
42
+ s.rubygems_version = "2.2.2"
42
43
  s.summary = "Helps parsing IO char by char"
43
44
 
44
45
  if s.respond_to? :specification_version then
45
- s.specification_version = 3
46
+ s.specification_version = 4
46
47
 
47
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<rake>, [">= 0"])
48
50
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
49
- s.add_development_dependency(%q<bundler>, [">= 0"])
50
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
51
+ s.add_development_dependency(%q<test-unit>, [">= 0"])
52
+ s.add_development_dependency(%q<jeweler>, ["= 1.8.7"])
51
53
  s.add_development_dependency(%q<flexmock>, ["~> 0.8"])
52
54
  else
55
+ s.add_dependency(%q<rake>, [">= 0"])
53
56
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
54
- s.add_dependency(%q<bundler>, [">= 0"])
55
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
57
+ s.add_dependency(%q<test-unit>, [">= 0"])
58
+ s.add_dependency(%q<jeweler>, ["= 1.8.7"])
56
59
  s.add_dependency(%q<flexmock>, ["~> 0.8"])
57
60
  end
58
61
  else
62
+ s.add_dependency(%q<rake>, [">= 0"])
59
63
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
60
- s.add_dependency(%q<bundler>, [">= 0"])
61
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
64
+ s.add_dependency(%q<test-unit>, [">= 0"])
65
+ s.add_dependency(%q<jeweler>, ["= 1.8.7"])
62
66
  s.add_dependency(%q<flexmock>, ["~> 0.8"])
63
67
  end
64
68
  end
@@ -6,22 +6,36 @@ require File.dirname(__FILE__) + "/impls/reader_strbuf"
6
6
  require File.dirname(__FILE__) + "/impls/reader_bare"
7
7
 
8
8
  module Bychar
9
- VERSION = '2.0.0'
9
+ VERSION = '3.0.0'
10
10
  DEFAULT_BUFFER_SIZE = 512 * 1024
11
11
 
12
- # Gets raised when you have exhausted the underlying IO
13
- class EOF < EOFError #:nodoc: all
12
+ # The basic wrapper that you get from wrap()
13
+ class Wrapper
14
+ def initialize(io_to_wrap)
15
+ @io = io_to_wrap
16
+ end
17
+
18
+ def read_one_char
19
+ @io.read_one_char
20
+ end
21
+
22
+ def each_char
23
+ while char = read_one_char do
24
+ yield char
25
+ end
26
+ end
14
27
  end
15
28
 
16
- # Returns a reader object that responds to read_one_char!
17
- # and raises an EOF if the IO is depleted
29
+ # Returns a reader object that responds to read_one_char
30
+ # and can be passed on to the actual parsers
18
31
  def self.wrap(io)
19
- if RUBY_PLATFORM == 'java'
32
+ reader = if RUBY_PLATFORM == 'java'
20
33
  ReaderIOBuf.new(io)
21
34
  elsif RUBY_VERSION < '1.9'
22
35
  ReaderBare.new(io)
23
36
  else
24
37
  ReaderStrbuf.new(io)
25
38
  end
39
+ Wrapper.new(reader)
26
40
  end
27
41
  end
@@ -6,10 +6,8 @@ module Bychar
6
6
  @io = io
7
7
  end
8
8
 
9
- def read_one_char!
10
- b = @io.read(1)
11
- raise Bychar::EOF if b.nil?
12
- b
9
+ def read_one_char
10
+ @io.read(1)
13
11
  end
14
12
  end
15
13
  end
@@ -10,9 +10,9 @@ module Bychar
10
10
  # Since you parse char by char, you will be tempted to do it in a tight loop
11
11
  # and to call eof? on each iteration. Don't. Instead. allow it to raise and do not check.
12
12
  # This takes the profile time down from 36 seconds to 30 seconds for a large file.
13
- def read_one_char!
13
+ def read_one_char
14
14
  cache if @buf.eos?
15
- raise EOF if @buf.eos?
15
+ return nil if @buf.eos?
16
16
 
17
17
  @buf.getch
18
18
  end
@@ -14,10 +14,11 @@ module Bychar
14
14
  # Will transparently read one byte off the contained IO, maintaining the internal cache.
15
15
  # If the cache has been depleted it will read a big chunk from the IO and cache it and then
16
16
  # return the byte
17
- def read_one_char!
17
+ def read_one_char
18
18
  if @pos_in_buf > @maximum_pos
19
19
  @buf = @io.read(DEFAULT_BUFFER_SIZE)
20
- raise EOF if @buf.nil?
20
+
21
+ return nil if @buf.nil?
21
22
 
22
23
  @maximum_pos = @buf.length - 1
23
24
  @pos_in_buf = 0
@@ -6,55 +6,53 @@ class TestBychar < Test::Unit::TestCase
6
6
  s = StringIO.new("This is a string")
7
7
 
8
8
  reader = Bychar::ReaderIOBuf.new(s)
9
- assert_equal "T", reader.read_one_char!
10
- assert_equal "h", reader.read_one_char!
9
+ assert_equal "T", reader.read_one_char
10
+ assert_equal "h", reader.read_one_char
11
11
  end
12
12
 
13
13
  def test_reads_set_buffer_size
14
14
  s = StringIO.new("abcd")
15
15
  flexmock(s).should_receive(:read).with(4).once.and_return("abcd")
16
16
  reader = Bychar::ReaderIOBuf.new(s, 4)
17
- reader.read_one_char!
17
+ reader.read_one_char
18
18
  end
19
19
 
20
20
  def test_reads_in_64kb_chunks_by_default
21
21
  s = StringIO.new("abcd")
22
22
  flexmock(s).should_receive(:read).with(Bychar::DEFAULT_BUFFER_SIZE).once.and_return("abcd")
23
23
  reader = Bychar::ReaderIOBuf.new(s)
24
- reader.read_one_char!
24
+ reader.read_one_char
25
25
  end
26
26
 
27
27
  def test_eof_with_empty
28
28
  s = StringIO.new
29
29
  reader = Bychar::ReaderIOBuf.new(s)
30
- assert_raise(Bychar::EOF) { reader.read_one_char! }
30
+ assert_nil reader.read_one_char
31
31
  end
32
32
 
33
33
  def test_eof_with_io_at_eof
34
34
  s = StringIO.new("foo")
35
35
  s.read(3)
36
36
  reader = Bychar::ReaderIOBuf.new(s)
37
- assert_raise(Bychar::EOF) { reader.read_one_char! }
37
+ assert_nil reader.read_one_char
38
38
  end
39
39
 
40
40
  def test_eof_with_string_to_size
41
41
  s = "Foobarboo another"
42
42
  s = StringIO.new(s)
43
43
  reader = Bychar::ReaderIOBuf.new(s, 1)
44
- s.length.times { reader.read_one_char! }
45
- assert_raise(Bychar::EOF) { reader.read_one_char! }
44
+ s.length.times { reader.read_one_char }
45
+ assert_nil reader.read_one_char
46
46
  end
47
47
 
48
- def test_read_one_byte_and_raise_at_eof
48
+ def test_read_one_byte_and_nil_at_eof
49
49
  str = "Frobobo"
50
50
 
51
51
  bytes = []
52
- assert_raise(Bychar::EOF) do
53
- s = Bychar::ReaderIOBuf.new(StringIO.new(str))
54
- loop { bytes << s.read_one_char! }
52
+ s = Bychar::ReaderIOBuf.new(StringIO.new(str))
53
+ while c = s.read_one_char do
54
+ bytes << c
55
55
  end
56
-
57
56
  assert_equal %w( F r o b o b o ), bytes
58
57
  end
59
-
60
58
  end
@@ -6,40 +6,28 @@ class TestReader < Test::Unit::TestCase
6
6
  s = StringIO.new("This is a string")
7
7
 
8
8
  reader = Bychar.wrap(s)
9
- assert_equal "T", reader.read_one_char!
10
- assert_equal "h", reader.read_one_char!
9
+ assert_equal "T", reader.read_one_char
10
+ assert_equal "h", reader.read_one_char
11
11
  end
12
12
 
13
13
  def test_eof_with_empty
14
14
  s = StringIO.new
15
15
  reader = Bychar.wrap(s)
16
- assert_raise(Bychar::EOF) { reader.read_one_char! }
16
+ assert_nil reader.read_one_char
17
17
  end
18
18
 
19
19
  def test_eof_with_io_at_eof
20
20
  s = StringIO.new("foo")
21
21
  s.read(3)
22
22
  reader = Bychar.wrap(s)
23
- assert_raise(Bychar::EOF) { reader.read_one_char! }
23
+ assert_nil reader.read_one_char
24
24
  end
25
25
 
26
26
  def test_eof_with_string_to_size
27
27
  s = "Foobarboo another"
28
28
  s = StringIO.new(s)
29
29
  reader = Bychar.wrap(s)
30
- s.length.times { reader.read_one_char! }
31
- assert_raise(Bychar::EOF) { reader.read_one_char! }
30
+ s.length.times { reader.read_one_char }
31
+ assert_nil reader.read_one_char
32
32
  end
33
-
34
- def test_read_one_byte_and_raise_at_eof
35
- str = "Frobobo"
36
- bytes = []
37
- assert_raise(Bychar::EOF) do
38
- s = Bychar.wrap(StringIO.new(str))
39
- loop { bytes << s.read_one_char! }
40
- end
41
-
42
- assert_equal %w( F r o b o b o ), bytes
43
- end
44
-
45
33
  end
@@ -6,41 +6,30 @@ class TestStrReader < Test::Unit::TestCase
6
6
  s = StringIO.new("This is a string")
7
7
 
8
8
  reader = Bychar::ReaderIOBuf.new(s)
9
- assert_equal "T", reader.read_one_char!
10
- assert_equal "h", reader.read_one_char!
9
+ assert_equal "T", reader.read_one_char
10
+ assert_equal "h", reader.read_one_char
11
11
  end
12
12
 
13
13
  def test_eof_with_empty
14
14
  s = StringIO.new
15
15
  reader = Bychar::ReaderStrbuf.new(s)
16
- assert_raise(Bychar::EOF) { reader.read_one_char! }
16
+ assert_nil reader.read_one_char
17
17
  end
18
18
 
19
19
  def test_eof_with_io_at_eof
20
20
  s = StringIO.new("foo")
21
21
  s.read(3)
22
22
  reader = Bychar::ReaderStrbuf.new(s)
23
- assert_raise(Bychar::EOF) { reader.read_one_char! }
23
+ assert_nil reader.read_one_char
24
24
  end
25
25
 
26
26
  def test_eof_with_string_to_size
27
27
  s = "Foobarboo another"
28
28
  s = StringIO.new(s)
29
29
  reader = Bychar::ReaderStrbuf.new(s)
30
- s.length.times { reader.read_one_char! }
31
- assert_raise(Bychar::EOF) { reader.read_one_char! }
30
+ s.length.times {
31
+ assert reader.read_one_char
32
+ }
33
+ assert_nil reader.read_one_char
32
34
  end
33
-
34
- def test_read_one_byte_and_raise_at_eof
35
- str = "Frobobo"
36
-
37
- bytes = []
38
- assert_raise(Bychar::EOF) do
39
- s = Bychar::ReaderStrbuf.new(StringIO.new(str))
40
- loop { bytes << s.read_one_char! }
41
- end
42
-
43
- assert_equal %w( F r o b o b o ), bytes
44
- end
45
-
46
35
  end
@@ -21,9 +21,9 @@ class BenchReader
21
21
  def run
22
22
  File.open(File.dirname(__FILE__) + "/huge_nuke_tcl.tcl") do | io |
23
23
  reader = get_reader(io)
24
- begin
25
- loop { reader.read_one_char! * 2 }
26
- rescue Bychar::EOF
24
+ str = []
25
+ while c = reader.read_one_char
26
+ str << (c * 2)
27
27
  end
28
28
  end
29
29
  end
metadata CHANGED
@@ -1,95 +1,98 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bychar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
5
- prerelease:
4
+ version: 3.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Julik Tarkhanov
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-23 00:00:00.000000000 Z
11
+ date: 2014-12-10 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rdoc
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
16
22
  version_requirements: !ruby/object:Gem::Requirement
17
23
  requirements:
18
- - - ~>
24
+ - - ">="
19
25
  - !ruby/object:Gem::Version
20
- version: '3.12'
21
- none: false
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
22
29
  requirement: !ruby/object:Gem::Requirement
23
30
  requirements:
24
- - - ~>
31
+ - - "~>"
25
32
  - !ruby/object:Gem::Version
26
33
  version: '3.12'
27
- none: false
28
- prerelease: false
29
34
  type: :development
30
- - !ruby/object:Gem::Dependency
31
- name: bundler
35
+ prerelease: false
32
36
  version_requirements: !ruby/object:Gem::Requirement
33
37
  requirements:
34
- - - ! '>='
38
+ - - "~>"
35
39
  - !ruby/object:Gem::Version
36
- version: !binary |-
37
- MA==
38
- none: false
40
+ version: '3.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
39
43
  requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
- - - ! '>='
45
+ - - ">="
42
46
  - !ruby/object:Gem::Version
43
- version: !binary |-
44
- MA==
45
- none: false
46
- prerelease: false
47
+ version: '0'
47
48
  type: :development
48
- - !ruby/object:Gem::Dependency
49
- name: jeweler
49
+ prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 1.8.4
55
- none: false
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jeweler
56
57
  requirement: !ruby/object:Gem::Requirement
57
58
  requirements:
58
- - - ~>
59
+ - - '='
59
60
  - !ruby/object:Gem::Version
60
- version: 1.8.4
61
- none: false
62
- prerelease: false
61
+ version: 1.8.7
63
62
  type: :development
64
- - !ruby/object:Gem::Dependency
65
- name: flexmock
63
+ prerelease: false
66
64
  version_requirements: !ruby/object:Gem::Requirement
67
65
  requirements:
68
- - - ~>
66
+ - - '='
69
67
  - !ruby/object:Gem::Version
70
- version: '0.8'
71
- none: false
68
+ version: 1.8.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: flexmock
72
71
  requirement: !ruby/object:Gem::Requirement
73
72
  requirements:
74
- - - ~>
73
+ - - "~>"
75
74
  - !ruby/object:Gem::Version
76
75
  version: '0.8'
77
- none: false
78
- prerelease: false
79
76
  type: :development
80
- description: ! ' Helps parsing IO char by char '
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.8'
83
+ description: " Helps parsing IO char by char "
81
84
  email: me@julik.nl
82
85
  executables: []
83
86
  extensions: []
84
87
  extra_rdoc_files:
85
88
  - LICENSE.txt
86
- - README.rdoc
89
+ - README.md
87
90
  files:
88
- - .document
89
- - .travis.yml
91
+ - ".document"
92
+ - ".travis.yml"
90
93
  - Gemfile
91
94
  - LICENSE.txt
92
- - README.rdoc
95
+ - README.md
93
96
  - Rakefile
94
97
  - bychar.gemspec
95
98
  - lib/bychar.rb
@@ -105,31 +108,25 @@ files:
105
108
  homepage: http://github.com/julik/bychar
106
109
  licenses:
107
110
  - MIT
108
- post_install_message:
111
+ metadata: {}
112
+ post_install_message:
109
113
  rdoc_options: []
110
114
  require_paths:
111
115
  - lib
112
116
  required_ruby_version: !ruby/object:Gem::Requirement
113
117
  requirements:
114
- - - ! '>='
118
+ - - ">="
115
119
  - !ruby/object:Gem::Version
116
- segments:
117
- - 0
118
- hash: 2
119
- version: !binary |-
120
- MA==
121
- none: false
120
+ version: '0'
122
121
  required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  requirements:
124
- - - ! '>='
123
+ - - ">="
125
124
  - !ruby/object:Gem::Version
126
- version: !binary |-
127
- MA==
128
- none: false
125
+ version: '0'
129
126
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 1.8.24
132
- signing_key:
133
- specification_version: 3
127
+ rubyforge_project:
128
+ rubygems_version: 2.2.2
129
+ signing_key:
130
+ specification_version: 4
134
131
  summary: Helps parsing IO char by char
135
132
  test_files: []
@@ -1,63 +0,0 @@
1
- = bychar
2
-
3
- A simple IO wrapper for libraries that tend to read the IO in the following way:
4
-
5
- * Only forward
6
- * By a single character
7
- * Without skipping
8
-
9
- This is how most of recursive-descent parsers and stateful parsers would work. However,
10
- reading a simple IO object byte by byte in Ruby is very slow. Orders slower in fact.
11
- Therefore, this gem creates a simple wrapper that you can get like this:
12
-
13
- wrapper = Bychar.wrap(io)
14
- loop do
15
- c = wrapper.read_one_char!
16
- rescye Bychar::EOF
17
- # Your IO ran out
18
- end
19
-
20
- The wrapper will cache some bytes from the passed IO object which will make parsing
21
- faster when you advance your parser char by char. You should not do any checks on the returned char
22
- since the reader will raise a Bychar::EOF once the IO is depleted. That exception inherits from EOFError.
23
-
24
- == Performance
25
-
26
- I told you reading char by char is not the best strategy. On Ruby 1.9:
27
-
28
- Bare IO using read(1): 3.400000 1.900000 5.300000 ( 5.300798)
29
- Bychar using StringIO: 1.790000 0.010000 1.800000 ( 1.795277)
30
- Bychar using a String buffer: 1.760000 0.000000 1.760000 ( 1.760440)
31
- Platform-picked Bychar.wrap(io) Bychar::ReaderStrbuf: 1.770000 0.010000 1.780000 ( 1.776339)
32
-
33
- while on Ruby 1.8 it is kinda sad:
34
-
35
- Bare IO using read(1): 2.380000 0.000000 2.380000 ( 2.393260)
36
- Bychar using StringIO: 2.270000 0.010000 2.280000 ( 2.275631)
37
- Bychar using a String buffer: 2.920000 0.000000 2.920000 ( 2.924574)
38
- Platform-picked Bychar.wrap(io) Bychar::ReaderBare: 2.380000 0.010000 2.390000 ( 2.384414)
39
-
40
- And on JRuby it's different still:
41
-
42
- Bare IO using read(1): 1.610000 0.040000 1.650000 ( 1.180000)
43
- Bychar using StringIO: 0.730000 0.020000 0.750000 ( 0.603000)
44
- Bychar using a String buffer: 1.040000 0.040000 1.080000 ( 0.790000)
45
- Platform-picked Bychar.wrap(io) Bychar::ReaderIOBuf: 0.560000 0.030000 0.590000 ( 0.546000)
46
-
47
- As you can see, Bychar will do some work to pick an implementation that makes sense for your Ruby platform.
48
-
49
- == Contributing to bychar
50
-
51
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
52
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
53
- * Fork the project.
54
- * Start a feature/bugfix branch.
55
- * Commit and push until you are happy with your contribution.
56
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
57
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
58
-
59
- == Copyright
60
-
61
- Copyright (c) 2013 Julik Tarkhanov. See LICENSE.txt for
62
- further details.
63
-