#!/usr/bin/env ruby require 'base32' # you will have to `gem install` this require 'uri/ni' # this too (i wrote it) # i had claude (sonnet 4.6) initially provide me the basics for these # two functions and then i cleaned up its silliness # note that's "variable integer" not a typo of "variant" def decode_varint string # don't raise here just return nil return if string.empty? # first make sure the string is bytes and duplicate it so we don't # hurt the original string = string.b.dup out = 0 # the return value shift = 0 # 7-bit increments to shift left loop do # do raise here though because this is actually an error raise ArgumentError, 'malformed varint' if string.empty? # peel off the first byte and get its ordinal value byte = string.slice!(0).ord # mask, shift left and OR it out |= (byte & 0x7f) << (7 * shift) # continue only if the high-order bit is set break if (byte & 0x80).zero? shift += 1 # the other 7 bits are data end # return the varint and remaining string [out, string] end def decode_cidv1_base32 cid # first character is the encoding raw = Base32.decode(cid[1..].upcase) # this will clip the varints off the front of the string version, raw = decode_varint raw codec, raw = decode_varint raw algo, raw = decode_varint raw len, raw = decode_varint raw # note: # * `version` would dispatch but we only deal with version 1 here # * `codec` tells us something about the thing that was hashed # * `algo` 0x12 is sha-256 so that's hard coded for now # * `len` is the payload length which we hope is fully present # # (see https://github.com/multiformats/multicodec/blob/master/table.csv) # # so we could just imagine mapping that table to hash algorithms and # mime types or whatever # here have some diagnostics if you like, lol # # warn (["0x%02x (%d)"] * 4).join(', ') % # [version, codec, algo, len].map { |x| [x, x] }.flatten # oh yeah ruby has base64 built into `pack` b64 = [raw[0, len]].pack('m0').tr('+/', '-_').delete(?=) # the content-type query parameter accounts for the `codec` component URI("ni:///sha-256;#{b64}?ct=application/vnd.ipld.dag-cbor") end if $0 == __FILE__ uri = decode_cidv1_base32 'bafyreiemgv3douzgbjnytcp5tbdb4iguk34l3ik2w5ahwnenzehmexdidm' puts uri # ni:///sha-256;jDV2N1MmCluJif2YRh4g1Fb4vaFat0B7NI3JDsJcaBs?ct=application/vnd.ipld.dag-cbor puts uri.hexdigest # 8c35763753260a5b8989fd98461e20d456f8bda15ab7407b348dc90ec25c681b end