lib/uuid/ncname.rb in uuid-ncname-0.2.3 vs lib/uuid/ncname.rb in uuid-ncname-0.2.4

- old
+ new

@@ -125,11 +125,11 @@ end def self.warn_version version if version.nil? warn 'Set an explicit :version to remove this warning. See documentation.' - version = 0 + version = 1 end raise 'Version must be 0 or 1' unless [0, 1].include? version version @@ -147,13 +147,13 @@ # # @param radix [32, 64] either the number 32 or the number 64. # # @param version [0, 1] An optional formatting version, where 0 is # the naïve original version and 1 moves the `variant` nybble out - # to the end of the identifier. You will be warned if you do not - # set this parameter explicitly. The default is currently 0, but - # will change in the next version. + # to the end of the identifier. You will be warned for the time + # being if you do not set this parameter explicitly. The default + # version is 1. # # @param align [true, false] Optional directive to treat the # terminating character as aligned to the numerical base of the # representation. Since the version nybble is removed from the # string and the first 120 bits divide evenly into both Base32 and @@ -200,31 +200,34 @@ encode_version(uuidver) + ENCODE[radix].call(content, align) end # Converts an NCName-encoded UUID back to its canonical # representation. Will return nil if the input doesn't match the - # radix (if supplied) or is otherwise malformed. doesn't match + # radix (if supplied) or is otherwise malformed. # # @param ncname [#to_s] an NCName-encoded UUID, either a # 22-character (Base64) variant, or a 26-character (Base32) variant. # # @param radix [nil, 32, 64] Optional radix; will use heuristic if omitted. # # @param format [:str, :hex, :b64, :bin] An optional formatting # parameter; defaults to `:str`, the canonical string representation. # - # @param version [0, 1] See `to_ncname`. Defaults (for now) to 0. + # @param version [0, 1] See #to_ncname. Defaults to 1. # - # @param align [true, false, nil] See `to_ncname` for details. + # @param align [nil, true, false] See #to_ncname for details. # Setting this parameter to `nil`, the default, will cause the # decoder to detect the alignment state from the identifier. # + # @param validate [false, true] Check that the ninth octet is + # correctly masked _after_ decoding. + # # @return [String, nil] The corresponding UUID or nil if the input # is malformed. def self.from_ncname ncname, - radix: nil, format: :str, version: nil, align: nil + radix: nil, format: :str, version: nil, align: nil, validate: false raise 'Format must be symbol-able' unless format.respond_to? :to_sym raise "Invalid format #{format}" unless FORMAT[format] raise 'Align must be true, false, or nil' unless [true, false, nil].include? align @@ -260,20 +263,23 @@ uuidver = decode_version uuidver content = DECODE[radix].call content, align bin = TRANSFORM[version][1].call uuidver, content + # double-check the variant (high-order bits have to be 10) + return if validate and bin[8].ord >> 6 != 2 + FORMAT[format].call bin end # Shorthand for conversion to the Base64 version # # @param uuid [#to_s] The UUID # - # @param version [0, 1] See `to_ncname`. + # @param version [0, 1] See #to_ncname. # - # @param align [true, false] See `to_ncname`. + # @param align [true, false] See #to_ncname. # # @return [String] The Base64-encoded NCName def self.to_ncname_64 uuid, version: nil, align: true to_ncname uuid, version: version, align: align @@ -283,13 +289,13 @@ # # @param ncname [#to_s] The Base64 variant of the NCName-encoded UUID # # @param format [:str, :hex, :b64, :bin] The format # - # @param version [0, 1] See `to_ncname`. + # @param version [0, 1] See #to_ncname. # - # @param align [true, false] See `to_ncname`. + # @param align [true, false] See #to_ncname. # # @return [String, nil] The corresponding UUID or nil if the input # is malformed. def self.from_ncname_64 ncname, format: :str, version: nil, align: nil @@ -298,13 +304,13 @@ # Shorthand for conversion to the Base32 version # # @param uuid [#to_s] The UUID # - # @param version [0, 1] See `to_ncname`. + # @param version [0, 1] See #to_ncname. # - # @param align [true, false] See `to_ncname`. + # @param align [true, false] See #to_ncname. # # @return [String] The Base32-encoded NCName def self.to_ncname_32 uuid, version: nil, align: true to_ncname uuid, radix: 32, version: version, align: align @@ -314,35 +320,48 @@ # # @param ncname [#to_s] The Base32 variant of the NCName-encoded UUID # # @param format [:str, :hex, :b64, :bin] The format # - # @param version [0, 1] See `to_ncname`. + # @param version [0, 1] See #to_ncname. # - # @param align [true, false] See `to_ncname`. + # @param align [true, false] See #to_ncname. # # @return [String, nil] The corresponding UUID or nil if the input # is malformed. def self.from_ncname_32 ncname, format: :str, version: nil, align: nil from_ncname ncname, radix: 32, format: format end - # Test if the given token is a UUID NCName, with a hint to its version. + # Test if the given token is a UUID NCName, with a hint to its + # version. This method can positively identify a token as a UUID + # NCName, but there is a small subset of UUIDs which will produce + # tokens which are valid in both versions. The method returns + # `false` if the token is invalid, otherwise it returns `0` or `1` + # for the guessed version. # + # @note Version 1 tokens always end with I, J, K, or L (with base32 + # being case-insensitive), so tokens that end in something else will + # be version 0. + # # @param token [#to_s] The token to test # # @return [false, 0, 1] def self.valid? token token = token.to_s - if /^[A-P](?:[0-9A-Za-z_-]{21}|[2-7A-Za-z]{25})$/.match token + if /^[A-Pa-p](?:[0-9A-Za-z_-]{21}|[2-7A-Za-z]{25})$/.match token # false is definitely version zero but true is only maybe version 1 - version = /[IJKLijkl]$/.match(token) ? 1 : 0 + version = /^(?:.{21}[I-L]|.{25}[I-Li-l])$/.match(token) ? 1 : 0 - if version == 1 - uu = from_ncname token, version: 1 - # lol even this isn't a guarantee - /[89ab]/.match(uu[19]) ? 1 : version + # try decoding with validation on + uu = from_ncname token, version: version, validate: true + + if version == 1 and !uu + # try version zero + uu = from_ncname token, version: 0, validate: true + # either zero or invalid + uu ? 0 : false else version end else false