#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; } } }