diff --git a/lib/skyfall/car_archive.rb b/lib/skyfall/car_archive.rb deleted file mode 100644 index 47c7491..0000000 --- a/lib/skyfall/car_archive.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: true - -require_relative 'cid' -require_relative 'errors' -require_relative 'extensions' - -require 'base64' -require 'cbor' -require 'stringio' - -# CAR v1: https://ipld.io/specs/transport/car/carv1/ -# multicodec codes: https://github.com/multiformats/multicodec/blob/master/table.csv - -module Skyfall - class CarSection - attr_reader :cid - - def initialize(cid, body_data) - @cid = cid - @body_data = body_data - end - - def body - @body ||= CarArchive.convert_data(CBOR.decode(@body_data)) - end - end - - class CarArchive - using Skyfall::Extensions - - attr_reader :roots, :sections - - def initialize(data) - @sections = [] - @buffer = StringIO.new(data) - - read_header(@buffer) - end - - def section_with_cid(cid) - if section = @sections.detect { |s| s.cid == cid } - return section.body - end - - if @buffer - while !@buffer.eof? - section = read_section(@buffer) - return section.body if section.cid == cid - end - end - - @buffer = nil - nil - end - - def sections - if @buffer - read_section(@buffer) while !@buffer.eof? - @buffer = nil - end - - @sections - end - - def self.convert_data(object) - if object.is_a?(Hash) - object.each do |k, v| - if v.is_a?(Hash) || v.is_a?(Array) - convert_data(v) - elsif v.is_a?(CBOR::Tagged) - object[k] = make_cid_link(v) - elsif v.is_a?(String) && v.encoding == Encoding::ASCII_8BIT - object[k] = make_bytes(v) - end - end - elsif object.is_a?(Array) - object.each_with_index do |v, i| - if v.is_a?(Hash) || v.is_a?(Array) - convert_data(v) - elsif v.is_a?(CBOR::Tagged) - object[i] = make_cid_link(v) - elsif v.is_a?(String) && v.encoding == Encoding::ASCII_8BIT - object[i] = make_bytes(v) - end - end - else - raise DecodeError, "Unexpected value type in record: #{object}" - end - end - - def self.make_cid_link(cid) - { '$link' => CID.from_cbor_tag(cid) } - end - - def self.make_bytes(data) - { '$bytes' => Base64.encode64(data).chomp.gsub(/=+$/, '') } - end - - def inspect - vars = instance_variables.map { |v| - if v == :@sections && @buffer - "#{v}=[...]" - else - "#{v}=#{instance_variable_get(v).inspect}" - end - } - - "#<#{self.class}:0x#{object_id} #{vars.join(", ")}>" - end - - private - - def read_header(buffer) - len = buffer.read_varint - - header_data = buffer.read(len) - raise DecodeError.new("Header too short: #{header_data}") unless header_data.length == len - - header = CBOR.decode(header_data) - raise UnsupportedError.new("Unexpected CAR version: #{header['version']}") unless header['version'] == 1 - @roots = header['roots'].map { |x| CID.from_cbor_tag(x) } - end - - def read_section(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 = sbuffer.read_varint - raise UnsupportedError.new("Unexpected CID version: #{version}") unless version == 1 - - codec = sbuffer.read_varint - raise UnsupportedError.new("Unexpected CID codec: #{codec}") unless codec == 0x71 # dag-cbor - - hash = sbuffer.read_varint - raise UnsupportedError.new("Unexpected CID hash: #{hash}") unless hash == 0x12 # sha2-256 - - clen = sbuffer.read_varint - raise UnsupportedError.new("Unexpected CID length: #{clen}") unless clen == 32 - - prefix = section_data[0...sbuffer.pos] - - cid_data = sbuffer.read(clen) - raise DecodeError.new("CID too short: #{cid_data}") unless cid_data.length == clen - - cid = CID.new(prefix + cid_data) - - body_data = sbuffer.read - new_section = CarSection.new(cid, body_data) - - @sections << new_section - new_section - end - end -end diff --git a/lib/skyfall/cid.rb b/lib/skyfall/cid.rb deleted file mode 100644 index ae62ca8..0000000 --- a/lib/skyfall/cid.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require_relative 'errors' - -require 'base32' - -# CIDs in DAG-CBOR: https://ipld.io/specs/codecs/dag-cbor/spec/ -# CIDs in JSON: https://ipld.io/specs/codecs/dag-json/spec/ -# multibase: https://github.com/multiformats/multibase - -module Skyfall - class CID - attr_reader :data - - def self.from_cbor_tag(tag) - data = tag.value - raise DecodeError.new("Unexpected first byte of CID: #{data[0]}") unless data[0] == "\x00" - CID.new(data[1..-1]) - end - - def self.from_json(string) - raise DecodeError.new("Unexpected CID length") unless string.length == 59 - raise DecodeError.new("Unexpected CID prefix") unless string[0] == 'b' - - data = Base32.decode(string[1..-1].upcase) - CID.new(data) - end - - def initialize(data) - @data = data - end - - def to_s - 'b' + Base32.encode(@data).downcase.gsub(/=+$/, '') - end - - def inspect - "CID(\"#{to_s}\")" - end - - def ==(other) - other.is_a?(CID) && @data == other.data - end - end -end diff --git a/lib/skyfall/extensions.rb b/lib/skyfall/extensions.rb deleted file mode 100644 index 6b89e4d..0000000 --- a/lib/skyfall/extensions.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'cbor' -require 'stringio' - -module Skyfall - - # @private - 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/firehose/commit_message.rb b/lib/skyfall/firehose/commit_message.rb index faf21a6..5552222 100644 --- a/lib/skyfall/firehose/commit_message.rb +++ b/lib/skyfall/firehose/commit_message.rb @@ -1,10 +1,9 @@ # frozen_string_literal: true -require_relative '../car_archive' -require_relative '../cid' require_relative '../firehose' require_relative 'message' require_relative 'operation' +require 'oxygene' module Skyfall @@ -39,19 +38,19 @@ module Skyfall @data_object['since'] end - # @return [CID, nil] CID (Content Identifier) of data of the previous commit in the repo + # @return [Oxygene::CID, nil] CID (Content Identifier) of data of the previous commit in the repo def prev_data - @prev_data ||= CID.from_cbor_tag(@data_object['prevData']) + @prev_data ||= Oxygene::CID.from_cbor_tag(@data_object['prevData']) end - # @return [CID] CID (Content Identifier) of the commit + # @return [Oxygene::CID] CID (Content Identifier) of the commit def commit - @commit ||= CID.from_cbor_tag(@data_object['commit']) + @commit ||= Oxygene::CID.from_cbor_tag(@data_object['commit']) end - # @return [Skyfall::CarArchive] commit data in the form of a parsed CAR archive + # @return [Oxygene::CARArchive] commit data in the form of a parsed CAR archive def blocks - @blocks ||= CarArchive.new(@data_object['blocks']) + @blocks ||= Oxygene::CARArchive.new(@data_object['blocks']) end # @return [Array] record operations (usually one) included in the commit diff --git a/lib/skyfall/firehose/message.rb b/lib/skyfall/firehose/message.rb index d2e8244..1882cdd 100644 --- a/lib/skyfall/firehose/message.rb +++ b/lib/skyfall/firehose/message.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require_relative '../errors' -require_relative '../extensions' require_relative '../firehose' require 'cbor' +require 'oxygene' require 'time' module Skyfall @@ -24,7 +24,7 @@ module Skyfall # the {Skyfall::Stream#on_raw_message} event handler. class Firehose::Message - using Skyfall::Extensions + using Oxygene::Extensions # Type of the message (e.g. `:commit`, `:identity` etc.) # @return [Symbol] diff --git a/lib/skyfall/firehose/operation.rb b/lib/skyfall/firehose/operation.rb index 5d0452f..768c788 100644 --- a/lib/skyfall/firehose/operation.rb +++ b/lib/skyfall/firehose/operation.rb @@ -2,6 +2,7 @@ require_relative '../collection' require_relative '../firehose' +require 'oxygene' module Skyfall @@ -73,9 +74,9 @@ module Skyfall "at://#{repo}/#{@json['path']}" end - # @return [CID, nil] CID (Content Identifier) of the record (nil for delete operations) + # @return [Oxygene::CID, nil] CID (Content Identifier) of the record (nil for delete operations) def cid - @cid ||= @json['cid'] && CID.from_cbor_tag(@json['cid']) + @cid ||= @json['cid'] && Oxygene::CID.from_cbor_tag(@json['cid']) end # @return [Hash, nil] record data as a plain Ruby Hash (nil for delete operations) diff --git a/lib/skyfall/firehose/sync_message.rb b/lib/skyfall/firehose/sync_message.rb index 6ba2b92..1d0147d 100644 --- a/lib/skyfall/firehose/sync_message.rb +++ b/lib/skyfall/firehose/sync_message.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative '../car_archive' require_relative '../firehose' require_relative 'message' +require 'oxygene' module Skyfall @@ -32,9 +32,9 @@ module Skyfall @rev ||= @data_object['rev'] end - # @return [Skyfall::CarArchive] commit data in the form of a parsed CAR archive + # @return [Oxygene::CARArchive] commit data in the form of a parsed CAR archive def blocks - @blocks ||= CarArchive.new(@data_object['blocks']) + @blocks ||= Oxygene::CARArchive.new(@data_object['blocks']) end end end diff --git a/lib/skyfall/jetstream/operation.rb b/lib/skyfall/jetstream/operation.rb index 8c0caea..5ef91c2 100644 --- a/lib/skyfall/jetstream/operation.rb +++ b/lib/skyfall/jetstream/operation.rb @@ -2,6 +2,7 @@ require_relative '../collection' require_relative '../jetstream' +require 'oxygene' module Skyfall @@ -73,9 +74,9 @@ module Skyfall "at://#{repo}/#{collection}/#{rkey}" end - # @return [CID, nil] CID (Content Identifier) of the record (nil for delete operations) + # @return [Oxygene::CID, nil] CID (Content Identifier) of the record (nil for delete operations) def cid - @cid ||= @json['cid'] && CID.from_json(@json['cid']) + @cid ||= @json['cid'] && Oxygene::CID.from_json(@json['cid']) end # @return [Hash, nil] record data as a plain Ruby Hash (nil for delete operations) diff --git a/lib/skyfall/label.rb b/lib/skyfall/label.rb index ac2dbb8..b09944e 100644 --- a/lib/skyfall/label.rb +++ b/lib/skyfall/label.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'errors' +require 'oxygene' require 'time' module Skyfall @@ -60,9 +61,9 @@ module Skyfall @data['uri'] end - # @return [CID, nil] CID of the specific version of the subject that this label applies to + # @return [Oxygene::CID, nil] CID of the specific version of the subject that this label applies to def cid - @cid ||= @data['cid'] && CID.from_json(@data['cid']) + @cid ||= @data['cid'] && Oxygene::CID.from_json(@data['cid']) end # @return [String] label value diff --git a/skyfall.gemspec b/skyfall.gemspec index 7042460..8b0f584 100644 --- a/skyfall.gemspec +++ b/skyfall.gemspec @@ -34,9 +34,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] - spec.add_dependency 'base32', '~> 0.3', '>= 0.3.4' - spec.add_dependency 'base64', '~> 0.1' - spec.add_dependency 'cbor', '~> 0.5', '>= 0.5.9.6' spec.add_dependency 'eventmachine', '~> 1.2', '>= 1.2.7' spec.add_dependency 'faye-websocket', '~> 0.12' + spec.add_dependency 'oxygene', '>= 0.0.1' end