/*
 * Copyright (c) 1990-1997 Sam Leffler
 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

#ifndef _FAX3_
#define _FAX3_
/*
 * TIFF Library.
 *
 * CCITT Group 3 (T.4) and Group 4 (T.6) Decompression Support.
 *
 * Decoder support is derived, with permission, from the code
 * in Frank Cringle's viewfax program;
 *      Copyright (C) 1990, 1995  Frank D. Cringle.
 */
#include "tiff.h"

/*
 * To override the default routine used to image decoded
 * spans one can use the pseudo tag TIFFTAG_FAXFILLFUNC.
 * The routine must have the type signature given below;
 * for example:
 *
 * fillruns(unsigned char* buf, uint32_t* runs, uint32_t* erun, uint32_t lastx)
 *
 * where buf is place to set the bits, runs is the array of b&w run
 * lengths (white then black), erun is the last run in the array, and
 * lastx is the width of the row in pixels.  Fill routines can assume
 * the run array has room for at least lastx runs and can overwrite
 * data in the run array as needed (e.g. to append zero runs to bring
 * the count up to a nice multiple).
 */
typedef void (*TIFFFaxFillFunc)(unsigned char *, uint32_t *, uint32_t *,
                                uint32_t);

/*
 * The default run filler; made external for other decoders.
 */
#if defined(__cplusplus)
extern "C"
{
#endif
    extern void _TIFFFax3fillruns(unsigned char *, uint32_t *, uint32_t *,
                                  uint32_t);
#if defined(__cplusplus)
}
#endif

/* finite state machine codes */
#define S_Null 0
#define S_Pass 1
#define S_Horiz 2
#define S_V0 3
#define S_VR 4
#define S_VL 5
#define S_Ext 6
#define S_TermW 7
#define S_TermB 8
#define S_MakeUpW 9
#define S_MakeUpB 10
#define S_MakeUp 11
#define S_EOL 12

/* WARNING: do not change the layout of this structure as the HylaFAX software
 */
/* really depends on it. See http://bugzilla.maptools.org/show_bug.cgi?id=2636
 */
typedef struct
{                        /* state table entry */
    unsigned char State; /* see above */
    unsigned char Width; /* width of code in bits */
    uint32_t Param;      /* unsigned 32-bit run length in bits (holds on 16 bit
                            actually, but cannot be changed. See above warning) */
} TIFFFaxTabEnt;

extern const TIFFFaxTabEnt TIFFFaxMainTable[];
extern const TIFFFaxTabEnt TIFFFaxWhiteTable[];
extern const TIFFFaxTabEnt TIFFFaxBlackTable[];

/*
 * The following macros define the majority of the G3/G4 decoder
 * algorithm using the state tables defined elsewhere.  To build
 * a decoder you need some setup code and some glue code. Note
 * that you may also need/want to change the way the NeedBits*
 * macros get input data if, for example, you know the data to be
 * decoded is properly aligned and oriented (doing so before running
 * the decoder can be a big performance win).
 *
 * Consult the decoder in the TIFF library for an idea of what you
 * need to define and setup to make use of these definitions.
 *
 * NB: to enable a debugging version of these macros define FAX3_DEBUG
 *     before including this file.  Trace output goes to stdout.
 */

#ifndef EndOfData
#define EndOfData() (cp >= ep)
#endif
/*
 * Need <=8 or <=16 bits of input data.  Unlike viewfax we
 * cannot use/assume a word-aligned, properly bit swizzled
 * input data set because data may come from an arbitrarily
 * aligned, read-only source such as a memory-mapped file.
 * Note also that the viewfax decoder does not check for
 * running off the end of the input data buffer.  This is
 * possible for G3-encoded data because it prescans the input
 * data to count EOL markers, but can cause problems for G4
 * data.  In any event, we don't prescan and must watch for
 * running out of data since we can't permit the library to
 * scan past the end of the input data buffer.
 *
 * Finally, note that we must handle remaindered data at the end
 * of a strip specially.  The coder asks for a fixed number of
 * bits when scanning for the next code.  This may be more bits
 * than are actually present in the data stream.  If we appear
 * to run out of data but still have some number of valid bits
 * remaining then we makeup the requested amount with zeros and
 * return successfully.  If the returned data is incorrect then
 * we should be called again and get a premature EOF error;
 * otherwise we should get the right answer.
 */
