В версии 1.8 метод String#[]
возвратил Fixnum, который был байтом указанного индекса. В более новой версии String#[]
возвращает строку, поскольку строки состоят из символов, а сопоставление символов в байтах зависит от кодировки. Похоже, вы используете строку в качестве байтового буфера, поэтому вы должны работать в массиве вместо строки:
class Array
def xor(key)
a = dup
a.length.times { |n| a[n] ^= key[n % key.size] }
a
end
end
А затем использовать его:
mangled_array = string.codepoints.to_a.xor(key.codepoints.to_a)
Тогда, если вам действительно нужна строка (которая будет содержать набор непечатаемых управляющих символов и ноль байтов и тому подобное), тогда:
mangled_string = mangled_array.inject('') { |s,c| s << c }
А потом распаковать:
mangled_string.
codepoints.
to_a.
xor(key.codepoints.to_a).
inject('') { |s,c| s << c }
Все это должно поддерживать UTF-8 на всем пути, и это то, что вы хотите.
Возможно, вы могли бы пропатчить свой xor
в Enumerable и пропустить бизнес to_a
, если хотите. Вы также можете адаптировать это для патча для String.
Вам больше не следует использовать String для байтовых буферов, для этого лучше использовать массивы Fixnum с явной обработкой кодировки.