ソースを参照

Merge pull request #7210 from fcarriedo/refactor-common-code

pkg/units: Refactor common code
Victor Vieux 11 年 前
コミット
f906f31877
3 ファイル変更120 行追加125 行削除
  1. 0 6
      pkg/units/duration_test.go
  2. 48 56
      pkg/units/size.go
  3. 72 63
      pkg/units/size_test.go

+ 0 - 6
pkg/units/duration_test.go

@@ -44,9 +44,3 @@ func TestHumanDuration(t *testing.T) {
 	assertEquals(t, "2.010959 years", HumanDuration(24*month+2*week))
 	assertEquals(t, "3.164384 years", HumanDuration(3*year+2*month))
 }
-
-func assertEquals(t *testing.T, expected, actual interface{}) {
-	if expected != actual {
-		t.Errorf("Expected '%s' but got '%s'", expected, actual)
-	}
-}

+ 48 - 56
pkg/units/size.go

@@ -7,54 +7,54 @@ import (
 	"strings"
 )
 
+// See: http://en.wikipedia.org/wiki/Binary_prefix
+const (
+	// Decimal
+	KB = 1000
+	MB = 1000 * KB
+	GB = 1000 * MB
+	TB = 1000 * GB
+	PB = 1000 * TB
+
+	// Binary
+	KiB = 1024
+	MiB = 1024 * KiB
+	GiB = 1024 * MiB
+	TiB = 1024 * GiB
+	PiB = 1024 * TiB
+)
+
+type unitMap map[string]int64
+
+var (
+	decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
+	binaryMap  = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
+)
+
+var sizeRegex *regexp.Regexp
+
+func init() {
+	sizeRegex = regexp.MustCompile("^(\\d+)([kKmMgGtTpP])?[bB]?$")
+}
+
+var unitAbbrs = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
+
 // HumanSize returns a human-readable approximation of a size
 // using SI standard (eg. "44kB", "17MB")
 func HumanSize(size int64) string {
 	i := 0
-	var sizef float64
-	sizef = float64(size)
-	units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
+	sizef := float64(size)
 	for sizef >= 1000.0 {
 		sizef = sizef / 1000.0
 		i++
 	}
-	return fmt.Sprintf("%.4g %s", sizef, units[i])
+	return fmt.Sprintf("%.4g %s", sizef, unitAbbrs[i])
 }
 
-// FromHumanSize returns an integer from a human-readable specification of a size
-// using SI standard (eg. "44kB", "17MB")
+// FromHumanSize returns an integer from a human-readable specification of a
+// size using SI standard (eg. "44kB", "17MB")
 func FromHumanSize(size string) (int64, error) {
-	re, err := regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$")
-	if err != nil {
-		return -1, fmt.Errorf("%s does not specify not a size", size)
-	}
-
-	matches := re.FindStringSubmatch(size)
-
-	if len(matches) != 3 {
-		return -1, fmt.Errorf("Invalid size: '%s'", size)
-	}
-
-	theSize, err := strconv.ParseInt(matches[1], 10, 0)
-	if err != nil {
-		return -1, err
-	}
-
-	unit := strings.ToLower(matches[2])
-
-	if unit == "k" {
-		theSize *= 1000
-	} else if unit == "m" {
-		theSize *= 1000 * 1000
-	} else if unit == "g" {
-		theSize *= 1000 * 1000 * 1000
-	} else if unit == "t" {
-		theSize *= 1000 * 1000 * 1000 * 1000
-	} else if unit == "p" {
-		theSize *= 1000 * 1000 * 1000 * 1000 * 1000
-	}
-
-	return theSize, nil
+	return parseSize(size, decimalMap)
 }
 
 // Parses a human-readable string representing an amount of RAM
