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
|
# Changelog
|
||||||
|
|
||||||
## 0.8.2 (April 12th, 2018)
|
## 0.8.2 (April 23rd, 2018)
|
||||||
|
|
||||||
FEATURES:
|
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
|
- [db] DebugDB shows better colorized output
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
||||||
|
- [common] Fix TestParallelAbort nondeterministic failure #201/#202
|
||||||
- [db] PrefixDB Iterator/ReverseIterator fixes
|
- [db] PrefixDB Iterator/ReverseIterator fixes
|
||||||
- [db] DebugDB fixes
|
- [db] DebugDB fixes
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package common
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@ -249,13 +250,14 @@ func (bA *BitArray) PickRandom() (int, bool) {
|
|||||||
return 0, false
|
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 {
|
func (bA *BitArray) String() string {
|
||||||
if bA == nil {
|
return bA.StringIndented("")
|
||||||
return "nil-BitArray"
|
|
||||||
}
|
|
||||||
bA.mtx.Lock()
|
|
||||||
defer bA.mtx.Unlock()
|
|
||||||
return bA.stringIndented("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bA *BitArray) StringIndented(indent string) string {
|
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 {
|
func (bA *BitArray) stringIndented(indent string) string {
|
||||||
|
|
||||||
lines := []string{}
|
lines := []string{}
|
||||||
bits := ""
|
bits := ""
|
||||||
for i := 0; i < bA.Bits; i++ {
|
for i := 0; i < bA.Bits; i++ {
|
||||||
if bA.getIndex(i) {
|
if bA.getIndex(i) {
|
||||||
bits += "X"
|
bits += "x"
|
||||||
} else {
|
} else {
|
||||||
bits += "_"
|
bits += "_"
|
||||||
}
|
}
|
||||||
@ -282,10 +283,10 @@ func (bA *BitArray) stringIndented(indent string) string {
|
|||||||
bits = ""
|
bits = ""
|
||||||
}
|
}
|
||||||
if i%10 == 9 {
|
if i%10 == 9 {
|
||||||
bits += " "
|
bits += indent
|
||||||
}
|
}
|
||||||
if i%50 == 49 {
|
if i%50 == 49 {
|
||||||
bits += " "
|
bits += indent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(bits) > 0 {
|
if len(bits) > 0 {
|
||||||
@ -320,3 +321,58 @@ func (bA *BitArray) Update(o *BitArray) {
|
|||||||
|
|
||||||
copy(bA.Elems, o.Elems)
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -210,8 +212,56 @@ func TestUpdateNeverPanics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) {
|
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) {
|
||||||
bitList := []int{-127, -128, -1<<31}
|
bitList := []int{-127, -128, -1 << 31}
|
||||||
for _, bits := range bitList {
|
for _, bits := range bitList {
|
||||||
_ = NewBitArray(bits)
|
_ = 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
|
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)
|
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