#ifndef NeedBits8
#define NeedBits8(n, eoflab)                                                   \
    do                                                                         \
    {                                                                          \
        if (BitsAvail < (n))                                                   \
        {                                                                      \
            if (EndOfData())                                                   \
            {                                                                  \
                if (BitsAvail == 0) /* no valid bits */                        \
                    goto eoflab;                                               \
                BitsAvail = (n); /* pad with zeros */                          \
            }                                                                  \
            else                                                               \
            {                                                                  \
                BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;              \
                BitsAvail += 8;                                                \
            }                                                                  \
        }                                                                      \
    } while (0)
#endif
#ifndef NeedBits16
#define NeedBits16(n, eoflab)                                                  \
    do                                                                         \
    {                                                                          \
        if (BitsAvail < (n))                                                   \
        {                                                                      \
            if (EndOfData())                                                   \
            {                                                                  \
                if (BitsAvail == 0) /* no valid bits */                        \
                    goto eoflab;                                               \
                BitsAvail = (n); /* pad with zeros */                          \
            }                                                                  \
            else                                                               \
            {                                                                  \
                BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;              \
                if ((BitsAvail += 8) < (n))                                    \
                {                                                              \
                    if (EndOfData())                                           \
                    {                                                          \
                        /* NB: we know BitsAvail is non-zero here */           \
                        BitsAvail = (n); /* pad with zeros */                  \
                    }                                                          \
                    else                                                       \
                    {                                                          \
                        BitAcc |= ((uint32_t)bitmap[*cp++]) << BitsAvail;      \
                        BitsAvail += 8;                                        \
                    }                                                          \
                }                                                              \
            }                                                                  \
        }                                                                      \
    } while (0)
#endif
#define GetBits(n) (BitAcc & ((1 << (n)) - 1))
#define ClrBits(n)                                                             \
    do                                                                         \
    {                                                                          \
        BitsAvail -= (n);                                                      \
        BitAcc >>= (n);                                                        \
    } while (0)

#ifdef FAX3_DEBUG
static const char *StateNames[] = {
    "Null   ", "Pass   ", "Horiz  ", "V0     ", "VR     ", "VL     ", "Ext    ",
    "TermW  ", "TermB  ", "MakeUpW", "MakeUpB", "MakeUp ", "EOL    ",
};
#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0')
#define LOOKUP8(wid, tab, eoflab)                                              \
    do                                                                         \
    {                                                                          \
        int t;                                                                 \
        NeedBits8(wid, eoflab);                                                \
        TabEnt = tab + GetBits(wid);                                           \
        printf("%08lX/%d: %s%5d\t", (long)BitAcc, BitsAvail,                   \
               StateNames[TabEnt->State], TabEnt->Param);                      \
        for (t = 0; t < TabEnt->Width; t++)                                    \
            DEBUG_SHOW;                                                        \
        putchar('\n');                                                         \
        fflush(stdout);                                                        \
        ClrBits(TabEnt->Width);                                                \
    } while (0)
#define LOOKUP16(wid, tab, eoflab)                                             \
    do                                                                         \
    {                                                                          \
        int t;                                                                 \
        NeedBits16(wid, eoflab);                                               \
        TabEnt = tab + GetBits(wid);                                           \
        printf("%08lX/%d: %s%5d\t", (long)BitAcc, BitsAvail,                   \
               StateNames[TabEnt->State], TabEnt->Param);                      \
        for (t = 0; t < TabEnt->Width; t++)                                    \
            DEBUG_SHOW;                                                        \
        putchar('\n');                                                         \
        fflush(stdout);                                                        \
        ClrBits(TabEnt->Width);                                                \
    } while (0)

