Merge branch 'master' into 6219-fuse-performance-testing
[arvados.git] / sdk / go / blockdigest / blockdigest.go
1 // Stores a Block Locator Digest compactly. Can be used as a map key.
2 package blockdigest
3
4 import (
5         "fmt"
6         "log"
7         "regexp"
8         "strconv"
9         "strings"
10 )
11
12 var LocatorPattern = regexp.MustCompile(
13         "^[0-9a-fA-F]{32}\\+[0-9]+(\\+[A-Z][A-Za-z0-9@_-]+)*$")
14
15 // Stores a Block Locator Digest compactly, up to 128 bits.
16 // Can be used as a map key.
17 type BlockDigest struct {
18         H uint64
19         L uint64
20 }
21
22 type DigestWithSize struct {
23         Digest BlockDigest
24         Size   uint32
25 }
26
27 type BlockLocator struct {
28         Digest BlockDigest
29         Size   int
30         Hints  []string
31 }
32
33 func (d BlockDigest) String() string {
34         return fmt.Sprintf("%016x%016x", d.H, d.L)
35 }
36
37 func (w DigestWithSize) String() string {
38         return fmt.Sprintf("%s+%d", w.Digest.String(), w.Size)
39 }
40
41 // Will create a new BlockDigest unless an error is encountered.
42 func FromString(s string) (dig BlockDigest, err error) {
43         if len(s) != 32 {
44                 err = fmt.Errorf("Block digest should be exactly 32 characters but this one is %d: %s", len(s), s)
45                 return
46         }
47
48         var d BlockDigest
49         d.H, err = strconv.ParseUint(s[:16], 16, 64)
50         if err != nil {
51                 return
52         }
53         d.L, err = strconv.ParseUint(s[16:], 16, 64)
54         if err != nil {
55                 return
56         }
57         dig = d
58         return
59 }
60
61 // Will fatal with the error if an error is encountered
62 func AssertFromString(s string) BlockDigest {
63         d, err := FromString(s)
64         if err != nil {
65                 log.Fatalf("Error creating BlockDigest from %s: %v", s, err)
66         }
67         return d
68 }
69
70 func IsBlockLocator(s string) bool {
71         return LocatorPattern.MatchString(s)
72 }
73
74 func ParseBlockLocator(s string) (b BlockLocator, err error) {
75         if !LocatorPattern.MatchString(s) {
76                 err = fmt.Errorf("String \"%s\" does not match BlockLocator pattern "+
77                         "\"%s\".",
78                         s,
79                         LocatorPattern.String())
80         } else {
81                 tokens := strings.Split(s, "+")
82                 var blockSize int64
83                 var blockDigest BlockDigest
84                 // We expect both of the following to succeed since LocatorPattern
85                 // restricts the strings appropriately.
86                 blockDigest, err = FromString(tokens[0])
87                 if err != nil {
88                         return
89                 }
90                 blockSize, err = strconv.ParseInt(tokens[1], 10, 0)
91                 if err != nil {
92                         return
93                 }
94                 b.Digest = blockDigest
95                 b.Size = int(blockSize)
96                 b.Hints = tokens[2:]
97         }
98         return
99 }