bindata 2.4.10 → 2.5.0

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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.rdoc +39 -0
  3. data/LICENSE +25 -0
  4. data/NEWS.rdoc +5 -0
  5. data/README.md +6 -9
  6. data/bindata.gemspec +9 -4
  7. data/examples/NBT.txt +1 -1
  8. data/examples/list.rb +1 -1
  9. data/lib/bindata/alignment.rb +15 -7
  10. data/lib/bindata/array.rb +54 -54
  11. data/lib/bindata/base.rb +14 -25
  12. data/lib/bindata/base_primitive.rb +24 -20
  13. data/lib/bindata/bits.rb +15 -15
  14. data/lib/bindata/buffer.rb +89 -11
  15. data/lib/bindata/choice.rb +9 -6
  16. data/lib/bindata/count_bytes_remaining.rb +1 -1
  17. data/lib/bindata/delayed_io.rb +18 -10
  18. data/lib/bindata/dsl.rb +37 -35
  19. data/lib/bindata/float.rb +3 -3
  20. data/lib/bindata/framework.rb +8 -10
  21. data/lib/bindata/int.rb +14 -16
  22. data/lib/bindata/io.rb +276 -253
  23. data/lib/bindata/name.rb +1 -1
  24. data/lib/bindata/params.rb +9 -7
  25. data/lib/bindata/primitive.rb +3 -3
  26. data/lib/bindata/registry.rb +18 -18
  27. data/lib/bindata/rest.rb +1 -1
  28. data/lib/bindata/sanitize.rb +9 -16
  29. data/lib/bindata/section.rb +97 -0
  30. data/lib/bindata/skip.rb +140 -51
  31. data/lib/bindata/string.rb +9 -9
  32. data/lib/bindata/stringz.rb +12 -10
  33. data/lib/bindata/struct.rb +92 -68
  34. data/lib/bindata/trace.rb +35 -42
  35. data/lib/bindata/transform/brotli.rb +35 -0
  36. data/lib/bindata/transform/lz4.rb +35 -0
  37. data/lib/bindata/transform/lzma.rb +35 -0
  38. data/lib/bindata/transform/xor.rb +19 -0
  39. data/lib/bindata/transform/xz.rb +35 -0
  40. data/lib/bindata/transform/zlib.rb +33 -0
  41. data/lib/bindata/transform/zstd.rb +35 -0
  42. data/lib/bindata/uint8_array.rb +2 -2
  43. data/lib/bindata/version.rb +1 -1
  44. data/lib/bindata/virtual.rb +4 -7
  45. data/lib/bindata/warnings.rb +1 -1
  46. data/lib/bindata.rb +1 -0
  47. data/test/alignment_test.rb +8 -8
  48. data/test/array_test.rb +98 -96
  49. data/test/base_primitive_test.rb +47 -47
  50. data/test/base_test.rb +24 -24
  51. data/test/bits_test.rb +15 -15
  52. data/test/buffer_test.rb +31 -22
  53. data/test/choice_test.rb +32 -32
  54. data/test/count_bytes_remaining_test.rb +8 -8
  55. data/test/delayed_io_test.rb +91 -30
  56. data/test/float_test.rb +8 -8
  57. data/test/int_test.rb +14 -14
  58. data/test/io_test.rb +110 -302
  59. data/test/lazy_test.rb +38 -38
  60. data/test/params_test.rb +19 -19
  61. data/test/primitive_test.rb +26 -26
  62. data/test/record_test.rb +99 -99
  63. data/test/registry_test.rb +43 -43
  64. data/test/rest_test.rb +5 -5
  65. data/test/section_test.rb +111 -0
  66. data/test/skip_test.rb +71 -26
  67. data/test/string_test.rb +60 -60
  68. data/test/stringz_test.rb +34 -26
  69. data/test/struct_test.rb +167 -92
  70. data/test/system_test.rb +159 -41
  71. data/test/test_helper.rb +24 -13
  72. data/test/uint8_array_test.rb +6 -6
  73. data/test/virtual_test.rb +7 -7
  74. data/test/warnings_test.rb +14 -2
  75. metadata +19 -22
  76. data/.gitignore +0 -2
  77. data/.travis.yml +0 -15
  78. data/BSDL +0 -22
  79. data/COPYING +0 -52
  80. data/INSTALL +0 -12
  81. data/lib/bindata/offset.rb +0 -94
  82. data/test/offset_test.rb +0 -100
