Skip to content

Instantly share code, notes, and snippets.

@bradland
Created January 9, 2015 16:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradland/50fc4cb66c2fe2a0dac7 to your computer and use it in GitHub Desktop.
Save bradland/50fc4cb66c2fe2a0dac7 to your computer and use it in GitHub Desktop.
class ByteBuffer
attr_reader :bytes
# A ByteBuffer accepts encoded string buffers and unpacks them to an
# internal array of bytes stored using fixnum so that binary operations can
# be easily performed.
def initialize(buffer, mode)
case mode
when 'a' # arbitrary string; converts to ascii char code (this is faster than String#ord)
@bytes = buffer.unpack('C*')
when 'B' # MSB-first binary string (a string of 1s and 0s)
@bytes = [buffer].pack('B*').unpack('C*')
when 'C' # array of Fixnum values
@bytes = buffer
when 'H' # Hex encoded string
@bytes = [buffer].pack('H*').unpack('C*')
when 'm' # Base64 encoded string
@bytes = buffer.unpack('m0').first.unpack('C*')
else
raise ArgumentError, "Invalid input mode #{mode}"
end
end
# Returns the XOR sum of self and ByteBuffer argument
def ^(byte_buffer)
# A basic XOR cipher performs an XOR action byte by byte on binary chunks.
# In the case of two ByteBuffers, the first operand would be the data, and
# the second the key. If our data is longer than our key, we simply repeat
# the cipher bytes. If our key is longer than the data, then we'll only
# use as many bytes as we need.
key_length = byte_buffer.size
key_bytes = byte_buffer.bytes
bytes_out = []
# This returns an array of Fixnum values
@bytes.each_with_index { |byte, i| bytes_out << (byte ^ key_bytes[i % key_length]) }
# Which we'll return as a ByteBuffer
ByteBuffer.new(bytes_out, 'C')
end
# Returns (slowly) the Hamming distance between self and ByteBuffer argument
def distance(byte_buffer)
(self ^ byte_buffer).to_binary.count("1")
end
# Returns true of string is ASCII printable without garbage
def is_printable?
@buffer =~ /[^[:print:]]/
end
# Returns the size of the byte buffer
def size
@size ||= @bytes.size
end
# Returns bytes encoded in base64 string
def to_base64
@to_base64 ||= [@bytes.pack('C*')].pack('m0')
end
# Returns bytes encoded in a binary string
def to_binary
@to_binary ||= @bytes.pack("C*").unpack("B*").first
end
# Returns bytes encoded in a hex string
def to_hex
@to_hex ||= @bytes.pack('C*').unpack('H*').first
end
# Returns a string with ASCII non-printable chars as [NAME] identifiers
def to_clear_string
return false if @bytes.max > ASCII_TABLE.length
@to_string ||= @bytes.map { |dec| ASCII_TABLE[dec] }.join
end
# Returns a string (might contain junk!)
def to_string
@to_string ||= @bytes.pack('C*').to_s
end
end
class ByteBufferError < StandardError
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment