shard.go 3.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
package flatfs

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

12 13 14 15 16 17
var IPFS_DEF_SHARD = NextToLast(2)
var IPFS_DEF_SHARD_STR = IPFS_DEF_SHARD.String()

const PREFIX = "/repo/flatfs/shard/"

const SHARDING_FN = "SHARDING"
Kevin Atkinson's avatar
Kevin Atkinson committed
18
const README_FN = "_README"
19

20
type ShardIdV1 struct {
21
	funName string
22 23
	param   int
	fun     ShardFunc
24 25
}

26
func (f *ShardIdV1) String() string {
27
	return fmt.Sprintf("%sv1/%s/%d", PREFIX, f.funName, f.param)
28 29 30 31
}

func (f *ShardIdV1) Func() ShardFunc {
	return f.fun
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
func Prefix(prefixLen int) *ShardIdV1 {
	padding := strings.Repeat("_", prefixLen)
	return &ShardIdV1{
		funName: "prefix",
		param:   prefixLen,
		fun: func(noslash string) string {
			return (noslash + padding)[:prefixLen]
		},
	}
}

func Suffix(suffixLen int) *ShardIdV1 {
	padding := strings.Repeat("_", suffixLen)
	return &ShardIdV1{
		funName: "suffix",
		param:   suffixLen,
		fun: func(noslash string) string {
			str := padding + noslash
			return str[len(str)-suffixLen:]
		},
	}
}

func NextToLast(suffixLen int) *ShardIdV1 {
	padding := strings.Repeat("_", suffixLen+1)
	return &ShardIdV1{
		funName: "next-to-last",
		param:   suffixLen,
		fun: func(noslash string) string {
			str := padding + noslash
			offset := len(str) - suffixLen - 1
			return str[offset : offset+suffixLen]
		},
	}
}

70
func ParseShardFunc(str string) (*ShardIdV1, error) {
71
	str = strings.TrimSpace(str)
72

73 74 75
	if len(str) == 0 {
		return nil, fmt.Errorf("empty shard identifier")
	}
76 77 78 79

	trimmed := strings.TrimPrefix(str, PREFIX)
	if str == trimmed { // nothing trimmed
		return nil, fmt.Errorf("invalid or no prefix in shard identifier: %s", str)
80
	}
81
	str = trimmed
82

83
	parts := strings.Split(str, "/")
84
	if len(parts) != 3 {
85
		return nil, fmt.Errorf("invalid shard identifier: %s", str)
86
	}
87

88 89 90 91 92
	version := parts[0]
	if version != "v1" {
		return nil, fmt.Errorf("expected 'v1' for version string got: %s\n", version)
	}

93
	funName := parts[1]
94

95
	param, err := strconv.Atoi(parts[2])
96
	if err != nil {
97
		return nil, fmt.Errorf("invalid parameter: %v", err)
98
	}
99

100
	switch funName {
101
	case "prefix":
102
		return Prefix(param), nil
103
	case "suffix":
104
		return Suffix(param), nil
105
	case "next-to-last":
106
		return NextToLast(param), nil
107
	default:
108
		return nil, fmt.Errorf("expected 'prefix', 'suffix' or 'next-to-last' got: %s", funName)
109 110 111 112
	}

}

113
func ReadShardFunc(dir string) (*ShardIdV1, error) {
114
	buf, err := ioutil.ReadFile(filepath.Join(dir, SHARDING_FN))
115
	if os.IsNotExist(err) {
Kevin Atkinson's avatar
Kevin Atkinson committed
116
		return nil, ErrShardingFileMissing
117
	} else if err != nil {
118
		return nil, err
119
	}
120
	return ParseShardFunc(string(buf))
121 122
}

123
func WriteShardFunc(dir string, id *ShardIdV1) error {
124
	file, err := os.OpenFile(filepath.Join(dir, SHARDING_FN), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
125 126 127 128
	if err != nil {
		return err
	}
	defer file.Close()
129
	_, err = file.WriteString(id.String())
130 131 132 133
	if err != nil {
		return err
	}
	_, err = file.WriteString("\n")
134 135 136 137
	return err
}

func WriteReadme(dir string, id *ShardIdV1) error {
138
	if id.String() == IPFS_DEF_SHARD.String() {
139
		err := ioutil.WriteFile(filepath.Join(dir, README_FN), []byte(README_IPFS_DEF_SHARD), 0444)
140 141 142 143
		if err != nil {
			return err
		}
	}
144 145
	return nil
}