data/test/string_test.rb CHANGED
@@ -5,17 +5,17 @@ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
5
5
  describe BinData::String, "with mutually exclusive parameters" do
6
6
  it ":value and :initial_value" do
7
7
  params = {value: "", initial_value: ""}
8
- lambda { BinData::String.new(params) }.must_raise ArgumentError
8
+ _ { BinData::String.new(params) }.must_raise ArgumentError
9
9
  end
10
10
 
11
11
  it ":length and :read_length" do
12
12
  params = {length: 5, read_length: 5}
13
- lambda { BinData::String.new(params) }.must_raise ArgumentError
13
+ _ { BinData::String.new(params) }.must_raise ArgumentError
14
14
  end
15
15
 
16
16
  it ":value and :length" do
17
17
  params = {value: "", length: 5}
18
- lambda { BinData::String.new(params) }.must_raise ArgumentError
18
+ _ { BinData::String.new(params) }.must_raise ArgumentError
19
19
  end
20
20
  end
21
21
 
@@ -25,12 +25,12 @@ describe BinData::String, "when assigning" do
25
25
 
26
26
  it "copies data from small to large" do
27
27
  large.assign(small)
28
- large.must_equal "AAABB"
28
+ _(large).must_equal "AAABB"
29
29
  end
30
30
 
31
31
  it "copies data from large to small" do
32
32
  small.assign(large)
33
- small.must_equal "BBB"
33
+ _(small).must_equal "BBB"
34
34
  end
35
35
  end
36
36
 
@@ -38,96 +38,96 @@ describe BinData::String do
38
38
  let(:obj) { BinData::String.new("testing") }
39
39
 
40
40
  it "compares with regexp" do
41
- (/es/ =~ obj).must_equal 1
41
+ _((/es/ =~ obj)).must_equal 1
42
42
  end
43
43
 
44
44
  it "compares with regexp" do
45
- (obj =~ /es/).must_equal 1
45
+ _((obj =~ /es/)).must_equal 1
46
46
  end
47
47
  end
48
48
 
49
49
  describe BinData::String, "with :read_length" do
50
50
  let(:obj) { BinData::String.new(read_length: 5) }
51
51
 
52
- specify { obj.num_bytes.must_equal 0 }
53
- specify { obj.value.must_equal "" }
52
+ specify { _(obj.num_bytes).must_equal 0 }
53
+ specify { _(obj.value).must_equal "" }
54
54
 
55
55
  it "reads :read_length bytes" do
56
56
  obj.read("abcdefghij")
57
- obj.must_equal "abcde"
57
+ _(obj).must_equal "abcde"
58
58
  end
59
59
 
60
60
  it "remembers :read_length after value is cleared" do
61
61
  obj.assign("abc")
62
- obj.num_bytes.must_equal 3
62
+ _(obj.num_bytes).must_equal 3
63
63
  obj.clear
64
64
 
65
65
  obj.read("abcdefghij")
66
- obj.must_equal "abcde"
66
+ _(obj).must_equal "abcde"
67
67
  end
68
68
  end
69
69
 
70
70
  describe BinData::String, "with :length" do
71
71
  let(:obj) { BinData::String.new(length: 5) }
72
72
 
73
- specify { obj.num_bytes.must_equal 5 }
74
- specify { obj.value.must_equal "\0\0\0\0\0" }
73
+ specify { _(obj.num_bytes).must_equal 5 }
74
+ specify { _(obj.value).must_equal "\0\0\0\0\0" }
75
75
 
76
76
  it "retains :length after value is set" do
77
77
  obj.assign("abcdefghij")
78
- obj.num_bytes.must_equal 5
78
+ _(obj.num_bytes).must_equal 5
79
79
  end
80
80
 
81
81
  it "reads :length bytes" do
82
82
  obj.read("abcdefghij")
83
- obj.must_equal "abcde"
83
+ _(obj).must_equal "abcde"
84
84
  end
85
85
 
86
86
  it "pads values less than :length" do
87
87
  obj.assign("abc")
