/*
 * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

// -*- C++ -*-
// Small program for unpacking specially compressed Java packages.
// John R. Rose

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <stdint.h>

#include "defines.h"
#include "bytes.h"
#include "utils.h"
#include "coding.h"

#include "constants.h"
#include "unpack.h"

extern coding basic_codings[];

// CODING_PRIVATE causes a lot of them
#pragma GCC diagnostic ignored "-Wunused-variable"

#define CODING_PRIVATE(spec)                                                                   \
    int spec_ = spec;                                                                          \
    int B = CODING_B(spec_);                                                                   \
    int H = CODING_H(spec_);                                                                   \
    int L = 256 - H;                                                                           \
    int S = CODING_S(spec_);                                                                   \
    int D = CODING_D(spec_)

#define IS_NEG_CODE(S, codeVal) ((((int)(codeVal) + 1) & ((1 << S) - 1)) == 0)

#define DECODE_SIGN_S1(ux) (((uint32_t)(ux) >> 1) ^ -((int)(ux) & 1))

static int decode_sign(int S, uint32_t ux)
{ // == Coding.decodeSign32
    assert(S > 0);
    uint32_t sigbits = (ux >> S);
    if (IS_NEG_CODE(S, ux))
        return (int)(~sigbits);
    else
        return (int)(ux - sigbits);
    // Note that (int)(ux-sigbits) can be negative, if ux is large enough.
}

coding *coding::init()
{
    if (umax > 0)
        return this;   // already done
    assert(spec != 0); // sanity

    // fill in derived fields
    CODING_PRIVATE(spec);

    // Return nullptr if 'arb(BHSD)' parameter constraints are not met:
    if (B < 1 || B > B_MAX)
        return nullptr;
    if (H < 1 || H > 256)
        return nullptr;
    if (S < 0 || S > 2)
        return nullptr;
    if (D < 0 || D > 1)
        return nullptr;
    if (B == 1 && H != 256)
        return nullptr; // 1-byte coding must be fixed-size
    if (B >= 5 && H == 256)
        return nullptr; // no 5-byte fixed-size coding

    // first compute the range of the coding, in 64 bits
    int64_t range = 0;
    {
        int64_t H_i = 1;
        for (int i = 0; i < B; i++)
        {
            range += H_i;
            H_i *= H;
        }
        range *= L;
        range += H_i;
    }
    assert(range > 0); // no useless codings, please

    int this_umax;

    // now, compute min and max
    if (range >= ((int64_t)1 << 32))
    {
        this_umax = INT_MAX_VALUE;
        this->umin = INT_MIN_VALUE;
        this->max = INT_MAX_VALUE;
        this->min = INT_MIN_VALUE;
    }
    else
    {
        this_umax = (range > INT_MAX_VALUE) ? INT_MAX_VALUE : (int)range - 1;
        this->max = this_umax;
        this->min = this->umin = 0;
        if (S != 0 && range != 0)
        {
            int64_t maxPosCode = range - 1;
            int64_t maxNegCode = range - 1;
            while (IS_NEG_CODE(S, maxPosCode))
                --maxPosCode;
            while (!IS_NEG_CODE(S, maxNegCode))
                --maxNegCode;
            int maxPos = decode_sign(S, (uint32_t)maxPosCode);
            if (maxPos < 0)
                this->max = INT_MAX_VALUE; // 32-bit wraparound
            else
                this->max = maxPos;
            if (maxNegCode < 0)
                this->min = 0; // No negative codings at all.
            else
                this->min = decode_sign(S, (uint32_t)maxNegCode);
        }
    }

    assert(!(isFullRange | isSigned | isSubrange)); // init
    if (min < 0)
        this->isSigned = true;
    if (max < INT_MAX_VALUE && range <= INT_MAX_VALUE)
        this->isSubrange = true;
    if (max == INT_MAX_VALUE && min == INT_MIN_VALUE)
        this->isFullRange = true;

    // do this last, to reduce MT exposure (should have a membar too)
    this->umax = this_umax;

    return this;
}

coding *coding::findBySpec(int spec)
{
    for (coding *scan = &basic_codings[0];; scan++)
    {
        if (scan->spec == spec)
            return scan->init();
        if (scan->spec == 0)
            break;
    }
    coding *ptr = NEW(coding, 1);
    if (!ptr)
        return nullptr;
    coding *c = ptr->initFrom(spec);
    if (c == nullptr)
    {
        ::free(ptr);
    }
    else
        // else caller should free it...
        c->isMalloc = true;
    return c;
}

coding *coding::findBySpec(int B, int H, int S, int D)
{
    if (B < 1 || B > B_MAX)
        return nullptr;
    if (H < 1 || H > 256)
        return nullptr;
    if (S < 0 || S > 2)
        return nullptr;
    if (D < 0 || D > 1)
        return nullptr;
    return findBySpec(CODING_SPEC(B, H, S, D));
}

void coding::free()
{
    if (isMalloc)
    {
        ::free(this);
    }
}

void coding_method::reset(value_stream *state)
{
    assert(state->rp == state->rplimit); // not in mid-stream, please
    // assert(this == vs0.cm);
    state[0] = vs0;
    if (uValues != nullptr)
    {
        uValues->reset(state->helper());
    }
}

uint32_t coding::parse(byte *&rp, int B, int H)
{
    int L = 256 - H;
    byte *ptr = rp;
    // hand peel the i==0 part of the loop:
    uint32_t b_i = *ptr++ & 0xFF;
    if (B == 1 || b_i < (uint32_t)L)
    {
        rp = ptr;
        return b_i;
    }
    uint32_t sum = b_i;
    uint32_t H_i = H;
    assert(B <= B_MAX);
    for (int i = 2; i <= B_MAX; i++)
    { // easy for compilers to unroll if desired
        b_i = *ptr++ & 0xFF;
        sum += b_i * H_i;
        if (i == B || b_i < (uint32_t)L)
        {
            rp = ptr;
            return sum;
        }
        H_i *= H;
    }
    assert(false);
    return 0;
}

uint32_t coding::parse_lgH(byte *&rp, int B, int H, int lgH)
{
    assert(H == (1 << lgH));
    int L = 256 - (1 << lgH);
    byte *ptr = rp;
    // hand peel the i==0 part of the loop:
    uint32_t b_i = *ptr++ & 0xFF;
    if (B == 1 || b_i < (uint32_t)L)
    {
        rp = ptr;
        return b_i;
    }
    uint32_t sum = b_i;
    uint32_t lg_H_i = lgH;
    assert(B <= B_MAX);
    for (int i = 2; i <= B_MAX; i++)
    { // easy for compilers to unroll if desired
        b_i = *ptr++ & 0xFF;
        sum += b_i << lg_H_i;
        if (i == B || b_i < (uint32_t)L)
        {
            rp = ptr;
            return sum;
        }
        lg_H_i += lgH;
    }
    assert(false);
    return 0;
}

static const char ERB[] = "EOF reading band";

void coding::parseMultiple(byte *&rp, int N, byte *limit, int B, int H)
{
    if (N < 0)
    {
        unpack_abort("bad value count");
        return;
    }
    byte *ptr = rp;
    if (B == 1 || H == 256)
    {
        size_t len = (size_t)N * B;
        if (len / B != (size_t)N || ptr + len > limit)
        {
            unpack_abort(ERB);
            return;
        }
        rp = ptr + len;
        return;
    }
    // Note:  We assume rp has enough zero-padding.
    int L = 256 - H;
    int n = B;
    while (N > 0)
    {
        ptr += 1;
        if (--n == 0)
        {
            // end of encoding at B bytes, regardless of byte value
        }
        else
        {
            int b = (ptr[-1] & 0xFF);
            if (b >= L)
            {
                // keep going, unless we find a byte < L
                continue;
            }
        }
        // found the last byte
        N -= 1;
        n = B; // reset length counter
        // do an error check here
        if (ptr > limit)
        {
            unpack_abort(ERB);
            return;
        }
    }
    rp = ptr;
    return;
}

bool value_stream::hasHelper()
{
    // If my coding method is a pop-style method,
    // then I need a second value stream to transmit
    // unfavored values.
    // This can be determined by examining fValues.
    return cm->fValues != nullptr;
}

void value_stream::init(byte *rp_, byte *rplimit_, coding *defc)
{
    rp = rp_;
    rplimit = rplimit_;
    sum = 0;
    cm = nullptr; // no need in the simple case
    setCoding(defc);
}

void value_stream::setCoding(coding *defc)
{
    if (defc == nullptr)
    {
        unpack_abort("bad coding");
        defc = coding::findByIndex(_meta_canon_min); // random pick for recovery
    }

    c = (*defc);

    // choose cmk
    cmk = cmk_ERROR;
    switch (c.spec)
    {
    case BYTE1_spec:
        cmk = cmk_BYTE1;
        break;
    case CHAR3_spec:
        cmk = cmk_CHAR3;
        break;
    case UNSIGNED5_spec:
        cmk = cmk_UNSIGNED5;
        break;
    case DELTA5_spec:
        cmk = cmk_DELTA5;
        break;
    case BCI5_spec:
        cmk = cmk_BCI5;
        break;
    case BRANCH5_spec:
        cmk = cmk_BRANCH5;
        break;
    default:
        if (c.D() == 0)
        {
            switch (c.S())
            {
            case 0:
                cmk = cmk_BHS0;
                break;
            case 1:
                cmk = cmk_BHS1;
                break;
            default:
                cmk = cmk_BHS;
                break;
            }
        }
        else
        {
            if (c.S() == 1)
            {
                if (c.isFullRange)
                    cmk = cmk_BHS1D1full;
                if (c.isSubrange)
                    cmk = cmk_BHS1D1sub;
            }
            if (cmk == cmk_ERROR)
                cmk = cmk_BHSD1;
        }
    }
}

static int getPopValue(value_stream *self, uint32_t uval)
{
    if (uval > 0)
    {
        // note that the initial parse performed a range check
        assert(uval <= (uint32_t)self->cm->fVlength);
        return self->cm->fValues[uval - 1];
    }
    else
    {
        // take an unfavored value
        return self->helper()->getInt();
    }
}

int coding::sumInUnsignedRange(int x, int y)
{
    assert(isSubrange);
    int range = (int)(umax + 1);
    assert(range > 0);
    x += y;
    if (x != (int)((int64_t)(x - y) + (int64_t)y))
    {
        // 32-bit overflow interferes with range reduction.
        // Back off from the overflow by adding a multiple of range:
        if (x < 0)
        {
            x -= range;
            assert(x >= 0);
        }
        else
        {
            x += range;
            assert(x < 0);
        }
    }
    if (x < 0)
    {
        x += range;
        if (x >= 0)
            return x;
    }
    else if (x >= range)
    {
        x -= range;
        if (x < range)
            return x;
    }
    else
    {
        // in range
        return x;
    }
    // do it the hard way
    x %= range;
    if (x < 0)
        x += range;
    return x;
}

static int getDeltaValue(value_stream *self, uint32_t uval, bool isSubrange)
{
    assert((uint32_t)(self->c.isSubrange) == (uint32_t)isSubrange);
    assert(self->c.isSubrange | self->c.isFullRange);
    if (isSubrange)
        return self->sum = self->c.sumInUnsignedRange(self->sum, (int)uval);
    else
        return self->sum += (int)uval;
}

bool value_stream::hasValue()
{
    if (rp < rplimit)
        return true;
    if (cm == nullptr)
        return false;
    if (cm->next == nullptr)
        return false;
    cm->next->reset(this);
    return hasValue();
}

int value_stream::getInt()
{
    if (rp >= rplimit)
    {
        // Advance to next coding segment.
        if (rp > rplimit || cm == nullptr || cm->next == nullptr)
        {
            // Must perform this check and throw an exception on bad input.
            unpack_abort(ERB);
            return 0;
        }
        cm->next->reset(this);
        return getInt();
    }

    CODING_PRIVATE(c.spec);
    uint32_t uval;
    enum
    {
        B5 = 5,
        B3 = 3,
        H128 = 128,
        H64 = 64,
        H4 = 4
    };
    switch (cmk)
    {
    case cmk_BHS:
        assert(D == 0);
        uval = coding::parse(rp, B, H);
        if (S == 0)
            return (int)uval;
        return decode_sign(S, uval);

    case cmk_BHS0:
        assert(S == 0 && D == 0);
        uval = coding::parse(rp, B, H);
        return (int)uval;

    case cmk_BHS1:
        assert(S == 1 && D == 0);
        uval = coding::parse(rp, B, H);
        return DECODE_SIGN_S1(uval);

    case cmk_BYTE1:
        assert(c.spec == BYTE1_spec);
        assert(B == 1 && H == 256 && S == 0 && D == 0);
        return *rp++ & 0xFF;

    case cmk_CHAR3:
        assert(c.spec == CHAR3_spec);
        assert(B == B3 && H == H128 && S == 0 && D == 0);
        return coding::parse_lgH(rp, B3, H128, 7);

    case cmk_UNSIGNED5:
        assert(c.spec == UNSIGNED5_spec);
        assert(B == B5 && H == H64 && S == 0 && D == 0);
        return coding::parse_lgH(rp, B5, H64, 6);

    case cmk_BHSD1:
        assert(D == 1);
        uval = coding::parse(rp, B, H);
        if (S != 0)
            uval = (uint32_t)decode_sign(S, uval);
        return getDeltaValue(this, uval, (bool)c.isSubrange);

    case cmk_BHS1D1full:
        assert(S == 1 && D == 1 && c.isFullRange);
        uval = coding::parse(rp, B, H);
        uval = (uint32_t)DECODE_SIGN_S1(uval);
        return getDeltaValue(this, uval, false);

    case cmk_BHS1D1sub:
        assert(S == 1 && D == 1 && c.isSubrange);
        uval = coding::parse(rp, B, H);
        uval = (uint32_t)DECODE_SIGN_S1(uval);
        return getDeltaValue(this, uval, true);

    case cmk_DELTA5:
        assert(c.spec == DELTA5_spec);
        assert(B == B5 && H == H64 && S == 1 && D == 1 && c.isFullRange);
        uval = coding::parse_lgH(rp, B5, H64, 6);
        sum += DECODE_SIGN_S1(uval);
        return sum;

    case cmk_BCI5:
        assert(c.spec == BCI5_spec);
        assert(B == B5 && H == H4 && S == 0 && D == 0);
        return coding::parse_lgH(rp, B5, H4, 2);

    case cmk_BRANCH5:
        assert(c.spec == BRANCH5_spec);
        assert(B == B5 && H == H4 && S == 2 && D == 0);
        uval = coding::parse_lgH(rp, B5, H4, 2);
        return decode_sign(S, uval);

    case cmk_pop:
        uval = coding::parse(rp, B, H);
        if (S != 0)
        {
            uval = (uint32_t)decode_sign(S, uval);
        }
        if (D != 0)
        {
            assert(c.isSubrange | c.isFullRange);
            if (c.isSubrange)
                sum = c.sumInUnsignedRange(sum, (int)uval);
            else
                sum += (int)uval;
            uval = (uint32_t)sum;
        }
        return getPopValue(this, uval);

    case cmk_pop_BHS0:
        assert(S == 0 && D == 0);
        uval = coding::parse(rp, B, H);
        return getPopValue(this, uval);

    case cmk_pop_BYTE1:
        assert(c.spec == BYTE1_spec);
        assert(B == 1 && H == 256 && S == 0 && D == 0);
        return getPopValue(this, *rp++ & 0xFF);

    default:
        break;
    }
    assert(false);
    return 0;
}

static int moreCentral(int x, int y)
{ // used to find end of Pop.{F}
    // Suggested implementation from the Pack200 specification:
    uint32_t kx = (x >> 31) ^ (x << 1);
    uint32_t ky = (y >> 31) ^ (y << 1);
    return (kx < ky ? x : y);
}
// static maybe_inline
// int moreCentral2(int x, int y, int min) {
//  // Strict implementation of buggy 150.7 specification.
//  // The bug is that the spec. says absolute-value ties are broken
//  // in favor of positive numbers, but the suggested implementation
//  // (also mentioned in the spec.) breaks ties in favor of negative numbers.
//  if ((x + y) != 0)
//    return min;
//  else
//    // return the other value, which breaks a tie in the positive direction
//    return (x > y)? x: y;
//}

static const byte *no_meta[] = {nullptr};
#define NO_META (*(byte **)no_meta)
enum
{
    POP_FAVORED_N = -2
};

// mode bits
#define DISABLE_RUN 1 // used immediately inside ACodee
#define DISABLE_POP 2 // used recursively in all pop sub-bands

// This function knows all about meta-coding.
void coding_method::init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode,
                         coding *defc, int N, intlist *valueSink)
{
    assert(N != 0);

    assert(u != nullptr); // must be pre-initialized
    // if (u == nullptr)  u = unpacker::current();  // expensive

    int op = (meta_rp == nullptr) ? _meta_default : (*meta_rp++ & 0xFF);
    coding *foundc = nullptr;
    coding *to_free = nullptr;

    if (op == _meta_default)
    {
        foundc = defc;
        // and fall through
    }
    else if (op >= _meta_canon_min && op <= _meta_canon_max)
    {
        foundc = coding::findByIndex(op);
        // and fall through
    }
    else if (op == _meta_arb)
    {
        int args = (*meta_rp++ & 0xFF);
        // args = (D:[0..1] + 2*S[0..2] + 8*(B:[1..5]-1))
        int D = ((args >> 0) & 1);
        int S = ((args >> 1) & 3);
        int B = ((args >> 3) & -1) + 1;
        // & (H[1..256]-1)
        int H = (*meta_rp++ & 0xFF) + 1;
        foundc = coding::findBySpec(B, H, S, D);
        to_free = foundc; // findBySpec may dynamically allocate
        if (foundc == nullptr)
        {
            unpack_abort("illegal arbitrary coding");
            return;
        }
        // and fall through
    }
    else if (op >= _meta_run && op < _meta_pop)
    {
        int args = (op - _meta_run);
        // args: KX:[0..3] + 4*(KBFlag:[0..1]) + 8*(ABDef:[0..2])
        int KX = ((args >> 0) & 3);
        int KBFlag = ((args >> 2) & 1);
        int ABDef = ((args >> 3) & -1);
        assert(ABDef <= 2);
        // & KB: one of [0..255] if KBFlag=1
        int KB = (!KBFlag ? 3 : (*meta_rp++ & 0xFF));
        int K = (KB + 1) << (KX * 4);
        int N2 = (N >= 0) ? N - K : N;
        if (N == 0 || (N2 <= 0 && N2 != N))
        {
            unpack_abort("illegal run encoding");
        }
        if ((mode & DISABLE_RUN) != 0)
        {
            unpack_abort("illegal nested run encoding");
        }

        // & Enc{ ACode } if ADef=0  (ABDef != 1)
        // No direct nesting of 'run' in ACode, but in BCode it's OK.
        int disRun = mode | DISABLE_RUN;
        if (ABDef == 1)
        {
            this->init(band_rp, band_limit, NO_META, disRun, defc, K, valueSink);
        }
        else
        {
            this->init(band_rp, band_limit, meta_rp, disRun, defc, K, valueSink);
        }

        // & Enc{ BCode } if BDef=0  (ABDef != 2)
        coding_method *tail = U_NEW(coding_method, 1);
        if (!tail)
            return;
        tail->u = u;

        // The 'run' codings may be nested indirectly via 'pop' codings.
        // This means that this->next may already be filled in, if
        // ACode was of type 'pop' with a 'run' token coding.
        // No problem:  Just chain the upcoming BCode onto the end.
        for (coding_method *self = this;; self = self->next)
        {
            if (self->next == nullptr)
            {
                self->next = tail;
                break;
            }
        }

        if (ABDef == 2)
        {
            tail->init(band_rp, band_limit, NO_META, mode, defc, N2, valueSink);
        }
        else
        {
            tail->init(band_rp, band_limit, meta_rp, mode, defc, N2, valueSink);
        }
        // Note:  The preceding calls to init should be tail-recursive.

        return; // done; no falling through
    }
    else if (op >= _meta_pop && op < _meta_limit)
    {
        int args = (op - _meta_pop);
        // args: (FDef:[0..1]) + 2*UDef:[0..1] + 4*(TDefL:[0..11])
        int FDef = ((args >> 0) & 1);
        int UDef = ((args >> 1) & 1);
        int TDefL = ((args >> 2) & -1);
        assert(TDefL <= 11);
        int TDef = (TDefL > 0);
        int TL = (TDefL <= 6) ? (2 << TDefL) : (256 - (4 << (11 - TDefL)));
        int TH = (256 - TL);
        if (N <= 0)
        {
            unpack_abort("illegal pop encoding");
        }
        if ((mode & DISABLE_POP) != 0)
        {
            unpack_abort("illegal nested pop encoding");
        }

        // No indirect nesting of 'pop', but 'run' is OK.
        int disPop = DISABLE_POP;

        // & Enc{ FCode } if FDef=0
        int FN = POP_FAVORED_N;
        assert(valueSink == nullptr);
        intlist fValueSink;
        fValueSink.init();
        coding_method fval;
        BYTES_OF(fval).clear();
        fval.u = u;
        if (FDef != 0)
        {
            fval.init(band_rp, band_limit, NO_META, disPop, defc, FN, &fValueSink);
        }
        else
        {
            fval.init(band_rp, band_limit, meta_rp, disPop, defc, FN, &fValueSink);
        }
        bytes fvbuf;
        fValues = (u->saveTo(fvbuf, fValueSink.b), (int *)fvbuf.ptr);
        fVlength = fValueSink.length(); // i.e., the parameter K
        fValueSink.free();

        // Skip the first {F} run in all subsequent passes.
        // The next call to this->init(...) will set vs0.rp to point after the {F}.

        // & Enc{ TCode } if TDef=0  (TDefL==0)
        if (TDef != 0)
        {
            coding *tcode = coding::findBySpec(1, 256); // BYTE1
            // find the most narrowly sufficient code:
            for (int B = 2; B <= B_MAX; B++)
            {
                if (fVlength <= tcode->umax)
                    break; // found it
                tcode->free();
                tcode = coding::findBySpec(B, TH);
                if (!tcode)
                    return;
            }
            if (!(fVlength <= tcode->umax))
            {
                unpack_abort("pop.L value too small");
            }
            this->init(band_rp, band_limit, NO_META, disPop, tcode, N, nullptr);
            tcode->free();
        }
        else
        {
            this->init(band_rp, band_limit, meta_rp, disPop, defc, N, nullptr);
        }

        // Count the number of zero tokens right now.
        // Also verify that they are in bounds.
        int UN = 0; // one {U} for each zero in {T}
        value_stream vs = vs0;
        for (int i = 0; i < N; i++)
        {
            uint32_t val = vs.getInt();
            if (val == 0)
                UN += 1;
            if (!(val <= (uint32_t)fVlength))
            {
                unpack_abort("pop token out of range");
            }
        }
        vs.done();

        // & Enc{ UCode } if UDef=0
        if (UN != 0)
        {
            uValues = U_NEW(coding_method, 1);
            if (uValues == nullptr)
                return;
            uValues->u = u;
            if (UDef != 0)
            {
                uValues->init(band_rp, band_limit, NO_META, disPop, defc, UN, nullptr);
            }
            else
            {
                uValues->init(band_rp, band_limit, meta_rp, disPop, defc, UN, nullptr);
            }
        }
        else
        {
            if (UDef == 0)
            {
                int uop = (*meta_rp++ & 0xFF);
                if (uop > _meta_canon_max)
                    // %%% Spec. requires the more strict (uop != _meta_default).
                    unpack_abort("bad meta-coding for empty pop/U");
            }
        }

        // Bug fix for 6259542
        // Last of all, adjust vs0.cmk to the 'pop' flavor
        for (coding_method *self = this; self != nullptr; self = self->next)
        {
            coding_method_kind cmk2 = cmk_pop;
            switch (self->vs0.cmk)
            {
            case cmk_BHS0:
                cmk2 = cmk_pop_BHS0;
                break;
            case cmk_BYTE1:
                cmk2 = cmk_pop_BYTE1;
                break;
            default:
                break;
            }
            self->vs0.cmk = cmk2;
            if (self != this)
            {
                assert(self->fValues == nullptr); // no double init
                self->fValues = this->fValues;
                self->fVlength = this->fVlength;
                assert(self->uValues == nullptr); // must stay nullptr
            }
        }

        return; // done; no falling through
    }
    else
    {
        unpack_abort("bad meta-coding");
    }

    // Common code here skips a series of values with one coding.
    assert(foundc != nullptr);

    assert(vs0.cmk == cmk_ERROR);   // no garbage, please
    assert(vs0.rp == nullptr);      // no garbage, please
    assert(vs0.rplimit == nullptr); // no garbage, please
    assert(vs0.sum == 0);           // no garbage, please

    vs0.init(band_rp, band_limit, foundc);

    // Done with foundc.  Free if necessary.
    if (to_free != nullptr)
    {
        to_free->free();
        to_free = nullptr;
    }
    foundc = nullptr;

    coding &c = vs0.c;
    CODING_PRIVATE(c.spec);
    // assert sane N
    assert((uint32_t)N < INT_MAX_VALUE || N == POP_FAVORED_N);

    // Look at the values, or at least skip over them quickly.
    if (valueSink == nullptr)
    {
        // Skip and ignore values in the first pass.
        c.parseMultiple(band_rp, N, band_limit, B, H);
    }
    else if (N >= 0)
    {
        // Pop coding, {F} sequence, initial run of values...
        assert((mode & DISABLE_POP) != 0);
        value_stream vs = vs0;
        for (int n = 0; n < N; n++)
        {
            int val = vs.getInt();
            valueSink->add(val);
        }
        band_rp = vs.rp;
    }
    else
    {
        // Pop coding, {F} sequence, final run of values...
        assert((mode & DISABLE_POP) != 0);
        assert(N == POP_FAVORED_N);
        int min = INT_MIN_VALUE; // farthest from the center
        // min2 is based on the buggy specification of centrality in version 150.7
        // no known implementations transmit this value, but just in case...
        // int min2 = INT_MIN_VALUE;
        int last = 0;
        // if there were initial runs, find the potential sentinels in them:
        for (int i = 0; i < valueSink->length(); i++)
        {
            last = valueSink->get(i);
            min = moreCentral(min, last);
            // min2 = moreCentral2(min2, last, min);
        }
        value_stream vs = vs0;
        for (;;)
        {
            int val = vs.getInt();
            if (valueSink->length() > 0 && (val == last || val == min)) //|| val == min2
                break;
            valueSink->add(val);
            last = val;
            min = moreCentral(min, last);
            // min2 = moreCentral2(min2, last, min);
        }
        band_rp = vs.rp;
    }

    // Get an accurate upper limit now.
    vs0.rplimit = band_rp;
    vs0.cm = this;

    return; // success
}

coding basic_codings[] = {
    // This one is not a usable irregular coding, but is used by cp_Utf8_chars.
    CODING_INIT(3, 128, 0, 0),

    // Fixed-length codings:
    CODING_INIT(1, 256, 0, 0), CODING_INIT(1, 256, 1, 0), CODING_INIT(1, 256, 0, 1),
    CODING_INIT(1, 256, 1, 1), CODING_INIT(2, 256, 0, 0), CODING_INIT(2, 256, 1, 0),
    CODING_INIT(2, 256, 0, 1), CODING_INIT(2, 256, 1, 1), CODING_INIT(3, 256, 0, 0),
    CODING_INIT(3, 256, 1, 0), CODING_INIT(3, 256, 0, 1), CODING_INIT(3, 256, 1, 1),
    CODING_INIT(4, 256, 0, 0), CODING_INIT(4, 256, 1, 0), CODING_INIT(4, 256, 0, 1),
    CODING_INIT(4, 256, 1, 1),

    // Full-range variable-length codings:
    CODING_INIT(5, 4, 0, 0),   CODING_INIT(5, 4, 1, 0),   CODING_INIT(5, 4, 2, 0),
    CODING_INIT(5, 16, 0, 0),  CODING_INIT(5, 16, 1, 0),  CODING_INIT(5, 16, 2, 0),
    CODING_INIT(5, 32, 0, 0),  CODING_INIT(5, 32, 1, 0),  CODING_INIT(5, 32, 2, 0),
    CODING_INIT(5, 64, 0, 0),  CODING_INIT(5, 64, 1, 0),  CODING_INIT(5, 64, 2, 0),
    CODING_INIT(5, 128, 0, 0), CODING_INIT(5, 128, 1, 0), CODING_INIT(5, 128, 2, 0),
    CODING_INIT(5, 4, 0, 1),   CODING_INIT(5, 4, 1, 1),   CODING_INIT(5, 4, 2, 1),
    CODING_INIT(5, 16, 0, 1),  CODING_INIT(5, 16, 1, 1),  CODING_INIT(5, 16, 2, 1),
    CODING_INIT(5, 32, 0, 1),  CODING_INIT(5, 32, 1, 1),  CODING_INIT(5, 32, 2, 1),
    CODING_INIT(5, 64, 0, 1),  CODING_INIT(5, 64, 1, 1),  CODING_INIT(5, 64, 2, 1),
    CODING_INIT(5, 128, 0, 1), CODING_INIT(5, 128, 1, 1), CODING_INIT(5, 128, 2, 1),

    // Variable length subrange codings:
    CODING_INIT(2, 192, 0, 0), CODING_INIT(2, 224, 0, 0), CODING_INIT(2, 240, 0, 0),
    CODING_INIT(2, 248, 0, 0), CODING_INIT(2, 252, 0, 0), CODING_INIT(2, 8, 0, 1),
    CODING_INIT(2, 8, 1, 1),   CODING_INIT(2, 16, 0, 1),  CODING_INIT(2, 16, 1, 1),
    CODING_INIT(2, 32, 0, 1),  CODING_INIT(2, 32, 1, 1),  CODING_INIT(2, 64, 0, 1),
    CODING_INIT(2, 64, 1, 1),  CODING_INIT(2, 128, 0, 1), CODING_INIT(2, 128, 1, 1),
    CODING_INIT(2, 192, 0, 1), CODING_INIT(2, 192, 1, 1), CODING_INIT(2, 224, 0, 1),
    CODING_INIT(2, 224, 1, 1), CODING_INIT(2, 240, 0, 1), CODING_INIT(2, 240, 1, 1),
    CODING_INIT(2, 248, 0, 1), CODING_INIT(2, 248, 1, 1), CODING_INIT(3, 192, 0, 0),
    CODING_INIT(3, 224, 0, 0), CODING_INIT(3, 240, 0, 0), CODING_INIT(3, 248, 0, 0),
    CODING_INIT(3, 252, 0, 0), CODING_INIT(3, 8, 0, 1),   CODING_INIT(3, 8, 1, 1),
    CODING_INIT(3, 16, 0, 1),  CODING_INIT(3, 16, 1, 1),  CODING_INIT(3, 32, 0, 1),
    CODING_INIT(3, 32, 1, 1),  CODING_INIT(3, 64, 0, 1),  CODING_INIT(3, 64, 1, 1),
    CODING_INIT(3, 128, 0, 1), CODING_INIT(3, 128, 1, 1), CODING_INIT(3, 192, 0, 1),
    CODING_INIT(3, 192, 1, 1), CODING_INIT(3, 224, 0, 1), CODING_INIT(3, 224, 1, 1),
    CODING_INIT(3, 240, 0, 1), CODING_INIT(3, 240, 1, 1), CODING_INIT(3, 248, 0, 1),
    CODING_INIT(3, 248, 1, 1), CODING_INIT(4, 192, 0, 0), CODING_INIT(4, 224, 0, 0),
    CODING_INIT(4, 240, 0, 0), CODING_INIT(4, 248, 0, 0), CODING_INIT(4, 252, 0, 0),
    CODING_INIT(4, 8, 0, 1),   CODING_INIT(4, 8, 1, 1),   CODING_INIT(4, 16, 0, 1),
    CODING_INIT(4, 16, 1, 1),  CODING_INIT(4, 32, 0, 1),  CODING_INIT(4, 32, 1, 1),
    CODING_INIT(4, 64, 0, 1),  CODING_INIT(4, 64, 1, 1),  CODING_INIT(4, 128, 0, 1),
    CODING_INIT(4, 128, 1, 1), CODING_INIT(4, 192, 0, 1), CODING_INIT(4, 192, 1, 1),
    CODING_INIT(4, 224, 0, 1), CODING_INIT(4, 224, 1, 1), CODING_INIT(4, 240, 0, 1),
    CODING_INIT(4, 240, 1, 1), CODING_INIT(4, 248, 0, 1), CODING_INIT(4, 248, 1, 1),
    CODING_INIT(0, 0, 0, 0)};
#define BASIC_INDEX_LIMIT (int)(sizeof(basic_codings) / sizeof(basic_codings[0]) - 1)

coding *coding::findByIndex(int idx)
{
    int index_limit = BASIC_INDEX_LIMIT;
    assert(_meta_canon_min == 1 && _meta_canon_max + 1 == index_limit);

    if (idx >= _meta_canon_min && idx <= _meta_canon_max)
        return basic_codings[idx].init();
    else
        return nullptr;
}