Exemplo n.º 1
0
void JIT::compileSetupVarargsFrame(Instruction* instruction, CallLinkInfo* info)
{
    int thisValue = instruction[3].u.operand;
    int arguments = instruction[4].u.operand;
    int firstFreeRegister = instruction[5].u.operand;
    int firstVarArgOffset = instruction[6].u.operand;

    emitLoad(arguments, regT1, regT0);
    callOperation(operationSizeFrameForVarargs, regT1, regT0, -firstFreeRegister, firstVarArgOffset);
    move(TrustedImm32(-firstFreeRegister), regT1);
    emitSetVarargsFrame(*this, returnValueGPR, false, regT1, regT1);
    addPtr(TrustedImm32(-(sizeof(CallerFrameAndPC) + WTF::roundUpToMultipleOf(stackAlignmentBytes(), 6 * sizeof(void*)))), regT1, stackPointerRegister);
    emitLoad(arguments, regT2, regT4);
    callOperation(operationSetupVarargsFrame, regT1, regT2, regT4, firstVarArgOffset, regT0);
    move(returnValueGPR, regT1);

    // Profile the argument count.
    load32(Address(regT1, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
    load8(info->addressOfMaxNumArguments(), regT0);
    Jump notBiggest = branch32(Above, regT0, regT2);
    Jump notSaturated = branch32(BelowOrEqual, regT2, TrustedImm32(255));
    move(TrustedImm32(255), regT2);
    notSaturated.link(this);
    store8(regT2, info->addressOfMaxNumArguments());
    notBiggest.link(this);
    
    // Initialize 'this'.
    emitLoad(thisValue, regT2, regT0);
    store32(regT0, Address(regT1, PayloadOffset + (CallFrame::thisArgumentOffset() * static_cast<int>(sizeof(Register)))));
    store32(regT2, Address(regT1, TagOffset + (CallFrame::thisArgumentOffset() * static_cast<int>(sizeof(Register)))));
    
    addPtr(TrustedImm32(sizeof(CallerFrameAndPC)), regT1, stackPointerRegister);
}
Exemplo n.º 2
0
void JIT::compileSetupVarargsFrame(Instruction* instruction, CallLinkInfo* info)
{
    int thisValue = instruction[3].u.operand;
    int arguments = instruction[4].u.operand;
    int firstFreeRegister = instruction[5].u.operand;
    int firstVarArgOffset = instruction[6].u.operand;

    JumpList slowCase;
    JumpList end;
    bool canOptimize = m_codeBlock->usesArguments()
        && arguments == m_codeBlock->argumentsRegister().offset()
        && !m_codeBlock->symbolTable()->slowArguments();

    if (canOptimize) {
        emitGetVirtualRegister(arguments, regT0);
        slowCase.append(branch64(NotEqual, regT0, TrustedImm64(JSValue::encode(JSValue()))));
        
        move(TrustedImm32(-firstFreeRegister), regT1);
        emitSetupVarargsFrameFastCase(*this, regT1, regT0, regT1, regT2, firstVarArgOffset, slowCase);
        end.append(jump());
        slowCase.link(this);
    }

    emitGetVirtualRegister(arguments, regT1);
    callOperation(operationSizeFrameForVarargs, regT1, -firstFreeRegister, firstVarArgOffset);
    move(TrustedImm32(-firstFreeRegister), regT1);
    emitSetVarargsFrame(*this, returnValueGPR, false, regT1, regT1);
    addPtr(TrustedImm32(-(sizeof(CallerFrameAndPC) + WTF::roundUpToMultipleOf(stackAlignmentBytes(), 5 * sizeof(void*)))), regT1, stackPointerRegister);
    emitGetVirtualRegister(arguments, regT2);
    callOperation(operationSetupVarargsFrame, regT1, regT2, firstVarArgOffset, regT0);
    move(returnValueGPR, regT1);

    if (canOptimize)
        end.link(this);
    
    // Profile the argument count.
    load32(Address(regT1, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
    load8(&info->maxNumArguments, regT0);
    Jump notBiggest = branch32(Above, regT0, regT2);
    Jump notSaturated = branch32(BelowOrEqual, regT2, TrustedImm32(255));
    move(TrustedImm32(255), regT2);
    notSaturated.link(this);
    store8(regT2, &info->maxNumArguments);
    notBiggest.link(this);
    
    // Initialize 'this'.
    emitGetVirtualRegister(thisValue, regT0);
    store64(regT0, Address(regT1, CallFrame::thisArgumentOffset() * static_cast<int>(sizeof(Register))));

    addPtr(TrustedImm32(sizeof(CallerFrameAndPC)), regT1, stackPointerRegister);
}
Exemplo n.º 3
0
// 08080168
u8 *battle_load_arguments(struct battle_config_entry *bce, u8 *cursor) {
	while (1) {
		switch (bce->target_type) {
			case LOAD_8:  *(u8 *)bce->target = load8 (cursor); cursor += 1; break;
			case LOAD_16: *(u16*)bce->target = load16(cursor); cursor += 2; break;
			case LOAD_32: *(u32*)bce->target = load32(cursor); cursor += 4; break;
			case ZERO_8:  *(u8 *)bce->target = 0;              cursor += 1; break;
			case ZERO_16: *(u16*)bce->target = 0;              cursor += 2; break;
			case ZERO_32: *(u32*)bce->target = 0;              cursor += 4; break;
			case END:     *(u32*)bce->target = (u32)cursor;    return cursor;
			default: break;
		}
		bce++;
	}
}
Exemplo n.º 4
0
uint32_t loadLength(FILE* pFic)
{
    uint32_t value = 0;
    bool readMore = true;
    while (readMore)
    {
        auto b = load8(pFic);
        if (b & 0x80)
        {
            readMore = true;
        }
        else
        {
            readMore = false;
        }
        value <<= 7;
        value |= b & 0x7F;
    }
    return value;
}
Exemplo n.º 5
0
TrackFrames buildTrackFramesFromMidi(const std::string& filename)
{
    TrackFrames trackFrames;

    FILE* pFic;
    fopen_s(&pFic, filename.c_str(), "rb");

    // Load header
    struct HeaderChunk
    {
        char HThd[4];
        uint32_t size;
        uint16_t format;
        uint16_t nbTrack;
        uint16_t division;
    } headerChunk;
    loadStr(headerChunk.HThd, 4, pFic);
    headerChunk.size = load32(pFic);
    headerChunk.format = load16(pFic);
    headerChunk.nbTrack = load16(pFic);
    headerChunk.division = load16(pFic);

    uint32_t maxTime = 0;

    std::set<int> channelSet;
    //int midiToChannels[] = { // mario
    //    5, 0, 1, 2,
    //    5, 5, 5, 5,
    //    5, 5, 5, 5,
    //    5, 5, 5, 5,
    //};
    //int midiToChannels[] = { // sm2
    //    0, 2, 1, 5,
    //    5, 5, 5, 5,
    //    5, 3, 5, 5,
    //    5, 5, 5, 5,
    //};
    int midiToChannels[] = {
        0, 1, 2, 5,
        5, 5, 5, 5,
        5, 5, 5, 5,
        5, 5, 5, 5,
    };
    //int midiToChannels[] = { // zelda
    //    0, 1, 2, 5,
    //    5, 5, 5, 5,
    //    5, 3, 5, 5,
    //    5, 5, 5, 5,
    //};

    // Load tracks
    for (auto i = 0u; i < headerChunk.nbTrack; ++i)
    {
        char MTrk[4];
        loadStr(MTrk, 4, pFic);
        auto len = load32(pFic);
        auto start = ftell(pFic);
        auto cur = start;
        uint32_t curTime = 0;

        while (cur - start < static_cast<decltype(cur)>(len))
        {
            auto v_time = loadLength(pFic);
            auto event = load8(pFic);

            curTime += v_time;// / (headerChunk.division / 24);
            maxTime = std::max(maxTime, curTime);
            trackFrames.resize(maxTime * 4 + 16);

            if (event == 0xFF)
            {
                // Meta event
                event = load8(pFic);
                if (event == 0x58)
                {
                    // Time Signature
                    event = load8(pFic);
                    assert(event == 0x04);
                    load32(pFic); // Ignore who f*****g cares
                }
                else if (event == 0x7F)
                {
                    // Sequencer Specific Meta-Event
                    auto dataLen = load8(pFic); // Len, ignore
                    while (dataLen)
                    {
                        load8(pFic);
                        --dataLen;
                    }
                }
                else if (event == 0x51)
                {
                    // Set Tempo
                    event = load8(pFic);
                    assert(event == 0x03);
                    load8(pFic);
                    load8(pFic);
                    load8(pFic);
                }
                else if (event == 0x2F)
                {
                    // End of Track
                    event = load8(pFic);
                    assert(event == 0x00);
                }
                else if (event == 0x03)
                {
                    // Sequence/Track Name
                    auto textLen = load8(pFic);
                    char text[257] = {0};
                    loadStr(text, textLen, pFic);
                    printf("Track found: %i, %s\n", i, text);
                }
                else if (event == 0x59)
                {
                    // Key Signature
                    event = load8(pFic);
                    assert(event == 0x02);
                    auto sf = load8(pFic);
                    auto mi = load8(pFic);
                }
                else if (event == 0x21)
                {
                    // End of Track
                    event = load8(pFic);
                    assert(event == 0x01);
                    load8(pFic);
                }
                else if (event == 0x20)
                {
                    // MIDI Channel Prefix
                    event = load8(pFic);
                    assert(event == 0x01);
                    auto channel = load8(pFic);
                }
                else
                {
                    assert(false);
                }
            }
            else if (event & 0x80)
            {
                auto channelNo = event & 0x0F;
                channelSet.insert(channelNo);
                if (channelNo < sizeof(midiToChannels) / sizeof(int)) channelNo = midiToChannels[channelNo];
                event = event & 0xF0;

                if (event == 0xB0)
                {
                    // Control Change
                    auto controllerNumber = load8(pFic) & 0x7F;
                    auto controllerValue = load8(pFic) & 0x7F;
                }
                else if (event == 0xC0)
                {
                    // Program Change
                    auto programNumber = load8(pFic) & 0x7F;
                }
                else if (event == 0x90)
                {
                    // Note On event
                    auto note = load8(pFic) & 0x7F;
                    auto vol = load8(pFic) & 0x7F;
                    if (channelNo < 3)
                    {
                        auto& trackFrame = trackFrames[curTime * 4 + channelNo];
                        trackFrame = NOTE(noteTable[note], channelNo, (vol * 15) / 127);
                    }
                    else if (channelNo == 3)
                    {
                        auto& trackFrame = trackFrames[curTime * 4 + channelNo];
                        trackFrame = NOTE(noteTable[note + 12], channelNo, (vol * 15) / 127);
                    }
                }
                else if (event == 0x80)
                {
                    // Note Off event
                    auto note = load8(pFic) & 0x7F;
                    auto vol = load8(pFic) & 0x7F;
                    if (channelNo == 2)
                    {
                        //auto& trackFrame = trackFrames[curTime * 4 + channelNo];
                        //trackFrame = NOTE(noteTable[note + 12], channelNo, 0);
                    }
                    if (channelNo == 3)
                    {
                        auto& trackFrame = trackFrames[curTime * 4 + channelNo];
                        trackFrame = NOTE(noteTable[note], channelNo, 0);
                    }
                }
                else if (event == 0xE0)
                {
                    // Pitch Wheel Change
                    auto l = load8(pFic) & 0x7F;
                    auto m = load8(pFic) & 0x7F;
                }
                else
                {
                    assert(false);
                }
            }
            else if (event <= 0x7F)
            {
                load8(pFic);
            }
            else
            {
                assert(false);
            }

            cur = ftell(pFic);
        }
    }

    fclose(pFic);

    for (auto& channelNo : channelSet)
    {
        printf("%i\n", channelNo);
    }

    return trackFrames;
}
Exemplo n.º 6
0
#if defined(_MSC_VER) && !defined(__clang__)
#  define restrict __restrict
#endif

// ===============================================================
// Coarse/fine copy, non overlapping buffers

/*! @abstract Copy at least \p nbytes bytes from \p src to \p dst, by blocks
 * of 8 bytes (may go beyond range). No overlap.
 * @return \p dst + \p nbytes. */
static inline unsigned char *lzvn_copy64(unsigned char *restrict dst,
                                         const unsigned char *restrict src,
                                         size_t nbytes) {
  for (size_t i = 0; i < nbytes; i += 8)
    store8(dst + i, load8(src + i));
  return dst + nbytes;
}

/*! @abstract Copy exactly \p nbytes bytes from \p src to \p dst (respects range).
 * No overlap.
 * @return \p dst + \p nbytes. */
static inline unsigned char *lzvn_copy8(unsigned char *restrict dst,
                                        const unsigned char *restrict src,
                                        size_t nbytes) {
  for (size_t i = 0; i < nbytes; i++)
    dst[i] = src[i];
  return dst + nbytes;
}

/*! @abstract Emit (L,0,0) instructions (final literal).
Exemplo n.º 7
0
void lzvn_decode(lzvn_decoder_state *state) {
#if HAVE_LABELS_AS_VALUES
  // Jump table for all instructions
  static const void *opc_tbl[256] = {
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&eos,   &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&nop,   &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&nop,   &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&udef,  &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&udef,  &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&udef,  &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&udef,  &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&udef,  &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,
      &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d,
      &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d,
      &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d,
      &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d, &&med_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&sml_d, &&pre_d, &&lrg_d,
      &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,
      &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,  &&udef,
      &&lrg_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l,
      &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l, &&sml_l,
      &&lrg_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m,
      &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m, &&sml_m};
#endif
  size_t src_len = state->src_end - state->src;
  size_t dst_len = state->dst_end - state->dst;
  if (src_len == 0 || dst_len == 0)
    return; // empty buffer

  const unsigned char *src_ptr = state->src;
  unsigned char *dst_ptr = state->dst;
  size_t D = state->d_prev;
  size_t M;
  size_t L;
  size_t opc_len;

  // Do we have a partially expanded match saved in state?
  if (state->L != 0 || state->M != 0) {
    L = state->L;
    M = state->M;
    D = state->D;
    opc_len = 0; // we already skipped the op
    state->L = state->M = state->D = 0;
    if (M == 0)
      goto copy_literal;
    if (L == 0)
      goto copy_match;
    goto copy_literal_and_match;
  }

  unsigned char opc = src_ptr[0];

#if HAVE_LABELS_AS_VALUES
  goto *opc_tbl[opc];
#else
  for (;;) {
    switch (opc) {
#endif
//  ===============================================================
//  These four opcodes (sml_d, med_d, lrg_d, and pre_d) encode both a
//  literal and a match. The bulk of their implementations are shared;
//  each label here only does the work of setting the opcode length (not
//  including any literal bytes), and extracting the literal length, match
//  length, and match distance (except in pre_d). They then jump into the
//  shared implementation to actually output the literal and match bytes.
//
//  No error checking happens in the first stage, except for ensuring that
//  the source has enough length to represent the full opcode before
//  reading past the first byte.
sml_d:
#if !HAVE_LABELS_AS_VALUES
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 8:
  case 9:
  case 10:
  case 11:
  case 12:
  case 13:
  case 16:
  case 17:
  case 18:
  case 19:
  case 20:
  case 21:
  case 24:
  case 25:
  case 26:
  case 27:
  case 28:
  case 29:
  case 32:
  case 33:
  case 34:
  case 35:
  case 36:
  case 37:
  case 40:
  case 41:
  case 42:
  case 43:
  case 44:
  case 45:
  case 48:
  case 49:
  case 50:
  case 51:
  case 52:
  case 53:
  case 56:
  case 57:
  case 58:
  case 59:
  case 60:
  case 61:
  case 64:
  case 65:
  case 66:
  case 67:
  case 68:
  case 69:
  case 72:
  case 73:
  case 74:
  case 75:
  case 76:
  case 77:
  case 80:
  case 81:
  case 82:
  case 83:
  case 84:
  case 85:
  case 88:
  case 89:
  case 90:
  case 91:
  case 92:
  case 93:
  case 96:
  case 97:
  case 98:
  case 99:
  case 100:
  case 101:
  case 104:
  case 105:
  case 106:
  case 107:
  case 108:
  case 109:
  case 128:
  case 129:
  case 130:
  case 131:
  case 132:
  case 133:
  case 136:
  case 137:
  case 138:
  case 139:
  case 140:
  case 141:
  case 144:
  case 145:
  case 146:
  case 147:
  case 148:
  case 149:
  case 152:
  case 153:
  case 154:
  case 155:
  case 156:
  case 157:
  case 192:
  case 193:
  case 194:
  case 195:
  case 196:
  case 197:
  case 200:
  case 201:
  case 202:
  case 203:
  case 204:
  case 205:
#endif
  UPDATE_GOOD;
  // "small distance": This opcode has the structure LLMMMDDD DDDDDDDD LITERAL
  //  where the length of literal (0-3 bytes) is encoded by the high 2 bits of
  //  the first byte. We first extract the literal length so we know how long
  //  the opcode is, then check that the source can hold both this opcode and
  //  at least one byte of the next (because any valid input stream must be
  //  terminated with an eos token).
  opc_len = 2;
  L = (size_t)extract(opc, 6, 2);
  M = (size_t)extract(opc, 3, 3) + 3;
  //  We need to ensure that the source buffer is long enough that we can
  //  safely read this entire opcode, the literal that follows, and the first
  //  byte of the next opcode.  Once we satisfy this requirement, we can
  //  safely unpack the match distance. A check similar to this one is
  //  present in all the opcode implementations.
  if (src_len <= opc_len + L)
    return; // source truncated
  D = (size_t)extract(opc, 0, 3) << 8 | src_ptr[1];
  goto copy_literal_and_match;

med_d:
#if !HAVE_LABELS_AS_VALUES
  case 160:
  case 161:
  case 162:
  case 163:
  case 164:
  case 165:
  case 166:
  case 167:
  case 168:
  case 169:
  case 170:
  case 171:
  case 172:
  case 173:
  case 174:
  case 175:
  case 176:
  case 177:
  case 178:
  case 179:
  case 180:
  case 181:
  case 182:
  case 183:
  case 184:
  case 185:
  case 186:
  case 187:
  case 188:
  case 189:
  case 190:
  case 191:
#endif
  UPDATE_GOOD;
  //  "medium distance": This is a minor variant of the "small distance"
  //  encoding, where we will now use two extra bytes instead of one to encode
  //  the restof the match length and distance. This allows an extra two bits
  //  for the match length, and an extra three bits for the match distance. The
  //  full structure of the opcode is 101LLMMM DDDDDDMM DDDDDDDD LITERAL.
  opc_len = 3;
  L = (size_t)extract(opc, 3, 2);
  if (src_len <= opc_len + L)
    return; // source truncated
  uint16_t opc23 = load2(&src_ptr[1]);
  M = (size_t)((extract(opc, 0, 3) << 2 | extract(opc23, 0, 2)) + 3);
  D = (size_t)extract(opc23, 2, 14);
  goto copy_literal_and_match;

lrg_d:
#if !HAVE_LABELS_AS_VALUES
  case 7:
  case 15:
  case 23:
  case 31:
  case 39:
  case 47:
  case 55:
  case 63:
  case 71:
  case 79:
  case 87:
  case 95:
  case 103:
  case 111:
  case 135:
  case 143:
  case 151:
  case 159:
  case 199:
  case 207:
#endif
  UPDATE_GOOD;
  //  "large distance": This is another variant of the "small distance"
  //  encoding, where we will now use two extra bytes to encode the match
  //  distance, which allows distances up to 65535 to be represented. The full
  //  structure of the opcode is LLMMM111 DDDDDDDD DDDDDDDD LITERAL.
  opc_len = 3;
  L = (size_t)extract(opc, 6, 2);
  M = (size_t)extract(opc, 3, 3) + 3;
  if (src_len <= opc_len + L)
    return; // source truncated
  D = load2(&src_ptr[1]);
  goto copy_literal_and_match;

pre_d:
#if !HAVE_LABELS_AS_VALUES
  case 70:
  case 78:
  case 86:
  case 94:
  case 102:
  case 110:
  case 134:
  case 142:
  case 150:
  case 158:
  case 198:
  case 206:
#endif
  UPDATE_GOOD;
  //  "previous distance": This opcode has the structure LLMMM110, where the
  //  length of the literal (0-3 bytes) is encoded by the high 2 bits of the
  //  first byte. We first extract the literal length so we know how long
  //  the opcode is, then check that the source can hold both this opcode and
  //  at least one byte of the next (because any valid input stream must be
  //  terminated with an eos token).
  opc_len = 1;
  L = (size_t)extract(opc, 6, 2);
  M = (size_t)extract(opc, 3, 3) + 3;
  if (src_len <= opc_len + L)
    return; // source truncated
  goto copy_literal_and_match;

copy_literal_and_match:
  //  Common implementation of writing data for opcodes that have both a
  //  literal and a match. We begin by advancing the source pointer past
  //  the opcode, so that it points at the first literal byte (if L
  //  is non-zero; otherwise it points at the next opcode).
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  //  Now we copy the literal from the source pointer to the destination.
  if (__builtin_expect(dst_len >= 4 && src_len >= 4, 1)) {
    //  The literal is 0-3 bytes; if we are not near the end of the buffer,
    //  we can safely just do a 4 byte copy (which is guaranteed to cover
    //  the complete literal, and may include some other bytes as well).
    store4(dst_ptr, load4(src_ptr));
  } else if (L <= dst_len) {
    //  We are too close to the end of either the input or output stream
    //  to be able to safely use a four-byte copy, but we will not exhaust
    //  either stream (we already know that the source will not be
    //  exhausted from checks in the individual opcode implementations,
    //  and we just tested that dst_len > L). Thus, we need to do a
    //  byte-by-byte copy of the literal. This is slow, but it can only ever
    //  happen near the very end of a buffer, so it is not an important case to
    //  optimize.
    for (size_t i = 0; i < L; ++i)
      dst_ptr[i] = src_ptr[i];
  } else {
    // Destination truncated: fill DST, and store partial match

    // Copy partial literal
    for (size_t i = 0; i < dst_len; ++i)
      dst_ptr[i] = src_ptr[i];
    // Save state
    state->src = src_ptr + dst_len;
    state->dst = dst_ptr + dst_len;
    state->L = L - dst_len;
    state->M = M;
    state->D = D;
    return; // destination truncated
  }
  //  Having completed the copy of the literal, we advance both the source
  //  and destination pointers by the number of literal bytes.
  PTR_LEN_INC(dst_ptr, dst_len, L);
  PTR_LEN_INC(src_ptr, src_len, L);
  //  Check if the match distance is valid; matches must not reference
  //  bytes that preceed the start of the output buffer, nor can the match
  //  distance be zero.
  if (D > dst_ptr - state->dst_begin || D == 0)
    goto invalid_match_distance;
copy_match:
  //  Now we copy the match from dst_ptr - D to dst_ptr. It is important to keep
  //  in mind that we may have D < M, in which case the source and destination
  //  windows overlap in the copy. The semantics of the match copy are *not*
  //  those of memmove( ); if the buffers overlap it needs to behave as though
  //  we were copying byte-by-byte in increasing address order. If, for example,
  //  D is 1, the copy operation is equivalent to:
  //
  //      memset(dst_ptr, dst_ptr[-1], M);
  //
  //  i.e. it splats the previous byte. This means that we need to be very
  //  careful about using wide loads or stores to perform the copy operation.
  if (__builtin_expect(dst_len >= M + 7 && D >= 8, 1)) {
    //  We are not near the end of the buffer, and the match distance
    //  is at least eight. Thus, we can safely loop using eight byte
    //  copies. The last of these may slop over the intended end of
    //  the match, but this is OK because we know we have a safety bound
    //  away from the end of the destination buffer.
    for (size_t i = 0; i < M; i += 8)
      store8(&dst_ptr[i], load8(&dst_ptr[i - D]));
  } else if (M <= dst_len) {
    //  Either the match distance is too small, or we are too close to
    //  the end of the buffer to safely use eight byte copies. Fall back
    //  on a simple byte-by-byte implementation.
    for (size_t i = 0; i < M; ++i)
      dst_ptr[i] = dst_ptr[i - D];
  } else {
    // Destination truncated: fill DST, and store partial match

    // Copy partial match
    for (size_t i = 0; i < dst_len; ++i)
      dst_ptr[i] = dst_ptr[i - D];
    // Save state
    state->src = src_ptr;
    state->dst = dst_ptr + dst_len;
    state->L = 0;
    state->M = M - dst_len;
    state->D = D;
    return; // destination truncated
  }
  //  Update the destination pointer and length to account for the bytes
  //  written by the match, then load the next opcode byte and branch to
  //  the appropriate implementation.
  PTR_LEN_INC(dst_ptr, dst_len, M);
  opc = src_ptr[0];
#if HAVE_LABELS_AS_VALUES
  goto *opc_tbl[opc];
#else
  break;
#endif

// ===============================================================
// Opcodes representing only a match (no literal).
//  These two opcodes (lrg_m and sml_m) encode only a match. The match
//  distance is carried over from the previous opcode, so all they need
//  to encode is the match length. We are able to reuse the match copy
//  sequence from the literal and match opcodes to perform the actual
//  copy implementation.
sml_m:
#if !HAVE_LABELS_AS_VALUES
  case 241:
  case 242:
  case 243:
  case 244:
  case 245:
  case 246:
  case 247:
  case 248:
  case 249:
  case 250:
  case 251:
  case 252:
  case 253:
  case 254:
  case 255:
#endif
  UPDATE_GOOD;
  //  "small match": This opcode has no literal, and uses the previous match
  //  distance (i.e. it encodes only the match length), in a single byte as
  //  1111MMMM.
  opc_len = 1;
  if (src_len <= opc_len)
    return; // source truncated
  M = (size_t)extract(opc, 0, 4);
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  goto copy_match;

lrg_m:
#if !HAVE_LABELS_AS_VALUES
  case 240:
#endif
  UPDATE_GOOD;
  //  "large match": This opcode has no literal, and uses the previous match
  //  distance (i.e. it encodes only the match length). It is encoded in two
  //  bytes as 11110000 MMMMMMMM.  Because matches smaller than 16 bytes can
  //  be represented by sml_m, there is an implicit bias of 16 on the match
  //  length; the representable values are [16,271].
  opc_len = 2;
  if (src_len <= opc_len)
    return; // source truncated
  M = src_ptr[1] + 16;
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  goto copy_match;

// ===============================================================
// Opcodes representing only a literal (no match).
//  These two opcodes (lrg_l and sml_l) encode only a literal.  There is no
//  match length or match distance to worry about (but we need to *not*
//  touch D, as it must be preserved between opcodes).
sml_l:
#if !HAVE_LABELS_AS_VALUES
  case 225:
  case 226:
  case 227:
  case 228:
  case 229:
  case 230:
  case 231:
  case 232:
  case 233:
  case 234:
  case 235:
  case 236:
  case 237:
  case 238:
  case 239:
#endif
  UPDATE_GOOD;
  //  "small literal": This opcode has no match, and encodes only a literal
  //  of length up to 15 bytes. The format is 1110LLLL LITERAL.
  opc_len = 1;
  L = (size_t)extract(opc, 0, 4);
  goto copy_literal;

lrg_l:
#if !HAVE_LABELS_AS_VALUES
  case 224:
#endif
  UPDATE_GOOD;
  //  "large literal": This opcode has no match, and uses the previous match
  //  distance (i.e. it encodes only the match length). It is encoded in two
  //  bytes as 11100000 LLLLLLLL LITERAL.  Because literals smaller than 16
  //  bytes can be represented by sml_l, there is an implicit bias of 16 on
  //  the literal length; the representable values are [16,271].
  opc_len = 2;
  if (src_len <= 2)
    return; // source truncated
  L = src_ptr[1] + 16;
  goto copy_literal;

copy_literal:
  //  Check that the source buffer is large enough to hold the complete
  //  literal and at least the first byte of the next opcode. If so, advance
  //  the source pointer to point to the first byte of the literal and adjust
  //  the source length accordingly.
  if (src_len <= opc_len + L)
    return; // source truncated
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  //  Now we copy the literal from the source pointer to the destination.
  if (dst_len >= L + 7 && src_len >= L + 7) {
    //  We are not near the end of the source or destination buffers; thus
    //  we can safely copy the literal using wide copies, without worrying
    //  about reading or writing past the end of either buffer.
    for (size_t i = 0; i < L; i += 8)
      store8(&dst_ptr[i], load8(&src_ptr[i]));
  } else if (L <= dst_len) {
    //  We are too close to the end of either the input or output stream
    //  to be able to safely use an eight-byte copy. Instead we copy the
    //  literal byte-by-byte.
    for (size_t i = 0; i < L; ++i)
      dst_ptr[i] = src_ptr[i];
  } else {
    // Destination truncated: fill DST, and store partial match

    // Copy partial literal
    for (size_t i = 0; i < dst_len; ++i)
      dst_ptr[i] = src_ptr[i];
    // Save state
    state->src = src_ptr + dst_len;
    state->dst = dst_ptr + dst_len;
    state->L = L - dst_len;
    state->M = 0;
    state->D = D;
    return; // destination truncated
  }
  //  Having completed the copy of the literal, we advance both the source
  //  and destination pointers by the number of literal bytes.
  PTR_LEN_INC(dst_ptr, dst_len, L);
  PTR_LEN_INC(src_ptr, src_len, L);
  //  Load the first byte of the next opcode, and jump to its implementation.
  opc = src_ptr[0];
#if HAVE_LABELS_AS_VALUES
  goto *opc_tbl[opc];
#else
  break;
#endif

// ===============================================================
// Other opcodes
nop:
#if !HAVE_LABELS_AS_VALUES
  case 6:
  case 14:
#endif
  UPDATE_GOOD;
  opc_len = 1;
  if (src_len <= opc_len)
    return; // source truncated
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  opc = src_ptr[0];
#if HAVE_LABELS_AS_VALUES
  goto *opc_tbl[opc];
#else
  break;
#endif

eos:
#if !HAVE_LABELS_AS_VALUES
  case 22:
#endif
  opc_len = 8;
  if (src_len < opc_len)
    return; // source truncated (here we don't need an extra byte for next op
            // code)
  PTR_LEN_INC(src_ptr, src_len, opc_len);
  state->end_of_stream = 1;
  UPDATE_GOOD;
  return; // end-of-stream

// ===============================================================
// Return on error
udef:
#if !HAVE_LABELS_AS_VALUES
  case 30:
  case 38:
  case 46:
  case 54:
  case 62:
  case 112:
  case 113:
  case 114:
  case 115:
  case 116:
  case 117:
  case 118:
  case 119:
  case 120:
  case 121:
  case 122:
  case 123:
  case 124:
  case 125:
  case 126:
  case 127:
  case 208:
  case 209:
  case 210:
  case 211:
  case 212:
  case 213:
  case 214:
  case 215:
  case 216:
  case 217:
  case 218:
  case 219:
  case 220:
  case 221:
  case 222:
  case 223:
#endif
invalid_match_distance:

  return; // we already updated state
#if !HAVE_LABELS_AS_VALUES
    }
  }
#endif
}