88
- obj.must_equal "abc\0\0"
88
+ _(obj).must_equal "abc\0\0"
89
89
  end
90
90
 
91
91
  it "accepts values exactly :length" do
92
92
  obj.assign("abcde")
93
- obj.must_equal "abcde"
93
+ _(obj).must_equal "abcde"
94
94
  end
95
95
 
96
96
  it "truncates values greater than :length" do
97
97
  obj.assign("abcdefghij")
98
- obj.must_equal "abcde"
98
+ _(obj).must_equal "abcde"
99
99
  end
100
100
  end
101
101
 
102
102
  describe BinData::String, "with :read_length and :initial_value" do
103
103
  let(:obj) { BinData::String.new(read_length: 5, initial_value: "abcdefghij") }
104
104
 
105
- specify { obj.num_bytes.must_equal 10 }
106
- specify { obj.value.must_equal "abcdefghij" }
105
+ specify { _(obj.num_bytes).must_equal 10 }
106
+ specify { _(obj.value).must_equal "abcdefghij" }
107
107
 
108
108
  it "uses :read_length for reading" do
109
109
  io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
110
110
  obj.read(io)
111
- io.pos.must_equal 5
111
+ _(io.pos).must_equal 5
112
112
  end
113
113
 
114
114
  it "forgets :initial_value after reading" do
115
115
  obj.read("ABCDEFGHIJKLMNOPQRST")
116
- obj.num_bytes.must_equal 5
117
- obj.must_equal "ABCDE"
116
+ _(obj.num_bytes).must_equal 5
117
+ _(obj).must_equal "ABCDE"
118
118
  end
119
119
  end
120
120
 
121
121
  describe BinData::String, "with :read_length and :value" do
122
122
  let(:obj) { BinData::String.new(read_length: 5, value: "abcdefghij") }
123
123
 
124
- specify { obj.num_bytes.must_equal 10 }
125
- specify { obj.value.must_equal "abcdefghij" }
124
+ specify { _(obj.num_bytes).must_equal 10 }
125
+ specify { _(obj.value).must_equal "abcdefghij" }
126
126
 
127
127
  it "uses :read_length for reading" do
128
128
  io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
129
129
  obj.read(io)
130
- io.pos.must_equal 5
130
+ _(io.pos).must_equal 5
131
131
  end
132
132
 
133
133
  describe "after reading" do
@@ -136,13 +136,13 @@ describe BinData::String, "with :read_length and :value" do
136
136
  end
137
137
 
138
138
  it "is not affected by :read_length after reading" do
139
- obj.num_bytes.must_equal 10
140
- obj.must_equal "abcdefghij"
139
+ _(obj.num_bytes).must_equal 10
140
+ _(obj).must_equal "abcdefghij"
141
141
  end
142
142
 
143
143
  it "returns read value while reading" do
144
144
  obj.stub :reading?, true do
145
- obj.must_equal "ABCDE"
145
+ _(obj).must_equal "ABCDE"
146
146
  end
147
147
  end
148
148
  end
@@ -151,15 +151,15 @@ end
151
151
  describe BinData::String, "with :length and :initial_value" do
152
152
  let(:obj) { BinData::String.new(length: 5, initial_value: "abcdefghij") }
153
153
 
154
- specify { obj.num_bytes.must_equal 5 }
155
- specify { obj.value.must_equal "abcde" }
154
+ specify { _(obj.num_bytes).must_equal 5 }
155
+ specify { _(obj.value).must_equal "abcde" }
156
156
 
157
157
  it "forgets :initial_value after reading" do
158
158
  io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
159
159
  obj.read(io)
160
- io.pos.must_equal 5
161
- obj.num_bytes.must_equal 5
162
- obj.must_equal "ABCDE"
160
+ _(io.pos).must_equal 5
161
+ _(obj.num_bytes).must_equal 5
162
+ _(obj).must_equal "ABCDE"
163
163
  end
164
164
  end
165
165
 
@@ -167,18 +167,18 @@ describe BinData::String, "with :pad_byte" do
167
167
  it "accepts a numeric value for :pad_byte" do
168
168
  str = BinData::String.new(length: 5, pad_byte: 6)
169
169
  str.assign("abc")
