#include "lzokay.h"
#include <cstring>
#include <algorithm>
#include <iterator>

/*
 * Based on documentation from the Linux sources: Documentation/lzo.txt
 * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/lzo.txt
 */

namespace lzokay {

#if _WIN32
#define HOST_BIG_ENDIAN 0
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define HOST_BIG_ENDIAN 1
#else
#define HOST_BIG_ENDIAN 0
#endif

#if HOST_BIG_ENDIAN
static uint16_t get_le16(const uint8_t* p) {
    uint16_t val = *reinterpret_cast<const uint16_t*>(p);
#if __GNUC__
    return __builtin_bswap16(val);
#elif _WIN32
    return _byteswap_ushort(val);
#else
    return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
#else
static uint16_t get_le16(const uint8_t* p) {
    return *reinterpret_cast<const uint16_t*>(p);
}
#endif

constexpr std::size_t Max255Count = std::size_t(~0) / 255 - 2;

#define NEEDS_IN(count) \
if (inp + (count) > inp_end) { \
        dst_size = outp - dst; \
        return EResult::InputOverrun; \
}

#define NEEDS_OUT(count) \
if (outp + (count) > outp_end) { \
        dst_size = outp - dst; \
        return EResult::OutputOverrun; \
}

#define CONSUME_ZERO_BYTE_LENGTH \
std::size_t offset; \
{ \
        const uint8_t *old_inp = inp; \
        while (*inp == 0) ++inp; \
        offset = inp - old_inp; \
        if (offset > Max255Count) { \
            dst_size = outp - dst; \
            return EResult::Error; \
    } \
}

#define WRITE_ZERO_BYTE_LENGTH(length) \
{ \
        std::size_t l; \
        for (l = length; l > 255; l -= 255) { *outp++ = 0; } \
        *outp++ = static_cast<uint8_t>(l); \
}

constexpr uint32_t M1MaxOffset = 0x0400;
constexpr uint32_t M2MaxOffset = 0x0800;
constexpr uint32_t M3MaxOffset = 0x4000;
// constexpr uint32_t M4MaxOffset = 0xbfff;

// constexpr uint32_t M1MinLen = 2;
// constexpr uint32_t M1MaxLen = 2;
constexpr uint32_t M2MinLen = 3;
constexpr uint32_t M2MaxLen = 8;
// constexpr uint32_t M3MinLen = 3;
constexpr uint32_t M3MaxLen = 33;
// constexpr uint32_t M4MinLen = 3;
constexpr uint32_t M4MaxLen = 9;

constexpr uint32_t M1Marker = 0x0;
// constexpr uint32_t M2Marker = 0x40;
constexpr uint32_t M3Marker = 0x20;
constexpr uint32_t M4Marker = 0x10;

constexpr uint32_t MaxMatchByLengthLen = 34; /* Max M3 len + 1 */

EResult decompress(const uint8_t* src, std::size_t src_size,
                   uint8_t* dst, std::size_t init_dst_size,
                   std::size_t& dst_size) {
    dst_size = init_dst_size;

    if (src_size < 3) {
        dst_size = 0;
        return EResult::InputOverrun;
    }

    const uint8_t* inp = src;
    const uint8_t* inp_end = src + src_size;
    uint8_t* outp = dst;
    uint8_t* outp_end = dst + dst_size;
    uint8_t* lbcur;
    std::size_t lblen;
    std::size_t state = 0;
    std::size_t nstate = 0;

    /* First byte encoding */
    if (*inp >= 22) {
        /* 22..255 : copy literal string
     *           length = (byte - 17) = 4..238
     *           state = 4 [ don't copy extra literals ]
     *           skip byte
     */
        std::size_t len = *inp++ - uint8_t(17);
        NEEDS_IN(len)
        NEEDS_OUT(len)
        for (std::size_t i = 0; i < len; ++i)
            *outp++ = *inp++;
        state = 4;
    } else if (*inp >= 18) {
        /* 18..21 : copy 0..3 literals
     *          state = (byte - 17) = 0..3  [ copy <state> literals ]
     *          skip byte
     */
        nstate = *inp++ - uint8_t(17);
        state = nstate;
        NEEDS_IN(nstate)
        NEEDS_OUT(nstate)
        for (std::size_t i = 0; i < nstate; ++i)
            *outp++ = *inp++;
    }
    /* 0..17 : follow regular instruction encoding, see below. It is worth
   *         noting that codes 16 and 17 will represent a block copy from
   *         the dictionary which is empty, and that they will always be
   *         invalid at this place.
   */

    while (true) {
        NEEDS_IN(1)
        uint8_t inst = *inp++;
        if (inst & 0xC0) {
            /* [M2]
       * 1 L L D D D S S  (128..255)
       *   Copy 5-8 bytes from block within 2kB distance
       *   state = S (copy S literals after this block)
       *   length = 5 + L
       * Always followed by exactly one byte : H H H H H H H H
       *   distance = (H << 3) + D + 1
       *
       * 0 1 L D D D S S  (64..127)
       *   Copy 3-4 bytes from block within 2kB distance
       *   state = S (copy S literals after this block)
       *   length = 3 + L
       * Always followed by exactly one byte : H H H H H H H H
       *   distance = (H << 3) + D + 1
       */
            NEEDS_IN(1)
            lbcur = outp - ((*inp++ << 3) + ((inst >> 2) & 0x7) + 1);
            lblen = std::size_t(inst >> 5) + 1;
            nstate = inst & uint8_t(0x3);
        } else if (inst & M3Marker) {
            /* [M3]
       * 0 0 1 L L L L L  (32..63)
       *   Copy of small block within 16kB distance (preferably less than 34B)
       *   length = 2 + (L ?: 31 + (zero_bytes * 255) + non_zero_byte)
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
       *   distance = D + 1
       *   state = S (copy S literals after this block)
       */
            lblen = std::size_t(inst & uint8_t(0x1f)) + 2;
            if (lblen == 2) {
                CONSUME_ZERO_BYTE_LENGTH
                    NEEDS_IN(1)
                    lblen += offset * 255 + 31 + *inp++;
            }
            NEEDS_IN(2)
            nstate = get_le16(inp);
            inp += 2;
            lbcur = outp - ((nstate >> 2) + 1);
            nstate &= 0x3;
        } else if (inst & M4Marker) {
            /* [M4]
       * 0 0 0 1 H L L L  (16..31)
       *   Copy of a block within 16..48kB distance (preferably less than 10B)
       *   length = 2 + (L ?: 7 + (zero_bytes * 255) + non_zero_byte)
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
       *   distance = 16384 + (H << 14) + D
       *   state = S (copy S literals after this block)
       *   End of stream is reached if distance == 16384
       */
            lblen = std::size_t(inst & uint8_t(0x7)) + 2;
            if (lblen == 2) {
                CONSUME_ZERO_BYTE_LENGTH
                    NEEDS_IN(1)
                    lblen += offset * 255 + 7 + *inp++;
            }
            NEEDS_IN(2)
            nstate = get_le16(inp);
            inp += 2;
            lbcur = outp - (((inst & 0x8) << 11) + (nstate >> 2));
            nstate &= 0x3;
            if (lbcur == outp)
                break; /* Stream finished */
            lbcur -= 16384;
        } else {
            /* [M1] Depends on the number of literals copied by the last instruction. */
            if (state == 0) {
                /* If last instruction did not copy any literal (state == 0), this
         * encoding will be a copy of 4 or more literal, and must be interpreted
         * like this :
         *
         *    0 0 0 0 L L L L  (0..15)  : copy long literal string
         *    length = 3 + (L ?: 15 + (zero_bytes * 255) + non_zero_byte)
         *    state = 4  (no extra literals are copied)
         */
                std::size_t len = inst + 3;
                if (len == 3) {
                    CONSUME_ZERO_BYTE_LENGTH
                        NEEDS_IN(1)
                        len += offset * 255 + 15 + *inp++;
                }
                /* copy_literal_run */
                NEEDS_IN(len)
                NEEDS_OUT(len)
                for (std::size_t i = 0; i < len; ++i)
                    *outp++ = *inp++;
                state = 4;
                continue;
            } else if (state != 4) {
                /* If last instruction used to copy between 1 to 3 literals (encoded in
         * the instruction's opcode or distance), the instruction is a copy of a
         * 2-byte block from the dictionary within a 1kB distance. It is worth
         * noting that this instruction provides little savings since it uses 2
         * bytes to encode a copy of 2 other bytes but it encodes the number of
         * following literals for free. It must be interpreted like this :
         *
         *    0 0 0 0 D D S S  (0..15)  : copy 2 bytes from <= 1kB distance
         *    length = 2
         *    state = S (copy S literals after this block)
         *  Always followed by exactly one byte : H H H H H H H H
         *    distance = (H << 2) + D + 1
         */
                NEEDS_IN(1)
                nstate = inst & uint8_t(0x3);
                lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 1);
                lblen = 2;
            } else {
                /* If last instruction used to copy 4 or more literals (as detected by
         * state == 4), the instruction becomes a copy of a 3-byte block from the
         * dictionary from a 2..3kB distance, and must be interpreted like this :
         *
         *    0 0 0 0 D D S S  (0..15)  : copy 3 bytes from 2..3 kB distance
         *    length = 3
         *    state = S (copy S literals after this block)
         *  Always followed by exactly one byte : H H H H H H H H
         *    distance = (H << 2) + D + 2049
         */
                NEEDS_IN(1)
                nstate = inst & uint8_t(0x3);
                lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 2049);
                lblen = 3;
            }
        }
        if (lbcur < dst) {
            dst_size = outp - dst;
            return EResult::LookbehindOverrun;
        }
        NEEDS_IN(nstate)
        NEEDS_OUT(lblen + nstate)
        /* Copy lookbehind */
        for (std::size_t i = 0; i < lblen; ++i)
            *outp++ = *lbcur++;
        state = nstate;
        /* Copy literal */
        for (std::size_t i = 0; i < nstate; ++i)
            *outp++ = *inp++;
    }

    dst_size = outp - dst;
    if (lblen != 3) /* Ensure terminating M4 was encountered */
        return EResult::Error;
    if (inp == inp_end)
        return EResult::Success;
    else if (inp < inp_end)
        return EResult::InputNotConsumed;
    else
        return EResult::InputOverrun;
}

struct State {
    const uint8_t* src;
    const uint8_t* src_end;
    const uint8_t* inp;
    uint32_t wind_sz;
    uint32_t wind_b;
    uint32_t wind_e;
    uint32_t cycle1_countdown;

    const uint8_t* bufp;
    uint32_t buf_sz;

    /* Access next input byte and advance both ends of circular buffer */
    void get_byte(uint8_t* buf) {
        if (inp >= src_end) {
            if (wind_sz > 0)
                --wind_sz;
            buf[wind_e] = 0;
            if (wind_e < DictBase::MaxMatchLen)
                buf[DictBase::BufSize + wind_e] = 0;
        } else {
            buf[wind_e] = *inp;
            if (wind_e < DictBase::MaxMatchLen)
                buf[DictBase::BufSize + wind_e] = *inp;
            ++inp;
        }
        if (++wind_e == DictBase::BufSize)
            wind_e = 0;
        if (++wind_b == DictBase::BufSize)
            wind_b = 0;
    }

    uint32_t pos2off(uint32_t pos) const {
        return wind_b > pos ? wind_b - pos : DictBase::BufSize - (pos - wind_b);
    }
};

class DictImpl : public DictBase {
public:
    struct Match3Impl : DictBase::Match3 {
        static uint32_t make_key(const uint8_t* data) {
            return ((0x9f5f * (((uint32_t(data[0]) << 5 ^ uint32_t(data[1])) << 5) ^ data[2])) >> 5) & 0x3fff;
        }

        uint16_t get_head(uint32_t key) const {
            return (chain_sz[key] == 0) ? uint16_t(UINT16_MAX) : head[key];
        }

        void init() {
            std::fill(std::begin(chain_sz), std::end(chain_sz), 0);
        }

        void remove(uint32_t pos, const uint8_t* b) {
            --chain_sz[make_key(b + pos)];
        }

        void advance(State& s, uint32_t& match_pos, uint32_t& match_count, const uint8_t* b) {
            uint32_t key = make_key(b + s.wind_b);
            match_pos = chain[s.wind_b] = get_head(key);
            match_count = chain_sz[key]++;
            if (match_count > DictBase::MaxMatchLen)
                match_count = DictBase::MaxMatchLen;
            head[key] = uint16_t(s.wind_b);
        }

        void skip_advance(State& s, const uint8_t* b) {
            uint32_t key = make_key(b + s.wind_b);
            chain[s.wind_b] = get_head(key);
            head[key] = uint16_t(s.wind_b);
            best_len[s.wind_b] = uint16_t(DictBase::MaxMatchLen + 1);
            chain_sz[key]++;
        }
    };

    struct Match2Impl : DictBase::Match2 {
        static uint32_t make_key(const uint8_t* data) {
            return uint32_t(data[0]) ^ (uint32_t(data[1]) << 8);
        }

        void init() {
            std::fill(std::begin(head), std::end(head), UINT16_MAX);
        }

        void add(uint16_t pos, const uint8_t* b) {
            head[make_key(b + pos)] = pos;
        }

        void remove(uint32_t pos, const uint8_t* b) {
            uint16_t& p = head[make_key(b + pos)];
            if (p == pos)
                p = UINT16_MAX;
        }

        bool search(State& s, uint32_t& lb_pos, uint32_t& lb_len,
                    uint32_t best_pos[MaxMatchByLengthLen], const uint8_t* b) const {
            uint16_t pos = head[make_key(b + s.wind_b)];
            if (pos == UINT16_MAX)
                return false;
            if (best_pos[2] == 0)
                best_pos[2] = pos + 1;
            if (lb_len < 2) {
                lb_len = 2;
                lb_pos = pos;
            }
            return true;
        }
    };

    void init(State& s, const uint8_t* src, std::size_t src_size) {
        auto& match3 = static_cast<Match3Impl&>(_storage->match3);
        auto& match2 = static_cast<Match2Impl&>(_storage->match2);

        s.cycle1_countdown = DictBase::MaxDist;
        match3.init();
        match2.init();

        s.src = src;
        s.src_end = src + src_size;
        s.inp = src;
        s.wind_sz = uint32_t(std::min(src_size, std::size_t(MaxMatchLen)));
        s.wind_b = 0;
        s.wind_e = s.wind_sz;
        std::copy_n(s.inp, s.wind_sz, _storage->buffer);
        s.inp += s.wind_sz;

        if (s.wind_e == DictBase::BufSize)
            s.wind_e = 0;

        if (s.wind_sz < 3)
            std::fill_n(_storage->buffer + s.wind_b + s.wind_sz, 3, 0);
    }

    void reset_next_input_entry(State& s, Match3Impl& match3, Match2Impl& match2) {
        /* Remove match from about-to-be-clobbered buffer entry */
        if (s.cycle1_countdown == 0) {
            match3.remove(s.wind_e, _storage->buffer);
            match2.remove(s.wind_e, _storage->buffer);
        } else {
            --s.cycle1_countdown;
        }
    }

