diff --git a/enumerator.c b/enumerator.c index 94c0fa3a03a05d..1dc59143504d88 100644 --- a/enumerator.c +++ b/enumerator.c @@ -29,6 +29,7 @@ #include "internal/numeric.h" #include "internal/range.h" #include "internal/rational.h" +#include "internal/set.h" #include "ruby/ruby.h" /* @@ -2642,14 +2643,14 @@ lazy_drop_while(VALUE obj) static int lazy_uniq_check(VALUE chain, VALUE memos, long memo_index) { - VALUE hash = rb_ary_entry(memos, memo_index); + VALUE set = rb_ary_entry(memos, memo_index); - if (NIL_P(hash)) { - hash = rb_obj_hide(rb_hash_new()); - rb_ary_store(memos, memo_index, hash); + if (NIL_P(set)) { + set = rb_obj_hide(rb_set_new()); + rb_ary_store(memos, memo_index, set); } - return rb_hash_add_new_element(hash, chain, Qfalse); + return !rb_set_add_no_check(set, chain); } static struct MEMO * diff --git a/ext/erb/escape/escape.c b/ext/erb/escape/escape.c index 1268aa7b34e020..66cb77ed332a7d 100644 --- a/ext/erb/escape/escape.c +++ b/ext/erb/escape/escape.c @@ -172,93 +172,11 @@ find_next_match_neon(search_state *search) // uint64_t >>= 64 is undefined behaviour RUBY_ASSERT(trailing_zeros < 64); search->matches_bitmap >>= trailing_zeros; - search->cstr += trailing_zeros; + search->cstr += trailing_zeros / 4; RUBY_ASSERT(search->cstr <= search->end); return true; } -// This 16-byte lookup table is indexed into by using the -// low nibble of each input byte. -// Note: index 0 is intentionally set to a character that will not match -// the NULL byte. -static const uint8x16_t escape_char_by_low_nibble = { - '\'', 0, '"', 0, - 0, 0, '&', '\'', - 0, 0, 0, 0, - '<', 0, '>', 0, -}; - -static inline uint8x16_t -neon_escape_matches(const uint8x16_t bytes) -{ - // An example to demonstrate how this works. The goal is to get a uint8x16_t - // with each lane to equal 0xFF if the corresponding byte in 'bytes' needs - // to be escaped, or 0x00 otherwise. - // - // To keep things very simple, I'm going to assume a vector of length 6, in - // reality, the vector would be 16 bytes wide. - // - // Assume the string is: "
" - // Converted to integers: - // [0x3c 0x62 0x72 0x20 0x2f 0x3e] - // - // Next, we mask off the top nibble so we are left only with the low nibble - // of each byte. We do this by AND'ing each byte with 0x0F. - // - // The result: - // [0x0c 0x02 0x02 0x00 0x0f 0x0e] - // - // Now, we use these low nibbles as indexes into the - // escape_char_by_low_nibble array and find the full byte - // value we expect to match in the input. - // - // The result: - // [0x3c 0x22 0x22 0x27 0x00 0x3e] - // - // Finally, we compare the bytes we expect with the actual input bytes. - // - // The result: - // [0xFF 0x00 0x00 0x00 0x00 0xFF] - const uint8x16_t low_nibbles = vandq_u8(bytes, vdupq_n_u8(0x0F)); - const uint8x16_t looked_up = vqtbl1q_u8(escape_char_by_low_nibble, low_nibbles); - return vceqq_u8(looked_up, bytes); -} - -static inline uint64_t -neon_matches_to_bitmap16(const uint8x16_t matches) -{ - static const uint8x16_t bit_mask = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - }; - - uint8x16_t folded = vandq_u8(matches, bit_mask); - folded = vpaddq_u8(folded, folded); - folded = vpaddq_u8(folded, folded); - folded = vpaddq_u8(folded, folded); - - return vgetq_lane_u16(vreinterpretq_u16_u8(folded), 0); -} - -static inline uint64_t -neon_matches_to_bitmap64(const uint8x16_t m0, const uint8x16_t m1, const uint8x16_t m2, const uint8x16_t m3) -{ - static const uint8x16_t bit_mask = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - }; - - const uint8x16_t t0 = vandq_u8(m0, bit_mask); - const uint8x16_t t1 = vandq_u8(m1, bit_mask); - const uint8x16_t t2 = vandq_u8(m2, bit_mask); - const uint8x16_t t3 = vandq_u8(m3, bit_mask); - - uint8x16_t folded = vpaddq_u8(vpaddq_u8(t0, t1), vpaddq_u8(t2, t3)); - folded = vpaddq_u8(folded, folded); - - return vgetq_lane_u64(vreinterpretq_u64_u8(folded), 0); -} - static inline bool find_next_neon(search_state *search) { @@ -266,34 +184,30 @@ find_next_neon(search_state *search) return find_next_match_neon(search); } - while ((size_t)(search->end - search->cstr) >= sizeof(uint8x16x4_t)) { - const uint8x16_t bytes0 = vld1q_u8(search->cstr + 0); - const uint8x16_t bytes1 = vld1q_u8(search->cstr + 16); - const uint8x16_t bytes2 = vld1q_u8(search->cstr + 32); - const uint8x16_t bytes3 = vld1q_u8(search->cstr + 48); - - const uint8x16_t m0 = neon_escape_matches(bytes0); - const uint8x16_t m1 = neon_escape_matches(bytes1); - const uint8x16_t m2 = neon_escape_matches(bytes2); - const uint8x16_t m3 = neon_escape_matches(bytes3); - - const uint64_t bitmap = neon_matches_to_bitmap64(m0, m1, m2, m3); - - if (bitmap) { - search->matches_bitmap = bitmap; - return find_next_match_neon(search); - } - - search->cstr += 64; - } + const uint8x16_t single_quote = vdupq_n_u8('\''); + const uint8x16_t double_quote = vdupq_n_u8('"'); + const uint8x16_t ampersand = vdupq_n_u8('&'); + const uint8x16_t lt = vdupq_n_u8('<'); + const uint8x16_t gt = vdupq_n_u8('>'); while ((size_t)(search->end - search->cstr) >= sizeof(uint8x16_t)) { const uint8x16_t bytes = vld1q_u8(search->cstr); - const uint8x16_t matches = neon_escape_matches(bytes); - const uint64_t bitmap = neon_matches_to_bitmap16(matches); + const uint8x16_t match1 = vceqq_u8(bytes, single_quote); + const uint8x16_t match2 = vceqq_u8(bytes, double_quote); + const uint8x16_t match3 = vceqq_u8(bytes, ampersand); + const uint8x16_t match4 = vceqq_u8(bytes, lt); + const uint8x16_t match5 = vceqq_u8(bytes, gt); + + const uint8x16_t mask1 = vorrq_u8(match1, match2); + const uint8x16_t mask2 = vorrq_u8(match3, match4); + const uint8x16_t mask3 = vorrq_u8(mask1, match5); + const uint8x16_t matches = vorrq_u8(mask2, mask3); + + const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4); + const uint64_t bitmap = vget_lane_u64(vreinterpret_u64_u8(res), 0); if (bitmap) { - search->matches_bitmap = bitmap; + search->matches_bitmap = bitmap & 0x8888888888888888ull; return find_next_match_neon(search); } search->cstr += sizeof(uint8x16_t); diff --git a/file.c b/file.c index e3e12052e9e2de..0cf60f44da0b61 100644 --- a/file.c +++ b/file.c @@ -3751,11 +3751,12 @@ rb_file_s_link(VALUE klass, VALUE from, VALUE to) * :markup: markdown * * call-seq: - * File.symlink(path, link_path) -> 0 + * File.symlink(target_path, link_path) -> 0 * * Not supported on some platforms. * - * Creates a symbolic link at `link_path` to the entry at `path`: + * Creates a [symbolic link](rdoc-ref:file/symbolic_links.md) + * at `link_path` to the entry at `target_path`: * * ```ruby * # Create paths. @@ -3768,6 +3769,9 @@ rb_file_s_link(VALUE klass, VALUE from, VALUE to) * File.delete(link_path) # Clean up. * ``` * + * If the entry at `target_path` is itself a symlink, that link is _not_ followed; + * thus the created symlink always points to `target_path`. + * * See also: ::read, ::readlink, ::symlink?. */ diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 2191beccd1137a..1a200256bd2b06 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -1914,9 +1914,10 @@ def lstat() File.lstat(@path) end # :markup: markdown # # call-seq: - # make_symlink(path) -> 0 + # make_symlink(target_path) -> 0 # - # Creates a symbolic link at the path in `self` to the entry at `path`: + # Creates a [symbolic link](rdoc-ref:file/symbolic_links.md) + # at the path in `self` to the entry at `target_path`: # # ```ruby # # Create Pathnames. @@ -1929,6 +1930,9 @@ def lstat() File.lstat(@path) end # link_pn.delete # Clean up. # ``` # + # If the entry at `target_path` is itself a symlink, that link is _not_ followed; + # thus the created symlink always points to `target_path`. + # # See also: #read, #readlink, #symlink?. def make_symlink(old) File.symlink(old, @path) end diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 5afa23bc0e6baf..413f5a42585d17 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -899,6 +899,12 @@ def test_ungetc_padding s.pos = 0 s.ungetc("b") assert_equal("b""\0""a", s.string) + + s = StringIO.new("abc") + s.pos = 5 + s.ungetc("d") + assert_equal("abc""\0""d", s.string) + assert_equal(4, s.pos) end def test_ungetc_fill @@ -944,6 +950,24 @@ def test_ungetbyte_padding s.pos = 0 s.ungetbyte("b".ord) assert_equal("b""\0""a", s.string) + + s = StringIO.new("abc") + s.pos = 5 + s.ungetbyte("d".ord) + assert_equal("abc""\0""d", s.string) + assert_equal(4, s.pos) + + s = StringIO.new("abcde") + s.pos = 9 + s.ungetbyte("f") + assert_equal("abcde""\0\0\0""f", s.string) + assert_equal(8, s.pos) + + s = StringIO.new("abc") + s.pos = 4 + s.ungetbyte("de") + assert_equal("abde", s.string) + assert_equal(2, s.pos) end def test_ungetbyte_fill