170
- str.must_equal "abc\x06\x06"
170
+ _(str).must_equal "abc\x06\x06"
171
171
  end
172
172
 
173
173
  it "accepts a character for :pad_byte" do
174
174
  str = BinData::String.new(length: 5, pad_byte: "R")
175
175
  str.assign("abc")
176
- str.must_equal "abcRR"
176
+ _(str).must_equal "abcRR"
177
177
  end
178
178
 
179
179
  it "does not accept a string for :pad_byte" do
180
180
  params = {length: 5, pad_byte: "RR"}
181
- lambda { BinData::String.new(params) }.must_raise ArgumentError
181
+ _ { BinData::String.new(params) }.must_raise ArgumentError
182
182
  end
183
183
  end
184
184
 
@@ -188,8 +188,8 @@ describe BinData::String, "with :trim_padding" do
188
188
  str2 = BinData::String.new(length: 5, trim_padding: false)
189
189
  str1.assign("abc")
190
190
  str2.assign("abc")
191
- str1.must_equal "abc\0\0"
192
- str2.must_equal "abc\0\0"
191
+ _(str1).must_equal "abc\0\0"
192
+ _(str2).must_equal "abc\0\0"
193
193
  end
194
194
 
195
195
  describe "trim padding set" do
@@ -197,22 +197,22 @@ describe BinData::String, "with :trim_padding" do
197
197
 
198
198
  it "trims the value" do
199
199
  obj.assign("abcRR")
200
- obj.must_equal "abc"
200
+ _(obj).must_equal "abc"
201
201
  end
202
202
 
203
203
  it "does not affect num_bytes" do
204
204
  obj.assign("abcRR")
205
- obj.num_bytes.must_equal 5
205
+ _(obj.num_bytes).must_equal 5
206
206
  end
207
207
 
208
208
  it "trims if last char is :pad_byte" do
209
209
  obj.assign("abcRR")
210
- obj.must_equal "abc"
210
+ _(obj).must_equal "abc"
211
211
  end
212
212
 
213
213
  it "does not trim if value contains :pad_byte not at the end" do
214
214
  obj.assign("abcRRde")
215
- obj.must_equal "abcRRde"
215
+ _(obj).must_equal "abcRRde"
216
216
  end
217
217
  end
218
218
  end
@@ -223,20 +223,20 @@ describe BinData::String, "with :pad_front" do
223
223
  str2 = BinData::String.new(length: 5, pad_front: false)
224
224
  str1.assign("abc")
225
225
  str2.assign("abc")
226
- str1.must_equal "abc\0\0"
227
- str2.must_equal "abc\0\0"
226
+ _(str1).must_equal "abc\0\0"
227
+ _(str2).must_equal "abc\0\0"
228
228
  end
229
229
 
230
230
  it "pads to the front" do
231
231
  str = BinData::String.new(length: 5, pad_byte: 'R', pad_front: true)
232
232
  str.assign("abc")
233
- str.must_equal "RRabc"
233
+ _(str).must_equal "RRabc"
234
234
  end
235
235
 
236
236
  it "can alternatively be accesses by :pad_left" do
237
237
  str = BinData::String.new(length: 5, pad_byte: 'R', pad_left: true)
238
238
  str.assign("abc")
239
- str.must_equal "RRabc"
239
+ _(str).must_equal "RRabc"
240
240
  end
241
241
 
242
242
  describe "and :trim_padding" do
@@ -244,17 +244,17 @@ describe BinData::String, "with :pad_front" do
244
244
 
245
245
  it "assigns" do
246
246
  obj.assign("abc")
247
- obj.must_equal "abc"
247
+ _(obj).must_equal "abc"
248
248
  end
249
249
 
250
250
  it "has to_binary_s" do
251
251
  obj.assign("abc")
252
- obj.to_binary_s.must_equal_binary "RRabc"
252
+ _(obj.to_binary_s).must_equal_binary "RRabc"
253
253
  end
254
254
 
255
255
  it "reads" do
256
256
  obj.read "RRabc"
257
- obj.must_equal "abc"
257
+ _(obj).must_equal "abc"
258
258
  end
259
259
  end
260
260
  end
