<commit_before>module ChunkyPNG
  
  # A PNG datastream consists of multiple chunks. This module, and the classes
  # contained within, help with handling these chunks. It supports both
  # reading and writing chunks.
  #
  # All chunck types are instances of the {ChunkyPNG::Chunk::Base} class. For
  # some chunk types a specialized class is available, e.g. the IHDR chunk is
  # represented by the {ChunkyPNG::Chunk::Header} class. These specialized
  # classes help accessing the content of the chunk. All other chunks are
  # represented by the {ChunkyPNG::Chunk::Generic} class.
  #
  # @see ChunkyPNG::Datastream
  module Chunk

    # Reads a chunk from an IO stream.
    #
    # @param [IO, #read] io The IO stream to read from.
    # @return [ChunkyPNG::Chung::Base] The loaded chunk instance.
    def self.read(io)

      length, type = io.read(8).unpack('Na4')
      content      = io.read(length)
      crc          = io.read(4).unpack('N').first

      verify_crc!(type, content, crc)

      CHUNK_TYPES.fetch(type, Generic).read(type, content)
    end
    
    # Verifies the CRC of a chunk.
    # @param [String] type The chunk's type.
    # @param [String] content The chunk's content.
    # @param [Fixnum] content The chunk's content.
    # @raise [RuntimeError] An exception is raised if the found CRC value
    #    is not equal to the expected CRC value.
    def self.verify_crc!(type, content, found_crc)
      expected_crc = Zlib.crc32(content, Zlib.crc32(type))
      raise "Chuck CRC mismatch!" if found_crc != expected_crc
    end

    # The base chunk class is the superclass for every chunk type. It contains
    # methods to write the chunk to an output stream.
    #
    # A subclass should implement the +content+ method, which gets called when
    # the chunk gets written to a PNG datastream
    #
    # @abstract
    class Base
      
      # The four-character type indicator for the chunk. This field is used to
      # find the correct class for a chunk when it is loaded from a PNG stream.
      # @return [String]
      attr_accessor :type

      # Initializes the chunk instance.
      # @param [String] type The four character chunk type indicator.
      # @param [Hash] attributes A hash of attributes to set on this chunk.
      def initialize(type, attributes = {})
        self.type = type
        attributes.each { |k, v| send("#{k}=", v) }
      end

      # Writes the chunk to the IO stream, using the provided content.
      # The checksum will be calculated and appended to the stream.
      # @param [IO] io The IO stream to write to.
      # @param [String] content The content for this chunk.
      def write_with_crc(io, content)
        io << [content.length].pack('N') << type << content
        io << [Zlib.crc32(content, Zlib.crc32(type))].pack('N')
      end

      # Writes the chunk to the IO stream.
      #
      # It will call te +content+ method to get the content for this chunk,
      # and will calculate and append the checksum automatically.
      # @param [IO] io The IO stream to write to.
      def write(io)
        write_with_crc(io, content || '')
      end
    end

    # The Generic chunk type will read the content from the chunk as it, 
    # and will write it back as it was read.
    class Generic < Base

      # The attribute to store the content from the chunk, which gets 
      # written by the +write+ method.
      attr_accessor :content

      
      def initialize(type, content = '')
        super(type, :content => content)
      end

      # Creates an instance, given the chunk's type and content.
      # @param [String] type The four character chunk type indicator.
      # @param [String] content The content read from the chunk.
      # @return [ChunkyPNG::Chunk::Generic] The new chunk instance.
      def self.read(type, content)
        self.new(type, content)
      end
    end

    # The header (IHDR) chunk is the first chunk of every PNG image, and 
    # contains information about the image: i.e. its width, height, color 
    # depth, color mode, compression method, filtering method and interlace
    # method.
    #
    # ChunkyPNG supports all values for these variables that are defined in
    # the PNG spec, except for color depth: Only 8-bit depth images are
    # supported. Note that it is still possible to access the chunk for such
    # an image, but ChunkyPNG will raise an exception if you try to access
    # the pixel data.
    class Header < Base

      attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace

      def initialize(attrs = {})
        super('IHDR', attrs)
        @depth       ||= 8
        @color       ||= ChunkyPNG::COLOR_TRUECOLOR
        @compression ||= ChunkyPNG::COMPRESSION_DEFAULT
        @filtering   ||= ChunkyPNG::FILTERING_DEFAULT
        @interlace   ||= ChunkyPNG::INTERLACING_NONE
      end

      # Reads the 13 bytes of content from the header chunk to set the image attributes.
      # @param [String] type The four character chunk type indicator (= "IHDR").
      # @param [String] content The 13 bytes of content read from the chunk.
      # @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the 
      #    variables set to the values according to the content.
      def self.read(type, content)
        fields = content.unpack('NNC5')
        self.new(:width => fields[0],  :height => fields[1], :depth => fields[2], :color => fields[3],
                       :compression => fields[4], :filtering => fields[5], :interlace => fields[6])
      end

      # Returns the content for this chunk when it gets written to a file, by packing the
      # image information variables into the correct format.
      # @return [String] The 13-byte content for the header chunk.
      def content
        [width, height, depth, color, compression, filtering, interlace].pack('NNC5')
      end
    end

    # The End (IEND) chunk indicates the last chunk of a PNG stream. It does not
    # contain any data.
    class End < Base
      
      def initialize
        super('IEND')
      end
      
      # Reads the END chunk. It will check if the content is empty.
      # @param [String] type The four character chunk type indicator (= "IEND").
      # @param [String] content The content read from the chunk. Should be empty.
      # @return [ChunkyPNG::Chunk::End] The new End chunk instance.
      # @raise [RuntimeError] Raises an exception if the content was not empty.
      def self.read(type, content)
        raise 'The IEND chunk should be empty!' if content != ''
        self.new
      end

      # Returns an empty string, because this chunk should always be empty.
      # @return [""] An empty string.
      def content
        ''
      end
    end

    # The Palette (PLTE) chunk contains the image's palette, i.e. the
    # 8-bit RGB colors this image is using.
    #
    # @see ChunkyPNG::Chunk::Transparency
    # @see ChunkyPNG::Palette 
    class Palette < Generic
    end

    # A transparency (tRNS) chunk contains the alpha channel for the colors 
    # defined in the Palette (PLTE) chunk
    #
    # @see ChunkyPNG::Chunk::Palette
    # @see ChunkyPNG::Palette
    class Transparency < Generic
    end

    class ImageData < Generic
      
      def self.combine_chunks(data_chunks)
        Zlib::Inflate.inflate(data_chunks.map(&:content).join(''))
      end
      
      def self.split_in_chunks(data, chunk_size = 2147483647)
        streamdata = Zlib::Deflate.deflate(data)
        # TODO: Split long streamdata over multiple chunks
        [ ChunkyPNG::Chunk::ImageData.new('IDAT', streamdata) ]
      end
    end

    # The Text (tEXt) chunk contains keyword/value metadata about the PNG stream.
    # In this chunk, the value is stored uncompressed.
    #
    # The tEXt chunk only supports Latin-1 encoded textual data. If you need UTF-8 
    # support, check out the InternationalText chunk type.
    #
    # @see ChunkyPNG::Chunk::CompressedText
    # @see ChunkyPNG::Chunk::InternationalText
    class Text < Base

      attr_accessor :keyword, :value

      def initialize(keyword, value)
        super('tEXt')
        @keyword, @value = keyword, value<commit_msg>Ruby 1.8.6 comaptibility fix.<commit_after>module ChunkyPNG
  
  # A PNG datastream consists of multiple chunks. This module, and the classes
  # contained within, help with handling these chunks. It supports both
  # reading and writing chunks.
  #
  # All chunck types are instances of the {ChunkyPNG::Chunk::Base} class. For
  # some chunk types a specialized class is available, e.g. the IHDR chunk is
  # represented by the {ChunkyPNG::Chunk::Header} class. These specialized
  # classes help accessing the content of the chunk. All other chunks are
  # represented by the {ChunkyPNG::Chunk::Generic} class.
  #
  # @see ChunkyPNG::Datastream
  module Chunk

    # Reads a chunk from an IO stream.
    #
    # @param [IO, #read] io The IO stream to read from.
    # @return [ChunkyPNG::Chung::Base] The loaded chunk instance.
    def self.read(io)

      length, type = io.read(8).unpack('Na4')
      content      = io.read(length)
      crc          = io.read(4).unpack('N').first

      verify_crc!(type, content, crc)

      CHUNK_TYPES.fetch(type, Generic).read(type, content)
    end
    
    # Verifies the CRC of a chunk.
    # @param [String] type The chunk's type.
    # @param [String] content The chunk's content.
    # @param [Fixnum] content The chunk's content.
    # @raise [RuntimeError] An exception is raised if the found CRC value
    #    is not equal to the expected CRC value.
    def self.verify_crc!(type, content, found_crc)
      expected_crc = Zlib.crc32(content, Zlib.crc32(type))
      raise "Chuck CRC mismatch!" if found_crc != expected_crc
    end

    # The base chunk class is the superclass for every chunk type. It contains
    # methods to write the chunk to an output stream.
    #
    # A subclass should implement the +content+ method, which gets called when
    # the chunk gets written to a PNG datastream
    #
    # @abstract
    class Base
      
      # The four-character type indicator for the chunk. This field is used to
      # find the correct class for a chunk when it is loaded from a PNG stream.
      # @return [String]
      attr_accessor :type

      # Initializes the chunk instance.
      # @param [String] type The four character chunk type indicator.
      # @param [Hash] attributes A hash of attributes to set on this chunk.
      def initialize(type, attributes = {})
        self.type = type
        attributes.each { |k, v| send("#{k}=", v) }
      end

      # Writes the chunk to the IO stream, using the provided content.
      # The checksum will be calculated and appended to the stream.
      # @param [IO] io The IO stream to write to.
      # @param [String] content The content for this chunk.
      def write_with_crc(io, content)
        io << [content.length].pack('N') << type << content
        io << [Zlib.crc32(content, Zlib.crc32(type))].pack('N')
      end

      # Writes the chunk to the IO stream.
      #
      # It will call te +content+ method to get the content for this chunk,
      # and will calculate and append the checksum automatically.
      # @param [IO] io The IO stream to write to.
      def write(io)
        write_with_crc(io, content || '')
      end
    end

    # The Generic chunk type will read the content from the chunk as it, 
    # and will write it back as it was read.
    class Generic < Base

      # The attribute to store the content from the chunk, which gets 
      # written by the +write+ method.
      attr_accessor :content

      
      def initialize(type, content = '')
        super(type, :content => content)
      end

      # Creates an instance, given the chunk's type and content.
      # @param [String] type The four character chunk type indicator.
      # @param [String] content The content read from the chunk.
      # @return [ChunkyPNG::Chunk::Generic] The new chunk instance.
      def self.read(type, content)
        self.new(type, content)
      end
    end

    # The header (IHDR) chunk is the first chunk of every PNG image, and 
    # contains information about the image: i.e. its width, height, color 
    # depth, color mode, compression method, filtering method and interlace
    # method.
    #
    # ChunkyPNG supports all values for these variables that are defined in
    # the PNG spec, except for color depth: Only 8-bit depth images are
    # supported. Note that it is still possible to access the chunk for such
    # an image, but ChunkyPNG will raise an exception if you try to access
    # the pixel data.
    class Header < Base

      attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace

      def initialize(attrs = {})
        super('IHDR', attrs)
        @depth       ||= 8
        @color       ||= ChunkyPNG::COLOR_TRUECOLOR
        @compression ||= ChunkyPNG::COMPRESSION_DEFAULT
        @filtering   ||= ChunkyPNG::FILTERING_DEFAULT
        @interlace   ||= ChunkyPNG::INTERLACING_NONE
      end

      # Reads the 13 bytes of content from the header chunk to set the image attributes.
      # @param [String] type The four character chunk type indicator (= "IHDR").
      # @param [String] content The 13 bytes of content read from the chunk.
      # @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the 
      #    variables set to the values according to the content.
      def self.read(type, content)
        fields = content.unpack('NNC5')
        self.new(:width => fields[0],  :height => fields[1], :depth => fields[2], :color => fields[3],
                       :compression => fields[4], :filtering => fields[5], :interlace => fields[6])
      end

      # Returns the content for this chunk when it gets written to a file, by packing the
      # image information variables into the correct format.
      # @return [String] The 13-byte content for the header chunk.
      def content
        [width, height, depth, color, compression, filtering, interlace].pack('NNC5')
      end
    end

    # The End (IEND) chunk indicates the last chunk of a PNG stream. It does not
    # contain any data.
    class End < Base
      
      def initialize
        super('IEND')
      end
      
      # Reads the END chunk. It will check if the content is empty.
      # @param [String] type The four character chunk type indicator (= "IEND").
      # @param [String] content The content read from the chunk. Should be empty.
      # @return [ChunkyPNG::Chunk::End] The new End chunk instance.
      # @raise [RuntimeError] Raises an exception if the content was not empty.
      def self.read(type, content)
        raise 'The IEND chunk should be empty!' if content != ''
        self.new
      end

      # Returns an empty string, because this chunk should always be empty.
      # @return [""] An empty string.
      def content
        ''
      end
    end

    # The Palette (PLTE) chunk contains the image's palette, i.e. the
    # 8-bit RGB colors this image is using.
    #
    # @see ChunkyPNG::Chunk::Transparency
    # @see ChunkyPNG::Palette 
    class Palette < Generic
    end

    # A transparency (tRNS) chunk contains the alpha channel for the colors 
    # defined in the Palette (PLTE) chunk
    #
    # @see ChunkyPNG::Chunk::Palette
    # @see ChunkyPNG::Palette
    class Transparency < Generic
    end

    class ImageData < Generic
      
      def self.combine_chunks(data_chunks)
        Zlib::Inflate.inflate(data_chunks.map { |c| c.content }.join(''))
      end
      
      def self.split_in_chunks(data, chunk_size = 2147483647)
        streamdata = Zlib::Deflate.deflate(data)
        # TODO: Split long streamdata over multiple chunks
        [ ChunkyPNG::Chunk::ImageData.new('IDAT', streamdata) ]
      end
    end

    # The Text (tEXt) chunk contains keyword/value metadata about the PNG stream.
    # In this chunk, the value is stored uncompressed.
    #
    # The tEXt chunk only supports Latin-1 encoded textual data. If you need UTF-8 
    # support, check out the InternationalText chunk type.
    #
    # @see ChunkyPNG::Chunk::CompressedText
    # @see ChunkyPNG::Chunk::InternationalText
    class Text < Base

      attr_accessor :keyword, :value

      def initialize(keyword, value)
        super('tEXt')
        @keyword, @value = keyword, value
{'fraction_non_alphanumeric': 0.08741705728374019, 'fraction_numerical': 0.005174407986850916, 'mean_word_length': 2.585333915320821, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4404453', 'n_tokens_mistral': 4818, 'n_tokens_neox': 4660, 'n_words': 1883}