/* filter.h --

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   Copyright (C) Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program 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 for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
 */

#pragma once

/*************************************************************************
// A filter is a reversible operation that modifies a given
// block of memory.
//
// A filter can fail and return false. In this case the buffer
// must be unmodified (or otherwise restored).
//
// If a filter fails and somehow cannot restore the block it must
// call throwFilterException() - this will cause the compression
// to fail.
//
// Unfilters throw exceptions in case of errors.
//
// The main idea behind filters is to convert relative jumps and calls
// to absolute addresses so that the buffer compresses better.
**************************************************************************/

class Filter final {
public:
    explicit Filter(int level) noexcept;
    void init(int id = 0, unsigned addvalue = 0) noexcept;

    bool filter(SPAN_0(byte) buf, unsigned buf_len);
    void unfilter(SPAN_0(byte) buf, unsigned buf_len, bool verify_checksum = false);
    void verifyUnfilter();
    bool scan(SPAN_0(const byte) buf, unsigned buf_len);

    static bool isValidFilter(int filter_id);
    static bool isValidFilter(int filter_id, const int *allowed_filters);

public:
    // Will be set by each call to filter()/unfilter().
    // Read-only afterwards.
    byte *buf = nullptr;
    unsigned buf_len = 0;

    // Checksum of the buffer before applying the filter
    // or after un-applying the filter.
    unsigned adler;

    // Input parameters used by various filters.
    unsigned addvalue;
    const int *preferred_ctos = nullptr;

    // Input/output parameters used by various filters
    byte cto; // call trick offset

    // Output used by various filters. Read only.
    unsigned calls;
    unsigned noncalls;
    unsigned wrongcalls;
    unsigned firstcall;
    unsigned lastcall;
    unsigned n_mru; // ctojr only

    // Read only.
    int id;

private:
    int clevel; // compression level
};

/*************************************************************************
// We don't want a full OO interface here because of
// certain implementation speed reasons.
//
// This class is private to Filter - don't look.
**************************************************************************/

class FilterImpl final {
    friend class Filter;

private:
    explicit FilterImpl() noexcept DELETED_FUNCTION;

    struct FilterEntry {
        int id; // 0 .. 255
        unsigned min_buf_len;
        unsigned max_buf_len;
        int (*do_filter)(Filter *);   // filter a buffer
        int (*do_unfilter)(Filter *); // unfilter a buffer
        int (*do_scan)(Filter *);     // scan a buffer
    };

    // get a specific filter entry
    static noinline const FilterEntry *getFilter(int id);

private:
    // strictly private filter database
    static const FilterEntry filters[];
    static const int n_filters; // number of filters[]
};

/* vim:set ts=4 sw=4 et: */
