DeltaContext.java

  1. package zserio.runtime.array;

  2. import java.io.IOException;
  3. import java.math.BigInteger;

  4. import zserio.runtime.array.ArrayElement.IntegralArrayElement;
  5. import zserio.runtime.array.ArrayTraits.IntegralArrayTraits;
  6. import zserio.runtime.io.BitStreamReader;
  7. import zserio.runtime.io.BitStreamWriter;

  8. /**
  9.  * Context for delta packing created for each packable field.
  10.  *
  11.  * Contexts are always newly created for each array operation (bitSizeOfPacked, initializeOffsetsPacked,
  12.  * readPacked, writePacked). They must be initialized at first via calling the init method for each packable
  13.  * element present in the array. After the full initialization, only a single method (bitSizeOf, read, write)
  14.  * can be repeatedly called for exactly the same sequence of packable elements.
  15.  */
  16. public final class DeltaContext extends PackingContext
  17. {
  18.     /**
  19.      * Calls the initialization step for a single element.
  20.      *
  21.      * @param arrayTraits Standard array traits.
  22.      * @param element Current element.
  23.      */
  24.     public void init(IntegralArrayTraits arrayTraits, IntegralArrayElement element)
  25.     {
  26.         numElements++;
  27.         unpackedBitSize += bitSizeOfUnpacked(arrayTraits, element);

  28.         if (previousElement == null)
  29.         {
  30.             previousElement = element.toBigInteger();
  31.             firstElementBitSize = (byte)unpackedBitSize;
  32.         }
  33.         else
  34.         {
  35.             if (maxBitNumber <= MAX_BIT_NUMBER_LIMIT)
  36.             {
  37.                 isPacked = true;
  38.                 final BigInteger bigElement = element.toBigInteger();
  39.                 final BigInteger delta = bigElement.subtract(previousElement);
  40.                 final byte maxBitNumber = bitLength(delta);
  41.                 if (maxBitNumber > this.maxBitNumber)
  42.                 {
  43.                     this.maxBitNumber = maxBitNumber;
  44.                     if (maxBitNumber > MAX_BIT_NUMBER_LIMIT)
  45.                         isPacked = false;
  46.                 }
  47.                 previousElement = bigElement;
  48.             }
  49.         }
  50.     }

  51.     /**
  52.      * Returns length of the packed element stored in the bit stream in bits.
  53.      *
  54.      * @param arrayTraits Standard array traits.
  55.      * @param element Value of the current element.
  56.      *
  57.      * @return Length of the packed element stored in the bit stream in bits.
  58.      */
  59.     public int bitSizeOf(IntegralArrayTraits arrayTraits, IntegralArrayElement element)
  60.     {
  61.         if (!processingStarted)
  62.         {
  63.             processingStarted = true;
  64.             finishInit();

  65.             return bitSizeOfDescriptor() + bitSizeOfUnpacked(arrayTraits, element);
  66.         }
  67.         else if (!isPacked)
  68.         {
  69.             return bitSizeOfUnpacked(arrayTraits, element);
  70.         }
  71.         else
  72.         {
  73.             return maxBitNumber + (maxBitNumber > 0 ? 1 : 0);
  74.         }
  75.     }

  76.     /**
  77.      * Reads a packed element from the bit stream.
  78.      *
  79.      * @param arrayTraits Standard array traits.
  80.      * @param reader Bit stream reader.
  81.      *
  82.      * @return Packed element.
  83.      *
  84.      * @throws IOException Failure during bit stream manipulation.
  85.      */
  86.     public IntegralArrayElement read(IntegralArrayTraits arrayTraits, BitStreamReader reader) throws IOException
  87.     {
  88.         if (!processingStarted)
  89.         {
  90.             processingStarted = true;
  91.             readDescriptor(reader);

  92.             return readUnpacked(arrayTraits, reader);
  93.         }
  94.         else if (!isPacked)
  95.         {
  96.             return readUnpacked(arrayTraits, reader);
  97.         }
  98.         else
  99.         {
  100.             if (maxBitNumber > 0)
  101.             {
  102.                 final BigInteger delta = BigInteger.valueOf(reader.readSignedBits(maxBitNumber + 1));
  103.                 previousElement = previousElement.add(delta);
  104.             }

  105.             return arrayTraits.fromBigInteger(previousElement);
  106.         }
  107.     }

  108.     /**
  109.      * Writes the packed element to the bit stream.
  110.      *
  111.      * @param arrayTraits Standard array traits.
  112.      * @param writer Bit stream writer.
  113.      * @param element Current element.
  114.      *
  115.      * @throws IOException Failure during bit stream manipulation.
  116.      */
  117.     public void write(IntegralArrayTraits arrayTraits, BitStreamWriter writer, IntegralArrayElement element)
  118.             throws IOException
  119.     {
  120.         if (!processingStarted)
  121.         {
  122.             processingStarted = true;
  123.             finishInit();
  124.             writeDescriptor(writer);

  125.             writeUnpacked(arrayTraits, writer, element);
  126.         }
  127.         else if (!isPacked)
  128.         {
  129.             writeUnpacked(arrayTraits, writer, element);
  130.         }
  131.         else
  132.         {
  133.             if (maxBitNumber > 0)
  134.             {
  135.                 final BigInteger bigElement = element.toBigInteger();
  136.                 final BigInteger delta = bigElement.subtract(previousElement);
  137.                 writer.writeSignedBits(delta.longValue(), maxBitNumber + 1);
  138.                 previousElement = bigElement;
  139.             }
  140.         }
  141.     }

  142.     private void finishInit()
  143.     {
  144.         if (isPacked)
  145.         {
  146.             final int deltaBitSize = maxBitNumber + (maxBitNumber > 0 ? 1 : 0);
  147.             final int packedBitSizeWithDescriptor = 1 + MAX_BIT_NUMBER_BITS + // descriptor
  148.                     firstElementBitSize + (numElements - 1) * deltaBitSize;
  149.             final int unpackedBitSizeWithDescriptor = 1 + unpackedBitSize;
  150.             if (packedBitSizeWithDescriptor >= unpackedBitSizeWithDescriptor)
  151.                 isPacked = false;
  152.         }
  153.     }

  154.     private int bitSizeOfDescriptor()
  155.     {
  156.         return isPacked ? 1 + MAX_BIT_NUMBER_BITS : 1;
  157.     }

  158.     private static int bitSizeOfUnpacked(IntegralArrayTraits arrayTraits, IntegralArrayElement element)
  159.     {
  160.         return arrayTraits.bitSizeOf(element);
  161.     }

  162.     private void readDescriptor(BitStreamReader reader) throws IOException
  163.     {
  164.         isPacked = reader.readBool();
  165.         if (isPacked)
  166.             maxBitNumber = (byte)reader.readBits(MAX_BIT_NUMBER_BITS);
  167.     }

  168.     private IntegralArrayElement readUnpacked(IntegralArrayTraits arrayTraits, BitStreamReader reader)
  169.             throws IOException
  170.     {
  171.         final IntegralArrayElement element = arrayTraits.read(reader);
  172.         previousElement = element.toBigInteger();
  173.         return element;
  174.     }

  175.     private void writeDescriptor(BitStreamWriter writer) throws IOException
  176.     {
  177.         writer.writeBool(isPacked);
  178.         if (isPacked)
  179.             writer.writeBits(maxBitNumber, MAX_BIT_NUMBER_BITS);
  180.     }

  181.     private void writeUnpacked(IntegralArrayTraits arrayTraits, BitStreamWriter writer,
  182.             IntegralArrayElement element) throws IOException
  183.     {
  184.         previousElement = element.toBigInteger();
  185.         arrayTraits.write(writer, element);
  186.     }

  187.     private static byte bitLength(BigInteger element)
  188.     {
  189.         // need to call abs() first to get the same behavior as in Python and C++
  190.         return (byte)element.abs().bitLength();
  191.     }

  192.     private static final byte MAX_BIT_NUMBER_BITS = 6;
  193.     private static final byte MAX_BIT_NUMBER_LIMIT = 62;

  194.     private BigInteger previousElement; // BigInteger covers all integral array element values
  195.     private byte maxBitNumber = 0;
  196.     private boolean isPacked = false;
  197.     private boolean processingStarted = false;

  198.     private byte firstElementBitSize = 0;
  199.     private int numElements = 0;
  200.     private int unpackedBitSize = 0;
  201. }