diff options
| -rw-r--r-- | io_buffer.c | 146 | ||||
| -rw-r--r-- | lib/bundler/source/rubygems.rb | 2 | ||||
| -rw-r--r-- | lib/bundler/source_list.rb | 4 | ||||
| -rw-r--r-- | spec/bundler/install/cooldown_spec.rb | 282 | ||||
| -rw-r--r-- | test/-ext-/symbol/test_inadvertent_creation.rb | 43 | ||||
| -rw-r--r-- | test/ruby/test_io_buffer.rb | 41 |
6 files changed, 485 insertions, 33 deletions
diff --git a/io_buffer.c b/io_buffer.c index d9f50fc234..dc8f547a32 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -899,8 +899,8 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) } // Internal function for accessing bytes for writing, wil -static inline void -io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size) +static void +io_buffer_validate_for_writing(struct rb_io_buffer *buffer) { if (buffer->flags & RB_IO_BUFFER_READONLY || (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) { @@ -910,6 +910,21 @@ io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t if (!io_buffer_validate(buffer)) { rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!"); } +} + +static struct rb_io_buffer * +get_io_buffer_for_writing(VALUE self) +{ + struct rb_io_buffer *buffer = NULL; + TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer); + io_buffer_validate_for_writing(buffer); + return buffer; +} + +static inline void +io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size) +{ + io_buffer_validate_for_writing(buffer); if (buffer->base) { *base = buffer->base; @@ -930,11 +945,17 @@ rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size) } static void -io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size) +io_buffer_validate_for_reading(struct rb_io_buffer *buffer) { if (!io_buffer_validate(buffer)) { rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!"); } +} + +static void +io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size) +{ + io_buffer_validate_for_reading(buffer); if (buffer->base) { *base = buffer->base; @@ -1373,14 +1394,23 @@ io_buffer_readonly_p(VALUE self) return RBOOL(rb_io_buffer_readonly_p(self)); } -static void -io_buffer_lock(struct rb_io_buffer *buffer) +static int +io_buffer_try_lock(struct rb_io_buffer *buffer) { if (buffer->flags & RB_IO_BUFFER_LOCKED) { - rb_raise(rb_eIOBufferLockedError, "Buffer already locked!"); + return 0; } buffer->flags |= RB_IO_BUFFER_LOCKED; + return 1; +} + +static void +io_buffer_lock(struct rb_io_buffer *buffer) +{ + if (!io_buffer_try_lock(buffer)) { + rb_raise(rb_eIOBufferLockedError, "Buffer already locked!"); + } } VALUE @@ -1548,6 +1578,8 @@ size_sum_is_bigger_than(size_t a, size_t b, size_t x) static inline void io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length) { + io_buffer_validate_for_reading(buffer); + if (size_sum_is_bigger_than(offset, length, buffer->size)) { rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!"); } @@ -1752,6 +1784,8 @@ rb_io_buffer_resize(VALUE self, size_t size) struct rb_io_buffer *buffer = NULL; TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer); + io_buffer_validate_for_reading(buffer); + if (buffer->flags & RB_IO_BUFFER_LOCKED) { rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!"); } @@ -1954,6 +1988,10 @@ ruby_swap128_int(rb_int128_t x) return conversion.int128; } +#define IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, offset, type) \ + (io_buffer_get_bytes_for_writing(buffer, &(base), &(size)), \ + io_buffer_validate_type(size, offset, sizeof(type))) + #define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ static ID RB_IO_BUFFER_DATA_TYPE_##name; \ \ @@ -1969,10 +2007,12 @@ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \ } \ \ static void \ -io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \ +io_buffer_write_##name(struct rb_io_buffer* buffer, size_t *offset, VALUE _value) \ { \ - io_buffer_validate_type(size, *offset, sizeof(type)); \ + void* base; size_t size; \ + IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \ type value = unwrap(_value); \ + IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \ if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \ memcpy((char*)base + *offset, &value, sizeof(type)); \ *offset += sizeof(type); \ @@ -2042,6 +2082,15 @@ io_buffer_buffer_type_size(ID buffer_type) rb_raise(rb_eArgError, "Invalid type name!"); } +static inline ID +io_buffer_type_id(VALUE name) +{ + Check_Type(name, T_SYMBOL); + if (!STATIC_SYM_P(name)) return 0; + return rb_sym2id(name); +} +#define TYPE_ID(name) io_buffer_type_id(name) + /* * call-seq: * size_of(buffer_type) -> byte size @@ -2058,12 +2107,12 @@ io_buffer_size_of(VALUE klass, VALUE buffer_type) if (RB_TYPE_P(buffer_type, T_ARRAY)) { size_t total = 0; for (long i = 0; i < RARRAY_LEN(buffer_type); i++) { - total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i))); + total += io_buffer_buffer_type_size(TYPE_ID(RARRAY_AREF(buffer_type, i))); } return SIZET2NUM(total); } else { - return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type))); + return SIZET2NUM(io_buffer_buffer_type_size(TYPE_ID(buffer_type))); } } @@ -2150,7 +2199,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) rb_io_buffer_get_bytes_for_reading(self, &base, &size); - return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); + return rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset); } /* @@ -2180,7 +2229,7 @@ io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset) for (long i = 0; i < RARRAY_LEN(buffer_types); i++) { VALUE type = rb_ary_entry(buffer_types, i); - VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); + VALUE value = rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset); rb_ary_push(array, value); } @@ -2249,7 +2298,7 @@ io_buffer_each(int argc, VALUE *argv, VALUE self) ID buffer_type; if (argc >= 1) { - buffer_type = RB_SYM2ID(argv[0]); + buffer_type = TYPE_ID(argv[0]); } else { buffer_type = RB_IO_BUFFER_DATA_TYPE_U8; @@ -2287,7 +2336,7 @@ io_buffer_values(int argc, VALUE *argv, VALUE self) ID buffer_type; if (argc >= 1) { - buffer_type = RB_SYM2ID(argv[0]); + buffer_type = TYPE_ID(argv[0]); } else { buffer_type = RB_IO_BUFFER_DATA_TYPE_U8; @@ -2347,9 +2396,10 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self) } static inline void -rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *offset, VALUE value) +rb_io_buffer_set_value(struct rb_io_buffer *buffer, VALUE buffer_type, size_t *offset, VALUE value) { -#define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;} + ID type = TYPE_ID(buffer_type); +#define IO_BUFFER_SET_VALUE(name) if (type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(buffer, offset, value); return;} IO_BUFFER_SET_VALUE(U8); IO_BUFFER_SET_VALUE(S8); @@ -2382,6 +2432,21 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of rb_raise(rb_eArgError, "Invalid type name!"); } +struct io_buffer_set_value_arguments { + struct rb_io_buffer *buffer; + size_t offset; + VALUE type, value; +}; + +static VALUE +io_buffer_set_value_try(VALUE arguments) +{ + struct io_buffer_set_value_arguments *args = (void *)arguments; + size_t offset = args->offset; + rb_io_buffer_set_value(args->buffer, args->type, &offset, args->value); + return SIZET2NUM(offset); +} + /* * call-seq: set_value(type, offset, value) -> offset * @@ -2415,13 +2480,30 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of static VALUE io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) { - void *base; - size_t size; - size_t offset = io_buffer_extract_offset(_offset); + struct io_buffer_set_value_arguments arguments = { + .buffer = get_io_buffer_for_writing(self), + .offset = io_buffer_extract_offset(_offset), + .type = type, + .value = value, + }; - rb_io_buffer_get_bytes_for_writing(self, &base, &size); + if (!io_buffer_try_lock(arguments.buffer)) { + return io_buffer_set_value_try((VALUE)&arguments); + } + return rb_ensure(io_buffer_set_value_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self); +} - rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); +static VALUE +io_buffer_set_values_try(VALUE arguments) +{ + struct io_buffer_set_value_arguments *args = (void *)arguments; + size_t offset = args->offset; + + for (long i = 0; i < RARRAY_LEN(args->type); i++) { + VALUE type = rb_ary_entry(args->type, i); + VALUE value = rb_ary_entry(args->value, i); + rb_io_buffer_set_value(args->buffer, type, &offset, value); + } return SIZET2NUM(offset); } @@ -2443,31 +2525,31 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) static VALUE io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values) { + struct io_buffer_set_value_arguments arguments = { + .buffer = get_io_buffer_for_writing(self), + }; + if (!RB_TYPE_P(buffer_types, T_ARRAY)) { rb_raise(rb_eArgError, "Argument buffer_types should be an array!"); } + arguments.type = buffer_types; + + arguments.offset = io_buffer_extract_offset(_offset); if (!RB_TYPE_P(values, T_ARRAY)) { rb_raise(rb_eArgError, "Argument values should be an array!"); } + arguments.value = values; if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) { rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!"); } - size_t offset = io_buffer_extract_offset(_offset); - - void *base; - size_t size; - rb_io_buffer_get_bytes_for_writing(self, &base, &size); - - for (long i = 0; i < RARRAY_LEN(buffer_types); i++) { - VALUE type = rb_ary_entry(buffer_types, i); - VALUE value = rb_ary_entry(values, i); - rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + if (!io_buffer_try_lock(arguments.buffer)) { + return io_buffer_set_values_try((VALUE)&arguments); } + return rb_ensure(io_buffer_set_values_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self); - return SIZET2NUM(offset); } static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024; diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index ed864604fe..9109f399a7 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -11,7 +11,7 @@ module Bundler API_REQUEST_SIZE = 100 REQUIRE_MUTEX = Mutex.new - attr_accessor :remotes + attr_accessor :remotes, :remote_cooldowns def initialize(options = {}) @options = options diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index ab7002d6e5..954efbb65f 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -169,6 +169,10 @@ module Bundler # locked sources never include credentials so always prefer remotes from the gemfile replacement_source.remotes = gemfile_source.remotes + # cooldowns are only ever declared in the Gemfile, so carry them over + # along with the remotes they apply to + replacement_source.remote_cooldowns = gemfile_source.remote_cooldowns + yield replacement_source if block_given? replacement_source diff --git a/spec/bundler/install/cooldown_spec.rb b/spec/bundler/install/cooldown_spec.rb index bad7b7cf34..01e87be663 100644 --- a/spec/bundler/install/cooldown_spec.rb +++ b/spec/bundler/install/cooldown_spec.rb @@ -143,6 +143,64 @@ RSpec.describe "bundle install with the cooldown setting" do expect(the_bundle).to include_gems("ripe_gem 1.0.0") end + it "applies per-source Gemfile cooldown on bundle update when a lockfile exists" do + # Converging the Gemfile sources with the lockfile sources used to drop + # the per-source cooldown, so it only ever worked on a first resolve + # without a lockfile. + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update ripe_gem", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("ripe_gem 1.0.0") + end + + it "applies per-source Gemfile cooldown to gems added after the lockfile was written" do + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + gem "child" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("ripe_gem 1.0.0", "child 1.0.0") + end + it "is overridden by CLI --cooldown when Gemfile sets a different per-source value" do gemfile <<-G source "https://gem.repo3", cooldown: 0 @@ -429,5 +487,229 @@ RSpec.describe "bundle install with the cooldown setting" do expect(the_bundle).to include_gems("upgradable 3.0.0") end + + it "keeps a top-level source cooldown through a partial update with multiple sources" do + now = Time.now.utc + build_repo4 do + build_gem "solo_gem", "1.0.0" do |s| + s.date = now - (30 * 86_400) + end + build_gem "solo_gem", "2.0.0" do |s| + s.date = now - (1 * 86_400) + end + end + + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + source "https://gem.repo4" do + gem "solo_gem" + end + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + GEM + remote: https://gem.repo4/ + specs: + solo_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + solo_gem! + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update ripe_gem", artifice: "compact_index_cooldown" + + # A partial update converges the still-locked sources, the path that used + # to drop cooldown. repo3's cooldown must survive that even with a second + # source in the Gemfile, so its in-window 2.0.0 stays excluded. + expect(the_bundle).to include_gems("ripe_gem 1.0.0", "solo_gem 1.0.0") + end + + it "carries cooldown declared on a gem-block source" do + now = Time.now.utc + build_repo4 do + build_gem "solo_gem", "1.0.0" do |s| + s.date = now - (30 * 86_400) + end + build_gem "solo_gem", "2.0.0" do |s| + s.date = now - (1 * 86_400) + end + end + + gemfile <<-G + source "https://gem.repo3" + gem "ripe_gem" + source "https://gem.repo4", cooldown: 7 do + gem "solo_gem" + end + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + GEM + remote: https://gem.repo4/ + specs: + solo_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + solo_gem! + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update solo_gem", artifice: "compact_index_cooldown" + + # The cooldown lives on the gem-block source, which is also converged from + # the lockfile. A partial update of solo_gem must keep that cooldown, so + # its in-window 2.0.0 stays excluded. + expect(the_bundle).to include_gems("ripe_gem 1.0.0", "solo_gem 1.0.0") + end + + it "applies per-source Gemfile cooldown to a gem added via bundle add" do + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "add child", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("child 1.0.0") + end + + it "applies per-source Gemfile cooldown on bundle lock --update" do + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update ripe_gem", artifice: "compact_index_cooldown" + + expect(lockfile).to include("ripe_gem (1.0.0)") + expect(lockfile).not_to include("ripe_gem (2.0.0)") + end + + it "ignores cooldown and installs the locked version when frozen" do + # Frozen installs read the lockfile instead of resolving, so cooldown has + # no say. A version already locked inside the window must still install. + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "config set frozen true" + bundle "install", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("ripe_gem 2.0.0") + end + + it "keys per-source cooldown by the declared URI even behind a mirror" do + # A mirror rewrites the fetch URI, but cooldown is recorded under the URI + # written in the Gemfile. The cooldown must still apply through the + # redirect to the mirror that actually serves the gems. + bundle "config set mirror.https://gem.repo2 https://gem.repo3" + + gemfile <<-G + source "https://gem.repo2", cooldown: 7 + gem "ripe_gem" + G + + bundle "install", artifice: "compact_index_cooldown", + env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo3.to_s } + + expect(the_bundle).to include_gems("ripe_gem 1.0.0") + end + end + + context "with a source that does not provide publish dates" do + before do + build_repo3 do + build_gem "ripe_gem", "1.0.0" + build_gem "ripe_gem", "2.0.0" + end + end + + it "cannot apply cooldown and installs the latest version" do + # The legacy dependency API does not expose per-version publish dates, so + # the cooldown filter has nothing to compare against and is silently + # inactive. This pins that limitation; flip the expectation if publish + # dates ever become available over this endpoint. + gemfile <<-G + source "https://gem.repo3", cooldown: 7 + gem "ripe_gem" + G + + bundle "install", artifice: "endpoint" + + expect(the_bundle).to include_gems("ripe_gem 2.0.0") + end end end diff --git a/test/-ext-/symbol/test_inadvertent_creation.rb b/test/-ext-/symbol/test_inadvertent_creation.rb index 995e01ee15..44705e10c7 100644 --- a/test/-ext-/symbol/test_inadvertent_creation.rb +++ b/test/-ext-/symbol/test_inadvertent_creation.rb @@ -489,5 +489,48 @@ module Test_Symbol Bug::Symbol.iv_get(obj, name) end end + + def assert_io_buffer_no_immortal_symbol_created(buffer = IO::Buffer.new(128)) + assert_no_immortal_symbol_created("io_buffer") do |name| + yield buffer, name.to_sym + end + end + + def test_io_buffer_size_of_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created(nil) do |_, name| + assert_raise(ArgumentError) {IO::Buffer.size_of(name)} + assert_raise(ArgumentError) {IO::Buffer.size_of([name])} + end + end + + def test_io_buffer_each_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created do |buffer, name| + assert_raise(ArgumentError) {buffer.each(name, 0, 1) {}} + end + end + + def test_io_buffer_get_value_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created do |buffer, name| + assert_raise(ArgumentError) {buffer.get_value(name, 0)} + end + end + + def test_io_buffer_get_values_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created do |buffer, name| + assert_raise(ArgumentError) {buffer.get_values([name], 0)} + end + end + + def test_io_buffer_set_value_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created do |buffer, name| + assert_raise(ArgumentError) {buffer.set_value(name, 0, 0)} + end + end + + def test_io_buffer_set_values_inadvertent_id_creation + assert_io_buffer_no_immortal_symbol_created do |buffer, name| + assert_raise(ArgumentError) {buffer.set_values([name], 0, [0])} + end + end end end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 327a3ece9c..fdf99589ef 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -242,6 +242,16 @@ class TestIOBuffer < Test::Unit::TestCase end end + def test_resize_invalidated_slice + inner = IO::Buffer.new(IO::Buffer::PAGE_SIZE) + slice = inner.slice(0, 8) + inner.free + + assert_raise(IO::Buffer::InvalidatedError) do + slice.resize(16) + end + end + def test_compare_same_size buffer1 = IO::Buffer.new(1) assert_equal buffer1, buffer1 @@ -376,6 +386,17 @@ class TestIOBuffer < Test::Unit::TestCase assert_raise_with_message(ArgumentError, /Offset can't be negative/) do buffer.get_string(-1) end + + encoding = Struct.new(:buffer) do + def to_str + buffer.free + "BINARY" + end + end.new(buffer.dup) + slice = encoding.buffer.slice(0, 8) + assert_raise(IO::Buffer::InvalidatedError) do + slice.get_string(0, 8, encoding) + end end def test_zero_length_get_string @@ -449,6 +470,26 @@ class TestIOBuffer < Test::Unit::TestCase end end + def test_set_values_invalidated_slice + to_int = Struct.new(:buffer) do + def to_int + buffer.free + 0x41 + end + end + buffer = IO::Buffer.new(128) + slice = buffer.slice(0, 8) + value = to_int.new(buffer) + assert_raise(IO::Buffer::InvalidatedError) {slice.set_value(:U8, 0, value)} + + buffer = IO::Buffer.new(128) + slice = buffer.slice(0, 8) + value = to_int.new(buffer) + assert_raise(IO::Buffer::InvalidatedError) { + slice.set_values([:U8, :U8], 0, [0, value]) + } + end + def test_zero_length_get_set_values buffer = IO::Buffer.new(0) |