@@ -62,33 +62,25 @@ func FromHumanSize(size string) (int64, error) {
 // returns the number of bytes, or -1 if the string is unparseable.
 // Units are case-insensitive, and the 'b' suffix is optional.
 func RAMInBytes(size string) (int64, error) {
-	re, err := regexp.Compile("^(\\d+)([kKmMgGtT])?[bB]?$")
-	if err != nil {
-		return -1, err
-	}
-
-	matches := re.FindStringSubmatch(size)
+	return parseSize(size, binaryMap)
+}
 
+// Parses the human-readable size string into the amount it represents
+func parseSize(sizeStr string, uMap unitMap) (int64, error) {
+	matches := sizeRegex.FindStringSubmatch(sizeStr)
 	if len(matches) != 3 {
-		return -1, fmt.Errorf("Invalid size: '%s'", size)
+		return -1, fmt.Errorf("Invalid size: '%s'", sizeStr)
 	}
 
-	memLimit, err := strconv.ParseInt(matches[1], 10, 0)
+	size, err := strconv.ParseInt(matches[1], 10, 0)
 	if err != nil {
 		return -1, err
 	}
 
-	unit := strings.ToLower(matches[2])
-
-	if unit == "k" {
-		memLimit *= 1024
-	} else if unit == "m" {
-		memLimit *= 1024 * 1024
-	} else if unit == "g" {
-		memLimit *= 1024 * 1024 * 1024
-	} else if unit == "t" {
-		memLimit *= 1024 * 1024 * 1024 * 1024
+	unitPrefix := strings.ToLower(matches[2])
+	if mul, ok := uMap[unitPrefix]; ok {
+		size *= mul
 	}
 
-	return memLimit, nil
+	return size, nil
 }

+ 72 - 63
pkg/units/size_test.go

@@ -1,6 +1,9 @@
 package units
 
 import (
+	"reflect"
+	"runtime"
+	"strings"
 	"testing"
 )
 
@@ -9,81 +12,87 @@ func TestHumanSize(t *testing.T) {
 	assertEquals(t, "1.024 kB", HumanSize(1024))
 	assertEquals(t, "1 MB", HumanSize(1000000))
 	assertEquals(t, "1.049 MB", HumanSize(1048576))
-	assertEquals(t, "2 MB", HumanSize(2*1000*1000))
-	assertEquals(t, "3.42 GB", HumanSize(3.42*1000*1000*1000))
-	assertEquals(t, "5.372 TB", HumanSize(5.372*1000*1000*1000*1000))
-	assertEquals(t, "2.22 PB", HumanSize(2.22*1000*1000*1000*1000*1000))
-	assertEquals(t, "2.22 EB", HumanSize(2.22*1000*1000*1000*1000*1000*1000))
-	assertEquals(t, "7.707 EB", HumanSize(7.707*1000*1000*1000*1000*1000*1000))
+	assertEquals(t, "2 MB", HumanSize(2*MB))
+	assertEquals(t, "3.42 GB", HumanSize(3.42*GB))
+	assertEquals(t, "5.372 TB", HumanSize(5.372*TB))
+	assertEquals(t, "2.22 PB", HumanSize(2.22*PB))
 }
 
 func TestFromHumanSize(t *testing.T) {
-	assertFromHumanSize(t, "32", false, 32)
-	assertFromHumanSize(t, "32b", false, 32)
-	assertFromHumanSize(t, "32B", false, 32)
-	assertFromHumanSize(t, "32k", false, 32*1000)
-	assertFromHumanSize(t, "32K", false, 32*1000)
-	assertFromHumanSize(t, "32kb", false, 32*1000)
-	assertFromHumanSize(t, "32Kb", false, 32*1000)
-	assertFromHumanSize(t, "32Mb", false, 32*1000*1000)
-	assertFromHumanSize(t, "32Gb", false, 32*1000*1000*1000)
-	assertFromHumanSize(t, "32Tb", false, 32*1000*1000*1000*1000)
-	assertFromHumanSize(t, "8Pb", false, 8*1000*1000*1000*1000*1000)
+	assertSuccessEquals(t, 32, FromHumanSize, "32")
+	assertSuccessEquals(t, 32, FromHumanSize, "32b")
+	assertSuccessEquals(t, 32, FromHumanSize, "32B")
+	assertSuccessEquals(t, 32*KB, FromHumanSize, "32k")
+	assertSuccessEquals(t, 32*KB, FromHumanSize, "32K")
+	assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb")
+	assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb")
+	assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb")
+	assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb")
+	assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb")
+	assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb")
 
-	assertFromHumanSize(t, "", true, -1)
-	assertFromHumanSize(t, "hello", true, -1)
-	assertFromHumanSize(t, "-32", true, -1)
-	assertFromHumanSize(t, " 32 ", true, -1)
-	assertFromHumanSize(t, "32 mb", true, -1)
-	assertFromHumanSize(t, "32m b", true, -1)
-	assertFromHumanSize(t, "32bm", true, -1)
+	assertError(t, FromHumanSize, "")
+	assertError(t, FromHumanSize, "hello")
+	assertError(t, FromHumanSize, "-32")
+	assertError(t, FromHumanSize, "32.3")
+	assertError(t, FromHumanSize, " 32 ")
+	assertError(t, FromHumanSize, "32.3Kb")
+	assertError(t, FromHumanSize, "32 mb")
+	assertError(t, FromHumanSize, "32m b")
+	assertError(t, FromHumanSize, "32bm")
 }
 
-func assertFromHumanSize(t *testing.T, size string, expectError bool, expectedBytes int64) {
-	actualBytes, err := FromHumanSize(size)
-	if (err != nil) && !expectError {
-		t.Errorf("Unexpected error parsing '%s': %s", size, err)
-	}
-	if (err == nil) && expectError {
-		t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes)
-	}
-	if actualBytes != expectedBytes {
-		t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes)
+func TestRAMInBytes(t *testing.T) {
+	assertSuccessEquals(t, 32, RAMInBytes, "32")
+	assertSuccessEquals(t, 32, RAMInBytes, "32b")
+	assertSuccessEquals(t, 32, RAMInBytes, "32B")
+	assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k")
+	assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K")
+	assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb")
+	assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb")
+	assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb")
+	assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb")
+	assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb")
+	assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb")
+	assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB")
+	assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P")
+
+	assertError(t, RAMInBytes, "")
+	assertError(t, RAMInBytes, "hello")
+	assertError(t, RAMInBytes, "-32")
+	assertError(t, RAMInBytes, "32.3")
+	assertError(t, RAMInBytes, " 32 ")
+	assertError(t, RAMInBytes, "32.3Kb")
+	assertError(t, RAMInBytes, "32 mb")
+	assertError(t, RAMInBytes, "32m b")
+	assertError(t, RAMInBytes, "32bm")
+}
+
+func assertEquals(t *testing.T, expected, actual interface{}) {
+	if expected != actual {
+		t.Errorf("Expected '%v' but got '%v'", expected, actual)
 	}
 }
 
-func TestRAMInBytes(t *testing.T) {
-	assertRAMInBytes(t, "32", false, 32)
-	assertRAMInBytes(t, "32b", false, 32)
-	assertRAMInBytes(t, "32B", false, 32)
-	assertRAMInBytes(t, "32k", false, 32*1024)
-	assertRAMInBytes(t, "32K", false, 32*1024)
-	assertRAMInBytes(t, "32kb", false, 32*1024)
-	assertRAMInBytes(t, "32Kb", false, 32*1024)
-	assertRAMInBytes(t, "32Mb", false, 32*1024*1024)
-	assertRAMInBytes(t, "32MB", false, 32*1024*1024)
-	assertRAMInBytes(t, "32Gb", false, 32*1024*1024*1024)
-	assertRAMInBytes(t, "32G", false, 32*1024*1024*1024)
-	assertRAMInBytes(t, "32Tb", false, 32*1024*1024*1024*1024)
+// func that maps to the parse function signatures as testing abstraction
+type parseFn func(string) (int64, error)
 
-	assertRAMInBytes(t, "", true, -1)
-	assertRAMInBytes(t, "hello", true, -1)
-	assertRAMInBytes(t, "-32", true, -1)
-	assertRAMInBytes(t, " 32 ", true, -1)
-	assertRAMInBytes(t, "32 mb", true, -1)
-	assertRAMInBytes(t, "32m b", true, -1)
-	assertRAMInBytes(t, "32bm", true, -1)
+// Define 'String()' for pretty-print
+func (fn parseFn) String() string {
+	fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
+	return fnName[strings.LastIndex(fnName, ".")+1:]
 }
 
-func assertRAMInBytes(t *testing.T, size string, expectError bool, expectedBytes int64) {
-	actualBytes, err := RAMInBytes(size)
-	if (err != nil) && !expectError {
-		t.Errorf("Unexpected error parsing '%s': %s", size, err)
-	}
-	if (err == nil) && expectError {
-		t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes)
+func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) {
+	res, err := fn(arg)
+	if err != nil || res != expected {
+		t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err)
 	}
-	if actualBytes != expectedBytes {
-		t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes)
+}
+
+func assertError(t *testing.T, fn parseFn, arg string) {
+	res, err := fn(arg)
+	if err == nil && res != -1 {
+		t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res)
 	}
 }