oxygene 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +9 -0
- data/CHANGELOG.md +28 -0
- data/README.md +141 -1
- data/lib/oxygene/base32.rb +284 -0
- data/lib/oxygene/car_archive.rb +179 -49
- data/lib/oxygene/car_repo.rb +121 -0
- data/lib/oxygene/car_section.rb +59 -0
- data/lib/oxygene/cid.rb +311 -18
- data/lib/oxygene/extensions.rb +13 -2
- data/lib/oxygene/version.rb +1 -1
- data/lib/oxygene.rb +18 -0
- metadata +5 -15
data/lib/oxygene/cid.rb
CHANGED
|
@@ -1,45 +1,338 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'base32'
|
|
3
4
|
require_relative 'errors'
|
|
5
|
+
require_relative 'extensions'
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
module Oxygene
|
|
6
8
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
|
+
# Represents a Content Identifier (CID) of some piece of content like an ATProto
|
|
11
|
+
# record or a CAR section.
|
|
12
|
+
#
|
|
13
|
+
# Only the [DASL](https://dasl.ing)-compatible CID versions that are used in ATProto are supported:
|
|
14
|
+
#
|
|
15
|
+
# - only CIDv1, not v0
|
|
16
|
+
# - in binary CBOR form: with SHA-256 hash, 32 bytes hash size, and codec being
|
|
17
|
+
# either 0x71 (DRISL) or 0x55 (raw) (DRISL is used in CIDs of CAR sections,
|
|
18
|
+
# and raw codec is used in CIDs of blobs)
|
|
19
|
+
# - in JSON string form: with 'b' prefix and Base32-encoded data, using lowercase
|
|
20
|
+
# alphabet and no '=' padding, and the CBOR form parameters listed above,
|
|
21
|
+
# resulting in an either "bafyrei" or "bafkrei" prefix
|
|
22
|
+
#
|
|
23
|
+
# The CID instances can be created either from JSON strings or binary CBOR form,
|
|
24
|
+
# and the binary form can optionally include the \\x00 prefix byte that is used
|
|
25
|
+
# when the binary CID is encoded in a CBOR tag 42 object. The instances will lazily
|
|
26
|
+
# convert between the two forms only when needed.
|
|
27
|
+
#
|
|
28
|
+
# The code in this class is heavily optimized for performance when creating and
|
|
29
|
+
# processing a large number of CIDs e.g. when parsing a CAR repo or processing
|
|
30
|
+
# firehose events, for example:
|
|
31
|
+
#
|
|
32
|
+
# - the input JSON or binary string is stored without copying or allocating new
|
|
33
|
+
# strings for modified versions, if possible
|
|
34
|
+
# - the other form (JSON/CBOR) is memoized and only created on demand, not up front
|
|
35
|
+
# - equality check tries to use whichever form exists in both instances if possible,
|
|
36
|
+
# to avoid unnecessary conversion
|
|
37
|
+
# - validation code uses pre-generated header constants to avoid checking the headers
|
|
38
|
+
# byte by byte, and operates mostly on byte code numbers to avoid unnecessary string allocations
|
|
39
|
+
#
|
|
40
|
+
# Related specifications:
|
|
41
|
+
#
|
|
42
|
+
# - [IPFS CID spec](https://specs.ipfs.tech/cid/)
|
|
43
|
+
# - [DASL CID spec](https://dasl.ing/cid.html)
|
|
44
|
+
# - [ATProto link and CID formats](https://atproto.com/specs/data-model#link-and-cid-formats)
|
|
45
|
+
# - [CIDs in DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/)
|
|
46
|
+
# - [CIDs in DAG-JSON](https://ipld.io/specs/codecs/dag-json/spec/)
|
|
47
|
+
# - [multicodec](https://github.com/multiformats/multicodec),
|
|
48
|
+
# [multihash](https://github.com/multiformats/multihash)
|
|
49
|
+
# and [multibase](https://github.com/multiformats/multibase)
|
|
50
|
+
#
|
|
10
51
|
|
|
11
|
-
module Oxygene
|
|
12
52
|
class CID
|
|
13
|
-
|
|
53
|
+
using Oxygene::Extensions
|
|
54
|
+
|
|
55
|
+
# Multibase prefix code for Base32, required for CIDs in JSON string form.
|
|
56
|
+
JSON_PREFIX = 'b'
|
|
57
|
+
|
|
58
|
+
# Pre-generated JSON string headers for quick checking
|
|
59
|
+
DRISL_JSON_PREFIX = "bafyrei"
|
|
60
|
+
RAW_JSON_PREFIX = "bafkrei"
|
|
61
|
+
|
|
62
|
+
private_constant :DRISL_JSON_PREFIX, :RAW_JSON_PREFIX
|
|
63
|
+
|
|
64
|
+
# Multibase "identity" prefix code, required for binary CIDs stored in a CBOR tag 42 object.
|
|
65
|
+
CBOR_TAG_PREFIX = "\x00".b.freeze
|
|
66
|
+
|
|
67
|
+
# The two possible CID codec IDs from multicodec:
|
|
68
|
+
|
|
69
|
+
# DAG-CBOR or DRISL (used e.g. in CIDs of CAR sections)
|
|
70
|
+
DRISL_CODEC_ID = 0x71
|
|
14
71
|
|
|
72
|
+
# Raw binary (used in CIDs of blobs)
|
|
73
|
+
RAW_CODEC_ID = 0x55
|
|
74
|
+
|
|
75
|
+
# Pre-generated binary string headers for quick checking
|
|
76
|
+
DRISL_BINARY_PREFIX = "\x01\x71\x12\x20".b.freeze
|
|
77
|
+
RAW_BINARY_PREFIX = "\x01\x55\x12\x20".b.freeze
|
|
78
|
+
|
|
79
|
+
# Pre-generated binary string header variants with identity prefix byte added
|
|
80
|
+
TAG_DRISL_BINARY_PREFIX = (CBOR_TAG_PREFIX + DRISL_BINARY_PREFIX).freeze
|
|
81
|
+
TAG_RAW_BINARY_PREFIX = (CBOR_TAG_PREFIX + RAW_BINARY_PREFIX).freeze
|
|
82
|
+
|
|
83
|
+
private_constant :DRISL_BINARY_PREFIX, :RAW_BINARY_PREFIX, :TAG_DRISL_BINARY_PREFIX, :TAG_RAW_BINARY_PREFIX
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Builds a CID from a CBOR tag 42 value.
|
|
87
|
+
#
|
|
88
|
+
# Expects a `CBOR::Tagged` object returned from `CBOR.decode` or `CBOR.decode_sequence`.
|
|
89
|
+
#
|
|
90
|
+
# @param tag [CBOR::Tagged] tagged CBOR object containing binary CID data with identity (0) prefix
|
|
91
|
+
# @return [CID] CID object wrapping that content identifier
|
|
92
|
+
# @raise [DecodeError] if the CID has an invalid size or is missing an expected prefix/suffix
|
|
93
|
+
# @raise [UnsupportedError] if the CID is in a form that is technically valid, but not supported here
|
|
94
|
+
#
|
|
15
95
|
def self.from_cbor_tag(tag)
|
|
16
|
-
|
|
17
|
-
raise DecodeError.new("Unexpected first byte of CID: #{data[0]}") unless data[0] == "\x00"
|
|
18
|
-
CID.new(data[1..-1])
|
|
96
|
+
CID.new(tag.value, binary: true, cbor_prefix: true)
|
|
19
97
|
end
|
|
20
98
|
|
|
99
|
+
# Builds a CID from a Base32-encoded JSON form string.
|
|
100
|
+
#
|
|
101
|
+
# @param string [String] 59-character string with a `b` prefix encoded with Base32
|
|
102
|
+
# @return [CID] CID object wrapping that content identifier
|
|
103
|
+
# @raise [ArgumentError] if the input string is nil
|
|
104
|
+
# @raise [DecodeError] if the CID has an invalid size or is missing an expected prefix/suffix
|
|
105
|
+
# @raise [UnsupportedError] if the CID is in a form that is technically valid, but not supported here
|
|
106
|
+
#
|
|
21
107
|
def self.from_json(string)
|
|
22
|
-
|
|
23
|
-
|
|
108
|
+
CID.new(string, binary: false)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Creates a CID instance from binary/CBOR data or a JSON string form.
|
|
112
|
+
#
|
|
113
|
+
# Three possible kinds of values are accepted as input:
|
|
114
|
+
# - a binary string with \\x00 identity prefix, as used when included in a
|
|
115
|
+
# CBOR tag object's value - use `binary: true` and `cbor_prefix: true`
|
|
116
|
+
# - a binary string without the identity prefix, as used e.g. when decoded
|
|
117
|
+
# from a CAR archive - use `binary: true` without `cbor_prefix`
|
|
118
|
+
# - a JSON string - use `binary: false`
|
|
119
|
+
#
|
|
120
|
+
# For backwards compatibility, the default when only the input argument is
|
|
121
|
+
# passed is to assume the "binary without CBOR prefix" form. For performance,
|
|
122
|
+
# for the "binary with CBOR prefix" and the JSON form types, the input string
|
|
123
|
+
# itself is stored without copying and frozen to prevent modification. If you
|
|
124
|
+
# need to modify the string later at the call site, call {dup} when passing the
|
|
125
|
+
# input argument here. (For the non-prefixed binary version, the input string is
|
|
126
|
+
# copied to add the prefix.)
|
|
127
|
+
#
|
|
128
|
+
# Optionally, the initializer can also enforce that the CID has to use a selected one
|
|
129
|
+
# of the two available codecs (DRISL/DAG-CBOR vs. raw binary).
|
|
130
|
+
#
|
|
131
|
+
# @param data [String] raw CID bytes, identity-prefixed CBOR bytes, or a Base32 JSON string
|
|
132
|
+
# @param binary [Boolean] true if `data` is in binary form, false if it's a JSON string
|
|
133
|
+
# @param cbor_prefix [Boolean] true if the `data` includes the leading null byte used inside CBOR tag 42
|
|
134
|
+
# @param codec [:drisl, :raw, nil] required content codec, or `nil` to accept either of the two supported codecs
|
|
135
|
+
#
|
|
136
|
+
# @raise [ArgumentError] if data is nil, codec param is invalid, or invalid combination of options is used
|
|
137
|
+
# @raise [DecodeError] if the CID has an invalid size or is missing an expected prefix/suffix
|
|
138
|
+
# @raise [UnsupportedError] if the CID is in a form that is technically valid, but not supported here
|
|
139
|
+
#
|
|
140
|
+
def initialize(data, binary: true, cbor_prefix: false, codec: nil)
|
|
141
|
+
raise ArgumentError.new("Data cannot be nil") if data.nil?
|
|
24
142
|
|
|
25
|
-
|
|
26
|
-
|
|
143
|
+
codec_id = case codec
|
|
144
|
+
when nil then nil
|
|
145
|
+
when :drisl then DRISL_CODEC_ID
|
|
146
|
+
when :raw then RAW_CODEC_ID
|
|
147
|
+
else raise ArgumentError.new("Unexpected CID codec: #{codec.inspect}")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if binary
|
|
151
|
+
if cbor_prefix
|
|
152
|
+
validate_binary_form(data, true, codec_id)
|
|
153
|
+
@cbor_form = data.freeze
|
|
154
|
+
else
|
|
155
|
+
validate_binary_form(data, false, codec_id)
|
|
156
|
+
@cbor_form = (CBOR_TAG_PREFIX + data).freeze
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
raise ArgumentError.new("cbor_prefix cannot be used with JSON input") if cbor_prefix
|
|
160
|
+
|
|
161
|
+
validate_json_form(data, codec_id)
|
|
162
|
+
@json_form = data.freeze
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Returns the CID data in binary form with a null byte identity prefix, as stored in CBOR tag 42.
|
|
167
|
+
#
|
|
168
|
+
# If the CID was created from a JSON form, the data is decoded from Base32 (and memoized).
|
|
169
|
+
# If it was created from a CBOR tag, the input is returned directly.
|
|
170
|
+
#
|
|
171
|
+
# @return [String] frozen binary string beginning with {CBOR_TAG_PREFIX}
|
|
172
|
+
# @raise [ArgumentError] if a JSON-form CID contains invalid Base32
|
|
173
|
+
|
|
174
|
+
def cbor_form
|
|
175
|
+
@cbor_form ||= Base32.decode(@json_form, 1, CBOR_TAG_PREFIX).freeze
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Returns the CID data in binary form, without the null byte identity prefix from CBOR tag.
|
|
179
|
+
#
|
|
180
|
+
# If the CID was created from a JSON form, the data is decoded from Base32 (and memoized).
|
|
181
|
+
#
|
|
182
|
+
# @return [String] frozen binary CID bytes
|
|
183
|
+
# @raise [ArgumentError] if a JSON-form CID contains invalid Base32
|
|
184
|
+
|
|
185
|
+
def raw_data
|
|
186
|
+
@raw_data ||= if @cbor_form
|
|
187
|
+
@cbor_form.byteslice(1, @cbor_form.bytesize - 1).freeze
|
|
188
|
+
else
|
|
189
|
+
Base32.decode(@json_form, 1).freeze
|
|
190
|
+
end
|
|
27
191
|
end
|
|
28
192
|
|
|
29
|
-
|
|
30
|
-
|
|
193
|
+
alias data raw_data
|
|
194
|
+
|
|
195
|
+
# Returns the CID's Base32-encoded JSON string representation.
|
|
196
|
+
#
|
|
197
|
+
# If the CID was created from a binary form, the data is encoded into Base32 (and memoized).
|
|
198
|
+
# If it was created from a JSON form, the input is returned directly.
|
|
199
|
+
#
|
|
200
|
+
# @return [String] frozen CID string beginning with {JSON_PREFIX}
|
|
201
|
+
|
|
202
|
+
def json_form
|
|
203
|
+
@json_form ||= Base32.encode(@cbor_form, 1, JSON_PREFIX).freeze
|
|
31
204
|
end
|
|
32
205
|
|
|
206
|
+
# Returns the CID's Base32-encoded JSON string representation (same as {#json_form}).
|
|
207
|
+
#
|
|
208
|
+
# @return [String] the CID in the JSON form
|
|
209
|
+
|
|
33
210
|
def to_s
|
|
34
|
-
|
|
211
|
+
json_form
|
|
35
212
|
end
|
|
36
213
|
|
|
214
|
+
# @return [String] a representation of the CID object for debugging
|
|
215
|
+
#
|
|
37
216
|
def inspect
|
|
38
|
-
"CID(\"#{
|
|
217
|
+
"CID(\"#{json_form}\")"
|
|
39
218
|
end
|
|
40
219
|
|
|
220
|
+
# Compares this CID with another CID to see if they're equal.
|
|
221
|
+
#
|
|
222
|
+
# If both CIDs have a generated CBOR or JSON form, those forms are used
|
|
223
|
+
# for comparison without conversion. If the two only have different forms,
|
|
224
|
+
# the JSON CID is converted to binary for comparison.
|
|
225
|
+
#
|
|
226
|
+
# @param other [Object] object to compare
|
|
227
|
+
# @return [Boolean] whether `other` is a CID with the same value
|
|
228
|
+
|
|
41
229
|
def ==(other)
|
|
42
|
-
other.is_a?(CID)
|
|
230
|
+
return false unless other.is_a?(CID)
|
|
231
|
+
|
|
232
|
+
if @cbor_form && (other_cbor = other.instance_variable_get('@cbor_form'))
|
|
233
|
+
@cbor_form == other_cbor
|
|
234
|
+
elsif @json_form && (other_json = other.instance_variable_get('@json_form'))
|
|
235
|
+
@json_form == other_json
|
|
236
|
+
else
|
|
237
|
+
self.cbor_form == other.cbor_form
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
alias eql? ==
|
|
242
|
+
|
|
243
|
+
# Returns a hash code for the purposes of a {Set} or {Hash}.
|
|
244
|
+
#
|
|
245
|
+
# The binary CBOR form of the CID is used to derive the hash.
|
|
246
|
+
#
|
|
247
|
+
# @return [Integer] hash code generated from the CID's binary form
|
|
248
|
+
|
|
249
|
+
def hash
|
|
250
|
+
cbor_form.hash
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
private
|
|
255
|
+
|
|
256
|
+
def validate_json_form(data, expected_codec)
|
|
257
|
+
raise DecodeError.new("Unexpected CID length") unless data.bytesize == 59
|
|
258
|
+
raise DecodeError.new("Unexpected CID prefix") unless data.getbyte(0) == 98 # 'b'
|
|
259
|
+
|
|
260
|
+
offset = 1
|
|
261
|
+
|
|
262
|
+
while offset < 59
|
|
263
|
+
byte = data.getbyte(offset)
|
|
264
|
+
raise DecodeError.new("Unexpected characters in CID") unless (byte >= 97 && byte <= 122) || (byte >= 50 && byte <= 55)
|
|
265
|
+
offset += 1
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
codec = if data.start_with?(DRISL_JSON_PREFIX)
|
|
269
|
+
DRISL_CODEC_ID
|
|
270
|
+
elsif data.start_with?(RAW_JSON_PREFIX)
|
|
271
|
+
RAW_CODEC_ID
|
|
272
|
+
else
|
|
273
|
+
raise UnsupportedError.new("Unexpected CID prefix")
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
if expected_codec && codec != expected_codec
|
|
277
|
+
raise UnsupportedError.new("Unexpected CID codec: #{codec}")
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# We're checking for existence of DRISL_BINARY_PREFIX / RAW_BINARY_PREFIX at the beginning,
|
|
281
|
+
# but we want to avoid encoding each JSON CID to binary immediately just to check that.
|
|
282
|
+
#
|
|
283
|
+
# So instead we're looking for the equivalent in JSON encoding - 'bafyrei...' or 'bafkrei...'.
|
|
284
|
+
# But the last bits of the 4th byte (24...32) are encoded into two bits of the 7th character
|
|
285
|
+
# in the base32 version (after the 'b' prefix), so the one after 'i'. So here we check if the
|
|
286
|
+
# 8th character of the string is in the expected range that has such two first bits.
|
|
287
|
+
|
|
288
|
+
char8 = data.getbyte(7)
|
|
289
|
+
raise UnsupportedError.new("Unexpected CID prefix") unless char8 >= 97 && char8 <= 104 # 'a'..'h'
|
|
290
|
+
|
|
291
|
+
# And the final character needs to have last two bits set to 0:
|
|
292
|
+
|
|
293
|
+
trailing_byte = data.getbyte(58)
|
|
294
|
+
position = (trailing_byte >= 97 && trailing_byte <= 122) ? (trailing_byte - 97) : (trailing_byte - 50 + 26)
|
|
295
|
+
canonical_trailing_bits = (position & 3) == 0
|
|
296
|
+
|
|
297
|
+
raise DecodeError.new("Unexpected CID trailing bits") unless canonical_trailing_bits
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def validate_binary_form(data, cbor_prefix, expected_codec)
|
|
301
|
+
expected_length = cbor_prefix ? 37 : 36
|
|
302
|
+
data_length = data.bytesize
|
|
303
|
+
|
|
304
|
+
raise DecodeError.new("CID too short: #{data}") if data_length < expected_length
|
|
305
|
+
raise DecodeError.new("CID too long: #{data}") if data_length > expected_length
|
|
306
|
+
|
|
307
|
+
if cbor_prefix
|
|
308
|
+
raise DecodeError.new("Unexpected first byte of CID: #{data[0]}") unless data.getbyte(0) == 0
|
|
309
|
+
|
|
310
|
+
return if data.start_with?(TAG_DRISL_BINARY_PREFIX) && (expected_codec.nil? || expected_codec == DRISL_CODEC_ID)
|
|
311
|
+
return if data.start_with?(TAG_RAW_BINARY_PREFIX) && (expected_codec.nil? || expected_codec == RAW_CODEC_ID)
|
|
312
|
+
else
|
|
313
|
+
return if data.start_with?(DRISL_BINARY_PREFIX) && (expected_codec.nil? || expected_codec == DRISL_CODEC_ID)
|
|
314
|
+
return if data.start_with?(RAW_BINARY_PREFIX) && (expected_codec.nil? || expected_codec == RAW_CODEC_ID)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# At this point we're rejecting the CID, we just check with what error message:
|
|
318
|
+
|
|
319
|
+
buffer = StringIO.new(data)
|
|
320
|
+
buffer.pos = 1 if cbor_prefix
|
|
321
|
+
|
|
322
|
+
version = buffer.read_varint
|
|
323
|
+
raise UnsupportedError.new("Unexpected CID version: #{version}") unless version == 1
|
|
324
|
+
|
|
325
|
+
codec = buffer.read_varint
|
|
326
|
+
supported_codec = expected_codec ? (codec == expected_codec) : (codec == DRISL_CODEC_ID || codec == RAW_CODEC_ID)
|
|
327
|
+
raise UnsupportedError.new("Unexpected CID codec: #{codec}") unless supported_codec
|
|
328
|
+
|
|
329
|
+
hash = buffer.read_varint
|
|
330
|
+
raise UnsupportedError.new("Unexpected CID hash: #{hash}") unless hash == 0x12
|
|
331
|
+
|
|
332
|
+
length = buffer.read_varint
|
|
333
|
+
raise UnsupportedError.new("Unexpected CID length: #{length}") unless length == 32
|
|
334
|
+
|
|
335
|
+
raise UnsupportedError.new("Non-canonical CID prefix")
|
|
43
336
|
end
|
|
44
337
|
end
|
|
45
338
|
end
|
data/lib/oxygene/extensions.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'cbor'
|
|
4
4
|
require 'stringio'
|
|
5
|
+
require_relative 'errors'
|
|
5
6
|
|
|
6
7
|
module Oxygene
|
|
7
8
|
|
|
@@ -22,13 +23,23 @@ module Oxygene
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
value
|
|
26
|
+
rescue EOFError
|
|
27
|
+
raise DecodeError, "Unexpected end of data while reading varint"
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
refine CBOR.singleton_class do
|
|
29
32
|
def decode_sequence(data)
|
|
30
|
-
unpacker = CBOR::Unpacker.new
|
|
31
|
-
unpacker.
|
|
33
|
+
unpacker = CBOR::Unpacker.new
|
|
34
|
+
unpacker.feed(data)
|
|
35
|
+
|
|
36
|
+
items = []
|
|
37
|
+
|
|
38
|
+
while !unpacker.buffer.empty?
|
|
39
|
+
items << unpacker.read
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
items
|
|
32
43
|
end
|
|
33
44
|
end
|
|
34
45
|
|
data/lib/oxygene/version.rb
CHANGED
data/lib/oxygene.rb
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'oxygene/base32'
|
|
3
4
|
require_relative 'oxygene/car_archive'
|
|
5
|
+
require_relative 'oxygene/car_repo'
|
|
4
6
|
require_relative 'oxygene/cid'
|
|
5
7
|
require_relative 'oxygene/version'
|
|
6
8
|
|
|
9
|
+
#
|
|
10
|
+
# Various data decoding related primitives for working with AT Protocol repositories
|
|
11
|
+
# and firehose events.
|
|
12
|
+
#
|
|
13
|
+
# This gem was extracted from [Skyfall](https://ruby.sdk.blue/skyfall/) and is mostly
|
|
14
|
+
# used there for the CBOR firehose decoding, but can also be used standalone for other
|
|
15
|
+
# ATProto data handling purposes, like decoding downloaded .car account repos.
|
|
16
|
+
#
|
|
17
|
+
# The functionality currently includes: Base32 encoder/decoder, CAR archive/repo parser,
|
|
18
|
+
# CID wrapper, and CBOR decoding (through the `cbor` gem for now).
|
|
19
|
+
#
|
|
20
|
+
# The gem intentionally only includes support for the parts of these standards that are
|
|
21
|
+
# in use in ATProto, i.e. doesn't handle all possible types of CIDs defined in the CID
|
|
22
|
+
# standard, only generates lowercase Base32, and so on.
|
|
23
|
+
#
|
|
24
|
+
|
|
7
25
|
module Oxygene
|
|
8
26
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oxygene
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kuba Suder
|
|
@@ -9,20 +9,6 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: base32
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - "~>"
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.3'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - "~>"
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0.3'
|
|
26
12
|
- !ruby/object:Gem::Dependency
|
|
27
13
|
name: base64
|
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,11 +43,15 @@ executables: []
|
|
|
57
43
|
extensions: []
|
|
58
44
|
extra_rdoc_files: []
|
|
59
45
|
files:
|
|
46
|
+
- AGENTS.md
|
|
60
47
|
- CHANGELOG.md
|
|
61
48
|
- LICENSE.txt
|
|
62
49
|
- README.md
|
|
63
50
|
- lib/oxygene.rb
|
|
51
|
+
- lib/oxygene/base32.rb
|
|
64
52
|
- lib/oxygene/car_archive.rb
|
|
53
|
+
- lib/oxygene/car_repo.rb
|
|
54
|
+
- lib/oxygene/car_section.rb
|
|
65
55
|
- lib/oxygene/cid.rb
|
|
66
56
|
- lib/oxygene/errors.rb
|
|
67
57
|
- lib/oxygene/extensions.rb
|