Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A minimal implementation of part 2 : msgpack #6

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions samples/part2/msgpack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module MsgPack
TYPE2EXT = [ Symbol ]
EXT2TYPE = Hash[TYPE2EXT.each_with_index.to_a]
end
27 changes: 26 additions & 1 deletion samples/part2/packer.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
require_relative 'msgpack'

module Packer
# This method takes primitive Ruby objects and converts them into
# the equivalent byte array in MessagePack format.
def self.pack(obj)
[] # FIXME: Your code goes here.
case obj
when NilClass then [0xc0]
when TrueClass then [0xc3]
when FalseClass then [0xc2]
when Fixnum
raise unless obj >= 0 and obj < 0x80
[obj]
when Float
[0xcb] + [obj].pack('G').bytes
when Symbol
size = obj.to_s.bytesize
raise "Do not know how to dump Symbol of length #{size}" if size > 0xFF
[0xc7, size, MsgPack::EXT2TYPE[Symbol]] + obj.to_s.bytes
when String
raise if obj.bytesize > 31
[0xa0 + obj.bytesize] + obj.encode(Encoding::UTF_8).bytes
when Hash
raise if obj.size > 15
obj.each_pair.inject([0x80 + obj.size]) { |bytes, (key, val)|
bytes + pack(key) + pack(val)
}
else
raise "Unknown type: #{obj.class}"
end
end
end

Expand Down
35 changes: 34 additions & 1 deletion samples/part2/unpacker.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
require_relative 'msgpack'

module Unpacker
class << self
# This method takes a sequence of bytes in message pack format and convert
# it into an equivalent Ruby object
def unpack(bytes)
{} # FIXME: Your code goes here.
type = bytes.next
case type
when 0x00..0x7f then type
when 0xa0..0xbf
unpack_str(type - 0xa0, bytes).force_encoding('UTF-8')
when 0xc0 then nil
when 0xc2 then false
when 0xc3 then true
when 0xcb
unpack_str(8, bytes).unpack('G')[0]
when 0xc7 then
size = bytes.next
type = bytes.next
if MsgPack::TYPE2EXT[type] == Symbol
unpack_str(size, bytes).to_sym
else
raise "Unknown extended type #{type.to_s(16)}"
end
when 0x80..0x8f
(type - 0x80).times.with_object({}) { |_,map|
map[unpack(bytes)] = unpack(bytes)
}
else
raise "Unknown msgpack type: #{type.to_s(16)}"
end
end

private
def unpack_str(bytesize, bytes)
bytesize.times.with_object("".b) { |_,str|
str << bytes.next
}
end
end
end
Expand Down