// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 // Package blockdigest stores a Block Locator Digest compactly. Can be used as a map key. package blockdigest import ( "fmt" "regexp" "strconv" "strings" ) var LocatorPattern = regexp.MustCompile( "^[0-9a-fA-F]{32}\\+[0-9]+(\\+[A-Z][A-Za-z0-9@_-]*)*$") // BlockDigest stores a Block Locator Digest compactly, up to 128 bits. Can be // used as a map key. type BlockDigest struct { H uint64 L uint64 } type DigestWithSize struct { Digest BlockDigest Size uint32 } type BlockLocator struct { Digest BlockDigest Size int Hints []string } func (d BlockDigest) String() string { return fmt.Sprintf("%016x%016x", d.H, d.L) } func (w DigestWithSize) String() string { return fmt.Sprintf("%s+%d", w.Digest.String(), w.Size) } // FromString creates a new BlockDigest unless an error is encountered. func FromString(s string) (dig BlockDigest, err error) { if len(s) != 32 { err = fmt.Errorf("Block digest should be exactly 32 characters but this one is %d: %s", len(s), s) return } var d BlockDigest d.H, err = strconv.ParseUint(s[:16], 16, 64) if err != nil { return } d.L, err = strconv.ParseUint(s[16:], 16, 64) if err != nil { return } dig = d return } func IsBlockLocator(s string) bool { return LocatorPattern.MatchString(s) } func ParseBlockLocator(s string) (BlockLocator, error) { if !LocatorPattern.MatchString(s) { return BlockLocator{}, fmt.Errorf("String %q does not match block locator pattern %q.", s, LocatorPattern.String()) } tokens := strings.Split(s, "+") // We expect both of the following to succeed since // LocatorPattern restricts the strings appropriately. blockDigest, err := FromString(tokens[0]) if err != nil { return BlockLocator{}, err } blockSize, err := strconv.ParseInt(tokens[1], 10, 0) if err != nil { return BlockLocator{}, err } return BlockLocator{ Digest: blockDigest, Size: int(blockSize), Hints: tokens[2:], }, nil }