commit
d1fbe076e1
10 changed files with 873 additions and 671 deletions
|
@ -4,27 +4,33 @@
|
|||
package bitseq
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Block Sequence constants
|
||||
// block sequence constants
|
||||
// If needed we can think of making these configurable
|
||||
const (
|
||||
blockLen = 32
|
||||
blockLen = uint32(32)
|
||||
blockBytes = blockLen / 8
|
||||
blockMAX = 1<<blockLen - 1
|
||||
blockFirstBit = 1 << (blockLen - 1)
|
||||
blockMAX = uint32(1<<blockLen - 1)
|
||||
blockFirstBit = uint32(1) << (blockLen - 1)
|
||||
invalidPos = blockMAX
|
||||
)
|
||||
|
||||
var (
|
||||
errNoBitAvailable = fmt.Errorf("no bit available")
|
||||
)
|
||||
|
||||
// Handle contains the sequece representing the bitmask and its identifier
|
||||
type Handle struct {
|
||||
bits uint32
|
||||
unselected uint32
|
||||
head *Sequence
|
||||
head *sequence
|
||||
app string
|
||||
id string
|
||||
dbIndex uint64
|
||||
|
@ -41,9 +47,9 @@ func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32
|
|||
store: ds,
|
||||
bits: numElements,
|
||||
unselected: numElements,
|
||||
head: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: getNumBlocks(numElements),
|
||||
head: &sequence{
|
||||
block: 0x0,
|
||||
count: getNumBlocks(numElements),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -62,69 +68,64 @@ func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32
|
|||
return h, nil
|
||||
}
|
||||
|
||||
// Sequence reresents a recurring sequence of 32 bits long bitmasks
|
||||
type Sequence struct {
|
||||
Block uint32 // block representing 4 byte long allocation bitmask
|
||||
Count uint32 // number of consecutive blocks
|
||||
Next *Sequence // next sequence
|
||||
}
|
||||
|
||||
// NewSequence returns a sequence initialized to represent a bitmaks of numElements bits
|
||||
func NewSequence(numElements uint32) *Sequence {
|
||||
return &Sequence{Block: 0x0, Count: getNumBlocks(numElements), Next: nil}
|
||||
// sequence represents a recurring sequence of 32 bits long bitmasks
|
||||
type sequence struct {
|
||||
block uint32 // block is a symbol representing 4 byte long allocation bitmask
|
||||
count uint32 // number of consecutive blocks (symbols)
|
||||
next *sequence // next sequence
|
||||
}
|
||||
|
||||
// String returns a string representation of the block sequence starting from this block
|
||||
func (s *Sequence) String() string {
|
||||
func (s *sequence) toString() string {
|
||||
var nextBlock string
|
||||
if s.Next == nil {
|
||||
if s.next == nil {
|
||||
nextBlock = "end"
|
||||
} else {
|
||||
nextBlock = s.Next.String()
|
||||
nextBlock = s.next.toString()
|
||||
}
|
||||
return fmt.Sprintf("(0x%x, %d)->%s", s.Block, s.Count, nextBlock)
|
||||
return fmt.Sprintf("(0x%x, %d)->%s", s.block, s.count, nextBlock)
|
||||
}
|
||||
|
||||
// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
|
||||
func (s *Sequence) GetAvailableBit() (bytePos, bitPos int) {
|
||||
if s.Block == blockMAX || s.Count == 0 {
|
||||
return -1, -1
|
||||
func (s *sequence) getAvailableBit() (uint32, uint32, error) {
|
||||
if s.block == blockMAX || s.count == 0 {
|
||||
return invalidPos, invalidPos, fmt.Errorf("no available bit")
|
||||
}
|
||||
bits := 0
|
||||
bitSel := uint32(blockFirstBit)
|
||||
for bitSel > 0 && s.Block&bitSel != 0 {
|
||||
bits := uint32(0)
|
||||
bitSel := blockFirstBit
|
||||
for bitSel > 0 && s.block&bitSel != 0 {
|
||||
bitSel >>= 1
|
||||
bits++
|
||||
}
|
||||
return bits / 8, bits % 8
|
||||
return bits / 8, bits % 8, nil
|
||||
}
|
||||
|
||||
// GetCopy returns a copy of the linked list rooted at this node
|
||||
func (s *Sequence) GetCopy() *Sequence {
|
||||
n := &Sequence{Block: s.Block, Count: s.Count}
|
||||
func (s *sequence) getCopy() *sequence {
|
||||
n := &sequence{block: s.block, count: s.count}
|
||||
pn := n
|
||||
ps := s.Next
|
||||
ps := s.next
|
||||
for ps != nil {
|
||||
pn.Next = &Sequence{Block: ps.Block, Count: ps.Count}
|
||||
pn = pn.Next
|
||||
ps = ps.Next
|
||||
pn.next = &sequence{block: ps.block, count: ps.count}
|
||||
pn = pn.next
|
||||
ps = ps.next
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Equal checks if this sequence is equal to the passed one
|
||||
func (s *Sequence) Equal(o *Sequence) bool {
|
||||
func (s *sequence) equal(o *sequence) bool {
|
||||
this := s
|
||||
other := o
|
||||
for this != nil {
|
||||
if other == nil {
|
||||
return false
|
||||
}
|
||||
if this.Block != other.Block || this.Count != other.Count {
|
||||
if this.block != other.block || this.count != other.count {
|
||||
return false
|
||||
}
|
||||
this = this.Next
|
||||
other = other.Next
|
||||
this = this.next
|
||||
other = other.next
|
||||
}
|
||||
// Check if other is longer than this
|
||||
if other != nil {
|
||||
|
@ -134,23 +135,23 @@ func (s *Sequence) Equal(o *Sequence) bool {
|
|||
}
|
||||
|
||||
// ToByteArray converts the sequence into a byte array
|
||||
// TODO (aboch): manage network/host order stuff
|
||||
func (s *Sequence) ToByteArray() ([]byte, error) {
|
||||
func (s *sequence) toByteArray() ([]byte, error) {
|
||||
var bb []byte
|
||||
|
||||
p := s
|
||||
for p != nil {
|
||||
bb = append(bb, netutils.U32ToA(p.Block)...)
|
||||
bb = append(bb, netutils.U32ToA(p.Count)...)
|
||||
p = p.Next
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint32(b[0:], p.block)
|
||||
binary.BigEndian.PutUint32(b[4:], p.count)
|
||||
bb = append(bb, b...)
|
||||
p = p.next
|
||||
}
|
||||
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
// FromByteArray construct the sequence from the byte array
|
||||
// TODO (aboch): manage network/host order stuff
|
||||
func (s *Sequence) FromByteArray(data []byte) error {
|
||||
// fromByteArray construct the sequence from the byte array
|
||||
func (s *sequence) fromByteArray(data []byte) error {
|
||||
l := len(data)
|
||||
if l%8 != 0 {
|
||||
return fmt.Errorf("cannot deserialize byte sequence of lenght %d (%v)", l, data)
|
||||
|
@ -159,69 +160,141 @@ func (s *Sequence) FromByteArray(data []byte) error {
|
|||
p := s
|
||||
i := 0
|
||||
for {
|
||||
p.Block = netutils.ATo32(data[i : i+4])
|
||||
p.Count = netutils.ATo32(data[i+4 : i+8])
|
||||
p.block = binary.BigEndian.Uint32(data[i : i+4])
|
||||
p.count = binary.BigEndian.Uint32(data[i+4 : i+8])
|
||||
i += 8
|
||||
if i == l {
|
||||
break
|
||||
}
|
||||
p.Next = &Sequence{}
|
||||
p = p.Next
|
||||
p.next = &sequence{}
|
||||
p = p.next
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFirstAvailable returns the byte and bit position of the first unset bit
|
||||
func (h *Handle) GetFirstAvailable() (int, int, error) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
return GetFirstAvailable(h.head)
|
||||
func (h *Handle) getCopy() *Handle {
|
||||
return &Handle{
|
||||
bits: h.bits,
|
||||
unselected: h.unselected,
|
||||
head: h.head.getCopy(),
|
||||
app: h.app,
|
||||
id: h.id,
|
||||
dbIndex: h.dbIndex,
|
||||
dbExists: h.dbExists,
|
||||
store: h.store,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||
// If the ordinal is beyond the Sequence limits, a negative response is returned
|
||||
func (h *Handle) CheckIfAvailable(ordinal int) (int, int, error) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
return CheckIfAvailable(h.head, ordinal)
|
||||
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
|
||||
func (h *Handle) SetAny() (uint32, error) {
|
||||
if h.Unselected() == 0 {
|
||||
return invalidPos, errNoBitAvailable
|
||||
}
|
||||
return h.set(0, true, false)
|
||||
}
|
||||
|
||||
// PushReservation pushes the bit reservation inside the bitmask.
|
||||
func (h *Handle) PushReservation(bytePos, bitPos int, release bool) error {
|
||||
// Create a copy of the current handler
|
||||
h.Lock()
|
||||
nh := &Handle{
|
||||
app: h.app,
|
||||
id: h.id,
|
||||
store: h.store,
|
||||
dbIndex: h.dbIndex,
|
||||
head: h.head.GetCopy(),
|
||||
dbExists: h.dbExists,
|
||||
// Set atomically sets the corresponding bit in the sequence
|
||||
func (h *Handle) Set(ordinal uint32) error {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return err
|
||||
}
|
||||
h.Unlock()
|
||||
|
||||
nh.head = PushReservation(bytePos, bitPos, nh.head, release)
|
||||
|
||||
err := nh.writeToStore()
|
||||
if err == nil {
|
||||
// Commit went through, save locally
|
||||
h.Lock()
|
||||
h.head = nh.head
|
||||
if release {
|
||||
h.unselected++
|
||||
} else {
|
||||
h.unselected--
|
||||
}
|
||||
// Can't use SetIndex() since we're locked.
|
||||
h.dbIndex = nh.Index()
|
||||
h.dbExists = true
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
_, err := h.set(ordinal, false, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unset atomically unsets the corresponding bit in the sequence
|
||||
func (h *Handle) Unset(ordinal uint32) error {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := h.set(ordinal, false, true)
|
||||
return err
|
||||
}
|
||||
|
||||
// IsSet atomically checks if the ordinal bit is set. In case ordinal
|
||||
// is outside of the bit sequence limits, false is returned.
|
||||
func (h *Handle) IsSet(ordinal uint32) bool {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return false
|
||||
}
|
||||
h.Lock()
|
||||
_, _, err := checkIfAvailable(h.head, ordinal)
|
||||
h.Unlock()
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// set/reset the bit
|
||||
func (h *Handle) set(ordinal uint32, any bool, release bool) (uint32, error) {
|
||||
var (
|
||||
bitPos uint32
|
||||
bytePos uint32
|
||||
ret uint32
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
h.Lock()
|
||||
// Get position if available
|
||||
if release {
|
||||
bytePos, bitPos = ordinalToPos(ordinal)
|
||||
} else {
|
||||
if any {
|
||||
bytePos, bitPos, err = getFirstAvailable(h.head)
|
||||
ret = posToOrdinal(bytePos, bitPos)
|
||||
} else {
|
||||
bytePos, bitPos, err = checkIfAvailable(h.head, ordinal)
|
||||
ret = ordinal
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
h.Unlock()
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Create a private copy of h and work on it, also copy the current db index
|
||||
nh := h.getCopy()
|
||||
ci := h.dbIndex
|
||||
h.Unlock()
|
||||
|
||||
nh.head = pushReservation(bytePos, bitPos, nh.head, release)
|
||||
if release {
|
||||
nh.unselected++
|
||||
} else {
|
||||
nh.unselected--
|
||||
}
|
||||
|
||||
// Attempt to write private copy to store
|
||||
if err := nh.writeToStore(); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return ret, fmt.Errorf("internal failure while setting the bit: %v", err)
|
||||
}
|
||||
// Retry
|
||||
continue
|
||||
}
|
||||
|
||||
// Unless unexpected error, save private copy to local copy
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
if h.dbIndex != ci {
|
||||
return ret, fmt.Errorf("unexected database index change")
|
||||
}
|
||||
h.unselected = nh.unselected
|
||||
h.head = nh.head
|
||||
h.dbExists = nh.dbExists
|
||||
h.dbIndex = nh.dbIndex
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
|
||||
func (h *Handle) validateOrdinal(ordinal uint32) error {
|
||||
if ordinal > h.bits {
|
||||
return fmt.Errorf("bit does not belong to the sequence")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy removes from the datastore the data belonging to this handle
|
||||
func (h *Handle) Destroy() {
|
||||
h.deleteFromStore()
|
||||
|
@ -229,13 +302,13 @@ func (h *Handle) Destroy() {
|
|||
|
||||
// ToByteArray converts this handle's data into a byte array
|
||||
func (h *Handle) ToByteArray() ([]byte, error) {
|
||||
ba := make([]byte, 8)
|
||||
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
copy(ba[0:4], netutils.U32ToA(h.bits))
|
||||
copy(ba[4:8], netutils.U32ToA(h.unselected))
|
||||
bm, err := h.head.ToByteArray()
|
||||
ba := make([]byte, 8)
|
||||
binary.BigEndian.PutUint32(ba[0:], h.bits)
|
||||
binary.BigEndian.PutUint32(ba[4:], h.unselected)
|
||||
bm, err := h.head.toByteArray()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize head: %s", err.Error())
|
||||
}
|
||||
|
@ -250,16 +323,16 @@ func (h *Handle) FromByteArray(ba []byte) error {
|
|||
return fmt.Errorf("nil byte array")
|
||||
}
|
||||
|
||||
nh := &Sequence{}
|
||||
err := nh.FromByteArray(ba[8:])
|
||||
nh := &sequence{}
|
||||
err := nh.fromByteArray(ba[8:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialize head: %s", err.Error())
|
||||
}
|
||||
|
||||
h.Lock()
|
||||
h.head = nh
|
||||
h.bits = netutils.ATo32(ba[0:4])
|
||||
h.unselected = netutils.ATo32(ba[4:8])
|
||||
h.bits = binary.BigEndian.Uint32(ba[0:4])
|
||||
h.unselected = binary.BigEndian.Uint32(ba[4:8])
|
||||
h.Unlock()
|
||||
|
||||
return nil
|
||||
|
@ -277,64 +350,70 @@ func (h *Handle) Unselected() uint32 {
|
|||
return h.unselected
|
||||
}
|
||||
|
||||
// GetFirstAvailable looks for the first unset bit in passed mask
|
||||
func GetFirstAvailable(head *Sequence) (int, int, error) {
|
||||
byteIndex := 0
|
||||
current := head
|
||||
for current != nil {
|
||||
if current.Block != blockMAX {
|
||||
bytePos, bitPos := current.GetAvailableBit()
|
||||
return byteIndex + bytePos, bitPos, nil
|
||||
}
|
||||
byteIndex += int(current.Count * blockBytes)
|
||||
current = current.Next
|
||||
}
|
||||
return -1, -1, fmt.Errorf("no bit available")
|
||||
func (h *Handle) String() string {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, bits: %d, unselected: %d, sequence: %s",
|
||||
h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString())
|
||||
}
|
||||
|
||||
// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||
// If the ordinal is beyond the Sequence limits, a negative response is returned
|
||||
func CheckIfAvailable(head *Sequence, ordinal int) (int, int, error) {
|
||||
// getFirstAvailable looks for the first unset bit in passed mask
|
||||
func getFirstAvailable(head *sequence) (uint32, uint32, error) {
|
||||
byteIndex := uint32(0)
|
||||
current := head
|
||||
for current != nil {
|
||||
if current.block != blockMAX {
|
||||
bytePos, bitPos, err := current.getAvailableBit()
|
||||
return byteIndex + bytePos, bitPos, err
|
||||
}
|
||||
byteIndex += current.count * blockBytes
|
||||
current = current.next
|
||||
}
|
||||
return invalidPos, invalidPos, errNoBitAvailable
|
||||
}
|
||||
|
||||
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||
// If the ordinal is beyond the sequence limits, a negative response is returned
|
||||
func checkIfAvailable(head *sequence, ordinal uint32) (uint32, uint32, error) {
|
||||
bytePos := ordinal / 8
|
||||
bitPos := ordinal % 8
|
||||
|
||||
// Find the Sequence containing this byte
|
||||
// Find the sequence containing this byte
|
||||
current, _, _, inBlockBytePos := findSequence(head, bytePos)
|
||||
|
||||
if current != nil {
|
||||
// Check whether the bit corresponding to the ordinal address is unset
|
||||
bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos))
|
||||
if current.Block&bitSel == 0 {
|
||||
bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
|
||||
if current.block&bitSel == 0 {
|
||||
return bytePos, bitPos, nil
|
||||
}
|
||||
}
|
||||
|
||||
return -1, -1, fmt.Errorf("requested bit is not available")
|
||||
return invalidPos, invalidPos, fmt.Errorf("requested bit is not available")
|
||||
}
|
||||
|
||||
// Given the byte position and the sequences list head, return the pointer to the
|
||||
// sequence containing the byte (current), the pointer to the previous sequence,
|
||||
// the number of blocks preceding the block containing the byte inside the current sequence.
|
||||
// If bytePos is outside of the list, function will return (nil, nil, 0, -1)
|
||||
func findSequence(head *Sequence, bytePos int) (*Sequence, *Sequence, uint32, int) {
|
||||
// Find the Sequence containing this byte
|
||||
// If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos)
|
||||
func findSequence(head *sequence, bytePos uint32) (*sequence, *sequence, uint32, uint32) {
|
||||
// Find the sequence containing this byte
|
||||
previous := head
|
||||
current := head
|
||||
n := bytePos
|
||||
for current.Next != nil && n >= int(current.Count*blockBytes) { // Nil check for less than 32 addresses masks
|
||||
n -= int(current.Count * blockBytes)
|
||||
for current.next != nil && n >= (current.count*blockBytes) { // Nil check for less than 32 addresses masks
|
||||
n -= (current.count * blockBytes)
|
||||
previous = current
|
||||
current = current.Next
|
||||
current = current.next
|
||||
}
|
||||
|
||||
// If byte is outside of the list, let caller know
|
||||
if n >= int(current.Count*blockBytes) {
|
||||
return nil, nil, 0, -1
|
||||
if n >= (current.count * blockBytes) {
|
||||
return nil, nil, 0, invalidPos
|
||||
}
|
||||
|
||||
// Find the byte position inside the block and the number of blocks
|
||||
// preceding the block containing the byte inside this sequence
|
||||
precBlocks := uint32(n / blockBytes)
|
||||
precBlocks := n / blockBytes
|
||||
inBlockBytePos := bytePos % blockBytes
|
||||
|
||||
return current, previous, precBlocks, inBlockBytePos
|
||||
|
@ -343,33 +422,33 @@ func findSequence(head *Sequence, bytePos int) (*Sequence, *Sequence, uint32, in
|
|||
// PushReservation pushes the bit reservation inside the bitmask.
|
||||
// Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit.
|
||||
// Create a new block with the modified bit according to the operation (allocate/release).
|
||||
// Create a new Sequence containing the new Block and insert it in the proper position.
|
||||
// Create a new sequence containing the new block and insert it in the proper position.
|
||||
// Remove current sequence if empty.
|
||||
// Check if new Sequence can be merged with neighbour (previous/Next) sequences.
|
||||
// Check if new sequence can be merged with neighbour (previous/next) sequences.
|
||||
//
|
||||
//
|
||||
// Identify "current" Sequence containing block:
|
||||
// [prev seq] [current seq] [Next seq]
|
||||
// Identify "current" sequence containing block:
|
||||
// [prev seq] [current seq] [next seq]
|
||||
//
|
||||
// Based on block position, resulting list of sequences can be any of three forms:
|
||||
//
|
||||
// Block position Resulting list of sequences
|
||||
// A) Block is first in current: [prev seq] [new] [modified current seq] [Next seq]
|
||||
// B) Block is last in current: [prev seq] [modified current seq] [new] [Next seq]
|
||||
// C) Block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [Next seq]
|
||||
func PushReservation(bytePos, bitPos int, head *Sequence, release bool) *Sequence {
|
||||
// block position Resulting list of sequences
|
||||
// A) block is first in current: [prev seq] [new] [modified current seq] [next seq]
|
||||
// B) block is last in current: [prev seq] [modified current seq] [new] [next seq]
|
||||
// C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq]
|
||||
func pushReservation(bytePos, bitPos uint32, head *sequence, release bool) *sequence {
|
||||
// Store list's head
|
||||
newHead := head
|
||||
|
||||
// Find the Sequence containing this byte
|
||||
// Find the sequence containing this byte
|
||||
current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos)
|
||||
if current == nil {
|
||||
return newHead
|
||||
}
|
||||
|
||||
// Construct updated block
|
||||
bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos))
|
||||
newBlock := current.Block
|
||||
bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
|
||||
newBlock := current.block
|
||||
if release {
|
||||
newBlock &^= bitSel
|
||||
} else {
|
||||
|
@ -377,40 +456,40 @@ func PushReservation(bytePos, bitPos int, head *Sequence, release bool) *Sequenc
|
|||
}
|
||||
|
||||
// Quit if it was a redundant request
|
||||
if current.Block == newBlock {
|
||||
if current.block == newBlock {
|
||||
return newHead
|
||||
}
|
||||
|
||||
// Current Sequence inevitably looses one block, upadate Count
|
||||
current.Count--
|
||||
// Current sequence inevitably looses one block, upadate count
|
||||
current.count--
|
||||
|
||||
// Create new sequence
|
||||
newSequence := &Sequence{Block: newBlock, Count: 1}
|
||||
newSequence := &sequence{block: newBlock, count: 1}
|
||||
|
||||
// Insert the new sequence in the list based on block position
|
||||
if precBlocks == 0 { // First in sequence (A)
|
||||
newSequence.Next = current
|
||||
newSequence.next = current
|
||||
if current == head {
|
||||
newHead = newSequence
|
||||
previous = newHead
|
||||
} else {
|
||||
previous.Next = newSequence
|
||||
previous.next = newSequence
|
||||
}
|
||||
removeCurrentIfEmpty(&newHead, newSequence, current)
|
||||
mergeSequences(previous)
|
||||
} else if precBlocks == current.Count-2 { // Last in sequence (B)
|
||||
newSequence.Next = current.Next
|
||||
current.Next = newSequence
|
||||
} else if precBlocks == current.count-2 { // Last in sequence (B)
|
||||
newSequence.next = current.next
|
||||
current.next = newSequence
|
||||
mergeSequences(current)
|
||||
} else { // In between the sequence (C)
|
||||
currPre := &Sequence{Block: current.Block, Count: precBlocks, Next: newSequence}
|
||||
currPre := &sequence{block: current.block, count: precBlocks, next: newSequence}
|
||||
currPost := current
|
||||
currPost.Count -= precBlocks
|
||||
newSequence.Next = currPost
|
||||
currPost.count -= precBlocks
|
||||
newSequence.next = currPost
|
||||
if currPost == head {
|
||||
newHead = currPre
|
||||
} else {
|
||||
previous.Next = currPre
|
||||
previous.next = currPre
|
||||
}
|
||||
// No merging or empty current possible here
|
||||
}
|
||||
|
@ -419,29 +498,29 @@ func PushReservation(bytePos, bitPos int, head *Sequence, release bool) *Sequenc
|
|||
}
|
||||
|
||||
// Removes the current sequence from the list if empty, adjusting the head pointer if needed
|
||||
func removeCurrentIfEmpty(head **Sequence, previous, current *Sequence) {
|
||||
if current.Count == 0 {
|
||||
func removeCurrentIfEmpty(head **sequence, previous, current *sequence) {
|
||||
if current.count == 0 {
|
||||
if current == *head {
|
||||
*head = current.Next
|
||||
*head = current.next
|
||||
} else {
|
||||
previous.Next = current.Next
|
||||
current = current.Next
|
||||
previous.next = current.next
|
||||
current = current.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given a pointer to a Sequence, it checks if it can be merged with any following sequences
|
||||
// Given a pointer to a sequence, it checks if it can be merged with any following sequences
|
||||
// It stops when no more merging is possible.
|
||||
// TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list
|
||||
func mergeSequences(seq *Sequence) {
|
||||
func mergeSequences(seq *sequence) {
|
||||
if seq != nil {
|
||||
// Merge all what possible from seq
|
||||
for seq.Next != nil && seq.Block == seq.Next.Block {
|
||||
seq.Count += seq.Next.Count
|
||||
seq.Next = seq.Next.Next
|
||||
for seq.next != nil && seq.block == seq.next.block {
|
||||
seq.count += seq.next.count
|
||||
seq.next = seq.next.next
|
||||
}
|
||||
// Move to Next
|
||||
mergeSequences(seq.Next)
|
||||
// Move to next
|
||||
mergeSequences(seq.next)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,3 +531,11 @@ func getNumBlocks(numBits uint32) uint32 {
|
|||
}
|
||||
return numBlocks
|
||||
}
|
||||
|
||||
func ordinalToPos(ordinal uint32) (uint32, uint32) {
|
||||
return ordinal / 8, ordinal % 8
|
||||
}
|
||||
|
||||
func posToOrdinal(bytePos, bitPos uint32) uint32 {
|
||||
return bytePos*8 + bitPos
|
||||
}
|
||||
|
|
|
@ -2,136 +2,97 @@ package bitseq
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "github.com/docker/libnetwork/netutils"
|
||||
)
|
||||
|
||||
func TestSequenceGetAvailableBit(t *testing.T) {
|
||||
input := []struct {
|
||||
head *Sequence
|
||||
bytePos int
|
||||
bitPos int
|
||||
head *sequence
|
||||
bytePos uint32
|
||||
bitPos uint32
|
||||
}{
|
||||
{&Sequence{Block: 0x0, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 0, 0},
|
||||
{&Sequence{Block: 0x0, Count: 100}, 0, 0},
|
||||
{&sequence{block: 0x0, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0x0, count: 1}, 0, 0},
|
||||
{&sequence{block: 0x0, count: 100}, 0, 0},
|
||||
|
||||
{&Sequence{Block: 0x80000000, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0x80000000, Count: 1}, 0, 1},
|
||||
{&Sequence{Block: 0x80000000, Count: 100}, 0, 1},
|
||||
{&sequence{block: 0x80000000, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0x80000000, count: 1}, 0, 1},
|
||||
{&sequence{block: 0x80000000, count: 100}, 0, 1},
|
||||
|
||||
{&Sequence{Block: 0xFF000000, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFF000000, Count: 1}, 1, 0},
|
||||
{&Sequence{Block: 0xFF000000, Count: 100}, 1, 0},
|
||||
{&sequence{block: 0xFF000000, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFF000000, count: 1}, 1, 0},
|
||||
{&sequence{block: 0xFF000000, count: 100}, 1, 0},
|
||||
|
||||
{&Sequence{Block: 0xFF800000, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFF800000, Count: 1}, 1, 1},
|
||||
{&Sequence{Block: 0xFF800000, Count: 100}, 1, 1},
|
||||
{&sequence{block: 0xFF800000, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFF800000, count: 1}, 1, 1},
|
||||
{&sequence{block: 0xFF800000, count: 100}, 1, 1},
|
||||
|
||||
{&Sequence{Block: 0xFFC0FF00, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFC0FF00, Count: 1}, 1, 2},
|
||||
{&Sequence{Block: 0xFFC0FF00, Count: 100}, 1, 2},
|
||||
{&sequence{block: 0xFFC0FF00, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFC0FF00, count: 1}, 1, 2},
|
||||
{&sequence{block: 0xFFC0FF00, count: 100}, 1, 2},
|
||||
|
||||
{&Sequence{Block: 0xFFE0FF00, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFE0FF00, Count: 1}, 1, 3},
|
||||
{&Sequence{Block: 0xFFE0FF00, Count: 100}, 1, 3},
|
||||
{&sequence{block: 0xFFE0FF00, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFE0FF00, count: 1}, 1, 3},
|
||||
{&sequence{block: 0xFFE0FF00, count: 100}, 1, 3},
|
||||
|
||||
{&Sequence{Block: 0xFFFEFF00, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFEFF00, Count: 1}, 1, 7},
|
||||
{&Sequence{Block: 0xFFFEFF00, Count: 100}, 1, 7},
|
||||
{&sequence{block: 0xFFFEFF00, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFEFF00, count: 1}, 1, 7},
|
||||
{&sequence{block: 0xFFFEFF00, count: 100}, 1, 7},
|
||||
|
||||
{&Sequence{Block: 0xFFFFC0FF, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFFC0FF, Count: 1}, 2, 2},
|
||||
{&Sequence{Block: 0xFFFFC0FF, Count: 100}, 2, 2},
|
||||
{&sequence{block: 0xFFFFC0FF, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFFC0FF, count: 1}, 2, 2},
|
||||
{&sequence{block: 0xFFFFC0FF, count: 100}, 2, 2},
|
||||
|
||||
{&Sequence{Block: 0xFFFFFF00, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFFFF00, Count: 1}, 3, 0},
|
||||
{&Sequence{Block: 0xFFFFFF00, Count: 100}, 3, 0},
|
||||
{&sequence{block: 0xFFFFFF00, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFFFF00, count: 1}, 3, 0},
|
||||
{&sequence{block: 0xFFFFFF00, count: 100}, 3, 0},
|
||||
|
||||
{&Sequence{Block: 0xFFFFFFFE, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFFFFFE, Count: 1}, 3, 7},
|
||||
{&Sequence{Block: 0xFFFFFFFE, Count: 100}, 3, 7},
|
||||
{&sequence{block: 0xFFFFFFFE, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFFFFFE, count: 1}, 3, 7},
|
||||
{&sequence{block: 0xFFFFFFFE, count: 100}, 3, 7},
|
||||
|
||||
{&Sequence{Block: 0xFFFFFFFF, Count: 0}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFFFFFF, Count: 1}, -1, -1},
|
||||
{&Sequence{Block: 0xFFFFFFFF, Count: 100}, -1, -1},
|
||||
{&sequence{block: 0xFFFFFFFF, count: 0}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFFFFFF, count: 1}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xFFFFFFFF, count: 100}, invalidPos, invalidPos},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
b, bb := i.head.GetAvailableBit()
|
||||
b, bb, err := i.head.getAvailableBit()
|
||||
if b != i.bytePos || bb != i.bitPos {
|
||||
t.Fatalf("Error in Sequence.getAvailableBit() (%d).\nExp: (%d, %d)\nGot: (%d, %d),", n, i.bytePos, i.bitPos, b, bb)
|
||||
t.Fatalf("Error in sequence.getAvailableBit() (%d).\nExp: (%d, %d)\nGot: (%d, %d), err: %v", n, i.bytePos, i.bitPos, b, bb, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceEqual(t *testing.T) {
|
||||
input := []struct {
|
||||
first *Sequence
|
||||
second *Sequence
|
||||
first *sequence
|
||||
second *sequence
|
||||
areEqual bool
|
||||
}{
|
||||
{&Sequence{Block: 0x0, Count: 8, Next: nil}, &Sequence{Block: 0x0, Count: 8}, true},
|
||||
{&Sequence{Block: 0x0, Count: 0, Next: nil}, &Sequence{Block: 0x0, Count: 0}, true},
|
||||
{&Sequence{Block: 0x0, Count: 2, Next: nil}, &Sequence{Block: 0x0, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}, false},
|
||||
{&Sequence{Block: 0x0, Count: 2, Next: &Sequence{Block: 0x1, Count: 1}}, &Sequence{Block: 0x0, Count: 2}, false},
|
||||
{&sequence{block: 0x0, count: 8, next: nil}, &sequence{block: 0x0, count: 8}, true},
|
||||
{&sequence{block: 0x0, count: 0, next: nil}, &sequence{block: 0x0, count: 0}, true},
|
||||
{&sequence{block: 0x0, count: 2, next: nil}, &sequence{block: 0x0, count: 1, next: &sequence{block: 0x0, count: 1}}, false},
|
||||
{&sequence{block: 0x0, count: 2, next: &sequence{block: 0x1, count: 1}}, &sequence{block: 0x0, count: 2}, false},
|
||||
|
||||
{&Sequence{Block: 0x12345678, Count: 8, Next: nil}, &Sequence{Block: 0x12345678, Count: 8}, true},
|
||||
{&Sequence{Block: 0x12345678, Count: 8, Next: nil}, &Sequence{Block: 0x12345678, Count: 9}, false},
|
||||
{&Sequence{Block: 0x12345678, Count: 1, Next: &Sequence{Block: 0XFFFFFFFF, Count: 1}}, &Sequence{Block: 0x12345678, Count: 1}, false},
|
||||
{&Sequence{Block: 0x12345678, Count: 1}, &Sequence{Block: 0x12345678, Count: 1, Next: &Sequence{Block: 0XFFFFFFFF, Count: 1}}, false},
|
||||
{&sequence{block: 0x12345678, count: 8, next: nil}, &sequence{block: 0x12345678, count: 8}, true},
|
||||
{&sequence{block: 0x12345678, count: 8, next: nil}, &sequence{block: 0x12345678, count: 9}, false},
|
||||
{&sequence{block: 0x12345678, count: 1, next: &sequence{block: 0XFFFFFFFF, count: 1}}, &sequence{block: 0x12345678, count: 1}, false},
|
||||
{&sequence{block: 0x12345678, count: 1}, &sequence{block: 0x12345678, count: 1, next: &sequence{block: 0XFFFFFFFF, count: 1}}, false},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
if i.areEqual != i.first.Equal(i.second) {
|
||||
t.Fatalf("Error in Sequence.Equal() (%d).\nExp: %t\nGot: %t,", n, i.areEqual, !i.areEqual)
|
||||
if i.areEqual != i.first.equal(i.second) {
|
||||
t.Fatalf("Error in sequence.equal() (%d).\nExp: %t\nGot: %t,", n, i.areEqual, !i.areEqual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceCopy(t *testing.T) {
|
||||
s := &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 8,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 8,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 0,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 0,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 2,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 2,
|
||||
Next: &Sequence{
|
||||
Block: 0x1,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0x0,
|
||||
Count: 2,
|
||||
Next: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n := s.GetCopy()
|
||||
if !s.Equal(n) {
|
||||
s := getTestSequence()
|
||||
n := s.getCopy()
|
||||
if !s.equal(n) {
|
||||
t.Fatalf("copy of s failed")
|
||||
}
|
||||
if n == s {
|
||||
|
@ -141,45 +102,45 @@ func TestSequenceCopy(t *testing.T) {
|
|||
|
||||
func TestGetFirstAvailable(t *testing.T) {
|
||||
input := []struct {
|
||||
mask *Sequence
|
||||
bytePos int
|
||||
bitPos int
|
||||
mask *sequence
|
||||
bytePos uint32
|
||||
bitPos uint32
|
||||
}{
|
||||
{&Sequence{Block: 0xffffffff, Count: 2048}, -1, -1},
|
||||
{&Sequence{Block: 0x0, Count: 8}, 0, 0},
|
||||
{&Sequence{Block: 0x80000000, Count: 8}, 0, 1},
|
||||
{&Sequence{Block: 0xC0000000, Count: 8}, 0, 2},
|
||||
{&Sequence{Block: 0xE0000000, Count: 8}, 0, 3},
|
||||
{&Sequence{Block: 0xF0000000, Count: 8}, 0, 4},
|
||||
{&Sequence{Block: 0xF8000000, Count: 8}, 0, 5},
|
||||
{&Sequence{Block: 0xFC000000, Count: 8}, 0, 6},
|
||||
{&Sequence{Block: 0xFE000000, Count: 8}, 0, 7},
|
||||
{&sequence{block: 0xffffffff, count: 2048}, invalidPos, invalidPos},
|
||||
{&sequence{block: 0x0, count: 8}, 0, 0},
|
||||
{&sequence{block: 0x80000000, count: 8}, 0, 1},
|
||||
{&sequence{block: 0xC0000000, count: 8}, 0, 2},
|
||||
{&sequence{block: 0xE0000000, count: 8}, 0, 3},
|
||||
{&sequence{block: 0xF0000000, count: 8}, 0, 4},
|
||||
{&sequence{block: 0xF8000000, count: 8}, 0, 5},
|
||||
{&sequence{block: 0xFC000000, count: 8}, 0, 6},
|
||||
{&sequence{block: 0xFE000000, count: 8}, 0, 7},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x00000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 2},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 3},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 4},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 5},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 6},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 7},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 0},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 1},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 2},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 3},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 4},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 5},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 6},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 7},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 2},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 3},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 4},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 5},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 6},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 7},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 0},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 1},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 2},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 3},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 4},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 5},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 6},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 7},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 7, 7},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 7, 7},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x0, Count: 6}}, 8, 0},
|
||||
{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
bytePos, bitPos, _ := GetFirstAvailable(i.mask)
|
||||
bytePos, bitPos, _ := getFirstAvailable(i.mask)
|
||||
if bytePos != i.bytePos || bitPos != i.bitPos {
|
||||
t.Fatalf("Error in (%d) getFirstAvailable(). Expected (%d, %d). Got (%d, %d)", n, i.bytePos, i.bitPos, bytePos, bitPos)
|
||||
}
|
||||
|
@ -188,29 +149,29 @@ func TestGetFirstAvailable(t *testing.T) {
|
|||
|
||||
func TestFindSequence(t *testing.T) {
|
||||
input := []struct {
|
||||
head *Sequence
|
||||
bytePos int
|
||||
head *sequence
|
||||
bytePos uint32
|
||||
precBlocks uint32
|
||||
inBlockBytePos int
|
||||
inBlockBytePos uint32
|
||||
}{
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 0, 0, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 31, 0, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 100, 0, -1},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 0, 0, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 31, 0, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 100, 0, invalidPos},
|
||||
|
||||
{&Sequence{Block: 0x0, Count: 1}, 0, 0, 0},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 1, 0, 1},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 31, 0, -1},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 60, 0, -1},
|
||||
{&sequence{block: 0x0, count: 1}, 0, 0, 0},
|
||||
{&sequence{block: 0x0, count: 1}, 1, 0, 1},
|
||||
{&sequence{block: 0x0, count: 1}, 31, 0, invalidPos},
|
||||
{&sequence{block: 0x0, count: 1}, 60, 0, invalidPos},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 0, 0, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 3, 0, 3},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 4, 1, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 7, 1, 3},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 8, 2, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10}, 39, 9, 3},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 0, 0, 0},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 3, 0, 3},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 4, 1, 0},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 7, 1, 3},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 8, 2, 0},
|
||||
{&sequence{block: 0xffffffff, count: 10}, 39, 9, 3},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 10, Next: &Sequence{Block: 0xcc000000, Count: 10}}, 79, 9, 3},
|
||||
{&Sequence{Block: 0xffffffff, Count: 10, Next: &Sequence{Block: 0xcc000000, Count: 10}}, 80, 0, -1},
|
||||
{&sequence{block: 0xffffffff, count: 10, next: &sequence{block: 0xcc000000, count: 10}}, 79, 9, 3},
|
||||
{&sequence{block: 0xffffffff, count: 10, next: &sequence{block: 0xcc000000, count: 10}}, 80, 0, invalidPos},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
|
@ -223,205 +184,314 @@ func TestFindSequence(t *testing.T) {
|
|||
|
||||
func TestCheckIfAvailable(t *testing.T) {
|
||||
input := []struct {
|
||||
head *Sequence
|
||||
ordinal int
|
||||
bytePos int
|
||||
bitPos int
|
||||
head *sequence
|
||||
ordinal uint32
|
||||
bytePos uint32
|
||||
bitPos uint32
|
||||
}{
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 0, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 31, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 0}, 100, -1, -1},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 0, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 31, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 0}, 100, invalidPos, invalidPos},
|
||||
|
||||
{&Sequence{Block: 0x0, Count: 1}, 0, 0, 0},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 1, 0, 1},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 31, 3, 7},
|
||||
{&Sequence{Block: 0x0, Count: 1}, 60, -1, -1},
|
||||
{&sequence{block: 0x0, count: 1}, 0, 0, 0},
|
||||
{&sequence{block: 0x0, count: 1}, 1, 0, 1},
|
||||
{&sequence{block: 0x0, count: 1}, 31, 3, 7},
|
||||
{&sequence{block: 0x0, count: 1}, 60, invalidPos, invalidPos},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 31, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 32, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 33, 4, 1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1}}, 33, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1}}, 34, 4, 2},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 31, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 32, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 33, 4, 1},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1}}, 33, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1}}, 34, 4, 2},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 55, 6, 7},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 56, -1, -1},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 63, -1, -1},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 55, 6, 7},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 56, invalidPos, invalidPos},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 63, invalidPos, invalidPos},
|
||||
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 64, 8, 0},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 95, 11, 7},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 96, -1, -1},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 64, 8, 0},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 95, 11, 7},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 96, invalidPos, invalidPos},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
bytePos, bitPos, _ := CheckIfAvailable(i.head, i.ordinal)
|
||||
bytePos, bitPos, err := checkIfAvailable(i.head, i.ordinal)
|
||||
if bytePos != i.bytePos || bitPos != i.bitPos {
|
||||
t.Fatalf("Error in (%d) checkIfAvailable(ord:%d). Expected (%d, %d). Got (%d, %d)", n, i.ordinal, i.bytePos, i.bitPos, bytePos, bitPos)
|
||||
t.Fatalf("Error in (%d) checkIfAvailable(ord:%d). Expected (%d, %d). Got (%d, %d). err: %v", n, i.ordinal, i.bytePos, i.bitPos, bytePos, bitPos, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeSequences(t *testing.T) {
|
||||
input := []struct {
|
||||
original *Sequence
|
||||
merged *Sequence
|
||||
original *sequence
|
||||
merged *sequence
|
||||
}{
|
||||
{&Sequence{Block: 0xFE000000, Count: 8, Next: &Sequence{Block: 0xFE000000, Count: 2}}, &Sequence{Block: 0xFE000000, Count: 10}},
|
||||
{&Sequence{Block: 0xFFFFFFFF, Count: 8, Next: &Sequence{Block: 0xFFFFFFFF, Count: 1}}, &Sequence{Block: 0xFFFFFFFF, Count: 9}},
|
||||
{&Sequence{Block: 0xFFFFFFFF, Count: 1, Next: &Sequence{Block: 0xFFFFFFFF, Count: 8}}, &Sequence{Block: 0xFFFFFFFF, Count: 9}},
|
||||
{&sequence{block: 0xFE000000, count: 8, next: &sequence{block: 0xFE000000, count: 2}}, &sequence{block: 0xFE000000, count: 10}},
|
||||
{&sequence{block: 0xFFFFFFFF, count: 8, next: &sequence{block: 0xFFFFFFFF, count: 1}}, &sequence{block: 0xFFFFFFFF, count: 9}},
|
||||
{&sequence{block: 0xFFFFFFFF, count: 1, next: &sequence{block: 0xFFFFFFFF, count: 8}}, &sequence{block: 0xFFFFFFFF, count: 9}},
|
||||
|
||||
{&Sequence{Block: 0xFFFFFFF0, Count: 8, Next: &Sequence{Block: 0xFFFFFFF0, Count: 1}}, &Sequence{Block: 0xFFFFFFF0, Count: 9}},
|
||||
{&Sequence{Block: 0xFFFFFFF0, Count: 1, Next: &Sequence{Block: 0xFFFFFFF0, Count: 8}}, &Sequence{Block: 0xFFFFFFF0, Count: 9}},
|
||||
{&sequence{block: 0xFFFFFFF0, count: 8, next: &sequence{block: 0xFFFFFFF0, count: 1}}, &sequence{block: 0xFFFFFFF0, count: 9}},
|
||||
{&sequence{block: 0xFFFFFFF0, count: 1, next: &sequence{block: 0xFFFFFFF0, count: 8}}, &sequence{block: 0xFFFFFFF0, count: 9}},
|
||||
|
||||
{&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFE, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}}, &Sequence{Block: 0xFE, Count: 14}},
|
||||
{&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFE, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5, Next: &Sequence{Block: 0xFF, Count: 1}}}},
|
||||
&Sequence{Block: 0xFE, Count: 14, Next: &Sequence{Block: 0xFF, Count: 1}}},
|
||||
{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFE, count: 1, next: &sequence{block: 0xFE, count: 5}}}, &sequence{block: 0xFE, count: 14}},
|
||||
{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFE, count: 1, next: &sequence{block: 0xFE, count: 5, next: &sequence{block: 0xFF, count: 1}}}},
|
||||
&sequence{block: 0xFE, count: 14, next: &sequence{block: 0xFF, count: 1}}},
|
||||
|
||||
// No merge
|
||||
{&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xF8, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}},
|
||||
&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xF8, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}}},
|
||||
{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xF8, count: 1, next: &sequence{block: 0xFE, count: 5}}},
|
||||
&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xF8, count: 1, next: &sequence{block: 0xFE, count: 5}}}},
|
||||
|
||||
// No merge from head: // Merge function tries to merge from passed head. If it can't merge with Next, it does not reattempt with Next as head
|
||||
{&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFF, Count: 1, Next: &Sequence{Block: 0xFF, Count: 5}}},
|
||||
&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFF, Count: 6}}},
|
||||
// No merge from head: // Merge function tries to merge from passed head. If it can't merge with next, it does not reattempt with next as head
|
||||
{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFF, count: 1, next: &sequence{block: 0xFF, count: 5}}},
|
||||
&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFF, count: 6}}},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
mergeSequences(i.original)
|
||||
for !i.merged.Equal(i.original) {
|
||||
t.Fatalf("Error in (%d) mergeSequences().\nExp: %s\nGot: %s,", n, i.merged, i.original)
|
||||
for !i.merged.equal(i.original) {
|
||||
t.Fatalf("Error in (%d) mergeSequences().\nExp: %s\nGot: %s,", n, i.merged.toString(), i.original.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushReservation(t *testing.T) {
|
||||
input := []struct {
|
||||
mask *Sequence
|
||||
bytePos int
|
||||
bitPos int
|
||||
newMask *Sequence
|
||||
mask *sequence
|
||||
bytePos uint32
|
||||
bitPos uint32
|
||||
newMask *sequence
|
||||
}{
|
||||
// Create first Sequence and fill in 8 addresses starting from address 0
|
||||
{&Sequence{Block: 0x0, Count: 8, Next: nil}, 0, 0, &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0x80000000, Count: 8}, 0, 1, &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xC0000000, Count: 8}, 0, 2, &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xE0000000, Count: 8}, 0, 3, &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xF0000000, Count: 8}, 0, 4, &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xF8000000, Count: 8}, 0, 5, &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xFC000000, Count: 8}, 0, 6, &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 7, Next: nil}}},
|
||||
{&Sequence{Block: 0xFE000000, Count: 8}, 0, 7, &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 7, Next: nil}}},
|
||||
// Create first sequence and fill in 8 addresses starting from address 0
|
||||
{&sequence{block: 0x0, count: 8, next: nil}, 0, 0, &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 7, next: nil}}},
|
||||
{&sequence{block: 0x80000000, count: 8}, 0, 1, &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x80000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xC0000000, count: 8}, 0, 2, &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xC0000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xE0000000, count: 8}, 0, 3, &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xE0000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xF0000000, count: 8}, 0, 4, &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xF0000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xF8000000, count: 8}, 0, 5, &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xF8000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xFC000000, count: 8}, 0, 6, &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xFC000000, count: 7, next: nil}}},
|
||||
{&sequence{block: 0xFE000000, count: 8}, 0, 7, &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xFE000000, count: 7, next: nil}}},
|
||||
|
||||
{&Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7}}, 0, 1, &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7, Next: nil}}},
|
||||
{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 7}}, 0, 1, &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x0, count: 7, next: nil}}},
|
||||
|
||||
// Create second Sequence and fill in 8 addresses starting from address 32
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x00000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6, Next: nil}}}, 4, 0,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 1,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 2,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 3,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 4,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 5,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 6,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
// Create second sequence and fill in 8 addresses starting from address 32
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 6, next: nil}}}, 4, 0,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 1,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 2,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 3,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 4,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 5,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 6,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 7,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
// fill in 8 addresses starting from address 40
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 0,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 1,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 2,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 3,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 4,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 5,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 6,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFF0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 0,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 1,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 2,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 3,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 4,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 5,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 6,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 7,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFF0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
|
||||
|
||||
// Insert new Sequence
|
||||
{&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x0, Count: 6}}, 8, 0,
|
||||
&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}, 8, 1,
|
||||
&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}},
|
||||
// Insert new sequence
|
||||
{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0,
|
||||
&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5}}}},
|
||||
{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5}}}, 8, 1,
|
||||
&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x0, count: 5}}}},
|
||||
|
||||
// Merge affected with Next
|
||||
{&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 2, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 8, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffc, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 6}}}, 7, 6,
|
||||
&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 7}}},
|
||||
// Merge affected with next
|
||||
{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 2, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
|
||||
&sequence{block: 0xffffffff, count: 8, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}},
|
||||
{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffc, count: 1, next: &sequence{block: 0xfffffffe, count: 6}}}, 7, 6,
|
||||
&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 7}}},
|
||||
|
||||
// Merge affected with Next and Next.Next
|
||||
{&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 9}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1}}, 31, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 8}},
|
||||
// Merge affected with next and next.next
|
||||
{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
|
||||
&sequence{block: 0xffffffff, count: 9}},
|
||||
{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1}}, 31, 7,
|
||||
&sequence{block: 0xffffffff, count: 8}},
|
||||
|
||||
// Merge affected with previous and Next
|
||||
{&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 9}},
|
||||
// Merge affected with previous and next
|
||||
{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
|
||||
&sequence{block: 0xffffffff, count: 9}},
|
||||
|
||||
// Redundant push: No change
|
||||
{&Sequence{Block: 0xffff0000, Count: 1}, 0, 0, &Sequence{Block: 0xffff0000, Count: 1}},
|
||||
{&Sequence{Block: 0xffff0000, Count: 7}, 25, 7, &Sequence{Block: 0xffff0000, Count: 7}},
|
||||
{&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 7, 7,
|
||||
&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}},
|
||||
{&sequence{block: 0xffff0000, count: 1}, 0, 0, &sequence{block: 0xffff0000, count: 1}},
|
||||
{&sequence{block: 0xffff0000, count: 7}, 25, 7, &sequence{block: 0xffff0000, count: 7}},
|
||||
{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 7, 7,
|
||||
&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}},
|
||||
}
|
||||
|
||||
for n, i := range input {
|
||||
mask := PushReservation(i.bytePos, i.bitPos, i.mask, false)
|
||||
if !mask.Equal(i.newMask) {
|
||||
t.Fatalf("Error in (%d) pushReservation():\n%s + (%d,%d):\nExp: %s\nGot: %s,", n, i.mask, i.bytePos, i.bitPos, i.newMask, mask)
|
||||
mask := pushReservation(i.bytePos, i.bitPos, i.mask, false)
|
||||
if !mask.equal(i.newMask) {
|
||||
t.Fatalf("Error in (%d) pushReservation():\n%s + (%d,%d):\nExp: %s\nGot: %s,",
|
||||
n, i.mask.toString(), i.bytePos, i.bitPos, i.newMask.toString(), mask.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerializeDeserialize(t *testing.T) {
|
||||
s := &Sequence{
|
||||
Block: 0xffffffff,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0xFF000000,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0xffffffff,
|
||||
Count: 6,
|
||||
Next: &Sequence{
|
||||
Block: 0xffffffff,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0xFF800000,
|
||||
Count: 1,
|
||||
Next: &Sequence{
|
||||
Block: 0xffffffff,
|
||||
Count: 6,
|
||||
s := getTestSequence()
|
||||
|
||||
data, err := s.toByteArray()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := &sequence{}
|
||||
err = r.fromByteArray(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !s.equal(r) {
|
||||
t.Fatalf("Sequences are different: \n%v\n%v", s, r)
|
||||
}
|
||||
}
|
||||
|
||||
func getTestSequence() *sequence {
|
||||
// Returns a custom sequence of 1024 * 32 bits
|
||||
return &sequence{
|
||||
block: 0XFFFFFFFF,
|
||||
count: 100,
|
||||
next: &sequence{
|
||||
block: 0xFFFFFFFE,
|
||||
count: 1,
|
||||
next: &sequence{
|
||||
block: 0xFF000000,
|
||||
count: 10,
|
||||
next: &sequence{
|
||||
block: 0XFFFFFFFF,
|
||||
count: 50,
|
||||
next: &sequence{
|
||||
block: 0XFFFFFFFC,
|
||||
count: 1,
|
||||
next: &sequence{
|
||||
block: 0xFF800000,
|
||||
count: 1,
|
||||
next: &sequence{
|
||||
block: 0XFFFFFFFF,
|
||||
count: 87,
|
||||
next: &sequence{
|
||||
block: 0x0,
|
||||
count: 150,
|
||||
next: &sequence{
|
||||
block: 0XFFFFFFFF,
|
||||
count: 200,
|
||||
next: &sequence{
|
||||
block: 0x0000FFFF,
|
||||
count: 1,
|
||||
next: &sequence{
|
||||
block: 0x0,
|
||||
count: 399,
|
||||
next: &sequence{
|
||||
block: 0XFFFFFFFF,
|
||||
count: 23,
|
||||
next: &sequence{
|
||||
block: 0x1,
|
||||
count: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
data, err := s.ToByteArray()
|
||||
func TestSet(t *testing.T) {
|
||||
hnd, err := NewHandle("", nil, "", 1024*32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hnd.head = getTestSequence()
|
||||
|
||||
r := &Sequence{}
|
||||
err = r.FromByteArray(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
firstAv := uint32(32*100 + 31)
|
||||
last := uint32(1024*32 - 1)
|
||||
|
||||
if hnd.IsSet(100000) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if !s.Equal(r) {
|
||||
t.Fatalf("Sequences are different: \n%v\n%v", s, r)
|
||||
if !hnd.IsSet(0) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if hnd.IsSet(firstAv) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if !hnd.IsSet(last) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if err := hnd.Set(0); err == nil {
|
||||
t.Fatalf("Expected failure, but succeeded")
|
||||
}
|
||||
|
||||
os, err := hnd.SetAny()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure: %v", err)
|
||||
}
|
||||
if os != firstAv {
|
||||
t.Fatalf("SetAny returned unexpected ordinal. Expected %d. Got %d.", firstAv, os)
|
||||
}
|
||||
if !hnd.IsSet(firstAv) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if err := hnd.Unset(firstAv); err != nil {
|
||||
t.Fatalf("Unexpected failure: %v", err)
|
||||
}
|
||||
|
||||
if hnd.IsSet(firstAv) {
|
||||
t.Fatal("IsSet() returned wrong result")
|
||||
}
|
||||
|
||||
if err := hnd.Set(firstAv); err != nil {
|
||||
t.Fatalf("Unexpected failure: %v", err)
|
||||
}
|
||||
|
||||
if err := hnd.Set(last); err == nil {
|
||||
t.Fatalf("Expected failure, but succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUnset(t *testing.T) {
|
||||
numBits := uint32(64 * 1024)
|
||||
hnd, err := NewHandle("", nil, "", numBits)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// set and unset all one by one
|
||||
for hnd.Unselected() > 0 {
|
||||
hnd.SetAny()
|
||||
}
|
||||
i := uint32(0)
|
||||
for hnd.Unselected() < numBits {
|
||||
hnd.Unset(i)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ func (h *Handle) writeToStore() error {
|
|||
}
|
||||
err := store.PutObjectAtomic(h)
|
||||
if err == datastore.ErrKeyModified {
|
||||
return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
||||
return types.RetryErrorf("failed to perform atomic write (%v). Retry might fix the error", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Idm manages the reservation/release of numerical ids from a contiguos set
|
||||
|
@ -25,7 +24,7 @@ func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) {
|
|||
return nil, fmt.Errorf("Invalid set range: [%d, %d]", start, end)
|
||||
}
|
||||
|
||||
h, err := bitseq.NewHandle("idm", ds, id, uint32(1+end-start))
|
||||
h, err := bitseq.NewHandle("idm", ds, id, 1+end-start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize bit sequence handler: %s", err.Error())
|
||||
}
|
||||
|
@ -38,28 +37,8 @@ func (i *Idm) GetID() (uint32, error) {
|
|||
if i.handle == nil {
|
||||
return 0, fmt.Errorf("ID set is not initialized")
|
||||
}
|
||||
|
||||
for {
|
||||
bytePos, bitPos, err := i.handle.GetFirstAvailable()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("no available ids")
|
||||
}
|
||||
id := i.start + uint32(bitPos+bytePos*8)
|
||||
|
||||
// for sets which length is non multiple of 32 this check is needed
|
||||
if i.end < id {
|
||||
return 0, fmt.Errorf("no available ids")
|
||||
}
|
||||
|
||||
if err := i.handle.PushReservation(bytePos, bitPos, false); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return 0, fmt.Errorf("internal failure while reserving the id: %s", err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
ordinal, err := i.handle.SetAny()
|
||||
return i.start + ordinal, err
|
||||
}
|
||||
|
||||
// GetSpecificID tries to reserve the specified id
|
||||
|
@ -72,23 +51,10 @@ func (i *Idm) GetSpecificID(id uint32) error {
|
|||
return fmt.Errorf("Requested id does not belong to the set")
|
||||
}
|
||||
|
||||
for {
|
||||
bytePos, bitPos, err := i.handle.CheckIfAvailable(int(id - i.start))
|
||||
if err != nil {
|
||||
return fmt.Errorf("requested id is not available")
|
||||
}
|
||||
if err := i.handle.PushReservation(bytePos, bitPos, false); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return fmt.Errorf("internal failure while reserving the id: %s", err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return i.handle.Set(id - i.start)
|
||||
}
|
||||
|
||||
// Release releases the specified id
|
||||
func (i *Idm) Release(id uint32) {
|
||||
ordinal := id - i.start
|
||||
i.handle.PushReservation(int(ordinal/8), int(ordinal%8), true)
|
||||
i.handle.Unset(id - i.start)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package idm
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "github.com/docker/libnetwork/netutils"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
|
|
@ -21,7 +21,7 @@ const (
|
|||
minNetSizeV6Eff = 96
|
||||
// The size of the host subnet used internally, it's the most granular sequence addresses
|
||||
defaultInternalHostSize = 16
|
||||
// datastore keyes for ipam obkects
|
||||
// datastore keyes for ipam objects
|
||||
dsConfigKey = "ipam-config" // ipam-config/<domain>/<map of subent configs>
|
||||
dsDataKey = "ipam-data" // ipam-data/<domain>/<subnet>/<child-sudbnet>/<bitmask>
|
||||
)
|
||||
|
@ -80,8 +80,7 @@ func NewAllocator(ds datastore.DataStore) (*Allocator, error) {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to load address bitmask for configured subnet %s because of %s", v.Subnet.String(), err.Error())
|
||||
}
|
||||
a.insertAddressMasks(k, subnetList)
|
||||
return nil
|
||||
return a.insertAddressMasks(k, subnetList)
|
||||
})
|
||||
}
|
||||
a.Unlock()
|
||||
|
@ -212,19 +211,34 @@ retry:
|
|||
|
||||
// Create and insert the internal subnet(s) addresses masks into the address database. Mask data may come from the bitseq datastore.
|
||||
func (a *Allocator) insertAddressMasks(parentKey subnetKey, internalSubnetList []*net.IPNet) error {
|
||||
for _, intSub := range internalSubnetList {
|
||||
var err error
|
||||
ones, bits := intSub.Mask.Size()
|
||||
numAddresses := 1 << uint(bits-ones)
|
||||
smallKey := subnetKey{parentKey.addressSpace, parentKey.subnet, intSub.String()}
|
||||
ipVer := getAddressVersion(internalSubnetList[0].IP)
|
||||
num := len(internalSubnetList)
|
||||
ones, bits := internalSubnetList[0].Mask.Size()
|
||||
numAddresses := 1 << uint(bits-ones)
|
||||
|
||||
// Insert the new address masks. AddressMask content may come from datastore
|
||||
a.Lock()
|
||||
a.addresses[smallKey], err = bitseq.NewHandle(dsDataKey, a.store, smallKey.String(), uint32(numAddresses))
|
||||
a.Unlock()
|
||||
for i := 0; i < num; i++ {
|
||||
smallKey := subnetKey{parentKey.addressSpace, parentKey.subnet, internalSubnetList[i].String()}
|
||||
limit := uint32(numAddresses)
|
||||
|
||||
if ipVer == v4 && i == num-1 {
|
||||
// Do not let broadcast address be reserved
|
||||
limit--
|
||||
}
|
||||
|
||||
// Generate the new address masks. AddressMask content may come from datastore
|
||||
h, err := bitseq.NewHandle(dsDataKey, a.getStore(), smallKey.String(), limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ipVer == v4 && i == 0 {
|
||||
// Do not let network identifier address be reserved
|
||||
h.Set(0)
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
a.addresses[smallKey] = h
|
||||
a.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -297,7 +311,7 @@ func getInternalSubnets(inSubnet *net.IPNet, internalHostSize int) ([]*net.IPNet
|
|||
for i := 0; i < numIntSubs; i++ {
|
||||
intIP := make([]byte, len(subnet.IP))
|
||||
copy(intIP, subnet.IP) // IPv6 is too big, just work on the extra portion
|
||||
addIntToIP(intIP, i<<uint(internalHostSize))
|
||||
addIntToIP(intIP, uint32(i<<uint(internalHostSize)))
|
||||
subnetList[i] = &net.IPNet{IP: intIP, Mask: intMask}
|
||||
}
|
||||
}
|
||||
|
@ -417,37 +431,47 @@ func (a *Allocator) request(addrSpace AddressSpace, req *AddressRequest, version
|
|||
|
||||
// Release allows releasing the address from the specified address space
|
||||
func (a *Allocator) Release(addrSpace AddressSpace, address net.IP) {
|
||||
var (
|
||||
space *bitseq.Handle
|
||||
sub *net.IPNet
|
||||
)
|
||||
|
||||
if address == nil {
|
||||
log.Debugf("Requested to remove nil address from address space %s", addrSpace)
|
||||
return
|
||||
}
|
||||
|
||||
ver := getAddressVersion(address)
|
||||
if ver == v4 {
|
||||
address = address.To4()
|
||||
}
|
||||
for _, subKey := range a.getSubnetList(addrSpace, ver) {
|
||||
a.Lock()
|
||||
space := a.addresses[subKey]
|
||||
a.Unlock()
|
||||
sub := subKey.canonicalChildSubnet()
|
||||
if sub.Contains(address) {
|
||||
// Retrieve correspondent ordinal in the subnet
|
||||
ordinal := ipToInt(getHostPortionIP(address, sub))
|
||||
// Release it
|
||||
for {
|
||||
var err error
|
||||
if err = space.PushReservation(ordinal/8, ordinal%8, true); err == nil {
|
||||
break
|
||||
}
|
||||
if _, ok := err.(types.RetryError); ok {
|
||||
// bitmask must have changed, retry delete
|
||||
continue
|
||||
}
|
||||
log.Warnf("Failed to release address %s because of internal error: %s", address.String(), err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Find the subnet containing the address
|
||||
for _, subKey := range a.getSubnetList(addrSpace, ver) {
|
||||
sub = subKey.canonicalChildSubnet()
|
||||
if sub.Contains(address) {
|
||||
a.Lock()
|
||||
space = a.addresses[subKey]
|
||||
a.Unlock()
|
||||
break
|
||||
}
|
||||
}
|
||||
if space == nil {
|
||||
log.Debugf("Could not find subnet on address space %s containing %s on release", addrSpace, address.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve correspondent ordinal in the subnet
|
||||
hostPart, err := types.GetHostPartIP(address, sub.Mask)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to release address %s on address space %s because of internal error: %v", address.String(), addrSpace, err)
|
||||
return
|
||||
}
|
||||
ordinal := ipToUint32(hostPart)
|
||||
|
||||
// Release it
|
||||
if err := space.Unset(ordinal); err != nil {
|
||||
log.Warnf("Failed to release address %s on address space %s because of internal error: %v", address.String(), addrSpace, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,7 +502,7 @@ func (a *Allocator) reserveAddress(addrSpace AddressSpace, subnet *net.IPNet, pr
|
|||
bitmask, ok := a.addresses[key]
|
||||
a.Unlock()
|
||||
if !ok {
|
||||
fmt.Printf("\nDid not find a bitmask for subnet key: %s", key.String())
|
||||
log.Warnf("Did not find a bitmask for subnet key: %s", key.String())
|
||||
continue
|
||||
}
|
||||
address, err := a.getAddress(key.canonicalChildSubnet(), bitmask, prefAddress, ver)
|
||||
|
@ -509,42 +533,25 @@ func (a *Allocator) getSubnetList(addrSpace AddressSpace, ver ipVersion) []subne
|
|||
|
||||
func (a *Allocator) getAddress(subnet *net.IPNet, bitmask *bitseq.Handle, prefAddress net.IP, ver ipVersion) (net.IP, error) {
|
||||
var (
|
||||
bytePos, bitPos int
|
||||
ordinal int
|
||||
err error
|
||||
ordinal uint32
|
||||
err error
|
||||
)
|
||||
|
||||
// Look for free IP, skip .0 and .255, they will be automatically reserved
|
||||
for {
|
||||
if bitmask.Unselected() <= 0 {
|
||||
return nil, ErrNoAvailableIPs
|
||||
if bitmask.Unselected() <= 0 {
|
||||
return nil, ErrNoAvailableIPs
|
||||
}
|
||||
if prefAddress == nil {
|
||||
ordinal, err = bitmask.SetAny()
|
||||
} else {
|
||||
hostPart, e := types.GetHostPartIP(prefAddress, subnet.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
|
||||
}
|
||||
if prefAddress == nil {
|
||||
bytePos, bitPos, err = bitmask.GetFirstAvailable()
|
||||
} else {
|
||||
ordinal = ipToInt(getHostPortionIP(prefAddress, subnet))
|
||||
bytePos, bitPos, err = bitmask.CheckIfAvailable(ordinal)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, ErrNoAvailableIPs
|
||||
}
|
||||
|
||||
// Lock it
|
||||
if err = bitmask.PushReservation(bytePos, bitPos, false); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return nil, fmt.Errorf("internal failure while reserving the address: %s", err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Build IP ordinal
|
||||
ordinal = bitPos + bytePos*8
|
||||
|
||||
// For v4, let reservation of .0 and .255 happen automatically
|
||||
if ver == v4 && !isValidIP(ordinal) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
ordinal = ipToUint32(types.GetMinimalIP(hostPart))
|
||||
err = bitmask.Set(ordinal)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, ErrNoAvailableIPs
|
||||
}
|
||||
|
||||
// Convert IP ordinal for this subnet into IP address
|
||||
|
@ -566,13 +573,19 @@ func (a *Allocator) DumpDatabase() {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *Allocator) getStore() datastore.DataStore {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.store
|
||||
}
|
||||
|
||||
// It generates the ip address in the passed subnet specified by
|
||||
// the passed host address ordinal
|
||||
func generateAddress(ordinal int, network *net.IPNet) net.IP {
|
||||
func generateAddress(ordinal uint32, network *net.IPNet) net.IP {
|
||||
var address [16]byte
|
||||
|
||||
// Get network portion of IP
|
||||
if network.IP.To4() != nil {
|
||||
if getAddressVersion(network.IP) == v4 {
|
||||
copy(address[:], network.IP.To4())
|
||||
} else {
|
||||
copy(address[:], network.IP)
|
||||
|
@ -591,15 +604,9 @@ func getAddressVersion(ip net.IP) ipVersion {
|
|||
return v4
|
||||
}
|
||||
|
||||
// .0 and .255 will return false
|
||||
func isValidIP(i int) bool {
|
||||
lastByte := i & 0xff
|
||||
return lastByte != 0xff && lastByte != 0
|
||||
}
|
||||
|
||||
// Adds the ordinal IP to the current array
|
||||
// 192.168.0.0 + 53 => 192.168.53
|
||||
func addIntToIP(array []byte, ordinal int) {
|
||||
func addIntToIP(array []byte, ordinal uint32) {
|
||||
for i := len(array) - 1; i >= 0; i-- {
|
||||
array[i] |= (byte)(ordinal & 0xff)
|
||||
ordinal >>= 8
|
||||
|
@ -607,28 +614,11 @@ func addIntToIP(array []byte, ordinal int) {
|
|||
}
|
||||
|
||||
// Convert an ordinal to the respective IP address
|
||||
func ipToInt(ip []byte) int {
|
||||
value := 0
|
||||
func ipToUint32(ip []byte) uint32 {
|
||||
value := uint32(0)
|
||||
for i := 0; i < len(ip); i++ {
|
||||
j := len(ip) - 1 - i
|
||||
value += int(ip[i]) << uint(j*8)
|
||||
value += uint32(ip[i]) << uint(j*8)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Given an address and subnet, returns the host portion address
|
||||
func getHostPortionIP(address net.IP, subnet *net.IPNet) net.IP {
|
||||
hostPortion := make([]byte, len(address))
|
||||
for i := 0; i < len(subnet.Mask); i++ {
|
||||
hostPortion[i] = address[i] &^ subnet.Mask[i]
|
||||
}
|
||||
return hostPortion
|
||||
}
|
||||
|
||||
func printLine(head *bitseq.Sequence) {
|
||||
fmt.Println()
|
||||
for head != nil {
|
||||
fmt.Printf("-")
|
||||
head = head.Next
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,31 @@ package ipam
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
_ "github.com/docker/libnetwork/netutils"
|
||||
)
|
||||
|
||||
var ds datastore.DataStore
|
||||
|
||||
// enable w/ upper case
|
||||
func testMain(m *testing.M) {
|
||||
var err error
|
||||
ds, err = datastore.NewDataStore(&config.DatastoreCfg{Embedded: false, Client: config.DatastoreClientCfg{Provider: "consul", Address: "127.0.0.1:8500"}})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func getAllocator(t *testing.T, subnet *net.IPNet) *Allocator {
|
||||
a, err := NewAllocator(nil)
|
||||
a, err := NewAllocator(ds)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -19,32 +36,16 @@ func getAllocator(t *testing.T, subnet *net.IPNet) *Allocator {
|
|||
}
|
||||
|
||||
func TestInt2IP2IntConversion(t *testing.T) {
|
||||
for i := 0; i < 256*256*256; i++ {
|
||||
for i := uint32(0); i < 256*256*256; i++ {
|
||||
var array [4]byte // new array at each cycle
|
||||
addIntToIP(array[:], i)
|
||||
j := ipToInt(array[:])
|
||||
j := ipToUint32(array[:])
|
||||
if j != i {
|
||||
t.Fatalf("Failed to convert ordinal %d to IP % x and back to ordinal. Got %d", i, array, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValid(t *testing.T) {
|
||||
list := []int{0, 255, 256, 511, 512, 767, 768}
|
||||
for _, i := range list {
|
||||
if isValidIP(i) {
|
||||
t.Fatalf("Failed to detect invalid IPv4 ordinal: %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
list = []int{1, 254, 257, 258, 510, 513, 769, 770}
|
||||
for _, i := range list {
|
||||
if !isValidIP(i) {
|
||||
t.Fatalf("Marked valid ipv4 as invalid: %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressVersion(t *testing.T) {
|
||||
if v4 != getAddressVersion(net.ParseIP("172.28.30.112")) {
|
||||
t.Fatalf("Failed to detect IPv4 version")
|
||||
|
@ -272,7 +273,32 @@ func TestGetInternalSubnets(t *testing.T) {
|
|||
for _, d := range input {
|
||||
assertInternalSubnet(t, d.internalHostSize, d.parentSubnet, d.firstIntSubnet, d.lastIntSubnet)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSameAddress(t *testing.T) {
|
||||
a, err := NewAllocator(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addSpace := AddressSpace("giallo")
|
||||
_, subnet, _ := net.ParseCIDR("192.168.100.0/24")
|
||||
if err := a.AddSubnet(addSpace, &SubnetInfo{Subnet: subnet}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ip := net.ParseIP("192.168.100.250")
|
||||
req := &AddressRequest{Subnet: *subnet, Address: ip}
|
||||
|
||||
_, err = a.Request(addSpace, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = a.Request(addSpace, req)
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddress(t *testing.T) {
|
||||
|
@ -383,20 +409,19 @@ func TestRequest(t *testing.T) {
|
|||
lastIP string
|
||||
}{
|
||||
{"192.168.59.0/24", 254, "192.168.59.254"},
|
||||
{"192.168.240.0/20", 254, "192.168.240.254"},
|
||||
{"192.168.0.0/16", 254, "192.168.0.254"},
|
||||
{"10.16.0.0/16", 254, "10.16.0.254"},
|
||||
{"10.128.0.0/12", 254, "10.128.0.254"},
|
||||
{"10.0.0.0/8", 254, "10.0.0.254"},
|
||||
{"192.168.0.0/16", 256, "192.168.1.2"},
|
||||
{"10.0.0.0/8", 256, "10.0.1.2"},
|
||||
{"192.168.240.0/20", 255, "192.168.240.255"},
|
||||
{"192.168.0.0/16", 255, "192.168.0.255"},
|
||||
{"192.168.0.0/16", 256, "192.168.1.0"},
|
||||
{"10.16.0.0/16", 255, "10.16.0.255"},
|
||||
{"10.128.0.0/12", 255, "10.128.0.255"},
|
||||
{"10.0.0.0/8", 256, "10.0.1.0"},
|
||||
|
||||
{"192.168.128.0/18", 4 * 254, "192.168.131.254"},
|
||||
{"192.168.240.0/20", 16 * 254, "192.168.255.254"},
|
||||
{"192.168.128.0/18", 4*256 - 1, "192.168.131.255"},
|
||||
{"192.168.240.0/20", 16*256 - 2, "192.168.255.254"},
|
||||
|
||||
{"192.168.0.0/16", 256 * 254, "192.168.255.254"},
|
||||
{"10.0.0.0/8", 2 * 254, "10.0.1.254"},
|
||||
{"10.0.0.0/8", 5 * 254, "10.0.4.254"},
|
||||
{"192.168.0.0/16", 256*256 - 2, "192.168.255.254"},
|
||||
{"10.0.0.0/8", 2 * 256, "10.0.2.0"},
|
||||
{"10.0.0.0/8", 5 * 256, "10.0.5.0"},
|
||||
//{"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"},
|
||||
}
|
||||
|
||||
|
@ -473,7 +498,7 @@ func assertInternalSubnet(t *testing.T, hostSize int, bigSubnet, firstSmall, las
|
|||
list, _ := getInternalSubnets(subnet, hostSize)
|
||||
count := 1
|
||||
ones, bits := subnet.Mask.Size()
|
||||
diff := bits - ones - hostSize
|
||||
diff := bits - ones - int(hostSize)
|
||||
if diff > 0 {
|
||||
count <<= uint(diff)
|
||||
}
|
||||
|
|
|
@ -180,53 +180,3 @@ func GenerateIfaceName(prefix string, len int) (string, error) {
|
|||
}
|
||||
return "", types.InternalErrorf("could not generate interface name")
|
||||
}
|
||||
|
||||
func byteArrayToInt(array []byte, numBytes int) uint64 {
|
||||
if numBytes <= 0 || numBytes > 8 {
|
||||
panic("Invalid argument")
|
||||
}
|
||||
num := 0
|
||||
for i := 0; i <= len(array)-1; i++ {
|
||||
num += int(array[len(array)-1-i]) << uint(i*8)
|
||||
}
|
||||
return uint64(num)
|
||||
}
|
||||
|
||||
// ATo64 converts a byte array into a uint32
|
||||
func ATo64(array []byte) uint64 {
|
||||
return byteArrayToInt(array, 8)
|
||||
}
|
||||
|
||||
// ATo32 converts a byte array into a uint32
|
||||
func ATo32(array []byte) uint32 {
|
||||
return uint32(byteArrayToInt(array, 4))
|
||||
}
|
||||
|
||||
// ATo16 converts a byte array into a uint16
|
||||
func ATo16(array []byte) uint16 {
|
||||
return uint16(byteArrayToInt(array, 2))
|
||||
}
|
||||
|
||||
func intToByteArray(val uint64, numBytes int) []byte {
|
||||
array := make([]byte, numBytes)
|
||||
for i := numBytes - 1; i >= 0; i-- {
|
||||
array[i] = byte(val & 0xff)
|
||||
val = val >> 8
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// U64ToA converts a uint64 to a byte array
|
||||
func U64ToA(val uint64) []byte {
|
||||
return intToByteArray(uint64(val), 8)
|
||||
}
|
||||
|
||||
// U32ToA converts a uint64 to a byte array
|
||||
func U32ToA(val uint32) []byte {
|
||||
return intToByteArray(uint64(val), 4)
|
||||
}
|
||||
|
||||
// U16ToA converts a uint64 to a byte array
|
||||
func U16ToA(val uint16) []byte {
|
||||
return intToByteArray(uint64(val), 2)
|
||||
}
|
||||
|
|
|
@ -197,6 +197,59 @@ func CompareIPNet(a, b *net.IPNet) bool {
|
|||
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
|
||||
}
|
||||
|
||||
// GetMinimalIP returns the address in its shortest form
|
||||
func GetMinimalIP(ip net.IP) net.IP {
|
||||
if ip != nil && ip.To4() != nil {
|
||||
return ip.To4()
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation
|
||||
func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
|
||||
if nw == nil {
|
||||
return nil
|
||||
}
|
||||
if len(nw.IP) == 16 && nw.IP.To4() != nil {
|
||||
m := nw.Mask
|
||||
if len(m) == 16 {
|
||||
m = m[12:16]
|
||||
}
|
||||
return &net.IPNet{IP: nw.IP.To4(), Mask: m}
|
||||
}
|
||||
return nw
|
||||
}
|
||||
|
||||
var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
|
||||
// GetHostPartIP returns the host portion of the ip address identified by the mask.
|
||||
// IP address representation is not modified. If address and mask are not compatible
|
||||
// an error is returned.
|
||||
func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||
// Find the effective starting of address and mask
|
||||
is := 0
|
||||
ms := 0
|
||||
if len(ip) == net.IPv6len && ip.To4() != nil {
|
||||
is = 12
|
||||
}
|
||||
if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) {
|
||||
ms = 12
|
||||
}
|
||||
|
||||
// Check if address and mask are semantically compatible
|
||||
if len(ip[is:]) != len(mask[ms:]) {
|
||||
return nil, fmt.Errorf("cannot compute host portion ip address as ip and mask are not compatible: (%#v, %#v)", ip, mask)
|
||||
}
|
||||
|
||||
// Compute host portion
|
||||
out := GetIPCopy(ip)
|
||||
for i := 0; i < len(mask[ms:]); i++ {
|
||||
out[is+i] &= ^mask[ms+i]
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
const (
|
||||
// NEXTHOP indicates a StaticRoute with an IP next hop.
|
||||
NEXTHOP = iota
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -109,3 +110,61 @@ func TestErrorConstructors(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilGetHostPortionIP(t *testing.T) {
|
||||
input := []struct {
|
||||
ip net.IP
|
||||
mask net.IPMask
|
||||
host net.IP
|
||||
err error
|
||||
}{
|
||||
{ // ip in v4Inv6 representation, mask in v4 representation
|
||||
ip: net.IPv4(172, 28, 30, 1),
|
||||
mask: []byte{0xff, 0xff, 0xff, 0},
|
||||
host: net.IPv4(0, 0, 0, 1),
|
||||
},
|
||||
{ // ip and mask in v4Inv6 representation
|
||||
ip: net.IPv4(172, 28, 30, 2),
|
||||
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0},
|
||||
host: net.IPv4(0, 0, 0, 2),
|
||||
},
|
||||
{ // ip in v4 representation, mask in v4Inv6 representation
|
||||
ip: net.IPv4(172, 28, 30, 3)[12:],
|
||||
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0},
|
||||
host: net.IPv4(0, 0, 0, 3)[12:],
|
||||
},
|
||||
{ // ip and mask in v4 representation
|
||||
ip: net.IPv4(172, 28, 30, 4)[12:],
|
||||
mask: []byte{0xff, 0xff, 0xff, 0},
|
||||
host: net.IPv4(0, 0, 0, 4)[12:],
|
||||
},
|
||||
{ // ip and mask as v6
|
||||
ip: net.ParseIP("2005:2004:2002:2001:FFFF:ABCD:EEAB:00CD"),
|
||||
mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0},
|
||||
host: net.ParseIP("0::AB:00CD"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, i := range input {
|
||||
h, err := GetHostPartIP(i.ip, i.mask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !i.host.Equal(h) {
|
||||
t.Fatalf("Failed to return expected host ip. Expected: %s. Got: %s", i.host, h)
|
||||
}
|
||||
}
|
||||
|
||||
// ip as v6 and mask as v4 are not compatible
|
||||
if _, err := GetHostPartIP(net.ParseIP("2005:2004:2002:2001:FFFF:ABCD:EEAB:00CD"), []byte{0xff, 0xff, 0xff, 0}); err == nil {
|
||||
t.Fatalf("Unexpected success")
|
||||
}
|
||||
// ip as v4 and non conventional mask
|
||||
if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0}); err == nil {
|
||||
t.Fatalf("Unexpected success")
|
||||
}
|
||||
// ip as v4 and non conventional mask
|
||||
if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0}); err == nil {
|
||||
t.Fatalf("Unexpected success")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue