diff --git a/lib/skyfall/car_archive.rb b/lib/skyfall/car_archive.rb index 5257833..5f651ea 100644 --- a/lib/skyfall/car_archive.rb +++ b/lib/skyfall/car_archive.rb @@ -1,5 +1,6 @@ require_relative 'cid' require_relative 'errors' +require_relative 'extensions' require 'cbor' require 'stringio' @@ -18,6 +19,8 @@ module Skyfall end class CarArchive + using Skyfall::Extensions + attr_reader :roots, :sections def initialize(data) @@ -30,7 +33,7 @@ module Skyfall end def read_header(buffer) - len = read_varint(buffer) + len = buffer.read_varint header_data = buffer.read(len) raise DecodeError.new("Header too short: #{header_data}") unless header_data.length == len @@ -41,23 +44,23 @@ module Skyfall end def read_section(buffer) - len = read_varint(buffer) + len = buffer.read_varint section_data = buffer.read(len) raise DecodeError.new("Section too short: #{section_data}") unless section_data.length == len sbuffer = StringIO.new(section_data) - version = read_varint(sbuffer) + version = sbuffer.read_varint raise UnsupportedError.new("Unexpected CID version: #{version}") unless version == 1 - codec = read_varint(sbuffer) + codec = sbuffer.read_varint raise UnsupportedError.new("Unexpected CID codec: #{codec}") unless codec == 0x71 # dag-cbor - hash = read_varint(sbuffer) + hash = sbuffer.read_varint raise UnsupportedError.new("Unexpected CID hash: #{hash}") unless hash == 0x12 # sha2-256 - clen = read_varint(sbuffer) + clen = sbuffer.read_varint raise UnsupportedError.new("Unexpected CID length: #{clen}") unless clen == 32 prefix = section_data[0...sbuffer.pos] @@ -72,19 +75,5 @@ module Skyfall @sections << CarSection.new(cid, body) end - - def read_varint(buffer) - shift = 1 - value = 0 - - loop do - byte = buffer.readbyte - value += byte % 128 * shift - break if byte < 128 - shift *= 128 - end - - value - end end end diff --git a/lib/skyfall/extensions.rb b/lib/skyfall/extensions.rb new file mode 100644 index 0000000..d6ae1f4 --- /dev/null +++ b/lib/skyfall/extensions.rb @@ -0,0 +1,32 @@ +require 'cbor' +require 'stringio' + +module Skyfall + module Extensions + + refine StringIO do + # https://en.wikipedia.org/wiki/LEB128 + def read_varint + shift = 1 + value = 0 + + loop do + byte = self.readbyte + value += byte % 128 * shift + break if byte < 128 + shift *= 128 + end + + value + end + end + + refine CBOR.singleton_class do + def decode_sequence(data) + unpacker = CBOR::Unpacker.new(StringIO.new(data)) + unpacker.each.to_a + end + end + + end +end diff --git a/lib/skyfall/websocket_message.rb b/lib/skyfall/websocket_message.rb index e7761b8..f3e1847 100644 --- a/lib/skyfall/websocket_message.rb +++ b/lib/skyfall/websocket_message.rb @@ -1,18 +1,20 @@ require_relative 'car_archive' require_relative 'cid' require_relative 'errors' +require_relative 'extensions' require_relative 'operation' require 'cbor' -require 'stringio' require 'time' module Skyfall class WebsocketMessage + using Skyfall::Extensions + attr_reader :type_object, :data_object, :repo, :date, :commit, :ops, :blocks, :operations def initialize(data) - objects = decode_cbor_sequence(data) + objects = CBOR.decode_sequence(data) raise DecodeError.new("Invalid number of objects: #{objects.length}") unless objects.length == 2 @type_object, @data_object = objects @@ -34,10 +36,5 @@ module Skyfall Operation.new(@repo, path, action, cid, record) } end - - def decode_cbor_sequence(data) - unpacker = CBOR::Unpacker.new(StringIO.new(data)) - unpacker.each.to_a - end end end