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