#region Using
#endregion
namespace SharpCompress.Compressor.PPMd.I1
{
///
/// The PPM context structure. This is tightly coupled with .
///
///
///
/// This must be a structure rather than a class because several places in the associated code assume that
/// is a value type (meaning that assignment creates a completely new copy of
/// the instance rather than just copying a reference to the same instance).
///
///
internal partial class Model
{
///
/// The structure which represents the current PPM context. This is 12 bytes in size.
///
internal struct PpmContext
{
public uint Address;
public byte[] Memory;
public static readonly PpmContext Zero = new PpmContext(0, null);
public const int Size = 12;
///
/// Initializes a new instance of the structure.
///
public PpmContext(uint address, byte[] memory)
{
Address = address;
Memory = memory;
}
///
/// Gets or sets the number statistics.
///
public byte NumberStatistics
{
get { return Memory[Address]; }
set { Memory[Address] = value; }
}
///
/// Gets or sets the flags.
///
public byte Flags
{
get { return Memory[Address + 1]; }
set { Memory[Address + 1] = value; }
}
///
/// Gets or sets the summary frequency.
///
public ushort SummaryFrequency
{
get { return (ushort)(((ushort)Memory[Address + 2]) | ((ushort)Memory[Address + 3]) << 8); }
set
{
Memory[Address + 2] = (byte)value;
Memory[Address + 3] = (byte)(value >> 8);
}
}
///
/// Gets or sets the statistics.
///
public PpmState Statistics
{
get { return new PpmState(((uint)Memory[Address + 4]) | ((uint)Memory[Address + 5]) << 8 | ((uint)Memory[Address + 6]) << 16 | ((uint)Memory[Address + 7]) << 24, Memory); }
set
{
Memory[Address + 4] = (byte)value.Address;
Memory[Address + 5] = (byte)(value.Address >> 8);
Memory[Address + 6] = (byte)(value.Address >> 16);
Memory[Address + 7] = (byte)(value.Address >> 24);
}
}
///
/// Gets or sets the suffix.
///
public PpmContext Suffix
{
get { return new PpmContext(((uint)Memory[Address + 8]) | ((uint)Memory[Address + 9]) << 8 | ((uint)Memory[Address + 10]) << 16 | ((uint)Memory[Address + 11]) << 24, Memory); }
set
{
Memory[Address + 8] = (byte)value.Address;
Memory[Address + 9] = (byte)(value.Address >> 8);
Memory[Address + 10] = (byte)(value.Address >> 16);
Memory[Address + 11] = (byte)(value.Address >> 24);
}
}
///
/// The first PPM state associated with the PPM context.
///
///
///
/// The first PPM state overlaps this PPM context instance (the context.SummaryFrequency and context.Statistics members
/// of PpmContext use 6 bytes and so can therefore fit into the space used by the Symbol, Frequency and
/// Successor members of PpmState, since they also add up to 6 bytes).
///
///
/// PpmContext (context.SummaryFrequency and context.Statistics use 6 bytes)
/// 1 context.NumberStatistics
/// 1 context.Flags
/// 2 context.SummaryFrequency
/// 4 context.Statistics (pointer to PpmState)
/// 4 context.Suffix (pointer to PpmContext)
///
///
/// PpmState (total of 6 bytes)
/// 1 Symbol
/// 1 Frequency
/// 4 Successor (pointer to PpmContext)
///
///
///
public PpmState FirstState
{
get { return new PpmState(Address + 2, Memory); }
}
///
/// Gets or sets the symbol of the first PPM state. This is provided for convenience. The same
/// information can be obtained using the Symbol property on the PPM state provided by the
/// property.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The property getter is provided for completeness.")]
public byte FirstStateSymbol
{
get { return Memory[Address + 2]; }
set { Memory[Address + 2] = value; }
}
///
/// Gets or sets the frequency of the first PPM state. This is provided for convenience. The same
/// information can be obtained using the Frequency property on the PPM state provided by the
///context.FirstState property.
///
public byte FirstStateFrequency
{
get { return Memory[Address + 3]; }
set { Memory[Address + 3] = value; }
}
///
/// Gets or sets the successor of the first PPM state. This is provided for convenience. The same
/// information can be obtained using the Successor property on the PPM state provided by the
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The property getter is provided for completeness.")]
public PpmContext FirstStateSuccessor
{
get { return new PpmContext(((uint)Memory[Address + 4]) | ((uint)Memory[Address + 5]) << 8 | ((uint)Memory[Address + 6]) << 16 | ((uint)Memory[Address + 7]) << 24, Memory); }
set
{
Memory[Address + 4] = (byte)value.Address;
Memory[Address + 5] = (byte)(value.Address >> 8);
Memory[Address + 6] = (byte)(value.Address >> 16);
Memory[Address + 7] = (byte)(value.Address >> 24);
}
}
///
/// Allow a pointer to be implicitly converted to a PPM context.
///
///
///
public static implicit operator PpmContext(Pointer pointer)
{
return new PpmContext(pointer.Address, pointer.Memory);
}
///
/// Allow pointer-like addition on a PPM context.
///
///
///
///
public static PpmContext operator +(PpmContext context, int offset)
{
context.Address = (uint)(context.Address + offset * Size);
return context;
}
///
/// Allow pointer-like subtraction on a PPM context.
///
///
///
///
public static PpmContext operator -(PpmContext context, int offset)
{
context.Address = (uint)(context.Address - offset * Size);
return context;
}
///
/// Compare two PPM contexts.
///
///
///
///
public static bool operator <=(PpmContext context1, PpmContext context2)
{
return context1.Address <= context2.Address;
}
///
/// Compare two PPM contexts.
///
///
///
///
public static bool operator >=(PpmContext context1, PpmContext context2)
{
return context1.Address >= context2.Address;
}
///
/// Compare two PPM contexts.
///
///
///
///
public static bool operator ==(PpmContext context1, PpmContext context2)
{
return context1.Address == context2.Address;
}
///
/// Compare two PPM contexts.
///
///
///
///
public static bool operator !=(PpmContext context1, PpmContext context2)
{
return context1.Address != context2.Address;
}
///
/// Indicates whether this instance and a specified object are equal.
///
/// true if obj and this instance are the same type and represent the same value; otherwise, false.
/// Another object to compare to.
public override bool Equals(object obj)
{
if (obj is PpmContext)
{
PpmContext context = (PpmContext)obj;
return context.Address == Address;
}
return base.Equals(obj);
}
///
/// Returns the hash code for this instance.
///
/// A 32-bit signed integer that is the hash code for this instance.
public override int GetHashCode()
{
return Address.GetHashCode();
}
}
private void EncodeBinarySymbol(int symbol, PpmContext context)
{
PpmState state = context.FirstState;
int index1 = probabilities[state.Frequency - 1];
int index2 = numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + previousSuccess + context.Flags + ((runLength >> 26) & 0x20);
if (state.Symbol == symbol)
{
foundState = state;
state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0);
Coder.LowCount = 0;
Coder.HighCount = binarySummary[index1, index2];
binarySummary[index1, index2] += (ushort)(Interval - Mean(binarySummary[index1, index2], PeriodBitCount, 2));
previousSuccess = 1;
runLength++;
}
else
{
Coder.LowCount = binarySummary[index1, index2];
binarySummary[index1, index2] -= (ushort)Mean(binarySummary[index1, index2], PeriodBitCount, 2);
Coder.HighCount = BinaryScale;
initialEscape = ExponentialEscapes[binarySummary[index1, index2] >> 10];
characterMask[state.Symbol] = escapeCount;
previousSuccess = 0;
numberMasked = 0;
foundState = PpmState.Zero;
}
}
private void EncodeSymbol1(int symbol, PpmContext context)
{
uint lowCount;
uint index = context.Statistics.Symbol;
PpmState state = context.Statistics;
Coder.Scale = context.SummaryFrequency;
if (index == symbol)
{
Coder.HighCount = state.Frequency;
previousSuccess = (byte)((2 * Coder.HighCount >= Coder.Scale) ? 1 : 0);
foundState = state;
foundState.Frequency += 4;
context.SummaryFrequency += 4;
runLength += previousSuccess;
if (state.Frequency > MaximumFrequency)
Rescale(context);
Coder.LowCount = 0;
return;
}
lowCount = state.Frequency;
index = context.NumberStatistics;
previousSuccess = 0;
while ((++state).Symbol != symbol)
{
lowCount += state.Frequency;
if (--index == 0)
{
Coder.LowCount = lowCount;
characterMask[state.Symbol] = escapeCount;
numberMasked = context.NumberStatistics;
index = context.NumberStatistics;
foundState = PpmState.Zero;
do
{
characterMask[(--state).Symbol] = escapeCount;
} while (--index != 0);
Coder.HighCount = Coder.Scale;
return;
}
}
Coder.HighCount = (Coder.LowCount = lowCount) + state.Frequency;
Update1(state, context);
}
private void EncodeSymbol2(int symbol, PpmContext context)
{
See2Context see2Context = MakeEscapeFrequency(context);
uint currentSymbol;
uint lowCount = 0;
uint index = (uint)(context.NumberStatistics - numberMasked);
PpmState state = context.Statistics - 1;
do
{
do
{
currentSymbol = state[1].Symbol;
state++;
} while (characterMask[currentSymbol] == escapeCount);
characterMask[currentSymbol] = escapeCount;
if (currentSymbol == symbol)
goto SymbolFound;
lowCount += state.Frequency;
} while (--index != 0);
Coder.LowCount = lowCount;
Coder.Scale += Coder.LowCount;
Coder.HighCount = Coder.Scale;
see2Context.Summary += (ushort)Coder.Scale;
numberMasked = context.NumberStatistics;
return;
SymbolFound:
Coder.LowCount = lowCount;
lowCount += state.Frequency;
Coder.HighCount = lowCount;
for (PpmState p1 = state; --index != 0; )
{
do
{
currentSymbol = p1[1].Symbol;
p1++;
} while (characterMask[currentSymbol] == escapeCount);
lowCount += p1.Frequency;
}
Coder.Scale += lowCount;
see2Context.Update();
Update2(state, context);
}
private void DecodeBinarySymbol(PpmContext context)
{
PpmState state = context.FirstState;
int index1 = probabilities[state.Frequency - 1];
int index2 = numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + previousSuccess + context.Flags + ((runLength >> 26) & 0x20);
if (Coder.RangeGetCurrentShiftCount(TotalBitCount) < binarySummary[index1, index2])
{
foundState = state;
state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0);
Coder.LowCount = 0;
Coder.HighCount = binarySummary[index1, index2];
binarySummary[index1, index2] += (ushort)(Interval - Mean(binarySummary[index1, index2], PeriodBitCount, 2));
previousSuccess = 1;
runLength++;
}
else
{
Coder.LowCount = binarySummary[index1, index2];
binarySummary[index1, index2] -= (ushort)Mean(binarySummary[index1, index2], PeriodBitCount, 2);
Coder.HighCount = BinaryScale;
initialEscape = ExponentialEscapes[binarySummary[index1, index2] >> 10];
characterMask[state.Symbol] = escapeCount;
previousSuccess = 0;
numberMasked = 0;
foundState = PpmState.Zero;
}
}
private void DecodeSymbol1(PpmContext context)
{
uint index;
uint count;
uint highCount = context.Statistics.Frequency;
PpmState state = context.Statistics;
Coder.Scale = context.SummaryFrequency;
count = Coder.RangeGetCurrentCount();
if (count < highCount)
{
Coder.HighCount = highCount;
previousSuccess = (byte)((2 * Coder.HighCount >= Coder.Scale) ? 1 : 0);
foundState = state;
highCount += 4;
foundState.Frequency = (byte)highCount;
context.SummaryFrequency += 4;
runLength += previousSuccess;
if (highCount > MaximumFrequency)
Rescale(context);
Coder.LowCount = 0;
return;
}
index = context.NumberStatistics;
previousSuccess = 0;
while ((highCount += (++state).Frequency) <= count)
{
if (--index == 0)
{
Coder.LowCount = highCount;
characterMask[state.Symbol] = escapeCount;
numberMasked = context.NumberStatistics;
index = context.NumberStatistics;
foundState = PpmState.Zero;
do
{
characterMask[(--state).Symbol] = escapeCount;
} while (--index != 0);
Coder.HighCount = Coder.Scale;
return;
}
}
Coder.HighCount = highCount;
Coder.LowCount = Coder.HighCount - state.Frequency;
Update1(state, context);
}
private void DecodeSymbol2(PpmContext context)
{
See2Context see2Context = MakeEscapeFrequency(context);
uint currentSymbol;
uint count;
uint highCount = 0;
uint index = (uint)(context.NumberStatistics - numberMasked);
uint stateIndex = 0;
PpmState state = context.Statistics - 1;
do
{
do
{
currentSymbol = state[1].Symbol;
state++;
} while (characterMask[currentSymbol] == escapeCount);
highCount += state.Frequency;
decodeStates[stateIndex++] = state; // note that decodeStates is a static array that is re-used on each call to this method (for performance reasons)
} while (--index != 0);
Coder.Scale += highCount;
count = Coder.RangeGetCurrentCount();
stateIndex = 0;
state = decodeStates[stateIndex];
if (count < highCount)
{
highCount = 0;
while ((highCount += state.Frequency) <= count)
state = decodeStates[++stateIndex];
Coder.HighCount = highCount;
Coder.LowCount = Coder.HighCount - state.Frequency;
see2Context.Update();
Update2(state, context);
}
else
{
Coder.LowCount = highCount;
Coder.HighCount = Coder.Scale;
index = (uint)(context.NumberStatistics - numberMasked);
numberMasked = context.NumberStatistics;
do
{
characterMask[decodeStates[stateIndex].Symbol] = escapeCount;
stateIndex++;
} while (--index != 0);
see2Context.Summary += (ushort)Coder.Scale;
}
}
private void Update1(PpmState state, PpmContext context)
{
foundState = state;
foundState.Frequency += 4;
context.SummaryFrequency += 4;
if (state[0].Frequency > state[-1].Frequency)
{
Swap(state[0], state[-1]);
foundState = --state;
if (state.Frequency > MaximumFrequency)
Rescale(context);
}
}
private void Update2(PpmState state, PpmContext context)
{
foundState = state;
foundState.Frequency += 4;
context.SummaryFrequency += 4;
if (state.Frequency > MaximumFrequency)
Rescale(context);
escapeCount++;
runLength = initialRunLength;
}
private See2Context MakeEscapeFrequency(PpmContext context)
{
uint numberStatistics = (uint)2 * context.NumberStatistics;
See2Context see2Context;
if (context.NumberStatistics != 0xff)
{
// Note that context.Flags is always in the range 0 .. 28 (this ensures that the index used for the second
// dimension of the see2Contexts array is always in the range 0 .. 31).
numberStatistics = context.Suffix.NumberStatistics;
int index1 = probabilities[context.NumberStatistics + 2] - 3;
int index2 = ((context.SummaryFrequency > 11 * (context.NumberStatistics + 1)) ? 1 : 0) + ((2 * context.NumberStatistics < numberStatistics + numberMasked) ? 2 : 0) + context.Flags;
see2Context = see2Contexts[index1, index2];
Coder.Scale = see2Context.Mean();
}
else
{
see2Context = emptySee2Context;
Coder.Scale = 1;
}
return see2Context;
}
private void Rescale(PpmContext context)
{
uint oldUnitCount;
int adder;
uint escapeFrequency;
uint index = context.NumberStatistics;
byte localSymbol;
byte localFrequency;
PpmContext localSuccessor;
PpmState p1;
PpmState state;
for (state = foundState; state != context.Statistics; state--)
Swap(state[0], state[-1]);
state.Frequency += 4;
context.SummaryFrequency += 4;
escapeFrequency = (uint)(context.SummaryFrequency - state.Frequency);
adder = (orderFall != 0 || method > ModelRestorationMethod.Freeze) ? 1 : 0;
state.Frequency = (byte)((state.Frequency + adder) >> 1);
context.SummaryFrequency = state.Frequency;
do
{
escapeFrequency -= (++state).Frequency;
state.Frequency = (byte)((state.Frequency + adder) >> 1);
context.SummaryFrequency += state.Frequency;
if (state[0].Frequency > state[-1].Frequency)
{
p1 = state;
localSymbol = p1.Symbol;
localFrequency = p1.Frequency;
localSuccessor = p1.Successor;
do
{
Copy(p1[0], p1[-1]);
} while (localFrequency > (--p1)[-1].Frequency);
p1.Symbol = localSymbol;
p1.Frequency = localFrequency;
p1.Successor = localSuccessor;
}
} while (--index != 0);
if (state.Frequency == 0)
{
do
{
index++;
} while ((--state).Frequency == 0);
escapeFrequency += index;
oldUnitCount = (uint)((context.NumberStatistics + 2) >> 1);
context.NumberStatistics -= (byte)index;
if (context.NumberStatistics == 0)
{
localSymbol = context.Statistics.Symbol;
localFrequency = context.Statistics.Frequency;
localSuccessor = context.Statistics.Successor;
localFrequency = (byte)((2 * localFrequency + escapeFrequency - 1) / escapeFrequency);
if (localFrequency > MaximumFrequency / 3)
localFrequency = (byte)(MaximumFrequency / 3);
Allocator.FreeUnits(context.Statistics, oldUnitCount);
context.FirstStateSymbol = localSymbol;
context.FirstStateFrequency = localFrequency;
context.FirstStateSuccessor = localSuccessor;
context.Flags = (byte)((context.Flags & 0x10) + ((localSymbol >= 0x40) ? 0x08 : 0x00));
foundState = context.FirstState;
return;
}
context.Statistics = Allocator.ShrinkUnits(context.Statistics, oldUnitCount, (uint)((context.NumberStatistics + 2) >> 1));
context.Flags &= 0xf7;
index = context.NumberStatistics;
state = context.Statistics;
context.Flags |= (byte)((state.Symbol >= 0x40) ? 0x08 : 0x00);
do
{
context.Flags |= (byte)(((++state).Symbol >= 0x40) ? 0x08 : 0x00);
} while (--index != 0);
}
escapeFrequency -= (escapeFrequency >> 1);
context.SummaryFrequency += (ushort)escapeFrequency;
context.Flags |= 0x04;
foundState = context.Statistics;
}
private void Refresh(uint oldUnitCount, bool scale, PpmContext context)
{
int index = context.NumberStatistics;
int escapeFrequency;
int scaleValue = (scale ? 1 : 0);
context.Statistics = Allocator.ShrinkUnits(context.Statistics, oldUnitCount, (uint)((index + 2) >> 1));
PpmState statistics = context.Statistics;
context.Flags = (byte)((context.Flags & (0x10 + (scale ? 0x04 : 0x00))) + ((statistics.Symbol >= 0x40) ? 0x08 : 0x00));
escapeFrequency = context.SummaryFrequency - statistics.Frequency;
statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue);
context.SummaryFrequency = statistics.Frequency;
do
{
escapeFrequency -= (++statistics).Frequency;
statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue);
context.SummaryFrequency += statistics.Frequency;
context.Flags |= (byte)((statistics.Symbol >= 0x40) ? 0x08 : 0x00);
} while (--index != 0);
escapeFrequency = (escapeFrequency + scaleValue) >> scaleValue;
context.SummaryFrequency += (ushort)escapeFrequency;
}
private PpmContext CutOff(int order, PpmContext context)
{
int index;
PpmState state;
if (context.NumberStatistics == 0)
{
state = context.FirstState;
if ((Pointer)state.Successor >= Allocator.BaseUnit)
{
if (order < modelOrder)
state.Successor = CutOff(order + 1, state.Successor);
else
state.Successor = PpmContext.Zero;
if (state.Successor == PpmContext.Zero && order > OrderBound)
{
Allocator.SpecialFreeUnits(context);
return PpmContext.Zero;
}
return context;
}
else
{
Allocator.SpecialFreeUnits(context);
return PpmContext.Zero;
}
}
uint unitCount = (uint)((context.NumberStatistics + 2) >> 1);
context.Statistics = Allocator.MoveUnitsUp(context.Statistics, unitCount);
index = context.NumberStatistics;
for (state = context.Statistics + index; state >= context.Statistics; state--)
{
if (state.Successor < Allocator.BaseUnit)
{
state.Successor = PpmContext.Zero;
Swap(state, context.Statistics[index--]);
}
else if (order < modelOrder)
state.Successor = CutOff(order + 1, state.Successor);
else
state.Successor = PpmContext.Zero;
}
if (index != context.NumberStatistics && order != 0)
{
context.NumberStatistics = (byte)index;
state = context.Statistics;
if (index < 0)
{
Allocator.FreeUnits(state, unitCount);
Allocator.SpecialFreeUnits(context);
return PpmContext.Zero;
}
else if (index == 0)
{
context.Flags = (byte)((context.Flags & 0x10) + ((state.Symbol >= 0x40) ? 0x08 : 0x00));
Copy(context.FirstState, state);
Allocator.FreeUnits(state, unitCount);
context.FirstStateFrequency = (byte)((context.FirstStateFrequency + 11) >> 3);
}
else
{
Refresh(unitCount, context.SummaryFrequency > 16 * index, context);
}
}
return context;
}
private PpmContext RemoveBinaryContexts(int order, PpmContext context)
{
if (context.NumberStatistics == 0)
{
PpmState state = context.FirstState;
if ((Pointer)state.Successor >= Allocator.BaseUnit && order < modelOrder)
state.Successor = RemoveBinaryContexts(order + 1, state.Successor);
else
state.Successor = PpmContext.Zero;
if ((state.Successor == PpmContext.Zero) && (context.Suffix.NumberStatistics == 0 || context.Suffix.Flags == 0xff))
{
Allocator.FreeUnits(context, 1);
return PpmContext.Zero;
}
else
{
return context;
}
}
for (PpmState state = context.Statistics + context.NumberStatistics; state >= context.Statistics; state--)
{
if ((Pointer)state.Successor >= Allocator.BaseUnit && order < modelOrder)
state.Successor = RemoveBinaryContexts(order + 1, state.Successor);
else
state.Successor = PpmContext.Zero;
}
return context;
}
}
}