From 5034c9bb1154ea166be99569496df53e8fd1c62e Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Fri, 12 Jun 2015 12:03:33 -0700 Subject: [PATCH] Add bitseq package - Initial version - It allows handling reservation/release of a finite set of resources through large bitmask. - It represents the bitmask as a list of equal consecutive 32 bits long bitmask symbols. It basically operates on a run-length encoding of the bitmask without encode/decode processing. Signed-off-by: Alessandro Boch --- libnetwork/bitseq/sequence.go | 261 ++++++++++++++++++++++ libnetwork/bitseq/sequence_test.go | 333 +++++++++++++++++++++++++++++ 2 files changed, 594 insertions(+) create mode 100644 libnetwork/bitseq/sequence.go create mode 100644 libnetwork/bitseq/sequence_test.go diff --git a/libnetwork/bitseq/sequence.go b/libnetwork/bitseq/sequence.go new file mode 100644 index 0000000000..09f14ff9e3 --- /dev/null +++ b/libnetwork/bitseq/sequence.go @@ -0,0 +1,261 @@ +// Package bitseq provides a structure and utilities for representing long bitmask +// as sequence of run-lenght encoded blocks. It operates direclty on the encoded +// representation, it does not decode/encode. +package bitseq + +import ( + "fmt" +) + +// Block Sequence constants +// If needed we can think of making these configurable +const ( + blockLen = 32 + blockBytes = blockLen / 8 + blockMAX = 1<%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 + } + bits := 0 + bitSel := uint32(blockFirstBit) + for bitSel > 0 && s.Block&bitSel != 0 { + bitSel >>= 1 + bits++ + } + return bits / 8, bits % 8 +} + +// Equal checks if this sequence is equal to the passed one +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 { + return false + } + this = this.Next + other = other.Next + } + // Check if other is longer than this + if other != nil { + return false + } + return true +} + +// GetFirstAvailable looks for the first unset bit in passed mask +func GetFirstAvailable(head *Sequence) (int, int) { + byteIndex := 0 + current := head + for current != nil { + if current.Block != blockMAX { + bytePos, bitPos := current.GetAvailableBit() + return byteIndex + bytePos, bitPos + } + byteIndex += int(current.Count * blockBytes) + current = current.Next + } + return -1, -1 +} + +// 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) { + bytePos := ordinal / 8 + bitPos := ordinal % 8 + + // 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 { + return bytePos, bitPos + } + } + + return -1, -1 +} + +// 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 + 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) + previous = current + current = current.Next + } + + // If byte is outside of the list, let caller know + if n >= int(current.Count*blockBytes) { + return nil, nil, 0, -1 + } + + // 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) + inBlockBytePos := bytePos % blockBytes + + return current, previous, precBlocks, inBlockBytePos +} + +// 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. +// Remove current sequence if empty. +// Check if new Sequence can be merged with neighbour (previous/Next) sequences. +// +// +// 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 { + // Store list's head + newHead := head + + // 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 + if release { + newBlock &^= bitSel + } else { + newBlock |= bitSel + } + + // Quit if it was a redundant request + if current.Block == newBlock { + return newHead + } + + // Current Sequence inevitably looses one block, upadate Count + current.Count-- + + // Create new sequence + 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 + if current == head { + newHead = newSequence + previous = newHead + } else { + 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 + mergeSequences(current) + } else { // In between the sequence (C) + currPre := &Sequence{Block: current.Block, Count: precBlocks, Next: newSequence} + currPost := current + currPost.Count -= precBlocks + newSequence.Next = currPost + if currPost == head { + newHead = currPre + } else { + previous.Next = currPre + } + // No merging or empty current possible here + } + + return newHead +} + +// 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 { + if current == *head { + *head = current.Next + } else { + previous.Next = current.Next + current = current.Next + } + } +} + +// 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) { + 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 + } + // Move to Next + mergeSequences(seq.Next) + } +} + +// Serialize converts the sequence into a byte array +func Serialize(head *Sequence) ([]byte, error) { + return nil, nil +} + +// Deserialize decodes the byte array into a sequence +func Deserialize(data []byte) (*Sequence, error) { + return nil, nil +} + +func getNumBlocks(numBits uint32) uint32 { + numBlocks := numBits / blockLen + if numBits%blockLen != 0 { + numBlocks++ + } + return numBlocks +} diff --git a/libnetwork/bitseq/sequence_test.go b/libnetwork/bitseq/sequence_test.go new file mode 100644 index 0000000000..d23d4ac389 --- /dev/null +++ b/libnetwork/bitseq/sequence_test.go @@ -0,0 +1,333 @@ +package bitseq + +import ( + "testing" +) + +func TestSequenceGetAvailableBit(t *testing.T) { + input := []struct { + head *Sequence + bytePos int + bitPos int + }{ + {&Sequence{Block: 0x0, Count: 0}, -1, -1}, + {&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: 0xFF000000, Count: 0}, -1, -1}, + {&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: 0xFFC0FF00, Count: 0}, -1, -1}, + {&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: 0xFFFEFF00, Count: 0}, -1, -1}, + {&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: 0xFFFFFF00, Count: 0}, -1, -1}, + {&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: 0xFFFFFFFF, Count: 0}, -1, -1}, + {&Sequence{Block: 0xFFFFFFFF, Count: 1}, -1, -1}, + {&Sequence{Block: 0xFFFFFFFF, Count: 100}, -1, -1}, + } + + for n, i := range input { + b, bb := 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) + } + } +} + +func TestSequenceEqual(t *testing.T) { + input := []struct { + 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: 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) + } + } +} + +func TestGetFirstAvailable(t *testing.T) { + input := []struct { + mask *Sequence + bytePos int + bitPos int + }{ + {&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: 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: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 7, 7}, + + {&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x0, Count: 6}}, 8, 0}, + } + + for n, i := range input { + 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) + } + } +} + +func TestFindSequence(t *testing.T) { + input := []struct { + head *Sequence + bytePos int + precBlocks uint32 + inBlockBytePos int + }{ + {&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: 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: 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}, + } + + for n, i := range input { + _, _, precBlocks, inBlockBytePos := findSequence(i.head, i.bytePos) + if precBlocks != i.precBlocks || inBlockBytePos != i.inBlockBytePos { + t.Fatalf("Error in (%d) findSequence(). Expected (%d, %d). Got (%d, %d)", n, i.precBlocks, i.inBlockBytePos, precBlocks, inBlockBytePos) + } + } +} + +func TestCheckIfAvailable(t *testing.T) { + input := []struct { + head *Sequence + ordinal int + bytePos int + bitPos int + }{ + {&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: 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: 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: 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}}}, 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}, + } + + for n, i := range input { + bytePos, bitPos := 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) + } + } +} + +func TestMergeSequences(t *testing.T) { + input := []struct { + 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: 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}}}, + + // 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}}}}, + + // 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) + } + } +} + +func TestPushReservation(t *testing.T) { + input := []struct { + mask *Sequence + bytePos int + bitPos int + 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}}}, + + {&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}}}}, + // 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}}}}, + + // 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 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}}, + + // 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}}}}, + } + + 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) + } + } +}