#define SETVALUE(x)                                                            \
    do                                                                         \
    {                                                                          \
        *pa++ = RunLength + (x);                                               \
        printf("SETVALUE: %d\t%d\n", RunLength + (x), a0);                     \
        a0 += x;                                                               \
        RunLength = 0;                                                         \
    } while (0)
#else
#define LOOKUP8(wid, tab, eoflab)                                              \
    do                                                                         \
    {                                                                          \
        NeedBits8(wid, eoflab);                                                \
        TabEnt = tab + GetBits(wid);                                           \
        ClrBits(TabEnt->Width);                                                \
    } while (0)
#define LOOKUP16(wid, tab, eoflab)                                             \
    do                                                                         \
    {                                                                          \
        NeedBits16(wid, eoflab);                                               \
        TabEnt = tab + GetBits(wid);                                           \
        ClrBits(TabEnt->Width);                                                \
    } while (0)

/*
 * Append a run to the run length array for the
 * current row and reset decoding state.
 */
#define SETVALUE(x)                                                            \
    do                                                                         \
    {                                                                          \
        if (pa >= thisrun + sp->nruns)                                         \
        {                                                                      \
            TIFFErrorExtR(tif, module, "Buffer overflow at line %u of %s %u",  \
                          sp->line, isTiled(tif) ? "tile" : "strip",           \
                          isTiled(tif) ? tif->tif_curtile                      \
                                       : tif->tif_curstrip);                   \
            return (-1);                                                       \
        }                                                                      \
        *pa++ = RunLength + (x);                                               \
        a0 += (x);                                                             \
        RunLength = 0;                                                         \
    } while (0)
#endif

/*
 * Synchronize input decoding at the start of each
 * row by scanning for an EOL (if appropriate) and
 * skipping any trash data that might be present
 * after a decoding error.  Note that the decoding
 * done elsewhere that recognizes an EOL only consumes
 * 11 consecutive zero bits.  This means that if EOLcnt
 * is non-zero then we still need to scan for the final flag
 * bit that is part of the EOL code.
 */
#define SYNC_EOL(eoflab)                                                       \
    do                                                                         \
    {                                                                          \
        if (EOLcnt == 0)                                                       \
        {                                                                      \
            for (;;)                                                           \
            {                                                                  \
                NeedBits16(11, eoflab);                                        \
                if (GetBits(11) == 0)                                          \
                    break;                                                     \
                ClrBits(1);                                                    \
            }                                                                  \
        }                                                                      \
        for (;;)                                                               \
        {                                                                      \
            NeedBits8(8, eoflab);                                              \
            if (GetBits(8))                                                    \
                break;                                                         \
            ClrBits(8);                                                        \
        }                                                                      \
        while (GetBits(1) == 0)                                                \
            ClrBits(1);                                                        \
        ClrBits(1); /* EOL bit */                                              \
        EOLcnt = 0; /* reset EOL counter/flag */                               \
    } while (0)

/*
 * Cleanup the array of runs after decoding a row.
 * We adjust final runs to insure the user buffer is not
 * overwritten and/or undecoded area is white filled.
 */
#define CLEANUP_RUNS()                                                         \
    do                                                                         \
    {                                                                          \
        if (RunLength)                                                         \
            SETVALUE(0);                                                       \
        if (a0 != lastx)                                                       \
        {                                                                      \
            badlength(a0, lastx);                                              \
            while (a0 > lastx && pa > thisrun)                                 \
                a0 -= *--pa;                                                   \
            if (a0 < lastx)                                                    \
            {                                                                  \
                if (a0 < 0)                                                    \
                    a0 = 0;                                                    \
                if ((pa - thisrun) & 1)                                        \
                    SETVALUE(0);                                               \
                SETVALUE(lastx - a0);                                          \
            }                                                                  \
            else if (a0 > lastx)                                               \
            {                                                                  \
                SETVALUE(lastx);                                               \
                SETVALUE(0);                                                   \
            }                                                                  \
        }                                                                      \
    } while (0)

