2018-03-07 02:15:44 +01:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2018-05-19 19:05:30 +02:00
|
|
|
"bytes"
|
2018-03-24 00:18:00 +01:00
|
|
|
"crypto/rand"
|
2018-03-07 02:15:44 +01:00
|
|
|
"encoding/hex"
|
2018-05-19 19:05:30 +02:00
|
|
|
"strings"
|
2018-03-08 01:49:33 +01:00
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
"strconv"
|
|
|
|
|
2018-05-24 23:49:43 +02:00
|
|
|
"github.com/lbryio/lbry.go/errors"
|
2018-03-24 00:18:00 +01:00
|
|
|
"github.com/lyoshenka/bencode"
|
2018-03-07 02:15:44 +01:00
|
|
|
)
|
|
|
|
|
2018-05-24 19:05:05 +02:00
|
|
|
// TODO: http://roaringbitmap.org/
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Bitmap is a generalized representation of an identifier or data that can be sorted, compared fast. Used by the DHT
|
|
|
|
// package as a way to handle the unique identifiers of a DHT node.
|
2018-04-05 22:05:28 +02:00
|
|
|
type Bitmap [nodeIDLength]byte
|
2018-03-07 02:15:44 +01:00
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
func (b Bitmap) rawString() string {
|
2018-04-03 19:38:01 +02:00
|
|
|
return string(b[:])
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-05-19 19:05:30 +02:00
|
|
|
// BString returns the bitmap as a string of 0s and 1s
|
|
|
|
func (b Bitmap) BString() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
for i := 0; i < nodeIDBits; i++ {
|
|
|
|
if b.Get(i) {
|
|
|
|
buf.WriteString("1")
|
|
|
|
} else {
|
|
|
|
buf.WriteString("0")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Hex returns a hexadecimal representation of the bitmap.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) Hex() string {
|
2018-04-03 19:38:01 +02:00
|
|
|
return hex.EncodeToString(b[:])
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// HexShort returns a hexadecimal representation of the first 4 bytes.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) HexShort() string {
|
2018-04-03 19:38:01 +02:00
|
|
|
return hex.EncodeToString(b[:4])
|
2018-03-29 03:05:27 +02:00
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// HexSimplified returns the hexadecimal representation with all leading 0's removed
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) HexSimplified() string {
|
|
|
|
simple := strings.TrimLeft(b.Hex(), "0")
|
|
|
|
if simple == "" {
|
|
|
|
simple = "0"
|
|
|
|
}
|
|
|
|
return simple
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Equals returns T/F if every byte in bitmap are equal.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) Equals(other Bitmap) bool {
|
2018-03-07 02:15:44 +01:00
|
|
|
for k := range b {
|
|
|
|
if b[k] != other[k] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Less returns T/F if there exists a byte pair that is not equal AND this bitmap is less than the other.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) Less(other interface{}) bool {
|
2018-03-07 02:15:44 +01:00
|
|
|
for k := range b {
|
2018-04-05 22:05:28 +02:00
|
|
|
if b[k] != other.(Bitmap)[k] {
|
|
|
|
return b[k] < other.(Bitmap)[k]
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// LessOrEqual returns true if the bitmaps are equal, otherwise it checks if this bitmap is less than the other.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) LessOrEqual(other interface{}) bool {
|
|
|
|
if bm, ok := other.(Bitmap); ok && b.Equals(bm) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return b.Less(other)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Greater returns T/F if there exists a byte pair that is not equal AND this bitmap byte is greater than the other.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Greater(other interface{}) bool {
|
|
|
|
for k := range b {
|
|
|
|
if b[k] != other.(Bitmap)[k] {
|
|
|
|
return b[k] > other.(Bitmap)[k]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// GreaterOrEqual returns true if the bitmaps are equal, otherwise it checks if this bitmap is greater than the other.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) GreaterOrEqual(other interface{}) bool {
|
|
|
|
if bm, ok := other.(Bitmap); ok && b.Equals(bm) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return b.Greater(other)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Copy returns a duplicate value for the bitmap.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Copy() Bitmap {
|
|
|
|
var ret Bitmap
|
|
|
|
copy(ret[:], b[:])
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Xor returns a diff bitmap. If they are equal, the returned bitmap will be all 0's. If 100% unique the returned
|
|
|
|
// bitmap will be all 1's.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) Xor(other Bitmap) Bitmap {
|
|
|
|
var ret Bitmap
|
2018-03-07 02:15:44 +01:00
|
|
|
for k := range b {
|
|
|
|
ret[k] = b[k] ^ other[k]
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// And returns a comparison bitmap, that for each byte returns the AND true table result
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) And(other Bitmap) Bitmap {
|
|
|
|
var ret Bitmap
|
|
|
|
for k := range b {
|
|
|
|
ret[k] = b[k] & other[k]
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Or returns a comparison bitmap, that for each byte returns the OR true table result
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Or(other Bitmap) Bitmap {
|
|
|
|
var ret Bitmap
|
|
|
|
for k := range b {
|
|
|
|
ret[k] = b[k] | other[k]
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Not returns a complimentary bitmap that is an inverse. So b.NOT.NOT = b
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Not() Bitmap {
|
|
|
|
var ret Bitmap
|
|
|
|
for k := range b {
|
|
|
|
ret[k] = ^b[k]
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b Bitmap) add(other Bitmap) (Bitmap, bool) {
|
|
|
|
var ret Bitmap
|
|
|
|
carry := false
|
|
|
|
for i := nodeIDBits - 1; i >= 0; i-- {
|
|
|
|
bBit := getBit(b[:], i)
|
|
|
|
oBit := getBit(other[:], i)
|
|
|
|
setBit(ret[:], i, bBit != oBit != carry)
|
|
|
|
carry = (bBit && oBit) || (bBit && carry) || (oBit && carry)
|
|
|
|
}
|
|
|
|
return ret, carry
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Add returns a bitmap that treats both bitmaps as numbers and adding them together. Since the size of a bitmap is
|
|
|
|
// limited, an overflow is possible when adding bitmaps.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Add(other Bitmap) Bitmap {
|
|
|
|
ret, carry := b.add(other)
|
|
|
|
if carry {
|
2018-05-30 03:38:55 +02:00
|
|
|
panic("overflow in bitmap addition. limited to " + strconv.Itoa(nodeIDBits) + " bits.")
|
2018-05-19 19:05:30 +02:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Sub returns a bitmap that treats both bitmaps as numbers and subtracts then via the inverse of the other and adding
|
|
|
|
// then together a + (-b). Negative bitmaps are not supported so other must be greater than this.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Sub(other Bitmap) Bitmap {
|
|
|
|
if b.Less(other) {
|
2018-05-30 03:38:55 +02:00
|
|
|
// ToDo: Why is this not supported? Should it say not implemented? BitMap might have a generic use case outside of dht.
|
2018-05-19 19:05:30 +02:00
|
|
|
panic("negative bitmaps not supported")
|
|
|
|
}
|
|
|
|
complement, _ := other.Not().add(BitmapFromShortHexP("1"))
|
|
|
|
ret, _ := b.add(complement)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Get returns the binary bit at the position passed.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Get(n int) bool {
|
|
|
|
return getBit(b[:], n)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Set sets the binary bit at the position passed.
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Set(n int, one bool) Bitmap {
|
|
|
|
ret := b.Copy()
|
|
|
|
setBit(ret[:], n, one)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-03-07 02:15:44 +01:00
|
|
|
// PrefixLen returns the number of leading 0 bits
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) PrefixLen() int {
|
2018-03-07 02:15:44 +01:00
|
|
|
for i := range b {
|
|
|
|
for j := 0; j < 8; j++ {
|
|
|
|
if (b[i]>>uint8(7-j))&0x1 != 0 {
|
|
|
|
return i*8 + j
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-19 19:05:30 +02:00
|
|
|
return nodeIDBits
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-05-19 19:05:30 +02:00
|
|
|
// Prefix returns a copy of b with the first n bits set to 1 (if `one` is true) or 0 (if `one` is false)
|
2018-05-13 22:02:46 +02:00
|
|
|
// https://stackoverflow.com/a/23192263/182709
|
2018-05-19 19:05:30 +02:00
|
|
|
func (b Bitmap) Prefix(n int, one bool) Bitmap {
|
|
|
|
ret := b.Copy()
|
2018-05-13 22:02:46 +02:00
|
|
|
|
|
|
|
Outer:
|
|
|
|
for i := range ret {
|
|
|
|
for j := 0; j < 8; j++ {
|
|
|
|
if i*8+j < n {
|
2018-05-19 19:05:30 +02:00
|
|
|
if one {
|
|
|
|
ret[i] |= 1 << uint(7-j)
|
|
|
|
} else {
|
|
|
|
ret[i] &= ^(1 << uint(7-j))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break Outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// Suffix returns a copy of b with the last n bits set to 1 (if `one` is true) or 0 (if `one` is false)
|
2018-05-19 19:05:30 +02:00
|
|
|
// https://stackoverflow.com/a/23192263/182709
|
|
|
|
func (b Bitmap) Suffix(n int, one bool) Bitmap {
|
|
|
|
ret := b.Copy()
|
|
|
|
|
|
|
|
Outer:
|
|
|
|
for i := len(ret) - 1; i >= 0; i-- {
|
|
|
|
for j := 7; j >= 0; j-- {
|
|
|
|
if i*8+j >= nodeIDBits-n {
|
|
|
|
if one {
|
|
|
|
ret[i] |= 1 << uint(7-j)
|
|
|
|
} else {
|
|
|
|
ret[i] &= ^(1 << uint(7-j))
|
|
|
|
}
|
2018-05-13 22:02:46 +02:00
|
|
|
} else {
|
|
|
|
break Outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// MarshalBencode implements the Marshaller(bencode)/Message interface.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b Bitmap) MarshalBencode() ([]byte, error) {
|
2018-04-04 17:43:27 +02:00
|
|
|
str := string(b[:])
|
|
|
|
return bencode.EncodeBytes(str)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// UnmarshalBencode implements the Marshaller(bencode)/Message interface.
|
2018-04-05 22:05:28 +02:00
|
|
|
func (b *Bitmap) UnmarshalBencode(encoded []byte) error {
|
2018-03-08 01:49:33 +01:00
|
|
|
var str string
|
|
|
|
err := bencode.DecodeBytes(encoded, &str)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-03 19:38:01 +02:00
|
|
|
if len(str) != nodeIDLength {
|
2018-04-04 17:43:27 +02:00
|
|
|
return errors.Err("invalid bitmap length")
|
2018-04-03 19:38:01 +02:00
|
|
|
}
|
2018-03-08 01:49:33 +01:00
|
|
|
copy(b[:], str)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// BitmapFromBytes returns a bitmap as long as the byte array is of a specific length specified in the parameters.
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromBytes(data []byte) (Bitmap, error) {
|
|
|
|
var bmp Bitmap
|
|
|
|
|
|
|
|
if len(data) != len(bmp) {
|
|
|
|
return bmp, errors.Err("invalid bitmap of length %d", len(data))
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
copy(bmp[:], data)
|
2018-04-05 22:05:28 +02:00
|
|
|
return bmp, nil
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// BitmapFromBytesP returns a bitmap as long as the byte array is of a specific length specified in the parameters
|
|
|
|
// otherwise it wil panic.
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromBytesP(data []byte) Bitmap {
|
|
|
|
bmp, err := BitmapFromBytes(data)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-03-07 02:15:44 +01:00
|
|
|
return bmp
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromString returns a bitmap by converting the string to bytes and creating from bytes as long as the byte array
|
|
|
|
// is of a specific length specified in the parameters
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromString(data string) (Bitmap, error) {
|
|
|
|
return BitmapFromBytes([]byte(data))
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromStringP returns a bitmap by converting the string to bytes and creating from bytes as long as the byte array
|
|
|
|
// is of a specific length specified in the parameters otherwise it wil panic.
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromStringP(data string) Bitmap {
|
|
|
|
bmp, err := BitmapFromString(data)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return bmp
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromHex returns a bitmap by converting the hex string to bytes and creating from bytes as long as the byte array
|
|
|
|
// is of a specific length specified in the parameters
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromHex(hexStr string) (Bitmap, error) {
|
2018-03-07 02:15:44 +01:00
|
|
|
decoded, err := hex.DecodeString(hexStr)
|
2018-04-05 22:05:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return Bitmap{}, errors.Err(err)
|
|
|
|
}
|
|
|
|
return BitmapFromBytes(decoded)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromHexP returns a bitmap by converting the hex string to bytes and creating from bytes as long as the byte array
|
|
|
|
// is of a specific length specified in the parameters otherwise it wil panic.
|
2018-04-05 22:05:28 +02:00
|
|
|
func BitmapFromHexP(hexStr string) Bitmap {
|
|
|
|
bmp, err := BitmapFromHex(hexStr)
|
2018-03-07 02:15:44 +01:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-04-05 22:05:28 +02:00
|
|
|
return bmp
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromShortHex returns a bitmap by converting the hex string to bytes, adding the leading zeros prefix to the
|
|
|
|
// hex string and creating from bytes as long as the byte array is of a specific length specified in the parameters
|
2018-05-19 19:05:30 +02:00
|
|
|
func BitmapFromShortHex(hexStr string) (Bitmap, error) {
|
|
|
|
return BitmapFromHex(strings.Repeat("0", nodeIDLength*2-len(hexStr)) + hexStr)
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
//BitmapFromShortHexP returns a bitmap by converting the hex string to bytes, adding the leading zeros prefix to the
|
|
|
|
// hex string and creating from bytes as long as the byte array is of a specific length specified in the parameters
|
|
|
|
// otherwise it wil panic.
|
2018-05-19 19:05:30 +02:00
|
|
|
func BitmapFromShortHexP(hexStr string) Bitmap {
|
|
|
|
bmp, err := BitmapFromShortHex(hexStr)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return bmp
|
|
|
|
}
|
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// RandomBitmapP generates a cryptographically random bitmap with the confines of the parameters specified.
|
2018-04-05 22:05:28 +02:00
|
|
|
func RandomBitmapP() Bitmap {
|
|
|
|
var id Bitmap
|
2018-03-24 00:18:00 +01:00
|
|
|
_, err := rand.Read(id[:])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
return id
|
|
|
|
}
|
2018-05-19 19:05:30 +02:00
|
|
|
|
2018-05-30 03:38:55 +02:00
|
|
|
// RandomBitmapInRangeP generates a cryptographically random bitmap and while it is greater than the high threshold
|
|
|
|
// bitmap will subtract the diff between high and low until it is no longer greater that the high.
|
2018-05-19 19:05:30 +02:00
|
|
|
func RandomBitmapInRangeP(low, high Bitmap) Bitmap {
|
|
|
|
diff := high.Sub(low)
|
|
|
|
r := RandomBitmapP()
|
|
|
|
for r.Greater(diff) {
|
|
|
|
r = r.Sub(diff)
|
|
|
|
}
|
2018-05-30 03:38:55 +02:00
|
|
|
//ToDo - Adding the low at this point doesn't gurantee it will be within the range. Consider bitmaps as numbers and
|
|
|
|
// I have a range of 50-100. If get to say 60, and add 50, I would be at 110. Should protect against this?
|
2018-05-19 19:05:30 +02:00
|
|
|
return r.Add(low)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBit(b []byte, n int) bool {
|
|
|
|
i := n / 8
|
|
|
|
j := n % 8
|
|
|
|
return b[i]&(1<<uint(7-j)) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func setBit(b []byte, n int, one bool) {
|
|
|
|
i := n / 8
|
|
|
|
j := n % 8
|
|
|
|
if one {
|
|
|
|
b[i] |= 1 << uint(7-j)
|
|
|
|
} else {
|
|
|
|
b[i] &= ^(1 << uint(7-j))
|
|
|
|
}
|
|
|
|
}
|