Пример #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TristateBitArray"/> class.
 /// </summary>
 /// <param name="copy">The bit array to copy.  This is not modified.</param>
 public TristateBitArray(TristateBitArray copy)
 {
     numBits   = copy.numBits;
     numUlongs = copy.numUlongs;
     mask      = (ulong[])copy.mask.Clone();
     value     = (ulong[])copy.value.Clone();
 }
Пример #2
0
        /// <summary>
        /// Create a new array, with a consecutive series of bits (up to 64) with defined values.
        /// All other bits are left at their default undefined state.
        /// </summary>
        /// <param name="numBits">Number of bits in the resulting array.</param>
        /// <param name="start">Zero-based index of least significant bit in bitfield.</param>
        /// <param name="end">Index of most significant bit in bitfield (inclusive).</param>
        /// <param name="value">
        /// Contains values of bits to be inserted. If the bitfield represents fewer than 64 bits,
        /// these bits are found in the least significant bits of 'value'.
        /// </param>
        /// <returns></returns>
        public static TristateBitArray LoadBitfieldValue(int numBits, int start, int end, ulong value)
        {
            Debug.Assert(numBits > 0 && start >= 0 &&
                         end < numBits && end >= start && (1 + end - start) <= 64);

            var tmp = new TristateBitArray(numBits);

            tmp.MergeBitfieldValue(start, end, value);
            return(tmp);
        }
Пример #3
0
        /// <summary>
        /// Find the best set of bitfields. This method uses a recursive algorithm to build
        /// the set.
        /// </summary>
        private BitfieldSet?FindBestBitfieldSet(int min, int max, int ideal, int fields)
        {
            if (fields > 0)
            {
                BitfieldSet?best = null;

                // calculate maximum number of bits in THIS bitfield (which must leave at
                // least one bit for each of the remaining fields).
                int maxBits = max - (fields - 1);

                // iterate through number of possible bits in THIS bitfield
                for (int bits = 1; bits <= maxBits; bits++)
                {
                    // recursively find the solution to the problem, excluding this bitfield
                    BitfieldSet?bfSet = FindBestBitfieldSet(1, max - bits, ideal, fields - 1);
                    if (bfSet == null)
                    {
                        return(null);
                    }

                    // generate an exclusion mask for the bits in bfSet (since the bitfields in
                    // the set must not overlap)
                    TristateBitArray exclusion = new TristateBitArray(
                        ruleSet.Condition.DecodeBits).Union(bfSet.GetBitsForValue(0));

                    // find the best bitfield to add to the sub-solution
                    Bitfield?bf = FindBestBitfield(bits, bits, bits, exclusion);
                    if (bf != null)
                    {
                        // add the bitfield to the result
                        bfSet.Add(bf);

                        // track best solution
                        if (best == null || bfSet.Quality > best.Quality)
                        {
                            best = bfSet;
                        }
                    }
                }

                // return the best solution
                return(best);
            }
            else
            {
                // return an empty set
                return(new BitfieldSet(Spec, ideal));
            }
        }
