Sfoglia il codice sorgente

Add bytes format feature

Signed-off-by: Vishal Rana <vr@labstack.com>
Vishal Rana 9 anni fa
parent
commit
2e62be24db
4 ha cambiato i file con 165 aggiunte e 69 eliminazioni
  1. 1 1
      README.md
  2. 9 19
      bytes/README.md
  3. 78 33
      bytes/bytes.go
  4. 77 16
      bytes/bytes_test.go

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # Gommon [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/labstack/gommon) [![Build Status](http://img.shields.io/travis/labstack/gommon.svg?style=flat-square)](https://travis-ci.org/labstack/gommon) [![Coverage Status](http://img.shields.io/coveralls/labstack/gommon.svg?style=flat-square)](https://coveralls.io/r/labstack/gommon)
 
 Common packages for Go
-- [Bytes](https://github.com/labstack/gommon/tree/master/bytes) - Format bytes to string.
+- [Bytes](https://github.com/labstack/gommon/tree/master/bytes) - Format/parse bytes.
 - [Color](https://github.com/labstack/gommon/tree/master/color) - Style terminal text.
 - [Log](https://github.com/labstack/gommon/tree/master/log) - Simple logging.

+ 9 - 19
bytes/README.md

@@ -1,6 +1,7 @@
 # Bytes
 
-Format bytes to string
+- Format bytes integer to human readable bytes string.
+- Parse human readable bytes string to bytes integer.
 
 ## Installation
 
@@ -10,30 +11,19 @@ go get github.com/labstack/gommon/bytes
 
 ## [Usage](https://github.com/labstack/gommon/blob/master/bytes/bytes_test.go)
 
-```sh
-import github.com/labstack/gommon/bytes
-```
-
-### Decimal prefix
+### Format
 
 ```go
-fmt.Println(bytes.Format(1323))
+println(bytes.Format(13231323))
 ```
 
-`1.32 KB`
+`12.62MB`
 
-### Binary prefix
+### Parse
 
 ```go
-bytes.SetBinaryPrefix(true)
-fmt.Println(bytes.Format(1323))
+b, _ = Parse("2M")
+println(b)
 ```
 
-`1.29 KiB`
-
-### New instance
-
-```go
-g := New()
-fmt.Println(g.Format(13231323))
-```
+`2097152`

+ 78 - 33
bytes/bytes.go

@@ -2,60 +2,105 @@ package bytes
 
 import (
 	"fmt"
-	"math"
+	"regexp"
 	"strconv"
 )
 
-var (
-	global = New()
-)
-
 type (
 	Bytes struct {
-		iec bool
 	}
 )
 
+const (
+	B = 1 << (10 * iota)
+	KB
+	MB
+	GB
+	TB
+	PB
+	EB
+)
+
+var (
+	pattern = regexp.MustCompile(`(?i)^(-?\d+)([KMGTP]B?|B)$`)
+	global  = New()
+)
+
 // New creates a Bytes instance.
 func New() *Bytes {
 	return &Bytes{}
 }
 
-// SetBinaryPrefix sets binary prefix format.
-func (g *Bytes) SetBinaryPrefix(on bool) {
-	g.iec = on
-}
+// Format formats bytes integer to human readable string.
+// For example, 31323 bytes will return 30.59KB.
+func (*Bytes) Format(b int) string {
+	multiple := ""
+	value := float32(b)
 
-// Format formats bytes to string. For example, 1323 bytes will return 1.32 KB.
-// If binary prefix is set, it will return 1.29 KiB.
-func (g *Bytes) Format(b uint64) string {
-	unit := uint64(1000)
-	if g.iec {
-		unit = 1024
+	switch {
+	case b < KB:
+		return strconv.Itoa(b) + "B"
+	case b < MB:
+		value /= KB
+		multiple = "KB"
+	case b < MB:
+		value /= KB
+		multiple = "KB"
+	case b < GB:
+		value /= MB
+		multiple = "MB"
+	case b < TB:
+		value /= GB
+		multiple = "GB"
+	case b < PB:
+		value /= TB
+		multiple = "TB"
+	case b < EB:
+		value /= PB
+		multiple = "PB"
 	}
-	if b < unit {
-		return strconv.FormatUint(b, 10) + " B"
+
+	return fmt.Sprintf("%.02f%s", value, multiple)
+}
+
+// Parse parses human readable bytes string to bytes integer.
+// For example, 6GB (6G is also valid) will return 6442450944.
+func (*Bytes) Parse(value string) (i int, err error) {
+	parts := pattern.FindStringSubmatch(value)
+	if len(parts) < 3 {
+		return 0, fmt.Errorf("error parsing value=%s", value)
 	}
-	bb := float64(b)
-	uunit := float64(unit)
-	x := math.Floor(math.Log(bb) / math.Log(uunit))
-	pre := make([]byte, 1, 2)
-	pre[0] = "KMGTPE"[uint8(x)-1]
-	if g.iec {
-		pre = pre[:2]
-		pre[1] = 'i'
+	bytesString := parts[1]
+	multiple := parts[2]
+	bytes, err := strconv.Atoi(bytesString)
+	if err != nil {
+		return
 	}
-	// TODO: Improve performance?
-	return fmt.Sprintf("%.02f %sB", bb/math.Pow(uunit, x), pre)
 
-}
+	switch multiple {
+	case "B":
+		return bytes * B, nil
+	case "K", "KB":
+		return bytes * KB, nil
+	case "M", "MB":
+		return bytes * MB, nil
+	case "G", "GB":
+		return bytes * GB, nil
+	case "T", "TB":
+		return bytes * TB, nil
+	case "P", "PB":
+		return bytes * PB, nil
+	}
 
-// BinaryPrefix wraps global Bytes's BinaryPrefix function.
-func BinaryPrefix(on bool) {
-	global.SetBinaryPrefix(on)
+	return
 }
 
 // Format wraps global Bytes's Format function.
-func Format(b uint64) string {
+func Format(b int) string {
 	return global.Format(b)
 }
+
+// Parse wraps global Bytes's Parse function.
+func Parse(val string) (int, error) {
+	return global.Parse(val)
+}

+ 77 - 16
bytes/bytes_test.go

@@ -1,30 +1,91 @@
 package bytes
 
-import "testing"
+import (
+	"testing"
 
-func TestBytes(t *testing.T) {
+	"github.com/stretchr/testify/assert"
+)
+
+func TestBytesFormat(t *testing.T) {
 	// B
 	b := Format(515)
-	if b != "515 B" {
-		t.Errorf("expected `515 B`, got %s", b)
-	}
+	assert.Equal(t, "515B", b)
+
+	// KB
+	b = Format(31323)
+	assert.Equal(t, "30.59KB", b)
 
 	// MB
 	b = Format(13231323)
-	if b != "13.23 MB" {
-		t.Errorf("expected `13.23 MB`, got %s", b)
+	assert.Equal(t, "12.62MB", b)
+
+	// GB
+	b = Format(7323232398)
+	assert.Equal(t, "6.82GB", b)
+
+	// TB
+	b = Format(7323232398434)
+	assert.Equal(t, "6.66TB", b)
+
+	// PB
+	b = Format(9923232398434432)
+	assert.Equal(t, "8.81PB", b)
+}
+
+func TestBytesParse(t *testing.T) {
+	// B
+	b, err := Parse("515B")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 515, b)
 	}
 
-	// Exact
-	b = Format(1000 * 1000 * 1000)
-	if b != "1.00 GB" {
-		t.Errorf("expected `1.00 GB`, got %s", b)
+	// KB
+	b, err = Parse("12KB")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 12288, b)
+	}
+	b, err = Parse("12K")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 12288, b)
 	}
 
-	// Binary prefix
-	BinaryPrefix(true)
-	b = Format(1323)
-	if b != "1.29 KiB" {
-		t.Errorf("expected `1.29 KiB`, got %s", b)
+	// MB
+	b, err = Parse("2MB")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 2097152, b)
+	}
+	b, err = Parse("2M")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 2097152, b)
+	}
+
+	// GB
+	b, err = Parse("6GB")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 6442450944, b)
+	}
+	b, err = Parse("6G")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 6442450944, b)
+	}
+
+	// TB
+	b, err = Parse("5TB")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 5497558138880, b)
+	}
+	b, err = Parse("5T")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 5497558138880, b)
+	}
+
+	// PB
+	b, err = Parse("9PB")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 10133099161583616, b)
+	}
+	b, err = Parse("9P")
+	if assert.NoError(t, err) {
+		assert.Equal(t, 10133099161583616, b)
 	}
 }