Beispiel #1
0
        public (int, string) DecodeAll(Span <byte> src_span, Span <double> to_span)
        {
            if (src_span.Length < 9)
            {
                return(0, null);
            }
            long  to_len = to_span.Length;
            ulong val;              // current value
            byte  trailingN   = 0;  // trailing zero count
            byte  meaningfulN = 64; // meaningful bit count

            // first byte is the compression type; always Gorilla
            src_span = src_span.Slice(1);//

            val = ByteWriter.ReadBigEndian(src_span);
            if (val == Constants.uvnan)
            {
                // special case: there were no values to decode
                return(0, null);
            }

            int result = 1;

            to_span[0] = Float64frombits(val);
            src_span   = src_span.Slice(8);
            // The bit reader code uses brCachedVal to store up to the next 8 bytes
            // of MSB data read from b. brValidBits stores the number of remaining unread
            // bits starting from the MSB. Before N bits are read from brCachedVal,
            // they are left-rotated N bits, such that they end up in the left-most position.
            // Using bits.RotateLeft64 results in a single instruction on many CPU architectures.
            // This approach permits simple tests, such as for the two control bits:
            //
            //    brCachedVal&1 > 0
            //
            // The alternative was to leave brCachedValue alone and perform shifts and
            // masks to read specific bits. The original approach looked like the
            // following:
            //
            //    brCachedVal&(1<<(brValidBits&0x3f)) > 0
            //
            ulong brCachedVal = 0; // a buffer of up to the next 8 bytes read from b in MSB order
            byte  brValidBits = 0; // the number of unread bits remaining in brCachedVal

            // Refill brCachedVal, reading up to 8 bytes from b
            if (src_span.Length >= 8)
            {
                // fast path reads 8 bytes directly
                brCachedVal = ByteWriter.ReadBigEndian(src_span);
                brValidBits = 64;
                src_span    = src_span.Slice(8);
            }
            else if (src_span.Length > 0)
            {
                brCachedVal = 0;
                brValidBits = (byte)(src_span.Length * 8);
                foreach (byte bi in src_span)
                {
                    brCachedVal = (brCachedVal << 8) | bi;
                }
                brCachedVal = RotateRight64(brCachedVal, brValidBits);
            }
            else
            {
                goto ERROR;
            }
            // The expected exit condition is for a uvnan to be decoded.
            // Any other error (EOF) indicates a truncated stream.
            while (result < to_len)
            {
                if (brValidBits > 0)
                {
                    // brValidBits > 0 is impossible to predict, so we place the
                    // most likely case inside the if and immediately jump, keeping
                    // the instruction pipeline consistently full.
                    // This is a similar approach to using the GCC __builtin_expect
                    // intrinsic, which modifies the order of branches such that the
                    // likely case follows the conditional jump.
                    goto READ0;
                }
                // Refill brCachedVal, reading up to 8 bytes from b
                if (src_span.Length >= 8)
                {
                    brCachedVal = ByteWriter.ReadBigEndian(src_span);
                    brValidBits = 64;
                    src_span    = src_span.Slice(8);
                }
                else if (src_span.Length > 0)
                {
                    brCachedVal = 0;
                    brValidBits = (byte)(src_span.Length * 8);
                    foreach (byte bi in src_span)
                    {
                        brCachedVal = (brCachedVal << 8) | bi;
                    }
                    brCachedVal = RotateRight64(brCachedVal, brValidBits);
                }
                else
                {
                    goto ERROR;
                }
READ0:
                brValidBits--;
                brCachedVal = RotateLeft64(brCachedVal, 1);
                if ((brCachedVal & 1) > 0)
                {
                    if (brValidBits > 0)
                    {
                        goto READ1;
                    }
                    // Refill brCachedVal, reading up to 8 bytes from b
                    if (src_span.Length >= 8)
                    {
                        brCachedVal = ByteWriter.ReadBigEndian(src_span);
                        brValidBits = 64;
                        src_span    = src_span.Slice(8);
                    }
                    else if (src_span.Length > 0)
                    {
                        brCachedVal = 0;
                        brValidBits = (byte)(src_span.Length * 8);
                        foreach (byte bi in src_span)
                        {
                            brCachedVal = (brCachedVal << 8) | bi;
                        }
                        brCachedVal = RotateRight64(brCachedVal, brValidBits);
                    }
                    else
                    {
                        goto ERROR;
                    }

READ1:
                    // read control bit 1
                    brValidBits--;
                    brCachedVal = RotateLeft64(brCachedVal, 1);
                    if ((brCachedVal & 1) > 0)
                    {
                        // read 5 bits for leading zero count and 6 bits for the meaningful data count
                        const int leadingTrailingBitCount = 11;
                        ulong     lmBits = 0;// leading + meaningful data counts
                        if (brValidBits >= leadingTrailingBitCount)
                        {
                            // decode 5 bits leading + 6 bits meaningful for a total of 11 bits
                            brValidBits -= leadingTrailingBitCount;
                            brCachedVal  = RotateLeft64(brCachedVal, leadingTrailingBitCount);
                            lmBits       = brCachedVal;
                        }
                        else
                        {
                            byte bits01 = 11;
                            if (brValidBits > 0)
                            {
                                bits01 -= brValidBits;
                                lmBits  = RotateLeft64(brCachedVal, 11);
                            }
                            // Refill brCachedVal, reading up to 8 bytes from b
                            if (src_span.Length >= 8)
                            {
                                brCachedVal = ByteWriter.ReadBigEndian(src_span);
                                brValidBits = 64;
                                src_span    = src_span.Slice(8);
                            }
                            else if (src_span.Length > 0)
                            {
                                brCachedVal = 0;
                                brValidBits = (byte)(src_span.Length * 8);
                                foreach (byte bi in src_span)
                                {
                                    brCachedVal = (brCachedVal << 8) | bi;
                                }
                                brCachedVal = RotateRight64(brCachedVal, brValidBits);
                            }
                            else
                            {
                                goto ERROR;
                            }
                            brCachedVal  = RotateLeft64(brCachedVal, bits01);
                            brValidBits -= bits01;
                            lmBits       = lmBits & ~bitMask[bits01 & 0x3F];
                            lmBits      |= brCachedVal & bitMask[bits01 & 0x3f];
                        }
                        lmBits &= 0x7FF;
                        byte leadingN = (byte)((lmBits >> 6) & 0x1F); //5 bits leading
                        meaningfulN = (byte)(lmBits & 0x3F);          // 6 bits meaningful
                        if (meaningfulN > 0)
                        {
                            trailingN = (byte)(64 - leadingN - meaningfulN);
                        }
                        else
                        {
                            // meaningfulN == 0 is a special case, such that all bits
                            // are meaningful
                            trailingN   = 0;
                            meaningfulN = 64;
                        }
                    }
                    ulong sBits = 0;// significant bits
                    if (brValidBits >= meaningfulN)
                    {
                        brValidBits -= meaningfulN;
                        brCachedVal  = RotateLeft64(brCachedVal, meaningfulN);
                        sBits        = brCachedVal;
                    }
                    else
                    {
                        byte mBits = meaningfulN;
                        if (brValidBits > 0)
                        {
                            mBits -= brValidBits;
                            sBits  = RotateLeft64(brCachedVal, meaningfulN);
                        }
                        // Refill brCachedVal, reading up to 8 bytes from b
                        if (src_span.Length >= 8)
                        {
                            brCachedVal = ByteWriter.ReadBigEndian(src_span);
                            brValidBits = 64;
                            src_span    = src_span.Slice(8);
                        }
                        else if (src_span.Length > 0)
                        {
                            brCachedVal = 0;
                            brValidBits = (byte)(src_span.Length * 8);
                            foreach (byte bi in src_span)
                            {
                                brCachedVal = (brCachedVal << 8) | bi;
                            }
                            brCachedVal = RotateRight64(brCachedVal, brValidBits);
                        }
                        else
                        {
                            goto ERROR;
                        }
                        brCachedVal  = RotateLeft64(brCachedVal, mBits);
                        brValidBits -= mBits;
                        sBits        = sBits & ~bitMask[mBits & 0x3F];
                        sBits       |= brCachedVal & bitMask[mBits & 0x3F];
                    }
                    sBits &= bitMask[meaningfulN & 0x3F];
                    val   ^= sBits << (trailingN & 0x3F);
                    if (val == Constants.uvnan)
                    {
                        // IsNaN, eof
                        break;
                    }
                }
                to_span[result++] = Float64frombits(val);
            }
            return(result, null);

            ERROR : return(0, "io.EOF");
        }