diff --git a/libnetwork/bitseq/sequence.go b/libnetwork/bitseq/sequence.go index d9c9ad5272..d71d76b3ec 100644 --- a/libnetwork/bitseq/sequence.go +++ b/libnetwork/bitseq/sequence.go @@ -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<%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 +} diff --git a/libnetwork/bitseq/sequence_test.go b/libnetwork/bitseq/sequence_test.go index 54e505f77b..26d7643343 100644 --- a/libnetwork/bitseq/sequence_test.go +++ b/libnetwork/bitseq/sequence_test.go @@ -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++ } } diff --git a/libnetwork/bitseq/store.go b/libnetwork/bitseq/store.go index b5b3f23142..553f2cdf4e 100644 --- a/libnetwork/bitseq/store.go +++ b/libnetwork/bitseq/store.go @@ -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 } diff --git a/libnetwork/idm/idm.go b/libnetwork/idm/idm.go index 67252752f0..8c15cfe4ec 100644 --- a/libnetwork/idm/idm.go +++ b/libnetwork/idm/idm.go @@ -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) } diff --git a/libnetwork/idm/idm_test.go b/libnetwork/idm/idm_test.go index 1004cb8bb5..155f3fdfd0 100644 --- a/libnetwork/idm/idm_test.go +++ b/libnetwork/idm/idm_test.go @@ -2,6 +2,8 @@ package idm import ( "testing" + + _ "github.com/docker/libnetwork/netutils" ) func TestNew(t *testing.T) { diff --git a/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go index 5e7734c03f..60c25613c0 100644 --- a/libnetwork/ipam/allocator.go +++ b/libnetwork/ipam/allocator.go @@ -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// dsDataKey = "ipam-data" // ipam-data//// ) @@ -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< 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 - } -} diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index cf67a75375..7f925f169a 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -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) } diff --git a/libnetwork/netutils/utils.go b/libnetwork/netutils/utils.go index 6ade406db4..315ca19a9d 100644 --- a/libnetwork/netutils/utils.go +++ b/libnetwork/netutils/utils.go @@ -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) -} diff --git a/libnetwork/types/types.go b/libnetwork/types/types.go index 3f394baa19..176f40d0f1 100644 --- a/libnetwork/types/types.go +++ b/libnetwork/types/types.go @@ -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 diff --git a/libnetwork/types/types_test.go b/libnetwork/types/types_test.go index efa26d7e2e..ed90915b01 100644 --- a/libnetwork/types/types_test.go +++ b/libnetwork/types/types_test.go @@ -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") + } +}