💨

[bugs.ruby][Feature #20196] バイナリデータを定義するリテラルを追加する提案

2024/03/04に公開

[Feature #20196] Proposal: Binary data literal

  • 以下のようなバイナリデータを定義するリテラル(%記法?)を追加する提案
%b[
  89504e470d0a1a0a # PNG header
  0000000d         # Length = 13 bytes
  49484452         # IHDR chunk
  00000060         # Width = 96px
  00000060         # Height = 96px
  08 06            # 8bpp RGBA
  00 00 00         # deflate / no filter / non-interlaced
]

# => "\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00`\x00\x00\x00`\b\x06\x00\x00\x00"
  • 仕様は以下のような感じ
    • 16進文字列(上位ニブルが先)
    • 他の%記法と同じルール
    • %b[]String を返し %B[]IO::Buffer を返す
    • 空白文字は無視される
    • コメントを書くことができる
    • エンコーディングは Encoding::BINARY になる
    • 結果の文字列は frozen になる
    • a-fA-F ともかける
  • 普段 Ruby のコードを書いている時にバイナリデータを直接コードを書くことはないんですが、あると使う場面があったりするんですかねー
  • 他には以下のような『空白文字を取り除きコメントをサポートする%記法を新しく追加する』みたいな話もあります
%c[
  The quick brown # comment
  fox jumped over # another comment
  the lazy dog.   # a third comment
] # => "The quick brown fox jumped over the lazy dog."

# 今回のケースはこういう風にかける
[%c[
    000102 # foo
    030405 # bar
].delete(' ')].pack("H*").freeze
  • これだと汎用性があるのでより利用できそうな場面はありそうですね
  • また、以下のように実装することもできるとコメントに書いてありますね
module Kernel
  def Binary(string, exception: true)
    hex = string.b
    hex.gsub!(/#[^#]*$\R*/, '')
    hex.gsub!(/"[^"]*"/) do |quotes|
      quotes[1..-2].unpack1('H*')
    end
    hex.delete!("\s\t")

    if hex.match?(/\H/)
      return unless exception

      invalid_chars = hex.scan(/\H/).to_set.join
      raise ArgumentError,
            "invalid non-hex chars for Binary(): #{invalid_chars.inspect}"
    end 
        
    [hex].pack('H*').freeze
  end   
end     
        
string = <<-BINARY
  89 "PNG" 0d0a1a0a # PNG header
  0000000d          # Length = 13 bytes
  "IHDR"            # IHDR chunk
  00000060          # Width = 96px
  00000060          # Height = 96px
  08 06             # 8bpp RGBA
  00 00 00          # deflate / no filter / non-interlaced}
BINARY  
        
binary = Binary(string)
pp string: binary, encoding: binary.encoding, frozen: binary.frozen?
# => {:string=>"\x89PNG\r\n" + "\x1A\n" + "\x00\x00\x00\rIHDR\x00\x00\x00`\x00\x00\x00`\b\x06\x00\x00\x00",
#    :encoding=>#<Encoding:ASCII-8BIT>,
#    :frozen=>true}
  • これはこれで面白い
GitHubで編集を提案

Discussion