/*
 * Decode a line of 1D-encoded data.
 *
 * The line expanders are written as macros so that they can be reused
 * but still have direct access to the local variables of the "calling"
 * function.
 *
 * Note that unlike the original version we have to explicitly test for
 * a0 >= lastx after each black/white run is decoded.  This is because
 * the original code depended on the input data being zero-padded to
 * insure the decoder recognized an EOL before running out of data.
 */
#define EXPAND1D(eoflab)                                                       \
    do                                                                         \
    {                                                                          \
        for (;;)                                                               \
        {                                                                      \
            for (;;)                                                           \
            {                                                                  \
                LOOKUP16(12, TIFFFaxWhiteTable, eof1d);                        \
                switch (TabEnt->State)                                         \
                {                                                              \
                    case S_EOL:                                                \
                        EOLcnt = 1;                                            \
                        goto done1d;                                           \
                    case S_TermW:                                              \
                        SETVALUE(TabEnt->Param);                               \
                        goto doneWhite1d;                                      \
                    case S_MakeUpW:                                            \
                    case S_MakeUp:                                             \
                        a0 += TabEnt->Param;                                   \
                        RunLength += TabEnt->Param;                            \
                        break;                                                 \
                    default:                                                   \
                        unexpected("WhiteTable", a0);                          \
                        goto done1d;                                           \
                }                                                              \
            }                                                                  \
        doneWhite1d:                                                           \
            if (a0 >= lastx)                                                   \
                goto done1d;                                                   \
            for (;;)                                                           \
            {                                                                  \
                LOOKUP16(13, TIFFFaxBlackTable, eof1d);                        \
                switch (TabEnt->State)                                         \
                {                                                              \
                    case S_EOL:                                                \
                        EOLcnt = 1;                                            \
                        goto done1d;                                           \
                    case S_TermB:                                              \
                        SETVALUE(TabEnt->Param);                               \
                        goto doneBlack1d;                                      \
                    case S_MakeUpB:                                            \
                    case S_MakeUp:                                             \
                        a0 += TabEnt->Param;                                   \
                        RunLength += TabEnt->Param;                            \
                        break;                                                 \
                    default:                                                   \
                        unexpected("BlackTable", a0);                          \
                        goto done1d;                                           \
                }                                                              \
            }                                                                  \
        doneBlack1d:                                                           \
            if (a0 >= lastx)                                                   \
                goto done1d;                                                   \
            if (*(pa - 1) == 0 && *(pa - 2) == 0)                              \
                pa -= 2;                                                       \
        }                                                                      \
    eof1d:                                                                     \
        prematureEOF(a0);                                                      \
        CLEANUP_RUNS();                                                        \
        goto eoflab;                                                           \
    done1d:                                                                    \
        CLEANUP_RUNS();                                                        \
    } while (0)

/*
 * Update the value of b1 using the array
 * of runs for the reference line.
 */
#define CHECK_b1                                                               \
    do                                                                         \
    {                                                                          \
        if (pa != thisrun)                                                     \
            while (b1 <= a0 && b1 < lastx)                                     \
            {                                                                  \
                if (pb + 1 >= sp->refruns + sp->nruns)                         \
                {                                                              \
                    TIFFErrorExtR(                                             \
                        tif, module, "Buffer overflow at line %u of %s %u",    \
                        sp->line, isTiled(tif) ? "tile" : "strip",             \
                        isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip);  \
                    return (-1);                                               \
                }                                                              \
                b1 += pb[0] + pb[1];                                           \
                pb += 2;                                                       \
            }                                                                  \
    } while (0)

/*
 * Expand a row of 2D-encoded data.
 */
