}
func (ks *keepstore) BlockRead(ctx context.Context, opts arvados.BlockReadOptions) (n int, err error) {
- li, err := parseLocator(opts.Locator)
+ li, err := getLocatorInfo(opts.Locator)
if err != nil {
return 0, err
}
}
var remoteClient *keepclient.KeepClient
var parts []string
- var size int
+ li, err := getLocatorInfo(opts.Locator)
+ if err != nil {
+ return 0, err
+ }
for i, part := range strings.Split(opts.Locator, "+") {
switch {
case i == 0:
}
remoteClient = kc
part = "A" + part[7:]
- case len(part) > 0 && part[0] >= '0' && part[0] <= '9':
- size, _ = strconv.Atoi(part)
}
parts = append(parts, part)
}
if opts.LocalLocator == nil {
// Read from remote cluster and stream response back
// to caller
- if rw, ok := opts.WriteTo.(http.ResponseWriter); ok && size > 0 {
- rw.Header().Set("Content-Length", fmt.Sprintf("%d", size))
+ if rw, ok := opts.WriteTo.(http.ResponseWriter); ok && li.size > 0 {
+ rw.Header().Set("Content-Length", fmt.Sprintf("%d", li.size))
}
return remoteClient.BlockRead(ctx, arvados.BlockReadOptions{
Locator: locator,
}
func (ks *keepstore) BlockUntrash(ctx context.Context, locator string) error {
- li, err := parseLocator(locator)
+ li, err := getLocatorInfo(locator)
if err != nil {
return err
}
}
func (ks *keepstore) BlockTouch(ctx context.Context, locator string) error {
- li, err := parseLocator(locator)
+ li, err := getLocatorInfo(locator)
if err != nil {
return err
}
if !ks.cluster.Collections.BlobTrash {
return errMethodNotAllowed
}
- li, err := parseLocator(locator)
+ li, err := getLocatorInfo(locator)
if err != nil {
return err
}
}
}
+// locatorInfo expresses the attributes of a locator that are relevant
+// for keepstore decision-making.
type locatorInfo struct {
hash string
size int
- remote bool
- signed bool
+ remote bool // locator has a +R hint
+ signed bool // locator has a +A hint
}
-func parseLocator(loc string) (locatorInfo, error) {
+func getLocatorInfo(loc string) (locatorInfo, error) {
var li locatorInfo
- for i, part := range strings.Split(loc, "+") {
- if i == 0 {
- if len(part) != 32 {
+ plus := 0 // number of '+' chars seen so far
+ partlen := 0 // chars since last '+'
+ for i, c := range loc + "+" {
+ if c == '+' {
+ if partlen == 0 {
+ // double/leading/trailing '+'
return li, errInvalidLocator
}
- li.hash = part
+ if plus == 0 {
+ if i != 32 {
+ return li, errInvalidLocator
+ }
+ li.hash = loc[:i]
+ }
+ if plus == 1 {
+ if size, err := strconv.Atoi(loc[i-partlen : i]); err == nil {
+ li.size = size
+ }
+ }
+ plus++
+ partlen = 0
continue
}
- if i == 1 {
- if size, err := strconv.Atoi(part); err == nil {
- li.size = size
- continue
+ partlen++
+ if partlen == 1 {
+ if c == 'A' {
+ li.signed = true
+ }
+ if c == 'R' {
+ li.remote = true
+ }
+ if plus > 1 && c >= '0' && c <= '9' {
+ // size, if present at all, must come first
+ return li, errInvalidLocator
}
}
- if len(part) == 0 {
- return li, errInvalidLocator
- }
- if part[0] == 'A' {
- li.signed = true
- }
- if part[0] == 'R' {
- li.remote = true
- }
- if part[0] >= '0' && part[0] <= '9' {
- // size, if present at all, must come first
+ if plus == 0 && !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
+ // non-hexadecimal char in hash part
return li, errInvalidLocator
}
}