@@ -272,41 +272,41 @@ describe BinData::String, "with Ruby 1.9 encodings" do
272
272
 
273
273
  it "stores assigned values as binary" do
274
274
  obj.assign(utf8_str)
275
- obj.to_binary_s.must_equal_binary binary_str
275
+ _(obj.to_binary_s).must_equal_binary binary_str
276
276
  end
277
277
 
278
278
  it "stores read values as binary" do
279
279
  obj = UTF8String.new(read_length: binary_str.bytesize)
280
280
  obj.read(binary_str)
281
281
 
282
- obj.to_binary_s.must_equal_binary binary_str
282
+ _(obj.to_binary_s).must_equal_binary binary_str
283
283
  end
284
284
 
285
285
  it "returns values in correct encoding" do
286
286
  obj.assign(utf8_str)
287
287
 
288
- obj.snapshot.must_equal utf8_str
288
+ _(obj.snapshot).must_equal utf8_str
289
289
  end
290
290
 
291
291
  it "has correct num_bytes" do
292
292
  obj.assign(utf8_str)
293
293
 
294
- obj.num_bytes.must_equal binary_str.bytesize
294
+ _(obj.num_bytes).must_equal binary_str.bytesize
295
295
  end
296
296
  end
297
297
 
298
298
  describe BinData::String, "warnings" do
299
299
  it "warns if has :asserted_value but no :length" do
300
300
  obj = BinData::String.new(asserted_value: "ABC")
301
- obj.must_warn "obj does not have a :read_length parameter - returning empty string" do
302
- lambda { obj.read("abcde") }.must_raise BinData::ValidityError
303
- end
301
+ _ {
302
+ _ { obj.read("abcde") }.must_raise BinData::ValidityError
303
+ }.must_warn "obj does not have a :read_length parameter - returning empty string"
304
304
  end
305
305
 
306
306
  it "warns if has :value but no :read_length" do
307
307
  obj = BinData::String.new(value: "ABC")
308
- obj.must_warn "obj does not have a :read_length parameter - returning empty string" do
308
+ _ {
309
309
  obj.read("abcde")
310
- end
310
+ }.must_warn "obj does not have a :read_length parameter - returning empty string"
311
311
  end
312
312
  end
data/test/stringz_test.rb CHANGED
@@ -6,9 +6,9 @@ describe BinData::Stringz, "when empty" do
6
6
  let(:obj) { BinData::Stringz.new }
7
7
 
8
8
  it "initial state" do
9
- obj.value.must_equal ""
10
- obj.num_bytes.must_equal 1
11
- obj.to_binary_s.must_equal_binary "\0"
9
+ _(obj.value).must_equal ""
10
+ _(obj.num_bytes).must_equal 1
11
+ _(obj.to_binary_s).must_equal_binary "\0"
12
12
  end
13
13
  end
14
14
 
@@ -16,9 +16,9 @@ describe BinData::Stringz, "with value set" do
16
16
  let(:obj) { BinData::Stringz.new("abcd") }
17
17
 
18
18
  it "initial state" do
19
- obj.value.must_equal "abcd"
20
- obj.num_bytes.must_equal 5
21
- obj.to_binary_s.must_equal_binary "abcd\0"
19
+ _(obj.value).must_equal "abcd"
20
+ _(obj.num_bytes).must_equal 5
21
+ _(obj.to_binary_s).must_equal_binary "abcd\0"
22
22
  end
23
23
  end
24
24
 
@@ -28,19 +28,19 @@ describe BinData::Stringz, "when reading" do
28
28
  it "stops at the first zero byte" do
29
29
  io = StringIO.new("abcd\0xyz\0")
30
30
  obj.read(io)
31
- io.pos.must_equal 5
32
- obj.must_equal "abcd"
31
+ _(io.pos).must_equal 5
32
+ _(obj).must_equal "abcd"
33
33
  end
34
34
 
35
35
  it "handles a zero length string" do
36
36
  io = StringIO.new("\0abcd")
37
37
  obj.read(io)
38
- io.pos.must_equal 1
39
- obj.must_equal ""
38
+ _(io.pos).must_equal 1
39
+ _(obj).must_equal ""
40
40
  end
41
41
 
42
42
  it "fails if no zero byte is found" do