#define EXPAND2D(eoflab)                                                       \
    do                                                                         \
    {                                                                          \
        while (a0 < lastx)                                                     \
        {                                                                      \
            if (pa >= thisrun + sp->nruns)                                     \
            {                                                                  \
                TIFFErrorExtR(                                                 \
                    tif, module, "Buffer overflow at line %u of %s %u",        \
                    sp->line, isTiled(tif) ? "tile" : "strip",                 \
                    isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip);      \
                return (-1);                                                   \
            }                                                                  \
            LOOKUP8(7, TIFFFaxMainTable, eof2d);                               \
            switch (TabEnt->State)                                             \
            {                                                                  \
                case S_Pass:                                                   \
                    CHECK_b1;                                                  \
                    if (pb + 1 >= sp->refruns + sp->nruns)                     \
                    {                                                          \
                        TIFFErrorExtR(tif, module,                             \
                                      "Buffer overflow at line %u of %s %u",   \
                                      sp->line,                                \
                                      isTiled(tif) ? "tile" : "strip",         \
                                      isTiled(tif) ? tif->tif_curtile          \
                                                   : tif->tif_curstrip);       \
                        return (-1);                                           \
                    }                                                          \
                    b1 += *pb++;                                               \
                    RunLength += b1 - a0;                                      \
                    a0 = b1;                                                   \
                    b1 += *pb++;                                               \
                    break;                                                     \
                case S_Horiz:                                                  \
                    if ((pa - thisrun) & 1)                                    \
                    {                                                          \
                        for (;;)                                               \
                        { /* black first */                                    \
                            LOOKUP16(13, TIFFFaxBlackTable, eof2d);            \
                            switch (TabEnt->State)                             \
                            {                                                  \
                                case S_TermB:                                  \
                                    SETVALUE(TabEnt->Param);                   \
                                    goto doneWhite2da;                         \
                                case S_MakeUpB:                                \
                                case S_MakeUp:                                 \
                                    a0 += TabEnt->Param;                       \
                                    RunLength += TabEnt->Param;                \
                                    break;                                     \
                                default:                                       \
                                    goto badBlack2d;                           \
                            }                                                  \
                        }                                                      \
                    doneWhite2da:;                                             \
                        for (;;)                                               \
                        { /* then white */                                     \
                            LOOKUP16(12, TIFFFaxWhiteTable, eof2d);            \
                            switch (TabEnt->State)                             \
                            {                                                  \
                                case S_TermW:                                  \
                                    SETVALUE(TabEnt->Param);                   \
                                    goto doneBlack2da;                         \
                                case S_MakeUpW:                                \
                                case S_MakeUp:                                 \
                                    a0 += TabEnt->Param;                       \
                                    RunLength += TabEnt->Param;                \
                                    break;                                     \
                                default:                                       \
                                    goto badWhite2d;                           \
                            }                                                  \
                        }                                                      \
                    doneBlack2da:;                                             \
                    }                                                          \
                    else                                                       \
                    {                                                          \
                        for (;;)                                               \
                        { /* white first */                                    \
                            LOOKUP16(12, TIFFFaxWhiteTable, eof2d);            \
                            switch (TabEnt->State)                             \
                            {                                                  \
                                case S_TermW:                                  \
                                    SETVALUE(TabEnt->Param);                   \
                                    goto doneWhite2db;                         \
                                case S_MakeUpW:                                \
                                case S_MakeUp:                                 \
                                    a0 += TabEnt->Param;                       \
                                    RunLength += TabEnt->Param;                \
                                    break;                                     \
                                default:                                       \
                                    goto badWhite2d;                           \
                            }                                                  \
                        }                                                      \
                    doneWhite2db:;                                             \
                        for (;;)                                               \
                        { /* then black */                                     \
                            LOOKUP16(13, TIFFFaxBlackTable, eof2d);            \
                            switch (TabEnt->State)                             \
                            {                                                  \
                                case S_TermB:                                  \
                                    SETVALUE(TabEnt->Param);                   \
                                    goto doneBlack2db;                         \
                                case S_MakeUpB:                                \
                                case S_MakeUp:                                 \
                                    a0 += TabEnt->Param;                       \
                                    RunLength += TabEnt->Param;                \
                                    break;                                     \
                                default:                                       \
                                    goto badBlack2d;                           \
                            }                                                  \
                        }                                                      \
                    doneBlack2db:;                                             \
                    }                                                          \
                    CHECK_b1;                                                  \
                    break;                                                     \
                case S_V0:                                                     \
                    CHECK_b1;                                                  \
                    SETVALUE(b1 - a0);                                         \
                    if (pb >= sp->refruns + sp->nruns)                         \
                    {                                                          \
                        TIFFErrorExtR(tif, module,                             \
                                      "Buffer overflow at line %u of %s %u",   \
                                      sp->line,                                \
                                      isTiled(tif) ? "tile" : "strip",         \
                                      isTiled(tif) ? tif->tif_curtile          \
                                                   : tif->tif_curstrip);       \
                        return (-1);                                           \
                    }                                                          \
                    b1 += *pb++;                                               \
                    break;                                                     \
                case S_VR:                                                     \
                    CHECK_b1;                                                  \
                    SETVALUE(b1 - a0 + TabEnt->Param);                         \
                    if (pb >= sp->refruns + sp->nruns)                         \
                    {                                                          \
                        TIFFErrorExtR(tif, module,                             \
                                      "Buffer overflow at line %u of %s %u",   \
                                      sp->line,                                \
                                      isTiled(tif) ? "tile" : "strip",         \
                                      isTiled(tif) ? tif->tif_curtile          \
                                                   : tif->tif_curstrip);       \
                        return (-1);                                           \
                    }                                                          \
                    b1 += *pb++;                                               \
                    break;                                                     \
                case S_VL:                                                     \
                    CHECK_b1;                                                  \
                    if (b1 < (int)(a0 + TabEnt->Param))                        \
                    {                                                          \
                        unexpected("VL", a0);                                  \
                        goto eol2d;                                            \
                    }                                                          \
                    SETVALUE(b1 - a0 - TabEnt->Param);                         \
                    b1 -= *--pb;                                               \
                    break;                                                     \
                case S_Ext:                                                    \
                    *pa++ = lastx - a0;                                        \
                    extension(a0);                                             \
                    goto eol2d;                                                \
                case S_EOL:                                                    \
                    *pa++ = lastx - a0;                                        \
                    NeedBits8(4, eof2d);                                       \
                    if (GetBits(4))                                            \
                        unexpected("EOL", a0);                                 \
                    ClrBits(4);                                                \
                    EOLcnt = 1;                                                \
                    goto eol2d;                                                \
                default:                                                       \
                badMain2d:                                                     \
                    unexpected("MainTable", a0);                               \
                    goto eol2d;                                                \
                badBlack2d:                                                    \
                    unexpected("BlackTable", a0);                              \
                    goto eol2d;                                                \
                badWhite2d:                                                    \
                    unexpected("WhiteTable", a0);                              \
                    goto eol2d;                                                \
                eof2d:                                                         \
                    prematureEOF(a0);                                          \
                    CLEANUP_RUNS();                                            \
                    goto eoflab;                                               \
            }                                                                  \
        }                                                                      \
        if (RunLength)                                                         \
        {                                                                      \
            if (RunLength + a0 < lastx)                                        \
            {                                                                  \
                /* expect a final V0 */                                        \
                NeedBits8(1, eof2d);                                           \
                if (!GetBits(1))                                               \
                    goto badMain2d;                                            \
                ClrBits(1);                                                    \
            }                                                                  \
            SETVALUE(0);                                                       \
        }                                                                      \
    eol2d:                                                                     \
        CLEANUP_RUNS();                                                        \
    } while (0)
#endif /* _FAX3_ */
