shard.go 2.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
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 {
22
		return fmt.Sprintf("/repo/flatfs/shard/v1/%s/%s", f.funName, f.param)
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
	}
}

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
	}
107 108 109 110 111 112
	if str == IPFS_DEF_SHARD {
		err := ioutil.WriteFile(filepath.Join(dir, "_README"), []byte(README_IPFS_DEF_SHARD), 0444)
		if err != nil {
			return err
		}
	}
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	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]
	}
}