    void advance(State& s, uint32_t& lb_off, uint32_t& lb_len,
                 uint32_t best_off[MaxMatchByLengthLen], bool skip) {
        auto& match3 = static_cast<Match3Impl&>(_storage->match3);
        auto& match2 = static_cast<Match2Impl&>(_storage->match2);

        if (skip) {
            for (uint32_t i = 0; i < lb_len - 1; ++i) {
                reset_next_input_entry(s, match3, match2);
                match3.skip_advance(s, _storage->buffer);
                match2.add(uint16_t(s.wind_b), _storage->buffer);
                s.get_byte(_storage->buffer);
            }
        }

        lb_len = 1;
        lb_off = 0;
        uint32_t lb_pos;

        uint32_t best_pos[MaxMatchByLengthLen] = {};
        uint32_t match_pos, match_count;
        match3.advance(s, match_pos, match_count, _storage->buffer);

        int best_char = _storage->buffer[s.wind_b];
        uint32_t best_len = lb_len;
        if (lb_len >= s.wind_sz) {
            if (s.wind_sz == 0)
                best_char = -1;
            lb_off = 0;
            match3.best_len[s.wind_b] = DictBase::MaxMatchLen + 1;
        } else {
            if (match2.search(s, lb_pos, lb_len, best_pos, _storage->buffer) && s.wind_sz >= 3) {
                for (uint32_t i = 0; i < match_count; ++i, match_pos = match3.chain[match_pos]) {
                    auto ref_ptr = _storage->buffer + s.wind_b;
                    auto match_ptr = _storage->buffer + match_pos;
                    auto mismatch = std::mismatch(ref_ptr, ref_ptr + s.wind_sz, match_ptr);
                    auto match_len = uint32_t(mismatch.first - ref_ptr);
                    if (match_len < 2)
                        continue;
                    if (match_len < MaxMatchByLengthLen && best_pos[match_len] == 0)
                        best_pos[match_len] = match_pos + 1;
                    if (match_len > lb_len) {
                        lb_len = match_len;
                        lb_pos = match_pos;
                        if (match_len == s.wind_sz || match_len > match3.best_len[match_pos])
                            break;
                    }
                }
            }
            if (lb_len > best_len)
                lb_off = s.pos2off(lb_pos);
            match3.best_len[s.wind_b] = uint16_t(lb_len);
            for (auto posit = std::begin(best_pos) + 2, offit = best_off + 2;
                 posit != std::end(best_pos); ++posit, ++offit) {
                *offit = (*posit > 0) ? s.pos2off(*posit - 1) : 0;
            }
        }

        reset_next_input_entry(s, match3, match2);

        match2.add(uint16_t(s.wind_b), _storage->buffer);

        s.get_byte(_storage->buffer);

        if (best_char < 0) {
            s.buf_sz = 0;
            lb_len = 0;
            /* Signal exit */
        } else {
            s.buf_sz = s.wind_sz + 1;
        }
        s.bufp = s.inp - s.buf_sz;
    }
};

static void find_better_match(const uint32_t best_off[MaxMatchByLengthLen], uint32_t& lb_len, uint32_t& lb_off) {
    if (lb_len <= M2MinLen || lb_off <= M2MaxOffset)
        return;
    if (lb_off > M2MaxOffset && lb_len >= M2MinLen + 1 && lb_len <= M2MaxLen + 1 &&
        best_off[lb_len - 1] != 0 && best_off[lb_len - 1] <= M2MaxOffset) {
        lb_len -= 1;
        lb_off = best_off[lb_len];
    } else if (lb_off > M3MaxOffset && lb_len >= M4MaxLen + 1 && lb_len <= M2MaxLen + 2 &&
               best_off[lb_len - 2] && best_off[lb_len] <= M2MaxOffset) {
        lb_len -= 2;
        lb_off = best_off[lb_len];
    } else if (lb_off > M3MaxOffset && lb_len >= M4MaxLen + 1 && lb_len <= M3MaxLen + 1 &&
               best_off[lb_len - 1] != 0 && best_off[lb_len - 2] <= M3MaxOffset) {
        lb_len -= 1;
        lb_off = best_off[lb_len];
    }
}

static EResult encode_literal_run(uint8_t*& outp, const uint8_t* outp_end, const uint8_t* dst, std::size_t& dst_size,
                                  const uint8_t* lit_ptr, uint32_t lit_len) {
    if (outp == dst && lit_len <= 238) {
        NEEDS_OUT(1);
        *outp++ = uint8_t(17 + lit_len);
    } else if (lit_len <= 3) {
        outp[-2] = uint8_t(outp[-2] | lit_len);
    } else if (lit_len <= 18) {
        NEEDS_OUT(1);
        *outp++ = uint8_t(lit_len - 3);
    } else {
        NEEDS_OUT((lit_len - 18) / 255 + 2);
        *outp++ = 0;
        WRITE_ZERO_BYTE_LENGTH(lit_len - 18);
    }
    NEEDS_OUT(lit_len);
    outp = std::copy_n(lit_ptr, lit_len, outp);
    return EResult::Success;
}

