All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.io.DecoratedBinaryDeltaCompressor Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.io;


import com.tangosol.util.Base;
import com.tangosol.util.Binary;
import com.tangosol.util.BinaryWriteBuffer;
import com.tangosol.util.ExternalizableHelper;

import java.io.IOException;


/**
* A DeltaCompressor implementation that works with decorated binary values.
* 

* The delta format is composed of a leading byte that indicates the format; * the format indicator byte is one of the DECO_* field values. If the delta * value does not begin with one of the leading DECO_* indicators, then the * entire delta is the delta for the "value" portion of the decorated result. * The grammar follows: *

*
* DecoratedBinaryDelta:
*   DECO_DELETE_ALL OptionalValueDelta
*   DecorationDeltaList DECO_TERM OptionalValueDelta
*   DECO_NO_CHANGES-opt ValueDelta
*   null
*
* OptionalValueDelta:
*   VALUE_NO_CHANGES
*   VALUE_CHANGES-opt ValueDelta
*
* ValueDelta:
*   Binary (as defined by the underlying DeltaCompressor)
*
* DecorationDeltaList:
*   DecorationDelta
*   DecorationDeltaList DecorationDelta
*
* DecorationDelta:
*   DECO_INSERT DecorationId Binary
*   DECO_UPDATE DecorationId BinaryDelta
*   DECO_DELETE DecorationId
*
* BinaryDelta:
*   Binary (as returned by the BinaryDeltaCompressor)
*
* Binary:
*   Length <bytes>
*
* DecorationId:
* Length:
*   <packed-integer>
* 
* * @author cp 2009.01.20 */ public class DecoratedBinaryDeltaCompressor extends ExternalizableHelper implements DeltaCompressor { // ----- constructors --------------------------------------------------- /** * Construct a DecoratedBinaryDeltaCompressor. * * @param compressorValue the compressor responsible for performing * delta compression on the underlying Binary * value (i.e. the DECO_VALUE decoration) */ public DecoratedBinaryDeltaCompressor(DeltaCompressor compressorValue) { Base.azzert(compressorValue != null, "DeltaCompressor is required"); m_compressorValue = compressorValue; } // ----- DeltaCompressor interface -------------------------------------- /** * {@inheritDoc} */ public ReadBuffer extractDelta(ReadBuffer bufOld, ReadBuffer bufNew) { if (Base.equals(bufOld, bufNew)) { // null means no-diff return null; } BinaryWriteBuffer bufDelta = null; if (isDecorated(bufNew)) { ReadBuffer[] abufDecoOld = getDecorations(bufOld); ReadBuffer[] abufDecoNew = getDecorations(bufNew); int cDecoOld = abufDecoOld.length; int cDecoNew = abufDecoNew.length; WriteBuffer.BufferOutput outDelta = null; try { for (int i = 1, c = Math.max(cDecoOld, cDecoNew); i < c; ++i) { ReadBuffer bufDecoOld = i < cDecoOld ? abufDecoOld[i] : null; ReadBuffer bufDecoNew = i < cDecoNew ? abufDecoNew[i] : null; if (!Base.equals(bufDecoOld, bufDecoNew)) { if (bufDelta == null) { bufDelta = new BinaryWriteBuffer(64); outDelta = bufDelta.getAppendingBufferOutput(); } if (bufDecoOld == null) { // insert the decoration outDelta.write(DECO_INSERT); outDelta.writePackedInt(i); outDelta.writePackedInt(bufDecoNew.length()); outDelta.writeBuffer(bufDecoNew); } else if (bufDecoNew == null) { // delete the decoration outDelta.write(DECO_DELETE); outDelta.writePackedInt(i); } else { // update the decoration outDelta.write(DECO_UPDATE); outDelta.writePackedInt(i); ReadBuffer bufDecoDelta = s_compressorBinary .extractDelta(bufDecoOld, bufDecoNew); Base.azzert(bufDecoDelta != null); outDelta.writePackedInt(bufDecoDelta.length()); outDelta.writeBuffer(bufDecoDelta); } } } if (outDelta != null) { // "cap" the series of decoration changes outDelta.write(DECO_TERM); } } catch (IOException e) { throw Base.ensureRuntimeException(e); } bufOld = abufDecoOld[ExternalizableHelper.DECO_VALUE]; bufNew = abufDecoNew[ExternalizableHelper.DECO_VALUE]; } else if (isDecorated(bufOld)) { // new value has no decorations, but the old value does; delete // them all bufDelta = new BinaryWriteBuffer(64); bufDelta.write(0, DECO_DELETE_ALL); bufOld = getUndecorated(bufOld); } ReadBuffer bufValueDelta = m_compressorValue.extractDelta(bufOld, bufNew); if (bufValueDelta == null) { // no delta for the value if (bufDelta != null) { bufDelta.write(bufDelta.length(), VALUE_NO_CHANGES); } } else if (bufDelta == null) { // there is no delta already, so the value delta becomes the // delta if it's non-ambiguous, otherwise a DECO_NO_CHANGES is // prepended boolean fAmbiguous = true; if (bufValueDelta.length() >= 1) { switch (bufValueDelta.byteAt(0)) { case DECO_NO_CHANGES: case DECO_DELETE_ALL: case DECO_INSERT: case DECO_UPDATE: case DECO_DELETE: // each of these is a legitimate leading byte for a // decorated delta break; default: fAmbiguous = false; break; } } if (fAmbiguous) { // prepend DECO_NO_CHANGES bufDelta = new BinaryWriteBuffer(1 + bufValueDelta.length()); bufDelta.write(0, DECO_NO_CHANGES); bufDelta.write(1, bufValueDelta); } } else { // there is a delta already, so the value delta is appended boolean fAmbiguous = true; if (bufValueDelta.length() >= 1) { switch (bufValueDelta.byteAt(0)) { case VALUE_CHANGES: case VALUE_NO_CHANGES: // each of these is a legitimate leading byte for a // value delta break; default: fAmbiguous = false; break; } } if (fAmbiguous) { // prepend VALUE_CHANGES bufDelta.write(bufDelta.length(), VALUE_CHANGES); } bufDelta.write(bufDelta.length(), bufValueDelta); } return bufDelta == null ? (bufValueDelta == null ? null : bufValueDelta) : bufDelta.toBinary(); } /** * {@inheritDoc} */ public ReadBuffer applyDelta(ReadBuffer bufOld, ReadBuffer bufDelta) { if (bufDelta == null) { // null delta indicates no change return bufOld; } ReadBuffer[] abufDeco = EMPTY_BINARY_ARRAY; switch (bufDelta.byteAt(0)) { case DECO_NO_CHANGES: // keep all the decorations (if any) from the old binary and // apply the value delta (which is the remainder of the delta // buffer) abufDeco = getDecorations(bufOld); bufDelta = bufDelta.getReadBuffer(1, bufDelta.length() - 1); break; case DECO_DELETE_ALL: { // keep only the undecorated value from the old binary and // apply the value delta (which is the remainder of the delta // buffer); note that the value delta can be an explicit // VALUE_NO_CHANGES token that indicates that the underlying // DeltaCompressor returned null from extractDelta() bufDelta = parseValueDelta(bufDelta, 1); } break; case DECO_INSERT: case DECO_UPDATE: case DECO_DELETE: { // start with the old set of decorations abufDeco = getDecorations(bufOld); // at least one decoration change occurred that needs to be // processed ReadBuffer.BufferInput inDelta = bufDelta.getBufferInput(); try { while (true) { byte b = inDelta.readByte(); if (b == DECO_TERM) { break; } int iDeco = inDelta.readPackedInt(); ReadBuffer bufDeco = null; switch (b) { case DECO_INSERT: { // insert is the only type of decoration // delta that can cause the array of // decorations to increase in size if (iDeco >= abufDeco.length) { ReadBuffer[] abufNew = new ReadBuffer[iDeco+1]; System.arraycopy(abufDeco, 0, abufNew, 0, abufDeco.length); abufDeco = abufNew; } int cbBin = inDelta.readPackedInt(); int ofBin = inDelta.getOffset(); bufDeco = bufDelta.getReadBuffer(ofBin, cbBin); inDelta.setOffset(ofBin + cbBin); } break; case DECO_UPDATE: { int cbDelta = inDelta.readPackedInt(); int ofDelta = inDelta.getOffset(); ReadBuffer bufDecoOld; try { bufDecoOld = abufDeco[iDeco]; } catch(IndexOutOfBoundsException e) { throw Base.ensureRuntimeException(e, "update decoration " + iDeco); } ReadBuffer bufDecoDelta = bufDelta.getReadBuffer(ofDelta, cbDelta); inDelta.setOffset(ofDelta + cbDelta); bufDeco = s_compressorBinary.applyDelta( bufDecoOld, bufDecoDelta); } break; case DECO_DELETE: break; default: throw new IllegalStateException( "byte=" + Base.toHexEscape(b)); } try { abufDeco[iDeco] = bufDeco; } catch(IndexOutOfBoundsException e) { throw Base.ensureRuntimeException(e, "decoration " + iDeco); } } } catch (IOException e) { throw Base.ensureRuntimeException(e); } // extract the remaining delta for the value itself (which // could be VALUE_NO_CHANGES) bufDelta = parseValueDelta(bufDelta, inDelta.getOffset()); } break; default: // the absence of a leading DECO_* format indicator is the // same as the DECO_NO_CHANGES indicator; apply the value // delta (which is the entire contents of bufDelta) abufDeco = getDecorations(bufOld); break; } // get the old value if (abufDeco.length > DECO_VALUE) { // if the decorations originally came from binOld , then the // old value is stored as decoration #0 bufOld = abufDeco[ExternalizableHelper.DECO_VALUE]; // failure to null out the 0th element will override the new // value abufDeco[ExternalizableHelper.DECO_VALUE] = null; } else { bufOld = getUndecorated(bufOld); } // apply the value delta (if there is one) ReadBuffer bufNew = bufDelta == null ? bufOld : m_compressorValue.applyDelta(bufOld, bufDelta); // add decorations (if there are any) return decorate(bufNew, abufDeco); } /** * Parse an OptionalValueDelta. * *
    * OptionalValueDelta:
    *   VALUE_NO_CHANGES
    *   VALUE_CHANGES-opt ValueDelta
    * 
* * @param bufDelta the entire decorated binary delta * @param of the offset of the optional value delta * * @return the value delta as a read buffer */ private static ReadBuffer parseValueDelta(ReadBuffer bufDelta, int of) { int cb = bufDelta.length(); if (cb <= of) { return NO_BINARY; } switch (bufDelta.byteAt(of)) { case VALUE_NO_CHANGES: return null; case VALUE_CHANGES: // the value is the remainder of the buffer from "of+1" return bufDelta.getReadBuffer(of + 1, cb - of - 1); default: // the value is the buffer starting at offset "of" return bufDelta.getReadBuffer(of, cb - of); } } // ----- Object methods ------------------------------------------------- /** * {@inheritDoc} */ public String toString() { return "DecoratedBinaryDeltaCompressor {" + m_compressorValue + "}"; } // ----- constants ------------------------------------------------------ /** * A decorated binary operator that instructs the {@link #applyDelta} * method that there are no decoration changes in the delta. * The format is the one-byte DECO_NO_CHANGES indicator. *

* This operator terminates the decorated binary delta stream, so the * following byte is the first byte of the value delta. */ private static final byte DECO_NO_CHANGES = (byte) 0xFD; /** * A decorated binary operator that instructs the {@link #applyDelta} * method to remove all decorations, leaving only the undecorated value. * The format is the one-byte DECO_DELETE_ALL indicator. *

* This operator terminates the decorated binary delta stream, so the * following byte is the first byte of the value delta. */ private static final byte DECO_DELETE_ALL = (byte) 0xFC; /** * A decorated binary operator that instructs the {@link #applyDelta} * method to insert the specified decoration. The format is the one-byte * DECO_INSERT indicator followed by a packed int decoration id and then * a binary value in the {@link Binary#writeExternal(java.io.DataOutput)} * format. */ private static final byte DECO_INSERT = (byte) 0xFB; /** * A decorated binary operator that instructs the {@link #applyDelta} * method to update the specified decoration. The format is the one-byte * DECO_UPDATE indicator followed by a packed int decoration id and then * a binary value that conforms to the BinaryDeltaCompressor format. */ private static final byte DECO_UPDATE = (byte) 0xFA; /** * A decorated binary operator that instructs the {@link #applyDelta} * method to remove the specified decoration. The format is the one-byte * DECO_DELETE indicator followed by a packed int decoration id. */ private static final byte DECO_DELETE = (byte) 0xF9; /** * A decorated binary operator that instructs the {@link #applyDelta} * method that there are no more decoration deltas in the decorated binary * delta stream. The format is the one-byte DECO_TERM indicator. *

* This operator terminates the decorated binary delta stream, so the * following byte is the first byte of the value delta. */ private static final byte DECO_TERM = (byte) 0xF8; /** * A place-holder for the value portion of the delta that indicates that * there was no delta to the value. *

* Note: This value re-uses the same value as DECO_NO_CHANGES. It is able * to do so because it can only occur after the conclusion of the * decoration stream. */ private static final byte VALUE_NO_CHANGES = (byte) 0xFD; /** * A place-holder for the value portion of the delta that indicates that * there was a delta to the value. This prefix must be used in any case * when VALUE_NO_CHANGES can be used and the value delta itself starts * with the VALUE_CHANGES or VALUE_NO_CHANGES byte. *

* Note: This value re-uses the same value as DECO_DELETE_ALL. It is able * to do so because it can only occur after the conclusion of the * decoration stream. */ private static final byte VALUE_CHANGES = (byte) 0xFC; /** * An empty Binary object. */ private static final Binary NO_BINARY = AbstractReadBuffer.NO_BINARY; // ----- data members --------------------------------------------------- /** * A Binary delta compressor that is used to compress individual * decorations. */ private static final BinaryDeltaCompressor s_compressorBinary = new BinaryDeltaCompressor(); /** * The delta compressor responsible for compressing the value portion of * the decorated binaries. */ private DeltaCompressor m_compressorValue; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy