bzip2-ruby 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.yardopts +1 -0
- data/CHANGELOG.rdoc +70 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/README.rdoc +44 -3
- data/Rakefile +2 -19
- data/bzip2-ruby.gemspec +14 -48
- data/ext/bzip2.c +1012 -823
- data/ext/extconf.rb +4 -1
- data/lib/bzip2-ruby.rb +1 -0
- data/lib/bzip2.rb +10 -2
- data/lib/bzip2/internals.rb +13 -0
- data/lib/bzip2/reader.rb +27 -0
- data/lib/bzip2/version.rb +3 -0
- data/lib/bzip2/writer.rb +63 -0
- data/spec/reader_spec.rb +118 -214
- data/spec/spec_helper.rb +9 -1
- data/spec/writer_spec.rb +64 -74
- metadata +50 -17
- data/VERSION.yml +0 -4
- data/tasks/extconf.rake +0 -13
- data/tasks/extconf/bz2.rake +0 -43
data/ext/extconf.rb
CHANGED
@@ -3,6 +3,9 @@ require 'mkmf'
|
|
3
3
|
dir_config('bz2')
|
4
4
|
have_header('bzlib.h')
|
5
5
|
|
6
|
+
$CFLAGS << ' -Wall -funroll-loops '
|
7
|
+
# $CFLAGS << ' -O0 -ggdb -Wextra'
|
8
|
+
|
6
9
|
if have_library("bz2", "BZ2_bzWriteOpen")
|
7
10
|
if enable_config("shared", true)
|
8
11
|
$static = nil
|
@@ -12,7 +15,7 @@ if have_library("bz2", "BZ2_bzWriteOpen")
|
|
12
15
|
$CFLAGS << ' -DRUBY_19_COMPATIBILITY'
|
13
16
|
end
|
14
17
|
|
15
|
-
create_makefile(
|
18
|
+
create_makefile('bzip2_ext')
|
16
19
|
else
|
17
20
|
puts "libbz2 not found, maybe try manually specifying --with-bz2-dir to find it?"
|
18
21
|
end
|
data/lib/bzip2-ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bzip2'
|
data/lib/bzip2.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
require 'bzip2_ext'
|
2
|
+
require 'bzip2/version'
|
2
3
|
|
4
|
+
# This is the base module for the +bzip2-ruby+ gem. Beneath it are the classes
|
5
|
+
# for writing and reading data from bzip2 compressed and uncompressed streams.
|
6
|
+
#
|
7
|
+
# For example usage, see the Bzip2::Reader or Bzip2::Writer or the
|
8
|
+
# {README}[link:docs/file/README.rdoc]
|
9
|
+
#
|
10
|
+
# @see Bzip2::Writer
|
11
|
+
# @see Bzip2::Reader
|
3
12
|
module Bzip2
|
4
|
-
|
5
|
-
end
|
13
|
+
end
|
data/lib/bzip2/reader.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# This file is mostly here for documentation purposes, do not require this
|
2
|
+
|
3
|
+
#
|
4
|
+
module Bzip2
|
5
|
+
# Bzip2::Reader is meant to read streams of bz2 compressed bytes. It behaves
|
6
|
+
# like an IO object with many similar methods. It also includes the Enumerable
|
7
|
+
# module and each element is a 'line' in the stream.
|
8
|
+
#
|
9
|
+
# It can both decompress files:
|
10
|
+
#
|
11
|
+
# reader = Bzip2::Reader.open('file')
|
12
|
+
# puts reader.read
|
13
|
+
#
|
14
|
+
# reader = Bzip2::Reader.new File.open('file')
|
15
|
+
# put reader.gets
|
16
|
+
#
|
17
|
+
# And it may just decompress raw strings
|
18
|
+
#
|
19
|
+
# reader = Bzip2::Reader.new compressed_string
|
20
|
+
# reader = Bzip2::Reader.new Bzip2.compress('compress-me')
|
21
|
+
class Reader
|
22
|
+
alias :each_line :each
|
23
|
+
alias :closed :closed?
|
24
|
+
alias :eoz :eoz?
|
25
|
+
alias :eof :eof?
|
26
|
+
end
|
27
|
+
end
|
data/lib/bzip2/writer.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# This file is mostly here for documentation purposes, do not require this
|
2
|
+
|
3
|
+
#
|
4
|
+
module Bzip2
|
5
|
+
# A Bzip2::Writer represents a stream which compresses data written to it.
|
6
|
+
# It can be constructed with another IO object (a File) which data can be
|
7
|
+
# written to. Otherwise, data is all stored internally as a string and can
|
8
|
+
# be retrieved via the Bzip2::Writer#flush method
|
9
|
+
#
|
10
|
+
# It can both write to files:
|
11
|
+
#
|
12
|
+
# writer = Bzip2::Writer.open('file')
|
13
|
+
# writer << data
|
14
|
+
# writer.close
|
15
|
+
#
|
16
|
+
# Bzip2::Writer.open('file'){ |f| f << data }
|
17
|
+
#
|
18
|
+
# writer = Bzip2::Writer.new File.open('file')
|
19
|
+
#
|
20
|
+
# And output data as a string
|
21
|
+
#
|
22
|
+
# writer = Bzip2::Writer.new
|
23
|
+
# writer << data
|
24
|
+
# writer.flush # => data compressed via bz2
|
25
|
+
#
|
26
|
+
# @see Bzip2::Writer#initialize The initialize method for examples
|
27
|
+
class Writer
|
28
|
+
|
29
|
+
alias :finish :flush
|
30
|
+
|
31
|
+
# Append some data to this buffer, returning the buffer so this method can
|
32
|
+
# be chained
|
33
|
+
#
|
34
|
+
# writer = Bzip2::Writer.new
|
35
|
+
# writer << 'asdf' << 1 << obj << 'a'
|
36
|
+
# writer.flush
|
37
|
+
#
|
38
|
+
# @param [#to_s] data anything responding to #to_s
|
39
|
+
# @see IO#<<
|
40
|
+
def << data
|
41
|
+
end
|
42
|
+
|
43
|
+
# Adds a number of strings to this buffer. A newline is also inserted into
|
44
|
+
# the buffer after each object
|
45
|
+
# @see IO#puts
|
46
|
+
def puts *objs
|
47
|
+
end
|
48
|
+
|
49
|
+
# Similar to Bzip2::Writer#puts except a newline is not appended after each
|
50
|
+
# object appended to this buffer
|
51
|
+
#
|
52
|
+
# @see IO#print
|
53
|
+
def print *objs
|
54
|
+
end
|
55
|
+
|
56
|
+
# Prints data to this buffer with the specified format.
|
57
|
+
#
|
58
|
+
# @see Kernel#sprintf
|
59
|
+
def printf format, *ojbs
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
data/spec/reader_spec.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
|
-
describe
|
5
|
-
before(:
|
6
|
-
@sample = "08: This is a line\n"
|
4
|
+
describe Bzip2::Writer do
|
5
|
+
before(:each) do
|
7
6
|
@file = "_10lines_"
|
8
7
|
@data = [
|
9
8
|
"00: This is a line\n",
|
@@ -15,343 +14,248 @@ describe "Bzip2::Writer" do
|
|
15
14
|
"06: This is a line\n",
|
16
15
|
"07: This is a line\n",
|
17
16
|
"08: This is a line\n",
|
18
|
-
"09: This is a line\n"
|
17
|
+
"09: This is a line\n"
|
19
18
|
]
|
20
19
|
|
21
20
|
open("|bzip2 > #{@file}", "w") do |f|
|
22
21
|
@data.each { |l| f.puts l }
|
23
22
|
end
|
24
23
|
end
|
25
|
-
|
26
|
-
after(:
|
27
|
-
File.
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
File.delete(@file) if File.exists?(@file)
|
28
27
|
end
|
29
28
|
|
30
|
-
it "
|
31
|
-
|
32
|
-
Bzip2::Reader.foreach(@file)
|
33
|
-
|
34
|
-
count.should == num
|
35
|
-
count += 1
|
36
|
-
end
|
37
|
-
@data.size.should == count
|
29
|
+
it "iterate over each line of the file via the foreach method" do
|
30
|
+
lines = []
|
31
|
+
Bzip2::Reader.foreach(@file){ |line| lines << line }
|
32
|
+
lines.should == @data
|
38
33
|
|
39
|
-
|
34
|
+
lines.clear
|
40
35
|
Bzip2::Reader.foreach(@file, nil) do |file|
|
41
|
-
file.split(/\n/).each
|
42
|
-
num = line[0..1].to_i
|
43
|
-
count.should == num
|
44
|
-
count += 1
|
45
|
-
end
|
36
|
+
file.split(/\n/).each{ |line| lines << line + "\n" }
|
46
37
|
end
|
47
|
-
|
38
|
+
lines.should == @data
|
48
39
|
|
49
40
|
count = 0
|
50
41
|
Bzip2::Reader.foreach(@file, ' ') do |thing|
|
51
42
|
count += 1
|
52
43
|
end
|
53
|
-
|
44
|
+
count.should == 41
|
54
45
|
end
|
55
|
-
|
56
|
-
it "
|
46
|
+
|
47
|
+
it "returns an array of the lines read via #readlines" do
|
57
48
|
lines = Bzip2::Reader.readlines(@file)
|
58
|
-
|
49
|
+
lines.should == @data
|
59
50
|
|
60
51
|
lines = Bzip2::Reader.readlines(@file, nil)
|
61
|
-
|
62
|
-
(@sample.length * @data.size).should == lines[0].size
|
52
|
+
lines.should == [@data.join]
|
63
53
|
end
|
64
54
|
|
65
|
-
it "
|
55
|
+
it "track when the stream has been closed" do
|
66
56
|
f = Bzip2::Reader.open(@file)
|
67
|
-
f.
|
57
|
+
f.should_not be_closed
|
68
58
|
f.close
|
69
|
-
f.
|
59
|
+
f.should be_closed
|
70
60
|
end
|
71
61
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
count += 1
|
62
|
+
shared_examples_for 'a line iterator' do |method|
|
63
|
+
it "iterates over the lines when using #each" do
|
64
|
+
Bzip2::Reader.open(@file) do |file|
|
65
|
+
list = []
|
66
|
+
file.send(method){ |l| list << l }
|
67
|
+
list.should == @data
|
79
68
|
end
|
80
|
-
@data.size.should == count
|
81
|
-
end
|
82
69
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
contents.split(/\n/).each do |line|
|
87
|
-
num = line[0..1].to_i
|
88
|
-
count.should == num
|
89
|
-
count += 1
|
70
|
+
Bzip2::Reader.open(@file) do |file|
|
71
|
+
file.send(method, nil) do |contents|
|
72
|
+
contents.should == @data.join
|
90
73
|
end
|
91
74
|
end
|
92
|
-
end
|
93
|
-
@data.size.should == count
|
94
75
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
76
|
+
count = 0
|
77
|
+
Bzip2::Reader.open(@file) do |file|
|
78
|
+
file.send(method, ' ') do |thing|
|
79
|
+
count += 1
|
80
|
+
end
|
99
81
|
end
|
82
|
+
41.should == count
|
100
83
|
end
|
101
|
-
41.should == count
|
102
84
|
end
|
103
85
|
|
104
|
-
|
105
|
-
|
106
|
-
data = @data.join
|
86
|
+
it_should_behave_like 'a line iterator', :each
|
87
|
+
it_should_behave_like 'a line iterator', :each_line
|
107
88
|
|
108
|
-
|
109
|
-
|
110
|
-
data.getbyte(count).should == b
|
111
|
-
count += 1
|
112
|
-
end
|
113
|
-
end
|
114
|
-
(@sample.length * @data.size).should == count
|
115
|
-
end
|
89
|
+
it "iterates over the decompressed bytes via #each_byte" do
|
90
|
+
bytes = @data.join.bytes.to_a
|
116
91
|
|
117
|
-
it "should test_f_each_line" do
|
118
|
-
count = 0
|
119
92
|
Bzip2::Reader.open(@file) do |file|
|
120
|
-
file.
|
121
|
-
|
122
|
-
count.should == num
|
123
|
-
count += 1
|
124
|
-
end
|
125
|
-
@data.size.should == count
|
126
|
-
end
|
127
|
-
|
128
|
-
count = 0
|
129
|
-
Bzip2::Reader.open(@file) do |file|
|
130
|
-
file.each_line(nil) do |contents|
|
131
|
-
contents.split(/\n/).each do |line|
|
132
|
-
num = line[0..1].to_i
|
133
|
-
count.should == num
|
134
|
-
count += 1
|
135
|
-
end
|
93
|
+
file.each_byte do |b|
|
94
|
+
b.should == bytes.shift
|
136
95
|
end
|
137
96
|
end
|
138
|
-
@data.size.should == count
|
139
97
|
|
140
|
-
|
141
|
-
Bzip2::Reader.open(@file) do |file|
|
142
|
-
file.each_line(' ') do |thing|
|
143
|
-
count += 1
|
144
|
-
end
|
145
|
-
end
|
146
|
-
41.should == count
|
98
|
+
bytes.size.should == 0
|
147
99
|
end
|
148
100
|
|
149
|
-
it "
|
101
|
+
it "keeps track of when eof has been reached" do
|
150
102
|
Bzip2::Reader.open(@file) do |file|
|
151
103
|
@data.size.times do
|
152
|
-
|
153
|
-
(!file.eof?).should be_true
|
104
|
+
file.should_not be_eof
|
154
105
|
file.gets
|
155
106
|
end
|
156
|
-
|
157
|
-
file.
|
107
|
+
|
108
|
+
file.should be_eof
|
158
109
|
end
|
159
110
|
end
|
160
111
|
|
161
|
-
it "
|
162
|
-
|
163
|
-
data = @data.join
|
112
|
+
it "gets only one byte at a time via getc and doesn't raise an exception" do
|
113
|
+
bytes = @data.join.bytes.to_a
|
164
114
|
|
165
115
|
Bzip2::Reader.open(@file) do |file|
|
166
|
-
while
|
167
|
-
|
168
|
-
count += 1
|
116
|
+
while ch = file.getc
|
117
|
+
ch.should == bytes.shift
|
169
118
|
end
|
119
|
+
|
170
120
|
file.getc.should be_nil
|
171
121
|
end
|
172
|
-
|
122
|
+
|
123
|
+
bytes.size.should == 0
|
173
124
|
end
|
174
125
|
|
175
|
-
it "
|
176
|
-
count = 0
|
126
|
+
it "reads an entire line via gets" do
|
177
127
|
Bzip2::Reader.open(@file) do |file|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
count += 1
|
128
|
+
lines = []
|
129
|
+
while line = file.gets
|
130
|
+
lines << line
|
182
131
|
end
|
132
|
+
lines.should == @data
|
133
|
+
|
183
134
|
file.gets.should be_nil
|
184
|
-
@data.size.should == count
|
185
135
|
end
|
186
136
|
|
187
|
-
count = 0
|
188
137
|
Bzip2::Reader.open(@file) do |file|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
count.should == num
|
193
|
-
count += 1
|
138
|
+
lines = []
|
139
|
+
while line = file.gets("line\n")
|
140
|
+
lines << line
|
194
141
|
end
|
142
|
+
lines.should == @data
|
143
|
+
|
195
144
|
file.gets.should be_nil
|
196
|
-
@data.size.should == count
|
197
145
|
end
|
198
146
|
|
199
|
-
|
147
|
+
lines = ''
|
200
148
|
Bzip2::Reader.open(@file) do |file|
|
201
|
-
while
|
202
|
-
|
203
|
-
num = line[0..1].to_i
|
204
|
-
count.should == num
|
205
|
-
count += 1
|
206
|
-
end
|
149
|
+
while contents = file.gets(nil)
|
150
|
+
lines << contents
|
207
151
|
end
|
208
152
|
end
|
209
|
-
|
153
|
+
lines.should == @data.join
|
210
154
|
|
211
155
|
count = 0
|
212
156
|
Bzip2::Reader.open(@file) do |file|
|
213
|
-
|
214
|
-
count += 1
|
215
|
-
end
|
157
|
+
count += 1 while file.gets(' ')
|
216
158
|
end
|
217
159
|
41.should == count
|
218
160
|
end
|
219
161
|
|
220
|
-
it "
|
162
|
+
it "reads the entire file or a specified length when using #read" do
|
221
163
|
Bzip2::Reader.open(@file) do |file|
|
222
|
-
|
223
|
-
(@sample.length * @data.size).should == content.length
|
224
|
-
count = 0
|
225
|
-
content.split(/\n/).each do |line|
|
226
|
-
num = line[0..1].to_i
|
227
|
-
count.should == num
|
228
|
-
count += 1
|
229
|
-
end
|
164
|
+
file.read.should == @data.join
|
230
165
|
end
|
231
166
|
|
232
167
|
Bzip2::Reader.open(@file) do |file|
|
233
|
-
"00: This is "
|
234
|
-
"a line\n01: T"
|
168
|
+
file.read(12).should == "00: This is "
|
169
|
+
file.read(12).should == "a line\n01: T"
|
235
170
|
end
|
236
171
|
end
|
237
172
|
|
238
|
-
it "
|
173
|
+
it "reads one character and returns the byte value of the character read" do
|
239
174
|
count = 0
|
240
175
|
data = @data.join
|
241
176
|
Bzip2::Reader.open(@file) do |file|
|
242
|
-
|
243
|
-
|
244
|
-
data.getbyte(count).should == ch
|
245
|
-
count += 1
|
177
|
+
@data.join.bytes do |byte|
|
178
|
+
file.readchar.should == byte
|
246
179
|
end
|
180
|
+
|
247
181
|
lambda { file.readchar }.should raise_error(Bzip2::EOZError)
|
248
182
|
end
|
249
183
|
end
|
250
184
|
|
251
|
-
it "
|
185
|
+
it "reads one line at a time and raises and exception when no more" do
|
252
186
|
count = 0
|
253
187
|
Bzip2::Reader.open(@file) do |file|
|
188
|
+
lines = []
|
254
189
|
@data.size.times do |count|
|
255
|
-
|
256
|
-
num = line[0..1].to_i
|
257
|
-
count.should == num
|
258
|
-
count += 1
|
190
|
+
lines << file.readline
|
259
191
|
end
|
192
|
+
|
193
|
+
lines.should == @data
|
260
194
|
lambda { file.readline }.should raise_error(Bzip2::EOZError)
|
261
195
|
end
|
262
196
|
|
263
|
-
count = 0
|
264
197
|
Bzip2::Reader.open(@file) do |file|
|
265
|
-
|
266
|
-
|
267
|
-
num = line[0..1].to_i
|
268
|
-
count.should == num
|
269
|
-
count += 1
|
270
|
-
end
|
198
|
+
file.readline(nil).should == @data.join
|
199
|
+
|
271
200
|
lambda { file.readline }.should raise_error(Bzip2::EOZError)
|
272
201
|
end
|
273
|
-
@data.size.should == count
|
274
202
|
|
275
|
-
count = 0
|
276
203
|
Bzip2::Reader.open(@file) do |file|
|
277
|
-
41.times
|
278
|
-
thing = file.readline(' ')
|
279
|
-
count += 1
|
280
|
-
end
|
204
|
+
41.times { |count| file.readline(' ') }
|
281
205
|
lambda { file.readline }.should raise_error(Bzip2::EOZError)
|
282
206
|
end
|
283
207
|
end
|
284
208
|
|
285
|
-
it "
|
209
|
+
it "returns an array of lines in the file" do
|
286
210
|
Bzip2::Reader.open(@file) do |file|
|
287
|
-
|
288
|
-
@data.size.should == lines.size
|
211
|
+
file.readlines.should == @data
|
289
212
|
end
|
290
213
|
|
291
214
|
Bzip2::Reader.open(@file) do |file|
|
292
|
-
|
293
|
-
1.should == lines.size
|
294
|
-
(@sample.length * @data.size).should == lines[0].size
|
215
|
+
file.readlines(nil).should == [@data.join]
|
295
216
|
end
|
296
217
|
end
|
297
218
|
|
298
|
-
|
299
|
-
# Bzip2::Reader.open(@file) do |file|
|
300
|
-
# ?0.getbyte(0).should == file.getc
|
301
|
-
# ?0.getbyte(0).should == file.getc
|
302
|
-
# ?:.getbyte(0).should == file.getc
|
303
|
-
# ?\s.getbyte(0).should == file.getc
|
304
|
-
# file.ungetc(?:.to_i).should be_nil
|
305
|
-
# ?:.getbyte(0).should == file.getc
|
306
|
-
# 1 while file.getc
|
307
|
-
# file.ungetc(?A).should be_nil
|
308
|
-
# ?A.should == file.getc
|
309
|
-
# end
|
310
|
-
# end
|
311
|
-
|
312
|
-
it "should test_f_ungets" do
|
313
|
-
count = 0
|
219
|
+
it "rewinds the stream when #ungetc is called and returns that byte next" do
|
314
220
|
Bzip2::Reader.open(@file) do |file|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
221
|
+
'0'.bytes.first.should == file.getc
|
222
|
+
'0'.bytes.first.should == file.getc
|
223
|
+
':'.bytes.first.should == file.getc
|
224
|
+
' '.bytes.first.should == file.getc
|
225
|
+
|
226
|
+
file.ungetc(':'.bytes.first).should be_nil
|
227
|
+
':'.bytes.first.should == file.getc
|
228
|
+
|
229
|
+
file.read
|
230
|
+
|
231
|
+
file.ungetc('A'.bytes.first).should be_nil
|
232
|
+
'A'.bytes.first.should == file.getc
|
320
233
|
end
|
321
234
|
end
|
322
235
|
|
323
|
-
it "
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
num = line[0..1].to_i
|
330
|
-
count.should == num
|
331
|
-
count += 1
|
236
|
+
it "rewinds the stream when #ungets is called" do
|
237
|
+
Bzip2::Reader.open(@file) do |file|
|
238
|
+
@data[0].should == file.gets
|
239
|
+
1.should == file.lineno
|
240
|
+
file.ungets(@data[0]).should be_nil
|
241
|
+
@data[0].should == file.gets
|
332
242
|
end
|
333
|
-
|
334
|
-
file.close
|
243
|
+
end
|
335
244
|
|
336
|
-
|
245
|
+
it "reads entire lines via readline and throws an exception when there is" do
|
246
|
+
string = File.read(@file)
|
337
247
|
file = Bzip2::Reader.new(string)
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
count.should == num
|
342
|
-
count += 1
|
248
|
+
lines = []
|
249
|
+
@data.size.times do |count|
|
250
|
+
lines << file.readline
|
343
251
|
end
|
252
|
+
lines.should == @data
|
344
253
|
lambda { file.readline }.should raise_error(Bzip2::EOZError)
|
345
|
-
@data.size.should == count
|
346
254
|
file.close
|
347
255
|
|
348
|
-
count = 0
|
349
256
|
file = Bzip2::Reader.new(string)
|
350
|
-
|
351
|
-
thing = file.readline(' ')
|
352
|
-
count += 1
|
353
|
-
end
|
257
|
+
file.readline(nil).should == @data.join
|
354
258
|
lambda { file.readline }.should raise_error(Bzip2::EOZError)
|
355
259
|
file.close
|
356
260
|
end
|
357
|
-
end
|
261
|
+
end
|