static EResult encode_lookback_match(uint8_t*& outp, const uint8_t* outp_end, const uint8_t* dst, std::size_t& dst_size,
                                     uint32_t lb_len, uint32_t lb_off, uint32_t last_lit_len) {
    if (lb_len == 2) {
        lb_off -= 1;
        NEEDS_OUT(2);
        *outp++ = uint8_t(M1Marker | ((lb_off & 0x3) << 2));
        *outp++ = uint8_t(lb_off >> 2);
    } else if (lb_len <= M2MaxLen && lb_off <= M2MaxOffset) {
        lb_off -= 1;
        NEEDS_OUT(2);
        *outp++ = uint8_t((lb_len - 1) << 5 | ((lb_off & 0x7) << 2));
        *outp++ = uint8_t(lb_off >> 3);
    } else if (lb_len == M2MinLen && lb_off <= M1MaxOffset + M2MaxOffset && last_lit_len >= 4) {
        lb_off -= 1 + M2MaxOffset;
        NEEDS_OUT(2);
        *outp++ = uint8_t(M1Marker | ((lb_off & 0x3) << 2));
        *outp++ = uint8_t(lb_off >> 2);
    } else if (lb_off <= M3MaxOffset) {
        lb_off -= 1;
        if (lb_len <= M3MaxLen) {
            NEEDS_OUT(1);
            *outp++ = uint8_t(M3Marker | (lb_len - 2));
        } else {
            lb_len -= M3MaxLen;
            NEEDS_OUT(lb_len / 255 + 2);
            *outp++ = uint8_t(M3Marker);
            WRITE_ZERO_BYTE_LENGTH(lb_len);
        }
        NEEDS_OUT(2);
        *outp++ = uint8_t(lb_off << 2);
        *outp++ = uint8_t(lb_off >> 6);
    } else {
        lb_off -= 0x4000;
        if (lb_len <= M4MaxLen) {
            NEEDS_OUT(1);
            *outp++ = uint8_t(M4Marker | ((lb_off & 0x4000) >> 11) | (lb_len - 2));
        } else {
            lb_len -= M4MaxLen;
            NEEDS_OUT(lb_len / 255 + 2);
            *outp++ = uint8_t(M4Marker | ((lb_off & 0x4000) >> 11));
            WRITE_ZERO_BYTE_LENGTH(lb_len);
        }
        NEEDS_OUT(2);
        *outp++ = uint8_t(lb_off << 2);
        *outp++ = uint8_t(lb_off >> 6);
    }
    return EResult::Success;
}

EResult compress(const uint8_t* src, std::size_t src_size,
                 uint8_t* dst, std::size_t init_dst_size,
                 std::size_t& dst_size, DictBase& dict) {
    EResult err;
    State s;
    auto& d = static_cast<DictImpl&>(dict);
    dst_size = init_dst_size;
    uint8_t* outp = dst;
    uint8_t* outp_end = dst + dst_size;
    uint32_t lit_len = 0;
    uint32_t lb_off, lb_len;
    uint32_t best_off[MaxMatchByLengthLen];
    d.init(s, src, src_size);
    const uint8_t* lit_ptr = s.inp;
    d.advance(s, lb_off, lb_len, best_off, false);
    while (s.buf_sz > 0) {
        if (lit_len == 0)
            lit_ptr = s.bufp;
        if (lb_len < 2 || (lb_len == 2 && (lb_off > M1MaxOffset || lit_len == 0 || lit_len >= 4)) ||
            (lb_len == 2 && outp == dst) || (outp == dst && lit_len == 0)) {
            lb_len = 0;
        } else if (lb_len == M2MinLen && lb_off > M1MaxOffset + M2MaxOffset && lit_len >= 4) {
            lb_len = 0;
        }
        if (lb_len == 0) {
            ++lit_len;
            d.advance(s, lb_off, lb_len, best_off, false);
            continue;
        }
        find_better_match(best_off, lb_len, lb_off);
        if ((err = encode_literal_run(outp, outp_end, dst, dst_size, lit_ptr, lit_len)) < EResult::Success)
            return err;
        if ((err = encode_lookback_match(outp, outp_end, dst, dst_size, lb_len, lb_off, lit_len)) < EResult::Success)
            return err;
        lit_len = 0;
        d.advance(s, lb_off, lb_len, best_off, true);
    }
    if ((err = encode_literal_run(outp, outp_end, dst, dst_size, lit_ptr, lit_len)) < EResult::Success)
        return err;

    /* Terminating M4 */
    NEEDS_OUT(3);
    *outp++ = M4Marker | 1;
    *outp++ = 0;
    *outp++ = 0;

    dst_size = outp - dst;
    return EResult::Success;
}

}
