libarchive/archive_options.c \
libarchive/archive_options_private.h \
libarchive/archive_platform.h \
+ libarchive/archive_ppmd.h \
+ libarchive/archive_ppmd7.c \
+ libarchive/archive_ppmd7.h \
+ libarchive/archive_ppmd7dec.c \
libarchive/archive_private.h \
libarchive/archive_rb.c \
libarchive/archive_rb.h \
libarchive/test/test_read_format_mtree.mtree.uu \
libarchive/test/test_read_format_mtree_nomagic.mtree.uu \
libarchive/test/test_read_format_rar.rar.uu \
+ libarchive/test/test_read_format_rar_compress_best.rar.uu \
libarchive/test/test_read_format_rar_compress_normal.rar.uu \
libarchive/test/test_read_format_rar_multi_lzss_blocks.rar.uu \
libarchive/test/test_read_format_rar_noeof.rar.uu \
--- /dev/null
+/* Ppmd.h -- PPMD codec common code
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#ifndef ARCHIVE_PPMD_H_INCLUDED
+#define ARCHIVE_PPMD_H_INCLUDED
+
+#include <stddef.h>
+
+#include "archive_read_private.h"
+
+/*** Begin defined in Types.h ***/
+
+typedef unsigned char Byte;
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+ NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+ struct archive_read *a;
+ Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
+} IByteIn;
+
+typedef struct
+{
+ struct archive_read *a;
+ void (*Write)(void *p, Byte b);
+} IByteOut;
+
+
+typedef struct
+{
+ void *(*Alloc)(void *p, size_t size);
+ void (*Free)(void *p, void *address); /* address can be 0 */
+} ISzAlloc;
+
+/*** End defined in Types.h ***/
+/*** Begin defined in CpuArch.h ***/
+
+#if defined(_M_IX86) || defined(__i386__)
+#define MY_CPU_X86
+#endif
+
+#if defined(MY_CPU_X86) || defined(_M_ARM)
+#define MY_CPU_32BIT
+#endif
+
+#ifdef MY_CPU_32BIT
+#define PPMD_32BIT
+#endif
+
+/*** End defined in CpuArch.h ***/
+
+#define PPMD_INT_BITS 7
+#define PPMD_PERIOD_BITS 7
+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))
+
+#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))
+
+#define PPMD_N1 4
+#define PPMD_N2 4
+#define PPMD_N3 4
+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)
+
+#pragma pack(push,1)
+
+/* SEE-contexts for PPM-contexts with masked symbols */
+typedef struct
+{
+ UInt16 Summ; /* Freq */
+ Byte Shift; /* Speed of Freq change; low Shift is for fast change */
+ Byte Count; /* Count to next change of Shift */
+} CPpmd_See;
+
+#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
+ { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }
+
+typedef struct
+{
+ Byte Symbol;
+ Byte Freq;
+ UInt16 SuccessorLow;
+ UInt16 SuccessorHigh;
+} CPpmd_State;
+
+#pragma pack(pop)
+
+typedef
+ #ifdef PPMD_32BIT
+ CPpmd_State *
+ #else
+ UInt32
+ #endif
+ CPpmd_State_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ void *
+ #else
+ UInt32
+ #endif
+ CPpmd_Void_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ Byte *
+ #else
+ UInt32
+ #endif
+ CPpmd_Byte_Ref;
+
+#define PPMD_SetAllBitsIn256Bytes(p) \
+ { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \
+ p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }}
+
+#endif
--- /dev/null
+/* Ppmd7.c -- PPMdH codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <memory.h>
+
+#include "archive_ppmd7.h"
+
+const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+ #define REF(ptr) (ptr)
+#else
+ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
+#define STATS(ctx) Ppmd7_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+typedef CPpmd7_Context * CTX_PTR;
+
+struct CPpmd7_Node_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Node_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Node_Ref;
+
+typedef struct CPpmd7_Node_
+{
+ UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
+ UInt16 NU;
+ CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
+ CPpmd7_Node_Ref Prev;
+} CPpmd7_Node;
+
+#ifdef PPMD_32BIT
+ #define NODE(ptr) (ptr)
+#else
+ #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
+#endif
+
+void Ppmd7_Construct(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ p->Base = 0;
+
+ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+ do { p->Units2Indx[k++] = (Byte)i; } while(--step);
+ p->Indx2Units[i] = (Byte)k;
+ }
+
+ p->NS2BSIndx[0] = (0 << 1);
+ p->NS2BSIndx[1] = (1 << 1);
+ memset(p->NS2BSIndx + 2, (2 << 1), 9);
+ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+ for (i = 0; i < 3; i++)
+ p->NS2Indx[i] = (Byte)i;
+ for (m = i, k = 1; i < 256; i++)
+ {
+ p->NS2Indx[i] = (Byte)m;
+ if (--k == 0)
+ k = (++m) - 2;
+ }
+
+ memset(p->HB2Flag, 0, 0x40);
+ memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
+}
+
+void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc)
+{
+ alloc->Free(alloc, p->Base);
+ p->Size = 0;
+ p->Base = 0;
+}
+
+Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc)
+{
+ if (p->Base == 0 || p->Size != size)
+ {
+ Ppmd7_Free(p, alloc);
+ p->AlignOffset =
+ #ifdef PPMD_32BIT
+ (4 - size) & 3;
+ #else
+ 4 - (size & 3);
+ #endif
+ if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size
+ #ifndef PPMD_32BIT
+ + UNIT_SIZE
+ #endif
+ )) == 0)
+ return False;
+ p->Size = size;
+ }
+ return True;
+}
+
+static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
+{
+ *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
+ p->FreeList[indx] = REF(node);
+}
+
+static void *RemoveNode(CPpmd7 *p, unsigned indx)
+{
+ CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
+ p->FreeList[indx] = *node;
+ return node;
+}
+
+static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+ unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+ ptr = (Byte *)ptr + U2B(I2U(newIndx));
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+ }
+ InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd7 *p)
+{
+ #ifdef PPMD_32BIT
+ CPpmd7_Node headItem;
+ CPpmd7_Node_Ref head = &headItem;
+ #else
+ CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
+ #endif
+
+ CPpmd7_Node_Ref n = head;
+ unsigned i;
+
+ p->GlueCount = 255;
+
+ /* create doubly-linked list of free blocks */
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ UInt16 nu = I2U(i);
+ CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
+ p->FreeList[i] = 0;
+ while (next != 0)
+ {
+ CPpmd7_Node *node = NODE(next);
+ node->Next = n;
+ n = NODE(n)->Prev = next;
+ next = *(const CPpmd7_Node_Ref *)node;
+ node->Stamp = 0;
+ node->NU = (UInt16)nu;
+ }
+ }
+ NODE(head)->Stamp = 1;
+ NODE(head)->Next = n;
+ NODE(n)->Prev = head;
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;
+
+ /* Glue free blocks */
+ while (n != head)
+ {
+ CPpmd7_Node *node = NODE(n);
+ UInt32 nu = (UInt32)node->NU;
+ for (;;)
+ {
+ CPpmd7_Node *node2 = NODE(n) + nu;
+ nu += node2->NU;
+ if (node2->Stamp != 0 || nu >= 0x10000)
+ break;
+ NODE(node2->Prev)->Next = node2->Next;
+ NODE(node2->Next)->Prev = node2->Prev;
+ node->NU = (UInt16)nu;
+ }
+ n = node->Next;
+ }
+
+ /* Fill lists of free blocks */
+ for (n = NODE(head)->Next; n != head;)
+ {
+ CPpmd7_Node *node = NODE(n);
+ unsigned nu;
+ CPpmd7_Node_Ref next = node->Next;
+ for (nu = node->NU; nu > 128; nu -= 128, node += 128)
+ InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, node + k, nu - k - 1);
+ }
+ InsertNode(p, node, i);
+ n = next;
+ }
+}
+
+static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
+{
+ unsigned i;
+ void *retVal;
+ if (p->GlueCount == 0)
+ {
+ GlueFreeBlocks(p);
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ }
+ i = indx;
+ do
+ {
+ if (++i == PPMD_NUM_INDEXES)
+ {
+ UInt32 numBytes = U2B(I2U(indx));
+ p->GlueCount--;
+ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+ }
+ }
+ while (p->FreeList[i] == 0);
+ retVal = RemoveNode(p, i);
+ SplitBlock(p, retVal, i, indx);
+ return retVal;
+}
+
+static void *AllocUnits(CPpmd7 *p, unsigned indx)
+{
+ UInt32 numBytes;
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ numBytes = U2B(I2U(indx));
+ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+ {
+ void *retVal = p->LoUnit;
+ p->LoUnit += numBytes;
+ return retVal;
+ }
+ return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
+ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }
+
+static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+ unsigned i0 = U2I(oldNU);
+ unsigned i1 = U2I(newNU);
+ if (i0 == i1)
+ return oldPtr;
+ if (p->FreeList[i1] != 0)
+ {
+ void *ptr = RemoveNode(p, i1);
+ MyMem12Cpy(ptr, oldPtr, newNU);
+ InsertNode(p, oldPtr, i0);
+ return ptr;
+ }
+ SplitBlock(p, oldPtr, i0, i1);
+ return oldPtr;
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+static void RestartModel(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ memset(p->FreeList, 0, sizeof(p->FreeList));
+ p->Text = p->Base + p->AlignOffset;
+ p->HiUnit = p->Text + p->Size;
+ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+ p->GlueCount = 0;
+
+ p->OrderFall = p->MaxOrder;
+ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+ p->PrevSuccess = 0;
+
+ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+ p->MinContext->Suffix = 0;
+ p->MinContext->NumStats = 256;
+ p->MinContext->SummFreq = 256 + 1;
+ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+ p->LoUnit += U2B(256 / 2);
+ p->MinContext->Stats = REF(p->FoundState);
+ for (i = 0; i < 256; i++)
+ {
+ CPpmd_State *s = &p->FoundState[i];
+ s->Symbol = (Byte)i;
+ s->Freq = 1;
+ SetSuccessor(s, 0);
+ }
+
+ for (i = 0; i < 128; i++)
+ for (k = 0; k < 8; k++)
+ {
+ UInt16 *dest = p->BinSumm[i] + k;
+ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
+ for (m = 0; m < 64; m += 8)
+ dest[m] = val;
+ }
+
+ for (i = 0; i < 25; i++)
+ for (k = 0; k < 16; k++)
+ {
+ CPpmd_See *s = &p->See[i][k];
+ s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
+ s->Count = 4;
+ }
+}
+
+void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
+{
+ p->MaxOrder = maxOrder;
+ RestartModel(p);
+ p->DummySee.Shift = PPMD_PERIOD_BITS;
+ p->DummySee.Summ = 0; /* unused */
+ p->DummySee.Count = 64; /* unused */
+}
+
+static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
+{
+ CPpmd_State upState;
+ CTX_PTR c = p->MinContext;
+ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+ CPpmd_State *ps[PPMD7_MAX_ORDER];
+ unsigned numPs = 0;
+
+ if (!skip)
+ ps[numPs++] = p->FoundState;
+
+ while (c->Suffix)
+ {
+ CPpmd_Void_Ref successor;
+ CPpmd_State *s;
+ c = SUFFIX(c);
+ if (c->NumStats != 1)
+ {
+ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+ }
+ else
+ s = ONE_STATE(c);
+ successor = SUCCESSOR(s);
+ if (successor != upBranch)
+ {
+ c = CTX(successor);
+ if (numPs == 0)
+ return c;
+ break;
+ }
+ ps[numPs++] = s;
+ }
+
+ upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
+ SetSuccessor(&upState, upBranch + 1);
+
+ if (c->NumStats == 1)
+ upState.Freq = ONE_STATE(c)->Freq;
+ else
+ {
+ UInt32 cf, s0;
+ CPpmd_State *s;
+ for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+ cf = s->Freq - 1;
+ s0 = c->SummFreq - c->NumStats - cf;
+ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+ }
+
+ do
+ {
+ /* Create Child */
+ CTX_PTR c1; /* = AllocContext(p); */
+ if (p->HiUnit != p->LoUnit)
+ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+ else if (p->FreeList[0] != 0)
+ c1 = (CTX_PTR)RemoveNode(p, 0);
+ else
+ {
+ c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+ if (!c1)
+ return NULL;
+ }
+ c1->NumStats = 1;
+ *ONE_STATE(c1) = upState;
+ c1->Suffix = REF(c);
+ SetSuccessor(ps[--numPs], REF(c1));
+ c = c1;
+ }
+ while (numPs != 0);
+
+ return c;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+ CPpmd_State tmp = *t1;
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static void UpdateModel(CPpmd7 *p)
+{
+ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+ CTX_PTR c;
+ unsigned s0, ns;
+
+ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+ {
+ c = SUFFIX(p->MinContext);
+
+ if (c->NumStats == 1)
+ {
+ CPpmd_State *s = ONE_STATE(c);
+ if (s->Freq < 32)
+ s->Freq++;
+ }
+ else
+ {
+ CPpmd_State *s = STATS(c);
+ if (s->Symbol != p->FoundState->Symbol)
+ {
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s[0].Freq >= s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ s--;
+ }
+ }
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ }
+
+ if (p->OrderFall == 0)
+ {
+ p->MinContext = p->MaxContext = CreateSuccessors(p, True);
+ if (p->MinContext == 0)
+ {
+ RestartModel(p);
+ return;
+ }
+ SetSuccessor(p->FoundState, REF(p->MinContext));
+ return;
+ }
+
+ *p->Text++ = p->FoundState->Symbol;
+ successor = REF(p->Text);
+ if (p->Text >= p->UnitsStart)
+ {
+ RestartModel(p);
+ return;
+ }
+
+ if (fSuccessor)
+ {
+ if (fSuccessor <= successor)
+ {
+ CTX_PTR cs = CreateSuccessors(p, False);
+ if (cs == NULL)
+ {
+ RestartModel(p);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+ if (--p->OrderFall == 0)
+ {
+ successor = fSuccessor;
+ p->Text -= (p->MaxContext != p->MinContext);
+ }
+ }
+ else
+ {
+ SetSuccessor(p->FoundState, successor);
+ fSuccessor = REF(p->MinContext);
+ }
+
+ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);
+
+ for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
+ {
+ unsigned ns1;
+ UInt32 cf, sf;
+ if ((ns1 = c->NumStats) != 1)
+ {
+ if ((ns1 & 1) == 0)
+ {
+ /* Expand for one UNIT */
+ unsigned oldNU = ns1 >> 1;
+ unsigned i = U2I(oldNU);
+ if (i != U2I(oldNU + 1))
+ {
+ void *ptr = AllocUnits(p, i + 1);
+ void *oldPtr;
+ if (!ptr)
+ {
+ RestartModel(p);
+ return;
+ }
+ oldPtr = STATS(c);
+ MyMem12Cpy(ptr, oldPtr, oldNU);
+ InsertNode(p, oldPtr, i);
+ c->Stats = STATS_REF(ptr);
+ }
+ }
+ c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
+ }
+ else
+ {
+ CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
+ if (!s)
+ {
+ RestartModel(p);
+ return;
+ }
+ *s = *ONE_STATE(c);
+ c->Stats = REF(s);
+ if (s->Freq < MAX_FREQ / 4 - 1)
+ s->Freq <<= 1;
+ else
+ s->Freq = MAX_FREQ - 4;
+ c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
+ }
+ cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
+ sf = (UInt32)s0 + c->SummFreq;
+ if (cf < 6 * sf)
+ {
+ cf = 1 + (cf > sf) + (cf >= 4 * sf);
+ c->SummFreq += 3;
+ }
+ else
+ {
+ cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
+ c->SummFreq = (UInt16)(c->SummFreq + cf);
+ }
+ {
+ CPpmd_State *s = STATS(c) + ns1;
+ SetSuccessor(s, successor);
+ s->Symbol = p->FoundState->Symbol;
+ s->Freq = (Byte)cf;
+ c->NumStats = (UInt16)(ns1 + 1);
+ }
+ }
+ p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+
+static void Rescale(CPpmd7 *p)
+{
+ unsigned i, adder, sumFreq, escFreq;
+ CPpmd_State *stats = STATS(p->MinContext);
+ CPpmd_State *s = p->FoundState;
+ {
+ CPpmd_State tmp = *s;
+ for (; s != stats; s--)
+ s[0] = s[-1];
+ *s = tmp;
+ }
+ escFreq = p->MinContext->SummFreq - s->Freq;
+ s->Freq += 4;
+ adder = (p->OrderFall != 0);
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq = s->Freq;
+
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ escFreq -= (++s)->Freq;
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq += s->Freq;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ CPpmd_State *s1 = s;
+ CPpmd_State tmp = *s1;
+ do
+ s1[0] = s1[-1];
+ while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+ *s1 = tmp;
+ }
+ }
+ while (--i);
+
+ if (s->Freq == 0)
+ {
+ unsigned numStats = p->MinContext->NumStats;
+ unsigned n0, n1;
+ do { i++; } while ((--s)->Freq == 0);
+ escFreq += i;
+ p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
+ if (p->MinContext->NumStats == 1)
+ {
+ CPpmd_State tmp = *stats;
+ do
+ {
+ tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
+ escFreq >>= 1;
+ }
+ while (escFreq > 1);
+ InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
+ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+ return;
+ }
+ n0 = (numStats + 1) >> 1;
+ n1 = (p->MinContext->NumStats + 1) >> 1;
+ if (n0 != n1)
+ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+ }
+ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+ p->FoundState = STATS(p->MinContext);
+}
+
+CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
+{
+ CPpmd_See *see;
+ unsigned nonMasked = p->MinContext->NumStats - numMasked;
+ if (p->MinContext->NumStats != 256)
+ {
+ see = p->See[p->NS2Indx[nonMasked - 1]] +
+ (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
+ 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
+ 4 * (numMasked > nonMasked) +
+ p->HiBitsFlag;
+ {
+ unsigned r = (see->Summ >> see->Shift);
+ see->Summ = (UInt16)(see->Summ - r);
+ *escFreq = r + (r == 0);
+ }
+ }
+ else
+ {
+ see = &p->DummySee;
+ *escFreq = 1;
+ }
+ return see;
+}
+
+static void NextContext(CPpmd7 *p)
+{
+ CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+ if (p->OrderFall == 0 && (Byte *)c > p->Text)
+ p->MinContext = p->MaxContext = c;
+ else
+ UpdateModel(p);
+}
+
+void Ppmd7_Update1(CPpmd7 *p)
+{
+ CPpmd_State *s = p->FoundState;
+ s->Freq += 4;
+ p->MinContext->SummFreq += 4;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ p->FoundState = --s;
+ if (s->Freq > MAX_FREQ)
+ Rescale(p);
+ }
+ NextContext(p);
+}
+
+void Ppmd7_Update1_0(CPpmd7 *p)
+{
+ p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
+ p->RunLength += p->PrevSuccess;
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ NextContext(p);
+}
+
+void Ppmd7_UpdateBin(CPpmd7 *p)
+{
+ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
+ p->PrevSuccess = 1;
+ p->RunLength++;
+ NextContext(p);
+}
+
+void Ppmd7_Update2(CPpmd7 *p)
+{
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ p->RunLength = p->InitRL;
+ UpdateModel(p);
+}
--- /dev/null
+/* Ppmd7.h -- PPMdH compression codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+/* This code supports virtual RangeDecoder and includes the implementation
+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#ifndef ARCHIVE_PPMD7_H_INCLUDED
+#define ARCHIVE_PPMD7_H_INCLUDED
+
+#include "archive_ppmd.h"
+
+#define PPMD7_MIN_ORDER 2
+#define PPMD7_MAX_ORDER 64
+
+#define PPMD7_MIN_MEM_SIZE (1 << 11)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3)
+
+struct CPpmd7_Context_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Context_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Context_Ref;
+
+typedef struct CPpmd7_Context_
+{
+ UInt16 NumStats;
+ UInt16 SummFreq;
+ CPpmd_State_Ref Stats;
+ CPpmd7_Context_Ref Suffix;
+} CPpmd7_Context;
+
+#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+typedef struct
+{
+ CPpmd7_Context *MinContext, *MaxContext;
+ CPpmd_State *FoundState;
+ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
+ Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+ UInt32 Size;
+ UInt32 GlueCount;
+ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+ UInt32 AlignOffset;
+
+ Byte Indx2Units[PPMD_NUM_INDEXES];
+ Byte Units2Indx[128];
+ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+ Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+ CPpmd_See DummySee, See[25][16];
+ UInt16 BinSumm[128][64];
+} CPpmd7;
+
+void Ppmd7_Construct(CPpmd7 *p);
+Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc);
+void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc);
+void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder);
+#define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+
+/* ---------- Internal Functions ---------- */
+
+extern const Byte PPMD7_kExpEscape[16];
+
+#ifdef PPMD_32BIT
+ #define Ppmd7_GetPtr(p, ptr) (ptr)
+ #define Ppmd7_GetContext(p, ptr) (ptr)
+ #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
+#else
+ #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+ #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
+ #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+void Ppmd7_Update1(CPpmd7 *p);
+void Ppmd7_Update1_0(CPpmd7 *p);
+void Ppmd7_Update2(CPpmd7 *p);
+void Ppmd7_UpdateBin(CPpmd7 *p);
+
+#define Ppmd7_GetBinSumm(p) \
+ &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
+ p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
+ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
+ 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
+ ((p->RunLength >> 26) & 0x20)]
+
+CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale);
+
+
+/* ---------- Decode ---------- */
+
+typedef struct
+{
+ UInt32 (*GetThreshold)(void *p, UInt32 total);
+ void (*Decode)(void *p, UInt32 start, UInt32 size);
+ UInt32 (*DecodeBit)(void *p, UInt32 size0);
+} IPpmd7_RangeDec;
+
+typedef struct
+{
+ IPpmd7_RangeDec p;
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 Low;
+ UInt32 Bottom;
+ IByteIn *Stream;
+} CPpmd7z_RangeDec;
+
+void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p);
+void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p);
+Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p);
+Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p);
+#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+
+int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc);
+
+
+/* ---------- Encode ---------- */
+
+/*typedef struct
+{
+ UInt64 Low;
+ UInt32 Range;
+ Byte Cache;
+ UInt64 CacheSize;
+ IByteOut *Stream;
+} CPpmd7z_RangeEnc;
+
+void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p);
+void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p);
+
+void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);
+*/
+#endif
--- /dev/null
+/* Ppmd7Dec.c -- PPMdH Decoder
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include "archive_ppmd7.h"
+
+#define kTopValue (1 << 24)
+
+static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ unsigned i;
+ p->Low = p->Bottom = 0;
+ p->Range = 0xFFFFFFFF;
+ for (i = 0; i < 4; i++)
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ return (p->Code < 0xFFFFFFFF);
+}
+
+Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (p->Stream->Read((void *)p->Stream) != 0)
+ return False;
+ return Ppmd_RangeDec_Init(p);
+}
+
+Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (!Ppmd_RangeDec_Init(p))
+ return False;
+ p->Bottom = 0x8000;
+ return True;
+}
+
+static UInt32 Range_GetThreshold(void *pp, UInt32 total)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ return (p->Code - p->Low) / (p->Range /= total);
+}
+
+static void Range_Normalize(CPpmd7z_RangeDec *p)
+{
+ while (1)
+ {
+ if((p->Low ^ (p->Low + p->Range)) >= kTopValue)
+ {
+ if(p->Range >= p->Bottom)
+ break;
+ else
+ p->Range = -p->Low & (p->Bottom - 1);
+ }
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ p->Range <<= 8;
+ p->Low <<= 8;
+ }
+}
+
+static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Code -= start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Low += start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 newBound = (p->Range >> 14) * size0;
+ UInt32 symbol;
+ if (p->Code < newBound)
+ {
+ symbol = 0;
+ p->Range = newBound;
+ }
+ else
+ {
+ symbol = 1;
+ p->Code -= newBound;
+ p->Range -= newBound;
+ }
+ Range_Normalize(p);
+ return symbol;
+}
+
+static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);
+ if(value < size0)
+ {
+ bit = 0;
+ p->p.Decode(p, 0, size0);
+ }
+ else
+ {
+ bit = 1;
+ p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);
+ }
+ return bit;
+}
+
+void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_7z;
+ p->p.DecodeBit = Range_DecodeBit_7z;
+}
+
+void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_RAR;
+ p->p.DecodeBit = Range_DecodeBit_RAR;
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 1)
+ {
+ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+ unsigned i;
+ UInt32 count, hiCnt;
+ if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+ {
+ Byte symbol;
+ rc->Decode(rc, 0, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1_0(p);
+ return symbol;
+ }
+ p->PrevSuccess = 0;
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ if ((hiCnt += (++s)->Freq) > count)
+ {
+ Byte symbol;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1(p);
+ return symbol;
+ }
+ }
+ while (--i);
+ if (count >= p->MinContext->SummFreq)
+ return -2;
+ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+ rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats - 1;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd7_GetBinSumm(p);
+ if (rc->DecodeBit(rc, *prob) == 0)
+ {
+ Byte symbol;
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
+ Ppmd7_UpdateBin(p);
+ return symbol;
+ }
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ for (;;)
+ {
+ CPpmd_State *ps[256], *s;
+ UInt32 freqSum, count, hiCnt;
+ CPpmd_See *see;
+ unsigned i, num, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return -1;
+ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+ hiCnt = 0;
+ s = Ppmd7_GetStats(p, p->MinContext);
+ i = 0;
+ num = p->MinContext->NumStats - numMasked;
+ do
+ {
+ int k = (int)(MASK(s->Symbol));
+ hiCnt += (s->Freq & k);
+ ps[i] = s++;
+ i -= k;
+ }
+ while (i != num);
+
+ see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
+ freqSum += hiCnt;
+ count = rc->GetThreshold(rc, freqSum);
+
+ if (count < hiCnt)
+ {
+ Byte symbol;
+ CPpmd_State **pps = ps;
+ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+ s = *pps;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ Ppmd_See_Update(see);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update2(p);
+ return symbol;
+ }
+ if (count >= freqSum)
+ return -2;
+ rc->Decode(rc, hiCnt, freqSum - hiCnt);
+ see->Summ = (UInt16)(see->Summ + freqSum);
+ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+ }
+}
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_ppmd7.h"
#include "archive_private.h"
#include "archive_read_private.h"
int64_t bytes_uncopied;
int64_t offset;
char valid;
+ unsigned char *unp_buffer;
+ unsigned int dictionary_size;
+ char start_new_block;
+
+ /* LZSS members */
struct huffman_code maincode;
struct huffman_code offsetcode;
struct huffman_code lowoffsetcode;
struct huffman_code lengthcode;
unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
- unsigned char *unp_buffer;
struct lzss lzss;
- unsigned int dictionary_size;
char output_last_match;
unsigned int lastlength;
unsigned int lastoffset;
unsigned int lastlowoffset;
unsigned int numlowoffsetrepeats;
int64_t filterstart;
- char start_new_block;
char start_new_table;
+ /* PPMd Variant H members */
+ char is_ppmd_block;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeDec range_dec;
+ IByteIn bytein;
+
/*
* Bit stream reader.
*/
struct archive_string_conv *);
static int read_data_stored(struct archive_read *, const void **, size_t *,
int64_t *);
-static int read_data_lzss(struct archive_read *, const void **, size_t *,
+static int read_data_compressed(struct archive_read *, const void **, size_t *,
int64_t *);
static int rar_br_preparation(struct archive_read *, struct rar_br *);
static int parse_codes(struct archive_read *);
static int copy_from_lzss_window(struct archive_read *, const void **,
int64_t, int);
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define rar_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define rar_br_bits(br, n) \
+ (((uint32_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define rar_br_bits_forced(br, n) \
+ (((uint32_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : there is no data in the stream. */
+#define rar_br_read_ahead(a, br, n) \
+ ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
+/* Notify how man bits we consumed. */
+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
+
+static const uint32_t cache_masks[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+ 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+ 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+ 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+ 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+ 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+ 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+rar_br_fillup(struct archive_read *a, struct rar_br *br)
+{
+/*
+ * x86 proccessor family can read misaligned data without an access error.
+ */
+#if defined(__i386__) || (defined(_MSC_VER) && defined(_M_IX86))
+# if defined(_WIN32) && !defined(__CYGWIN__)
+# define rar_be16dec(p) _byteswap_ushort(*(const uint16_t *)(p))
+# elif defined(be16toh)
+# define rar_be16dec(p) be16toh(*(const uint16_t *)(p))
+# elif defined(betoh16)
+# define rar_be16dec(p) betoh16(*(const uint16_t *)(p))
+# else
+# define rar_be16dec archive_be16dec
+# endif
+# if defined(_WIN32) && !defined(__CYGWIN__)
+# define rar_be32dec(p) _byteswap_ulong(*(const uint32_t *)(p))
+# elif defined(be32toh)
+# define rar_be32dec(p) be32toh(*(const uint32_t *)(p))
+# elif defined(betoh32)
+# define rar_be32dec(p) betoh32(*(const uint32_t *)(p))
+# else
+# define rar_be32dec archive_be32dec
+# endif
+# if defined(_WIN32) && !defined(__CYGWIN__)
+# define rar_be64dec(p) _byteswap_uint64(*(const uint64_t *)(p))
+# elif defined(be64toh)
+# define rar_be64dec(p) be64toh(*(const uint64_t *)(p))
+# elif defined(betoh64)
+# define rar_be64dec(p) betoh64(*(const uint64_t *)(p))
+# else
+# define rar_be64dec archive_be64dec
+# endif
+#else
+# define rar_be16dec archive_be16dec
+# define rar_be32dec archive_be32dec
+# define rar_be64dec archive_be64dec
+#endif
+ struct rar *rar = (struct rar *)(a->format->data);
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ switch (n >> 3) {
+ case 8:
+ if (br->avail_in >= 8) {
+ br->cache_buffer =
+ rar_be64dec(br->next_in);
+ br->next_in += 8;
+ br->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ rar->bytes_unconsumed += 8;
+ rar->bytes_remaining -= 8;
+ return (1);
+ }
+ break;
+ case 7:
+ if (br->avail_in >= 8) {/* Read extra one. */
+ br->cache_buffer =
+ (br->cache_buffer << 56) |
+ rar_be64dec(br->next_in) >> 8;
+ br->next_in += 7;
+ br->avail_in -= 7;
+ br->cache_avail += 7 * 8;
+ rar->bytes_unconsumed += 7;
+ rar->bytes_remaining -= 7;
+ return (1);
+ }
+ break;
+ case 6:
+ if (br->avail_in >= 6) {
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ (((uint64_t)rar_be32dec(
+ br->next_in)) << 16) |
+ rar_be16dec(&br->next_in[4]);
+ br->next_in += 6;
+ br->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ rar->bytes_unconsumed += 6;
+ rar->bytes_remaining -= 6;
+ return (1);
+ }
+ break;
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ if (br->avail_in <= 0) {
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor
+ * actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+ br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL)
+ return (0);
+ if (br->avail_in > rar->bytes_remaining)
+ br->avail_in = rar->bytes_remaining;
+ if (br->avail_in == 0)
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 8) | *br->next_in++;
+ br->avail_in--;
+ br->cache_avail += 8;
+ n -= 8;
+ rar->bytes_unconsumed++;
+ rar->bytes_remaining--;
+ }
+#undef rar_be16dec
+#undef rar_be32dec
+#undef rar_be64dec
+}
+
+static int
+rar_br_preparation(struct archive_read *a, struct rar_br *br)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (rar->bytes_remaining > 0) {
+ br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (br->avail_in > rar->bytes_remaining)
+ br->avail_in = rar->bytes_remaining;
+ if (br->cache_avail == 0)
+ (void)rar_br_fillup(a, br);
+ }
+ return (ARCHIVE_OK);
+}
+
/* Find last bit set */
static inline int
rar_fls(unsigned int word)
rar->lzss.position += length;
}
+static void *
+ppmd_alloc(void *p, size_t size)
+{
+ (void)p;
+ return malloc(size);
+}
+static void
+ppmd_free(void *p, void *address)
+{
+ (void)p;
+ free(address);
+}
+static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free };
+
+static Byte
+ppmd_read(void *p)
+{
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+ Byte b;
+ if (!rar_br_read_ahead(a, br, 1))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return 0;
+ }
+ b = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ return b;
+}
+
int
archive_read_support_format_rar(struct archive *_a)
{
case COMPRESS_METHOD_FASTEST:
case COMPRESS_METHOD_FAST:
case COMPRESS_METHOD_NORMAL:
- return read_data_lzss(a, buff, size, offset);
-
case COMPRESS_METHOD_GOOD:
case COMPRESS_METHOD_BEST:
+ return read_data_compressed(a, buff, size, offset);
+
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported compression method for RAR file.");
free_codes(a);
free(rar->unp_buffer);
free(rar->lzss.window);
+ Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
free(rar);
(a->format->data) = NULL;
return (ARCHIVE_OK);
rar->br.cache_avail = 0;
rar->br.avail_in = 0;
rar->valid = 1;
+ rar->is_ppmd_block = 0;
rar->start_new_table = 1;
free(rar->unp_buffer);
rar->unp_buffer = NULL;
memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+ Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
/* Don't set any archive entries for non-file header types */
if (head_type == NEWSUB_HEAD)
}
static int
-read_data_lzss(struct archive_read *a, const void **buff, size_t *size,
+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
int64_t *offset)
{
struct rar *rar;
int64_t start, end, actualend;
- int ret = (ARCHIVE_OK);
+ int ret = (ARCHIVE_OK), sym;
rar = (struct rar *)(a->format->data);
if (!rar->valid)
*buff = NULL;
*size = 0;
*offset = rar->offset;
+ Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
return (ARCHIVE_EOF);
}
- if (rar->dictionary_size && rar->bytes_uncopied > 0)
+ if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
{
*offset = rar->offset;
if (rar->offset + rar->bytes_uncopied > rar->unp_size)
if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
return (ret);
- start = rar->offset;
- end = start + rar->dictionary_size;
- rar->filterstart = INT64_MAX;
-
- if ((actualend = expand(a, end)) < 0)
- return ((int)actualend);
-
- rar->bytes_uncopied = actualend - start;
- if (rar->bytes_uncopied == 0) {
- /* Broken RAR files cause this case.
- * NOTE: If this case were possible on a normal RAR file
- * we would find out where it was actually bad and
- * what we would do to solve it. */
+ if (rar->is_ppmd_block)
+ {
+ if (!rar->unp_buffer)
+ {
+ if ((rar->unp_buffer = malloc(1)) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for uncompressed data.");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if ((sym = Ppmd7_DecodeSymbol(&rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Internal error extracting RAR file.");
- return (ARCHIVE_FAILED);
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ *offset = rar->offset++;
+ *size = 1;
+ *rar->unp_buffer = sym;
+ *buff = rar->unp_buffer;
+ return (ret);
}
- *offset = rar->offset;
- if (rar->offset + rar->bytes_uncopied > rar->unp_size)
- *size = rar->unp_size - rar->offset;
else
- *size = rar->bytes_uncopied;
- ret = copy_from_lzss_window(a, buff, *offset, *size);
- rar->offset += *size;
- rar->bytes_uncopied -= *size;
+ {
+ start = rar->offset;
+ end = start + rar->dictionary_size;
+ rar->filterstart = INT64_MAX;
+
+ if ((actualend = expand(a, end)) < 0)
+ return ((int)actualend);
+
+ rar->bytes_uncopied = actualend - start;
+ if (rar->bytes_uncopied == 0) {
+ /* Broken RAR files cause this case.
+ * NOTE: If this case were possible on a normal RAR file
+ * we would find out where it was actually bad and
+ * what we would do to solve it. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Internal error extracting RAR file.");
+ return (ARCHIVE_FAILED);
+ }
+ *offset = rar->offset;
+ if (rar->offset + rar->bytes_uncopied > rar->unp_size)
+ *size = rar->unp_size - rar->offset;
+ else
+ *size = rar->bytes_uncopied;
+ ret = copy_from_lzss_window(a, buff, *offset, *size);
+ rar->offset += *size;
+ rar->bytes_uncopied -= *size;
+ }
return ret;
}
-/*
- * Bit stream reader.
- */
-/* Check that the cache buffer has enough bits. */
-#define rar_br_has(br, n) ((br)->cache_avail >= n)
-/* Get compressed data by bit. */
-#define rar_br_bits(br, n) \
- (((uint32_t)((br)->cache_buffer >> \
- ((br)->cache_avail - (n)))) & cache_masks[n])
-#define rar_br_bits_forced(br, n) \
- (((uint32_t)((br)->cache_buffer << \
- ((n) - (br)->cache_avail))) & cache_masks[n])
-/* Read ahead to make sure the cache buffer has enough compressed data we
- * will use.
- * True : completed, there is enough data in the cache buffer.
- * False : there is no data in the stream. */
-#define rar_br_read_ahead(a, br, n) \
- ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
-/* Notify how man bits we consumed. */
-#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
-#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
-
-static const uint32_t cache_masks[] = {
- 0x00000000, 0x00000001, 0x00000003, 0x00000007,
- 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
- 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
- 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
- 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
- 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
- 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
- 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
- 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
-};
-
-/*
- * Shift away used bits in the cache data and fill it up with following bits.
- * Call this when cache buffer does not have enough bits you need.
- *
- * Returns 1 if the cache buffer is full.
- * Returns 0 if the cache buffer is not full; input buffer is empty.
- */
-static int
-rar_br_fillup(struct archive_read *a, struct rar_br *br)
-{
-/*
- * x86 proccessor family can read misaligned data without an access error.
- */
-#if defined(__i386__) || (defined(_MSC_VER) && defined(_M_IX86))
-# if defined(_WIN32) && !defined(__CYGWIN__)
-# define rar_be16dec(p) _byteswap_ushort(*(const uint16_t *)(p))
-# elif defined(be16toh)
-# define rar_be16dec(p) be16toh(*(const uint16_t *)(p))
-# elif defined(betoh16)
-# define rar_be16dec(p) betoh16(*(const uint16_t *)(p))
-# else
-# define rar_be16dec archive_be16dec
-# endif
-# if defined(_WIN32) && !defined(__CYGWIN__)
-# define rar_be32dec(p) _byteswap_ulong(*(const uint32_t *)(p))
-# elif defined(be32toh)
-# define rar_be32dec(p) be32toh(*(const uint32_t *)(p))
-# elif defined(betoh32)
-# define rar_be32dec(p) betoh32(*(const uint32_t *)(p))
-# else
-# define rar_be32dec archive_be32dec
-# endif
-# if defined(_WIN32) && !defined(__CYGWIN__)
-# define rar_be64dec(p) _byteswap_uint64(*(const uint64_t *)(p))
-# elif defined(be64toh)
-# define rar_be64dec(p) be64toh(*(const uint64_t *)(p))
-# elif defined(betoh64)
-# define rar_be64dec(p) betoh64(*(const uint64_t *)(p))
-# else
-# define rar_be64dec archive_be64dec
-# endif
-#else
-# define rar_be16dec archive_be16dec
-# define rar_be32dec archive_be32dec
-# define rar_be64dec archive_be64dec
-#endif
- struct rar *rar = (struct rar *)(a->format->data);
- int n = CACHE_BITS - br->cache_avail;
-
- for (;;) {
- switch (n >> 3) {
- case 8:
- if (br->avail_in >= 8) {
- br->cache_buffer =
- rar_be64dec(br->next_in);
- br->next_in += 8;
- br->avail_in -= 8;
- br->cache_avail += 8 * 8;
- rar->bytes_unconsumed += 8;
- rar->bytes_remaining -= 8;
- return (1);
- }
- break;
- case 7:
- if (br->avail_in >= 8) {/* Read extra one. */
- br->cache_buffer =
- (br->cache_buffer << 56) |
- rar_be64dec(br->next_in) >> 8;
- br->next_in += 7;
- br->avail_in -= 7;
- br->cache_avail += 7 * 8;
- rar->bytes_unconsumed += 7;
- rar->bytes_remaining -= 7;
- return (1);
- }
- break;
- case 6:
- if (br->avail_in >= 6) {
- br->cache_buffer =
- (br->cache_buffer << 48) |
- (((uint64_t)rar_be32dec(
- br->next_in)) << 16) |
- rar_be16dec(&br->next_in[4]);
- br->next_in += 6;
- br->avail_in -= 6;
- br->cache_avail += 6 * 8;
- rar->bytes_unconsumed += 6;
- rar->bytes_remaining -= 6;
- return (1);
- }
- break;
- case 0:
- /* We have enough compressed data in
- * the cache buffer.*/
- return (1);
- default:
- break;
- }
- if (br->avail_in <= 0) {
-
- if (rar->bytes_unconsumed > 0) {
- /* Consume as much as the decompressor
- * actually used. */
- __archive_read_consume(a, rar->bytes_unconsumed);
- rar->bytes_unconsumed = 0;
- }
- br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
- if (br->next_in == NULL)
- return (0);
- if (br->avail_in > rar->bytes_remaining)
- br->avail_in = rar->bytes_remaining;
- if (br->avail_in == 0)
- return (0);
- }
- br->cache_buffer =
- (br->cache_buffer << 8) | *br->next_in++;
- br->avail_in--;
- br->cache_avail += 8;
- n -= 8;
- rar->bytes_unconsumed++;
- rar->bytes_remaining--;
- }
-#undef rar_be16dec
-#undef rar_be32dec
-#undef rar_be64dec
-}
-
-static int
-rar_br_preparation(struct archive_read *a, struct rar_br *br)
-{
- struct rar *rar = (struct rar *)(a->format->data);
-
- if (rar->bytes_remaining > 0) {
- br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
- if (br->next_in == NULL) {
- archive_set_error(&a->archive,
- ARCHIVE_ERRNO_FILE_FORMAT,
- "Truncated RAR file data");
- return (ARCHIVE_FATAL);
- }
- if (br->avail_in > rar->bytes_remaining)
- br->avail_in = rar->bytes_remaining;
- if (br->cache_avail == 0)
- (void)rar_br_fillup(a, br);
- }
- return (ARCHIVE_OK);
-}
-
static int
parse_codes(struct archive_read *a)
{
int i, j, val, n, r;
- unsigned char bitlengths[MAX_SYMBOLS], zerocount;
+ unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
+ unsigned int maxorder;
struct huffman_code precode;
struct rar *rar = (struct rar *)(a->format->data);
struct rar_br *br = &(rar->br);
/* PPMd block flag */
if (!rar_br_read_ahead(a, br, 1))
goto truncated_data;
- if (rar_br_bits(br, 1))
+ if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
{
rar_br_consume(br, 1);
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported compression method for RAR file.");
- return (ARCHIVE_FAILED);
+ ppmd_flags = rar_br_bits(br, 7);
+ rar_br_consume(br, 7);
+
+ /* Memory is allocated in MB */
+ if (ppmd_flags & 0x20)
+ {
+ rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
+ rar_br_consume(br, 8);
+ }
+
+ if (ppmd_flags & 0x40)
+ {
+ rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ }
+
+ if (ppmd_flags & 0x20)
+ {
+ maxorder = (ppmd_flags & 0x1F) + 1;
+ if(maxorder > 16)
+ maxorder = 16 + (maxorder - 16) * 3;
+
+ if (maxorder == 1)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->bytein.a = a;
+ rar->bytein.Read = &ppmd_read;
+ PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);
+ rar->range_dec.Stream = &rar->bytein;
+ Ppmd7_Construct(&rar->ppmd7_context);
+
+ if (!Ppmd7_Alloc(&rar->ppmd7_context, rar->dictionary_size, &g_szalloc))
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (!PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ Ppmd7_Init(&rar->ppmd7_context, maxorder);
+ }
+ else
+ {
+ if (!PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ rar->start_new_table = 0;
+ return (ARCHIVE_OK);
}
rar_br_consume(br, 1);
{0, "test_read_format_cpio_bin_be.cpio"},
{0, "test_read_format_cpio_svr4_gzip_rpm.rpm"}, /* Test RPM unwrapper */
{0, "test_read_format_rar.rar"}, /* Uncompressed RAR test */
+ {0, "test_read_format_rar_compress_best.rar"}, /* Best Compressed RAR test */
{0, "test_read_format_rar_compress_normal.rar"}, /* Normal Compressed RAR
* test */
{0, "test_read_format_rar_multi_lzss_blocks.rar"}, /* Normal Compressed Multi
test_compress_normal(void)
{
const char reffile[] = "test_read_format_rar_compress_normal.rar";
-#define file1_size 20111
+ const int file1_size = 20111;
char file1_buff[file1_size];
const char file1_test_txt[] = "<P STYLE=\"margin-bottom: 0in\"><BR>\n"
"</P>\n"
"</BODY>\n"
"</HTML>";
-#define file2_size 20
+ const int file2_size = 20;
char file2_buff[file2_size];
const char file2_test_txt[] = "test text document\r\n";
struct archive_entry *ae;
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+static void
+test_compress_best(void)
+{
+ const char reffile[] = "test_read_format_rar_compress_best.rar";
+ const int file1_size = 20111;
+ char file1_buff[file1_size];
+ const char file1_test_txt[] = "<P STYLE=\"margin-bottom: 0in\"><BR>\n"
+ "</P>\n"
+ "</BODY>\n"
+ "</HTML>";
+ const int file2_size = 20;
+ char file2_buff[file2_size];
+ const char file2_test_txt[] = "test text document\r\n";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+ assertA(0 == archive_read_support_format_all(a));
+ assertA(0 == archive_read_open_file(a, reffile, 10240));
+
+ /* First header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(file1_size, archive_entry_size(ae));
+ assertEqualInt(33188, archive_entry_mode(ae));
+ assertA(file1_size == archive_read_data(a, file1_buff, file1_size));
+ assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1],
+ file1_test_txt, sizeof(file1_test_txt) - 1);
+
+ /* Second header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("testlink", archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(25, archive_entry_size(ae));
+ assertEqualInt(41471, archive_entry_mode(ae));
+ assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae));
+
+ /* Third header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("testdir/test.txt", archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(file2_size, archive_entry_size(ae));
+ assertEqualInt(33188, archive_entry_mode(ae));
+ assertA(file2_size == archive_read_data(a, file2_buff, file2_size));
+ assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)],
+ file2_test_txt, sizeof(file2_test_txt) - 1);
+
+ /* Fourth header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("testdir/LibarchiveAddingTest.html",
+ archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(file1_size, archive_entry_size(ae));
+ assertEqualInt(33188, archive_entry_mode(ae));
+ assertA(file1_size == archive_read_data(a, file1_buff, file1_size));
+ assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1],
+ file1_test_txt, sizeof(file1_test_txt) - 1);
+
+ /* Fifth header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("testdir", archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(0, archive_entry_size(ae));
+ assertEqualInt(16877, archive_entry_mode(ae));
+
+ /* Sixth header. */
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("testemptydir", archive_entry_pathname(ae));
+ assertA((int)archive_entry_mtime(ae));
+ assertA((int)archive_entry_ctime(ae));
+ assertA((int)archive_entry_atime(ae));
+ assertEqualInt(0, archive_entry_size(ae));
+ assertEqualInt(16877, archive_entry_mode(ae));
+
+ /* Test EOF */
+ assertA(1 == archive_read_next_header(a, &ae));
+ assertEqualInt(6, archive_file_count(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_read_format_rar)
{
test_basic();
test_unicode_CP932();
test_compress_normal();
test_multi_lzss_blocks();
+ test_compress_best();
}
--- /dev/null
+begin 644 -
+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
+D,`P`[4$``'1E<W1E;7!T>61I<H#,G:O5/CUZ]3[$/7L`0`<`
+`
+end