43
- lambda {obj.read("abcd") }.must_raise EOFError
43
+ _ {obj.read("abcd") }.must_raise EOFError
44
44
  end
45
45
  end
46
46
 
@@ -49,79 +49,87 @@ describe BinData::Stringz, "when setting the value" do
49
49
 
50
50
  it "includes the zero byte in num_bytes total" do
51
51
  obj.assign("abcd")
52
- obj.num_bytes.must_equal 5
52
+ _(obj.num_bytes).must_equal 5
53
53
  end
54
54
 
55
55
  it "accepts empty strings" do
56
56
  obj.assign("")
57
- obj.must_equal ""
57
+ _(obj).must_equal ""
58
58
  end
59
59
 
60
60
  it "accepts strings that aren't zero terminated" do
61
61
  obj.assign("abcd")
62
- obj.must_equal "abcd"
62
+ _(obj).must_equal "abcd"
63
63
  end
64
64
 
65
65
  it "accepts strings that are zero terminated" do
66
66
  obj.assign("abcd\0")
67
- obj.must_equal "abcd"
67
+ _(obj).must_equal "abcd"
68
68
  end
69
69
 
70
70
  it "accepts up to the first zero byte" do
71
71
  obj.assign("abcd\0xyz\0")
72
- obj.must_equal "abcd"
72
+ _(obj).must_equal "abcd"
73
73
  end
74
74
  end
75
75
 
76
76
  describe BinData::Stringz, "with max_length" do
77
77
  let(:obj) { BinData::Stringz.new(max_length: 5) }
78
78
 
79
+ it "fails if max_length is less that 1" do
80
+ obj = BinData::Stringz.new(max_length: 0)
81
+
82
+ _{ obj.read "abc\0" }.must_raise ArgumentError
83
+ _{ obj.to_binary_s }.must_raise ArgumentError
84
+ _{ obj.num_bytes }.must_raise ArgumentError
85
+ end
86
+
79
87
  it "reads less than max_length" do
80
88
  io = StringIO.new("abc\0xyz")
81
89
  obj.read(io)
82
- obj.must_equal "abc"
90
+ _(obj).must_equal "abc"
83
91
  end
84
92
 
85
93
  it "reads exactly max_length" do
86
94
  io = StringIO.new("abcd\0xyz")
87
95
  obj.read(io)
88
- obj.must_equal "abcd"
96
+ _(obj).must_equal "abcd"
89
97
  end
90
98
 
91
99
  it "reads no more than max_length" do
92
100
  io = StringIO.new("abcdefg\0xyz")
93
101
  obj.read(io)
94
- io.pos.must_equal 5
95
- obj.must_equal "abcd"
102
+ _(io.pos).must_equal 5
103
+ _(obj).must_equal "abcd"
96
104
  end
97
105
 
98
106
  it "accepts values less than max_length" do
99
107
  obj.assign("abc")
100
- obj.must_equal "abc"
108
+ _(obj).must_equal "abc"
101
109
  end
102
110
 
103
111
  it "accepts values exactly max_length" do
104
112
  obj.assign("abcd")
105
- obj.must_equal "abcd"
113
+ _(obj).must_equal "abcd"
106
114
  end
107
115
 
108
116
  it "trims values greater than max_length" do
109
117
  obj.assign("abcdefg")
110
- obj.must_equal "abcd"
118
+ _(obj).must_equal "abcd"
111
119
  end
112
120
 
113
121
  it "writes values greater than max_length" do
114
122
  obj.assign("abcdefg")
115
- obj.to_binary_s.must_equal_binary "abcd\0"
123
+ _(obj.to_binary_s).must_equal_binary "abcd\0"
116
124
  end
117
125
 
118
126
  it "writes values less than max_length" do
119
127
  obj.assign("abc")
120
- obj.to_binary_s.must_equal_binary "abc\0"
128
+ _(obj.to_binary_s).must_equal_binary "abc\0"
121
129
  end
122
130
 
123
131
  it "writes values exactly max_length" do
124
132
  obj.assign("abcd")
125
- obj.to_binary_s.must_equal_binary "abcd\0"
133
+ _(obj.to_binary_s).must_equal_binary "abcd\0"
126
134
  end
127
135
  end