Code Search for Developers
 
 
  

pack.rb from Thousand Parsec at Krugle


Show pack.rb syntax highlighted


module PackListExtensions
  def first_pack_list_type( template_string )
    list_types = [['[', ']', 'N', nil], ['{', '}', 'Q', nil], ['$', '', 'N', :string], ['%', nil, nil, nil]]

    list_types.each { |lt| lt.push template_string.index( lt[0] ) }
    list_types.delete_if {|lt| lt[4].nil? }
    list_types = list_types.sort_by {|lt| lt[4] }

    list_types.first
  end
  def handle_list_extensions( original, template_string )
    list_type = first_pack_list_type( template_string )

    return yield( split_template( template_string, list_type[0], list_type[1] ), list_type[2], list_type[3] ) if list_type
    send( original, template_string )
  end
  def split_template( template_string, left_bracket, right_bracket )
    parts = template_string.split(left_bracket, 2)
    parts[1], parts[2] = parts.last.split(right_bracket, 2) if right_bracket
    return parts
  end
  def unsigned_template_character_from_semisigned match_char
    case match_char when 'i' then 'n' when 'l' then 'N' end
  end
  def unsigned_template_character match_char
    '%' + match_char
  end
  def bit_size_of_unsigned_template_character match_char
    case match_char when 'S', 's', 'n' then 16 when 'N' then 32 end
  end
end

class Array
  include PackListExtensions
  alias original_pack pack
  def pack template_string
    handle_list_extensions :original_pack, template_string do |parts, size_format, special|
      # Ruby natively handles packing negative values into an unsigned,
      # so we just have to change it to use the right byte-order
      # character.
      parts[1].gsub!( /^[A-Za-z][0-9]*/ ) { |c| unsigned_template_character_from_semisigned( c ) || c }
      return pack(parts.join('')) unless size_format

      s = original_pack( parts[0] )
      a = s.unpack( parts[0] )
      rest = self[a.size, size]

      inner_value = rest.shift
      if special == :string
        s << [inner_value.size + 1, inner_value].pack( "#{size_format}a#{inner_value.size + 1}" )
      else
        inner_value = [inner_value] unless inner_value.is_a? Array
        s << [inner_value.size].pack(size_format)
        inner_value.each do |el|
          #puts "Packing inner #{((el.is_a? Array ) ? el : [el]).inspect} with #{parts[1].inspect}"
          s << ((el.is_a? Array ) ? el : [el]).pack( parts[1] )
        end
      end
      s << rest.pack( parts[2] )
    end
  end
end

class String
  include PackListExtensions
  alias original_unpack unpack
  def unpack template_string
    handle_list_extensions :original_unpack, template_string do |parts, size_format, special|
      s = dup

      unless size_format
        left = s.unpack!(parts[0])
        next_template = parts[1].slice!( /^[A-Za-z][0-9]*/ )
        bit_size = bit_size_of_unsigned_template_character( next_template )
        if bit_size
          value = s.unpack!( next_template ).first
          value -= 2 ** bit_size if value > 2 ** (bit_size - 1)
        else
          next_template = unsigned_template_character_from_semisigned( next_template )
          bit_size = bit_size_of_unsigned_template_character( next_template )
          value = s.unpack!( next_template ).first
          value = -1 if bit_size && value == 2 ** bit_size - 1
        end
        right = s.unpack(parts[1])
        return left + [value] + right
      end

      left_content = s.unpack!( parts[0] )
      middle_length = s.unpack!( size_format ).first
      middle_content = []
      middle_length.times do
        el = s.unpack!(parts[1])
        middle_content << (el.size == 1 ? el.first : el)
      end
      if special == :string
        middle_content = middle_content.join('')
        middle_content.chop! if !middle_content.empty? && middle_content[-1].zero?
      end
      right_content = s.unpack(parts[2])
      left_content + [middle_content] + right_content
    end
  end
  def unpack! template_string
    unpacked = unpack(template_string)
    slice! 0, unpacked.pack(template_string).size
    unpacked
  end
end

if $0 == __FILE__
  require 'test/unit'
  class PackTests < Test::Unit::TestCase
    def test_things
      t 'SZ*L$aZ*N', [17, 'xyzzy', 234125, 'whee whee', 'blah blah', 253523]
      t '[C]', ['foo foo'.split('').map{|c|c[0]}]
      t 'Z*%S', ['s', 1123]
      t '[Z*%S]', [[['s', 1123], ['xz', 3], ['--', 1789]]]
      t '[C][Z*%S]SZ*L$aZ*N', ['foo foo'.split('').map{|c|c[0]}, [['s', 1123], ['xz', -1], ['--', 1789]], 17, 'xyzzy', 234125, 'whee whee', 'blah blah', 253523]
      t 'a4[N]', ['abcd', [12, 34, 56, 78, 90, 123, 456, 789, 1234, 5687, 90123, 45678, 901234, 567890]]
      t 'a4[%N]', ['abcd', [12, -34, 56, -78, 90, -123, 456, -789, 1234, -5687, 90123, -45678, 901234, -567890]]
    end
    def t f, a
      s = a.pack(f)
      p s
      puts s.unpack('C*').map {|c| ('0' + c.to_s(16))[-2,2] }.join(' ').gsub(/ (..) /){|m|$1+' '}

      a2 = s.unpack(f)
      assert_equal a, a2

      s2 = a2.pack(f)
      assert_equal s, s2
    end
  end
end





See more files for this project here

Thousand Parsec

Thousand Parsec is a framework for turn based 4 X\'s game (eXplore, eXpand, eXploit, eXterminate). Designed for long games, supporting massive universes and has an easily expanded tech tree.

Project homepage: http://sourceforge.net/projects/thousandparsec
Programming language(s): C++,Python
License: other

  client.rb
  libtpproto-rb.tailor
  pack.rb
  packet.rb
  socket_support.rb
  test_client_help.rb
  test_server.rb
  wire-demo.rb