shard.go 2.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
package flatfs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type shardId struct {
	version string
	funName string
	param   string
}

func (f shardId) str() string {
	if f.funName == "" || f.funName == "auto" {
		return "auto"
	} else {
		return fmt.Sprintf("v1/%s/%s", f.funName, f.param)
	}
}

func parseShardFunc(str string) shardId {
	str = strings.TrimSpace(str)
	parts := strings.Split(str, "/")
	// ignore prefix for now
	if len(parts) > 3 {
		parts = parts[len(parts)-3:]
	}
	switch len(parts) {
	case 3:
		return shardId{version: parts[0], funName: parts[1], param: parts[2]}
	case 2:
		return shardId{funName: parts[0], param: parts[1]}
	case 1:
		return shardId{funName: parts[0]}
	default: // can only happen for len == 0
		return shardId{}
	}
}

func (f shardId) Func() (ShardFunc, error) {
	if f.version != "" && f.version != "v1" {
		return nil, fmt.Errorf("expected 'v1' for version string got: %s\n", f.version)
	}
	if f.param == "" {
		return nil, fmt.Errorf("'%s' function requires a parameter", f.funName)
	}
	len, err := strconv.Atoi(f.param)
	if err != nil {
		return nil, err
	}
	switch f.funName {
	case "prefix":
		return Prefix(len), nil
	case "suffix":
		return Suffix(len), nil
	case "next-to-last":
		return NextToLast(len), nil
	default:
		return nil, fmt.Errorf("expected 'prefix', 'suffix' or 'next-to-last' got: %s", f.funName)
	}
}

func NormalizeShardFunc(str string) string {
	return parseShardFunc(str).str()
}

func ShardFuncFromString(str string) (ShardFunc, error) {
	id := parseShardFunc(str)
	fun, err := id.Func()
	if err != nil {
		return nil, err
	}
	return fun, nil
}

func ReadShardFunc(dir string) (string, error) {
	fun := "auto"
	buf, err := ioutil.ReadFile(filepath.Join(dir, "SHARDING"))
	str := string(buf)
	if err == nil && len(str) != 0 {
		fun = NormalizeShardFunc(str)
	} else if err != os.ErrNotExist {
		fmt.Errorf("unable to read shard function from repo: %v", err)
	}
	return fun, nil
}

func WriteShardFunc(dir, str string) error {
	file, err := os.Create(filepath.Join(dir, "SHARDING"))
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(str)
	if err != nil {
		return err
	}
	_, err = file.WriteString("\n")
	if err != nil {
		return err
	}
	return nil
}

func Prefix(prefixLen int) ShardFunc {
	padding := strings.Repeat("_", prefixLen)
	return func(noslash string) string {
		return (noslash + padding)[:prefixLen]
	}
}

func Suffix(suffixLen int) ShardFunc {
	padding := strings.Repeat("_", suffixLen)
	return func(noslash string) string {
		str := padding + noslash
		return str[len(str)-suffixLen:]
	}
}

func NextToLast(suffixLen int) ShardFunc {
	padding := strings.Repeat("_", suffixLen+1)
	return func(noslash string) string {
		str := padding + noslash
		offset := len(str) - suffixLen - 1
		return str[offset : offset+suffixLen]
	}
}