mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 06:42:16 +00:00
parent
8fa4211bbd
commit
d94e312673
@ -1,13 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.2 (April 12th, 2018)
|
||||
## 0.8.2 (April 23rd, 2018)
|
||||
|
||||
FEATURES:
|
||||
|
||||
- [pubsub] TagMap, NewTagMap
|
||||
- [merkle] SimpleProofsFromMap()
|
||||
- [common] IsASCIIText()
|
||||
- [common] PrefixEndBytes // e.g. increment or nil
|
||||
- [common] BitArray.MarshalJSON/.UnmarshalJSON
|
||||
- [common] BitArray uses 'x' not 'X' for String() and above.
|
||||
- [db] DebugDB shows better colorized output
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- [common] Fix TestParallelAbort nondeterministic failure #201/#202
|
||||
- [db] PrefixDB Iterator/ReverseIterator fixes
|
||||
- [db] DebugDB fixes
|
||||
|
||||
|
@ -3,6 +3,7 @@ package common
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
@ -249,13 +250,14 @@ func (bA *BitArray) PickRandom() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// String returns a string representation of BitArray: BA{<bit-string>},
|
||||
// where <bit-string> is a sequence of 'x' (1) and '_' (0).
|
||||
// The <bit-string> includes spaces and newlines to help people.
|
||||
// For a simple sequence of 'x' and '_' characters with no spaces or newlines,
|
||||
// see the MarshalJSON() method.
|
||||
// Example: "BA{_x_}" or "nil-BitArray" for nil.
|
||||
func (bA *BitArray) String() string {
|
||||
if bA == nil {
|
||||
return "nil-BitArray"
|
||||
}
|
||||
bA.mtx.Lock()
|
||||
defer bA.mtx.Unlock()
|
||||
return bA.stringIndented("")
|
||||
return bA.StringIndented("")
|
||||
}
|
||||
|
||||
func (bA *BitArray) StringIndented(indent string) string {
|
||||
@ -268,12 +270,11 @@ func (bA *BitArray) StringIndented(indent string) string {
|
||||
}
|
||||
|
||||
func (bA *BitArray) stringIndented(indent string) string {
|
||||
|
||||
lines := []string{}
|
||||
bits := ""
|
||||
for i := 0; i < bA.Bits; i++ {
|
||||
if bA.getIndex(i) {
|
||||
bits += "X"
|
||||
bits += "x"
|
||||
} else {
|
||||
bits += "_"
|
||||
}
|
||||
@ -282,10 +283,10 @@ func (bA *BitArray) stringIndented(indent string) string {
|
||||
bits = ""
|
||||
}
|
||||
if i%10 == 9 {
|
||||
bits += " "
|
||||
bits += indent
|
||||
}
|
||||
if i%50 == 49 {
|
||||
bits += " "
|
||||
bits += indent
|
||||
}
|
||||
}
|
||||
if len(bits) > 0 {
|
||||
@ -320,3 +321,58 @@ func (bA *BitArray) Update(o *BitArray) {
|
||||
|
||||
copy(bA.Elems, o.Elems)
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface by marshaling bit array
|
||||
// using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit.
|
||||
func (bA *BitArray) MarshalJSON() ([]byte, error) {
|
||||
if bA == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
bA.mtx.Lock()
|
||||
defer bA.mtx.Unlock()
|
||||
|
||||
bits := `"`
|
||||
for i := 0; i < bA.Bits; i++ {
|
||||
if bA.getIndex(i) {
|
||||
bits += `x`
|
||||
} else {
|
||||
bits += `_`
|
||||
}
|
||||
}
|
||||
bits += `"`
|
||||
return []byte(bits), nil
|
||||
}
|
||||
|
||||
var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`)
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom
|
||||
// JSON description.
|
||||
func (bA *BitArray) UnmarshalJSON(bz []byte) error {
|
||||
b := string(bz)
|
||||
if b == "null" {
|
||||
// This is required e.g. for encoding/json when decoding
|
||||
// into a pointer with pre-allocated BitArray.
|
||||
bA.Bits = 0
|
||||
bA.Elems = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 'b'.
|
||||
match := bitArrayJSONRegexp.FindStringSubmatch(b)
|
||||
if match == nil {
|
||||
return fmt.Errorf("BitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b)
|
||||
}
|
||||
bits := match[1]
|
||||
|
||||
// Construct new BitArray and copy over.
|
||||
numBits := len(bits)
|
||||
bA2 := NewBitArray(numBits)
|
||||
for i := 0; i < numBits; i++ {
|
||||
if bits[i] == 'x' {
|
||||
bA2.SetIndex(i, true)
|
||||
}
|
||||
}
|
||||
*bA = *bA2
|
||||
return nil
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -210,8 +212,56 @@ func TestUpdateNeverPanics(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) {
|
||||
bitList := []int{-127, -128, -1<<31}
|
||||
bitList := []int{-127, -128, -1 << 31}
|
||||
for _, bits := range bitList {
|
||||
_ = NewBitArray(bits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONMarshalUnmarshal(t *testing.T) {
|
||||
|
||||
bA1 := NewBitArray(0)
|
||||
|
||||
bA2 := NewBitArray(1)
|
||||
|
||||
bA3 := NewBitArray(1)
|
||||
bA3.SetIndex(0, true)
|
||||
|
||||
bA4 := NewBitArray(5)
|
||||
bA4.SetIndex(0, true)
|
||||
bA4.SetIndex(1, true)
|
||||
|
||||
testCases := []struct {
|
||||
bA *BitArray
|
||||
marshalledBA string
|
||||
}{
|
||||
{nil, `null`},
|
||||
{bA1, `null`},
|
||||
{bA2, `"_"`},
|
||||
{bA3, `"x"`},
|
||||
{bA4, `"xx___"`},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.bA.String(), func(t *testing.T) {
|
||||
bz, err := json.Marshal(tc.bA)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.marshalledBA, string(bz))
|
||||
|
||||
var unmarshalledBA *BitArray
|
||||
err = json.Unmarshal(bz, &unmarshalledBA)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.bA == nil {
|
||||
require.Nil(t, unmarshalledBA)
|
||||
} else {
|
||||
require.NotNil(t, unmarshalledBA)
|
||||
assert.EqualValues(t, tc.bA.Bits, unmarshalledBA.Bits)
|
||||
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) {
|
||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -57,3 +57,18 @@ func SplitAndTrim(s, sep, cutset string) []string {
|
||||
}
|
||||
return spl
|
||||
}
|
||||
|
||||
// Returns true if s is a non-empty printable non-tab ascii character.
|
||||
func IsASCIIText(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, b := range []byte(s) {
|
||||
if 32 <= b && b <= 126 {
|
||||
// good
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -49,3 +49,18 @@ func TestSplitAndTrim(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, SplitAndTrim(tc.s, tc.sep, tc.cutset), "%s", tc.s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsASCIIText(t *testing.T) {
|
||||
notASCIIText := []string{
|
||||
"", "\xC2", "\xC2\xA2", "\xFF", "\x80", "\xF0", "\n", "\t",
|
||||
}
|
||||
for _, v := range notASCIIText {
|
||||
assert.False(t, IsHex(v), "%q is not ascii-text", v)
|
||||
}
|
||||
asciiText := []string{
|
||||
" ", ".", "x", "$", "_", "abcdefg;", "-", "0x00", "0", "123",
|
||||
}
|
||||
for _, v := range asciiText {
|
||||
assert.True(t, IsASCIIText(v), "%q is ascii-text", v)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user