module Sprockets::EncodingUtils

def base64(str)

Returns a encoded String

str - String data

Public: Use base64 to encode data.
def base64(str)
  Base64.strict_encode64(str)
end

def charlock_detect(str)

Returns encoded String.

To enable this code path, require 'charlock_holmes'

Internal: Use Charlock Holmes to detect encoding.
def charlock_detect(str)
  if defined? CharlockHolmes::EncodingDetector
    if detected = CharlockHolmes::EncodingDetector.detect(str)
      str.force_encoding(detected[:encoding]) if detected[:encoding]
    end
  end
  str
end

def deflate(str)

Returns a compressed String

str - String data

Public: Use deflate to compress data.
def deflate(str)
  deflater = Zlib::Deflate.new(
    Zlib::BEST_COMPRESSION,
    -Zlib::MAX_WBITS,
    Zlib::MAX_MEM_LEVEL,
    Zlib::DEFAULT_STRATEGY
  )
  deflater << str
  deflater.finish
end

def detect(str)

Returns encoded String.

str - ASCII-8BIT encoded String

environment's external encoding.
Attempts to parse any Unicode BOM otherwise falls back to the

Public: Basic string detecter.
def detect(str)
  str = detect_unicode_bom(str)
  # Attempt Charlock detection
  if str.encoding == Encoding::BINARY
    charlock_detect(str)
  end
  # Fallback to UTF-8
  if str.encoding == Encoding::BINARY
    str.force_encoding(Encoding.default_external)
  end
  str
end

def detect_css(str)

Returns a encoded String.

str - String.

Public: Detect and strip @charset from CSS style sheet.
def detect_css(str)
  str = detect_unicode_bom(str)
  if name = scan_css_charset(str)
    encoding = Encoding.find(name)
    str = str.dup
    str.force_encoding(encoding)
    len = "@charset \"#{name}\";".encode(encoding).size
    str.slice!(0, len)
    str
  end
  # Fallback to UTF-8
  if str.encoding == Encoding::BINARY
    str.force_encoding(Encoding::UTF_8)
  end
  str
end

def detect_html(str)

Returns a encoded String.

str - String.

Public: Detect charset from HTML document. Defaults to ISO-8859-1.
def detect_html(str)
  str = detect_unicode_bom(str)
  # Attempt Charlock detection
  if str.encoding == Encoding::BINARY
    charlock_detect(str)
  end
  # Fallback to ISO-8859-1
  if str.encoding == Encoding::BINARY
    str.force_encoding(Encoding::ISO_8859_1)
  end
  str
end

def detect_unicode(str)

Returns encoded String.

str - ASCII-8BIT encoded String

Attempts to parse Unicode BOM and falls back to UTF-8.

Public: Detect Unicode string.
def detect_unicode(str)
  str = detect_unicode_bom(str)
  # Fallback to UTF-8
  if str.encoding == Encoding::BINARY
    str.force_encoding(Encoding::UTF_8)
  end
  str
end

def detect_unicode_bom(str)

no BOM was present.
Returns UTF 8/16/32 encoded String without BOM or the original String if

str - ASCII-8BIT encoded String

Public: Detect and strip BOM from possible unicode string.
def detect_unicode_bom(str)
  bom_bytes = str.byteslice(0, 4).bytes.to_a
  BOM.each do |encoding, bytes|
    if bom_bytes[0, bytes.size] == bytes
      str = str.dup
      str.force_encoding(Encoding::BINARY)
      str.slice!(0, bytes.size)
      str.force_encoding(encoding)
      return str
    end
  end
  return str
end

def gzip(str)

Returns a compressed String

str - String data

Public: Use gzip to compress data.
def gzip(str)
  io = StringIO.new
  gz = Zlib::GzipWriter.new(io, Zlib::BEST_COMPRESSION)
  gz.mtime = 1
  gz << str
  gz.finish
  io.string
end

def scan_css_charset(str)

Returns encoding String name or nil.

str - ASCII-8BIT encoded String

Internal: Scan binary CSS string for @charset encoding name.
def scan_css_charset(str)
  buf = []
  i = 0
  str.each_byte.each do |byte|
    # Halt on line breaks
    break if byte == 0x0A || byte == 0x0D
    # Only ascii bytes
    next unless 0x0 < byte && byte <= 0xFF
    if i < CHARSET_SIZE
    elsif i == CHARSET_SIZE
      if buf == CHARSET_START
        buf = []
      else
        break
      end
    elsif byte == 0x22
      return buf.pack('C*')
    end
    buf << byte
    i += 1
  end
  nil
end

def unmarshaled_deflated(str, window_bits = -Zlib::MAX_WBITS)

Returns unmarshaled Object or raises an Exception.

window_bits - Integer deflate window size. See ZLib::Inflate.new()
str - Marshaled String

otherwise inflate the data an unmarshal.
Checks leading marshal header to see if the bytes are uncompressed

Internal: Unmarshal optionally deflated data.
def unmarshaled_deflated(str, window_bits = -Zlib::MAX_WBITS)
  major, minor = str[0], str[1]
  if major && major.ord == Marshal::MAJOR_VERSION &&
      minor && minor.ord <= Marshal::MINOR_VERSION
    marshaled = str
  else
    begin
      marshaled = Zlib::Inflate.new(window_bits).inflate(str)
    rescue Zlib::DataError
      marshaled = str
    end
  end
  Marshal.load(marshaled)
end