Пример #4
0
        /// <summary>
        /// Tests whether there are any bits that are defined in both arrays. Or, whether the
        /// intersection between both arrays' masks is non-zero.
        /// </summary>
        /// <param name="rhs">Compatible array to compare with. Must have the same number of bits.</param>
        /// <returns></returns>
        public bool MaskIntersectsWith(TristateBitArray rhs)
        {
            Debug.Assert(numBits == rhs.numBits);

            // iterate through ulongs, checking masks for intersections
            for (int i = 0; i < numUlongs; i++)
            {
                if ((mask[i] & rhs.mask[i]) != 0)
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #5
0
        /// <summary>
        /// The sole publicly-visible entry point.  Generates a tree, given a Specification.
        /// </summary>
        /// <param name="spec">The decoder specification.</param>
        /// <returns>The root of the decoder tree.</returns>
        public static Node BuildTree(Specification spec, TristateBitArray?flags)
        {
            if (flags == null)
            {
                flags = new TristateBitArray(spec.NumFlags);
            }

            // initialise a TreeBuilder with an empty condition and a RuleSet
            // including all rules.
            var     initialCondition = new Condition(spec, new TristateBitArray(spec.NumBits), flags);
            RuleSet ruleSet          = new RuleSet(spec).Derive(initialCondition);
            var     builder          = new TreeBuilder(spec, ruleSet);

            return(builder.Build());
        }
Пример #6
0
        /// <summary>
        /// Indicates whether two arrays are exactly equal in value.
        /// </summary>
        /// <param name="rhs">
        /// The array to compare with. Both arrays must have the same number of bits.
        /// </param>
        /// <returns>True if arrays are equal.</returns>
        public bool IsEqual(TristateBitArray rhs)
        {
            // make sure we're comparing like with like
            Debug.Assert(numBits == rhs.numBits);

            // iterate through ulongs, checking mask & value for equality
            for (int i = 0; i < numUlongs; i++)
            {
                if (mask[i] != rhs.mask[i] || value[i] != rhs.value[i])
                {
                    return(false);
                }
            }

            return(true);
        }
Пример #7
0
        /// <summary>
        /// Compute the intersection of two arrays.  The intersection of two bits is undefined
        /// if either or both bits are undefined, or the bit's value if both are defined.
        /// </summary>
        /// <param name="rhs">Compatible array to compare with.</param>
        /// <returns>The resulting bit array.</returns>
        public TristateBitArray Intersection(TristateBitArray rhs)
        {
            Debug.Assert(numBits == rhs.numBits);

            var tmp = new TristateBitArray(this);

            for (int i = 0; i < numUlongs; i++)
            {
                ulong maskIntersection = tmp.mask[i] & rhs.mask[i];
                tmp.mask[i]   = maskIntersection;
                tmp.value[i] &= maskIntersection;
                Debug.Assert(tmp.value[i] == (rhs.value[i] & maskIntersection));
            }

            return(tmp);
        }
Пример #8
0
        /// <summary>
        /// Indicate whether two arrays are 'compatible' with each other. Two arrays are compatible
        /// if the bits with definite values in both arrays have the same values. Put another way,
        /// they are compatible if they are equal in the intersection of their masks.
        /// </summary>
        /// <param name="rhs">The array to compare with. Must have the same number of bits.</param>
        /// <returns>True if the arrays are compatible.</returns>
        public bool IsCompatible(TristateBitArray rhs)
        {
            Debug.Assert(numBits == rhs.numBits);

            // iterate through ulongs, checking values for equality within the
            // intersection of the masks
            for (int i = 0; i < numUlongs; i++)
            {
                ulong maskIntersection = mask[i] & rhs.mask[i];
                if ((value[i] & maskIntersection) != (rhs.value[i] & maskIntersection))
                {
                    return(false);
                }
            }

            return(true);
        }
Пример #9
0
        /// <summary>
        /// Parse a pattern rule.  This consists of:
        ///   a bit pattern (mandatory)
        ///   a weight (optional)
        ///   a flags specification (optional)
        ///   one or more code fragments.
        /// </summary>
        private void ParseRule()
        {
            int lineNum = lexer.LineNum;

            Next();

            if (spec.NumBits == 0)
            {
                SyntaxError("Missing 'bits' directive");
            }

            // parse the pattern
            TristateBitArray bits = ParsePattern();

            var   flags    = new TristateBitArray(spec.NumFlags);
            var   fragment = new CodeFragment(spec);
            ulong weight   = 1;

            // parse optional weight
            if (CrntCode == Token.Type.Dollar)
            {
                NextExpect(Token.Type.Float);
                weight = lexer.Crnt.Ulong;
                Next();
            }

            // parse optional flags (between '[' and ']')
            if (CrntCode == Token.Type.BeginFlags)
            {
                Next();
                flags = ParseFlags();
                Expect(Token.Type.EndFlags);
                Next();
            }

            // parse 1+ code fragments
            Expect(Token.Type.CodeFragment);
            ParseFragments(fragment);

            // generate rule and add it to the Specification
            var condition = new Condition(spec, bits, flags);
            var rule      = new Rule(spec, condition, fragment, (int)weight, lineNum);

            spec.AddRule(rule);
        }
Пример #10
0
        /// <summary>
        /// Compute the union of two arrays. The union of two bits is undefined if both bits are
        /// undefined, or the value of the bit if either is defined. Since the arrays must be
        /// compatible, a bit defined in both must have the same value.
        /// </summary>
        /// <param name="rhs">Compatible array to compare with.</param>
        /// <returns>The resulting bit array.</returns>
        public TristateBitArray Union(TristateBitArray rhs)
        {
            Debug.Assert(numBits == rhs.numBits);

            // iterate through ulongs, computing union
            var tmp = new TristateBitArray(this);

            for (int i = 0; i < numUlongs; i++)
            {
                ulong maskIntersection = tmp.mask[i] & rhs.mask[i];
                Debug.Assert((tmp.value[i] & maskIntersection) == (rhs.value[i] & maskIntersection));

                tmp.mask[i]  |= rhs.mask[i];
                tmp.value[i] |= rhs.value[i];
            }

            return(tmp);
        }
Пример #11
0
        /// <summary>
        /// Compute the difference between two arrays. The difference is undefined if the right-hand
        /// side is defined, or the bit's value otherwise.
        /// </summary>
        /// <param name="rhs">Compatible array to compare with.</param>
        /// <returns>The resulting bit array.</returns>
        public TristateBitArray Subtract(TristateBitArray rhs)
        {
            Debug.Assert(numBits == rhs.numBits);

            // iterate through ulongs, computing the difference
            var tmp = new TristateBitArray(this);

            for (int i = 0; i < numUlongs; i++)
            {
                Debug.Assert((tmp.value[i] & rhs.mask[i]) == rhs.value[i]);

                ulong maskRhsInverse = ~rhs.mask[i];
                tmp.mask[i]  &= maskRhsInverse;
                tmp.value[i] &= maskRhsInverse;
            }

            return(tmp);
        }
Пример #12
0
        /// <summary>
        /// Parse the 'bits' part of a pattern specification. This consists of a sequence of '0',
        /// '1', or '.' bits, and is terminated by an EndPattern token (which doesn't directly map
        /// to an input character, but is generated by the lexer when it finds a non-bit character.
        /// </summary>
        /// <returns>Equivalent bit array.</returns>
        private TristateBitArray ParsePattern()
        {
            int i     = spec.NumBits - 1;
            var value = new TristateBitArray(spec.NumBits);

            // loop over input tokens until we reach the terminator
            while (CrntCode != Token.Type.EndPattern)
            {
                // map the input token to a bit value
                TristateBitArray.BitValue bitValue = CrntCode switch
                {
                    Token.Type.Zero => TristateBitArray.BitValue.Zero,
                    Token.Type.One => TristateBitArray.BitValue.One,
                    Token.Type.Dot => TristateBitArray.BitValue.Undefined,
                    _ => throw new SyntaxErrorException(SyntaxErrorMessage(UnexpectedErrorMessage()))
                };

                // abort if we've received too many bits
                if (i < 0)
                {
                    break;
                }

                // set the bit
                value.SetBit(i, bitValue);
                i--;

                Next();
            }

            // verify that we parsed the correct number of bits
            if (i != -1)
            {
                SyntaxError("Incorrect bit count in pattern");
            }

            // skip 'endpattern' token
            Next();

            return(value);
        }
Пример #13
0
        /// <summary>
        /// Parse a flag specification: a comma-separated list of flag identifiers, each
        /// preceded by an optional '!' token.  A flag specification is allowed to be
        /// empty.
        /// </summary>
        /// <returns>Equivalent bit pattern for flags.</returns>
        private TristateBitArray ParseFlags()
        {
            var bits = new TristateBitArray(spec.NumFlags);

            while (CrntCode == Token.Type.Not || CrntCode == Token.Type.Identifier)
            {
                // parse optional '!'
                bool invert = false;
                if (CrntCode == Token.Type.Not)
                {
                    invert = true;
                    Next();
                }

                // now parse identifier
                Expect(Token.Type.Identifier);
                Flag?flag = spec.GetFlag(lexer.Crnt.String);
                if (flag == null)
                {
                    SyntaxError($"Undeclared flag {lexer.Crnt.String}");
                }
                else
                {
                    bits.SetBit(flag.Index, invert ? TristateBitArray.BitValue.Zero : TristateBitArray.BitValue.One);
                }

                // is the following token a comma?  if so, we can loop around to the
                // next flag
                Next();
                if (CrntCode != Token.Type.Comma)
                {
                    break;
                }

                // skip over comma
                Next();
            }

            return(bits);
        }
Пример #14
0
        /// <summary>
        /// Find the best single bitfield suitable for use in a switch statement.
        ///
        /// This algorithm is not perfect: it can sometimes return a sub-optimal bitfield. It uses a
        /// heuristic approach, under the assumption that the best bitfield is the same as the
        /// bitfield with the highest total bit quality (and nearest the desired bit length). This
        /// assumption tends to favour longer bitfields. In some cases, however, a shorter bitfield
        /// may have an equal ability to discriminate between rules.
        /// </summary>
        /// <param name="min">Fewest number of acceptable bits.</param>
        /// <param name="max">Largest number of acceptable bits.</param>
        /// <param name="ideal">Preferred number of acceptable bits.</param>
        /// <param name="exclusion">A mask indicating bits that should be excluded (optional).</param>
        /// <returns>The best bitfield meeting the criteria, or null in case of failure.</returns>
        private Bitfield?FindBestBitfield(int min, int max, int ideal, TristateBitArray?exclusion = null)
        {
            if (exclusion == null)
            {
                exclusion = ruleSet.Condition.DecodeBits;
            }

            Bitfield?best = null;

            // iterate through candidate start bits
            for (int start = MinSignificantBit; start <= MaxSignificantBit; start++)
            {
                // iterate through candidate end bits
                for (int end = start + min - 1; end <= start + max - 1; end++)
                {
                    // discard any bitfield that provably contains zero-quality bits
                    if (end <= MaxSignificantBit)
                    {
                        // viable bitfields cannot intersect with the exclusion mask
                        var bits = TristateBitArray.LoadBitfieldValue(Spec.NumBits, start, end, 0);
                        if (!exclusion.MaskIntersectsWith(bits))
                        {
                            // calculate total bit quality over the range
                            if (CalculateTotalQuality(start, end, out float totalQuality))
                            {
                                var tmp = new Bitfield(Spec, start, end, ideal, totalQuality);

                                // track highest overall quality
                                if (best == null || tmp.Quality > best.Quality)
                                {
                                    best = tmp;
                                }
                            }
                        }
                    }
                }
            }

            return(best);
        }
Пример #15
0
        /// <summary>
        /// Try to improve efficiency by 'lifting' a test higher up the decoder tree. This is
        /// only done if the test is identical for every member of a rule set.
        /// </summary>
        private Node?BuildLift(string name, Func <Condition, TristateBitArray> extract, Func <TristateBitArray, Condition> init)
        {
            if (ruleSet.NumRules < 2)
            {
                return(null);
            }

            // extract component from 1st rule & verify that flags are specified
            TristateBitArray component = extract(ruleSet[0].EffectiveCondition);

            if (component.IsEmpty)
            {
                return(null);
            }

            // check that all other rules have the same component spec
            for (int i = 1; i < ruleSet.NumRules; i++)
            {
                if (!extract(ruleSet[i].EffectiveCondition).IsEqual(component))
                {
                    return(null);
                }
            }

            if (spec.Config.Verbose)
            {
                Console.WriteLine($"Performing {name} lifting optimisation.");
            }

            // build subtree without flags
            Condition cond    = init(component);
            RuleSet   rSet    = ruleSet.Derive(cond);
            var       builder = new TreeBuilder(spec, rSet, this);
            Node      node    = builder.Build();

            // and attach it to an if-node
            return(new IfElseNode(spec, cond, node, new EmptyNode(spec)));
        }
Пример #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Condition"/> class.
 /// </summary>
 /// <param name="spec">Associated specification.</param>
 /// <param name="bitwise">Decode bit pattern.</param>
 /// <param name="flags">Flags pattern.</param>
 public Condition(Specification spec, TristateBitArray bitwise, TristateBitArray flags)
     : base(spec)
 {
     decodeBits = new TristateBitArray(bitwise);
     this.flags = new TristateBitArray(flags);
 }
Пример #17
0
        /// <summary>
        /// Generate cache of quality ratings for each bit.  Called by constructor.
        /// </summary>
        private void CalculateBitQuality()
        {
            // initialise temporary arrays
            int[]   total    = new int[Spec.NumBits];
            int[]   totalOne = new int[Spec.NumBits];
            float[] score    = new float[Spec.NumBits];

            // iterate through each Rule in the RuleSet
            foreach (RuleSetEntry entry in ruleSet)
            {
                for (int i = 0; i < Spec.NumBits; i++)
                {
                    // is this bit significant to the rule after taking the ruleset's
                    // condition into account?
                    if (entry.EffectiveCondition.DecodeBits.GetMaskBit(i))
                    {
                        // accumulate totals
                        total[i]++;

                        // calculate total score, taking into account the Rule's weight
                        TristateBitArray tmp = entry.EffectiveCondition.Flags;

                        if (!tmp.IsEmpty)
                        {
                            score[i] += entry.Rule.Weight * Spec.Config.BitFlagCoef;
                        }
                        else
                        {
                            score[i] += entry.Rule.Weight;
                        }

                        // tally up bits which must equal one
                        if (entry.EffectiveCondition.DecodeBits.GetValueBit(i))
                        {
                            totalOne[i]++;
                        }
                    }
                }
            }

            // calculate total of all individual bit scores
            float totalScore = 0F;

            for (int i = 0; i < Spec.NumBits; i++)
            {
                totalScore += score[i];
            }

            for (int i = 0; i < Spec.NumBits; i++)
            {
                // calculate smaller of total ones or zeroes
                int totalZero    = total[i] - totalOne[i];
                int totalSmaller = (totalZero < totalOne[i]) ? totalZero : totalOne[i];

                // quantify, on 0-1 scale, how well balanced the values are.
                // that is, how evenly distributed are ones and zeroes?  a bit that is set for
                // every rule is utterly useless for a switch, since all rules would fall into
                // a single case.
                float balance = 2F * (totalSmaller / (float)total[i]);

                // calculate overall bit quality
                float bitQ;
                if (score[i] != 0F)
                {
                    bitQ = balance * score[i] / totalScore;
                }
                else
                {
                    bitQ = 0F;  // avoid NaNs in calculation
                }

                // store calculated quality in cache
                bitQuality[i] = bitQ;

                // accumulate totals
                totalBitQuality += bitQ;
                if (bitQ != 0F)
                {
                    // track least significant bit
                    if (i < minSignificantBit)
                    {
                        minSignificantBit = i;
                    }

                    // track most significant bit
                    if (i > maxSignificantBit)
                    {
                        maxSignificantBit = i;
                    }
                }
            }
        }