← source index

src/Helper.h

195 lines  ·  6.7 K  ·  cpp
#pragma once
#include "../Include.h"

inline const int64_t PX_MAGNITUDE[7] = 
{
    1, 10, 100, 1000, 10000, 100000, 1000000
};

namespace Helper
{
    //http://howardhinnant.github.io/date_algorithms.html#days_from_civil
    inline int64_t daysFromCivil(int64_t y, unsigned m, unsigned d)
    {
        y -= m <= 2;
        const int64_t era = (y >= 0 ? y : y - 399) / 400;
        const unsigned yoe = static_cast<unsigned>(y - era * 400);             // [0, 399]
        const unsigned doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1;  // [0, 365]
        const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;            // [0, 146096]
        return era * 146097 + static_cast<int64_t>(doe) - 719468;
    }

    //http://howardhinnant.github.io/date_algorithms.html#days_from_civil
    inline std::tuple<int64_t, unsigned, unsigned> civilFromDays(int64_t z) noexcept
    {
        static_assert(std::numeric_limits<unsigned>::digits >= 18,
             "This algorithm has not been ported to a 16 bit unsigned integer");
        static_assert(std::numeric_limits<int64_t>::digits >= 20,
             "This algorithm has not been ported to a 16 bit signed integer");
        z += 719468;
        const int64_t era = (z >= 0 ? z : z - 146096) / 146097;
        const unsigned doe = static_cast<unsigned>(z - era * 146097);          // [0, 146096]
        const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;  // [0, 399]
        const int64_t y = static_cast<int64_t>(yoe) + era * 400;
        const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);                // [0, 365]
        const unsigned mp = (5*doy + 2)/153;                                   // [0, 11]
        const unsigned d = doy - (153*mp+2)/5 + 1;                             // [1, 31]
        const unsigned m = mp < 10 ? mp+3 : mp-9;                            // [1, 12]
        return std::tuple<int64_t, unsigned, unsigned>(y + (m <= 2), m, d);
    }

    //Ts example: 2026-09-05T12:43:37.768687Z
    inline int64_t parseTsNs(std::string_view sv, int64_t fixedNanoMul = 0)
    {
        auto digits2 = [&](int i) { return (sv[i] - '0') * 10 + (sv[i + 1] - '0'); };
        auto digits4 = [&](int i) { return digits2(i) * 100 + digits2(i + 2); };

        int y = digits4(0);
        unsigned m = digits2(5);
        unsigned d = digits2(8);
        int64_t days = daysFromCivil(y, m, d);
        int64_t secs = days * 86400 + digits2(11) * 3600 + digits2(14) * 60 + digits2(17);

        int64_t nanos = 0;
        if(sv.size() > 19 && sv[19] == '.')
        {
            for(size_t i = 20; i < sv.size() && sv[i] != 'Z'; i++)
            {
                nanos = nanos * 10 + (sv[i] - '0');
            }
        }

        if(!fixedNanoMul)
        {
            int size = static_cast<int>(sv.size()) - 21;
            for(int i = 0; (i < 9 - size) && size > 0; i++) nanos *= 10;
        }
        else nanos *= fixedNanoMul;

        return secs * 1000000000LL + nanos;
    } 

    inline size_t parseNsTs(int64_t ts, char* out)
    {
        auto digits2 = [&](char* o, unsigned n) { o[0] = '0' + n / 10; o[1] = '0' + n % 10; };
        auto digits4 = [&](char* o, unsigned n) { digits2(o, n / 100); digits2(o + 2, n % 100); };

        int64_t secs = ts / 1000000000LL;
        int64_t nanos = ts % 1000000000LL;
        if (nanos < 0) { nanos += 1000000000LL; --secs; } // Fix for negative Ns

        int64_t days = secs / 86400;
        int64_t rem = secs % 86400;
        if (rem < 0) { rem += 86400; --days; } // Fix for negative Ns

        auto [y, m, d] = civilFromDays(days);

        digits4(out, static_cast<unsigned>(y));
        out[4] = '-';
        digits2(out + 5, m);
        out[7] = '-';
        digits2(out + 8, d);
        out[10] = 'T';
        digits2(out + 11, static_cast<unsigned>(rem / 3600));
        out[13] = ':';
        digits2(out + 14, static_cast<unsigned>((rem % 3600) / 60));
        out[16] = ':';
        digits2(out + 17, static_cast<unsigned>(rem % 60));
        out[19] = '.';

        const unsigned us = static_cast<unsigned>(nanos / 1000);
        digits2(out + 20, us / 10000);
        digits2(out + 22, (us / 100) % 100);
        digits2(out + 24, us % 100);
        out[26] = 'Z';
        out[27] = '\0';

        return 27;
    }

    inline int64_t parseToPx(std::string_view rawPx)
    {
        int64_t result = 0;
        auto status = std::from_chars(
            rawPx.data(),
            rawPx.data() + rawPx.size(),
            result);

        if(status.ec != std::errc{})
        {
            std::cout << "parseToPx ERROR: " << static_cast<int>(status.ec) << '\n';
            return 0;
        }

        if(status.ptr != (rawPx.data() + rawPx.size()))
        {
            if(*status.ptr != '.')
            {
                std::cout << "parseToPx ERROR: not a price" << '\n';
                return 0;
            }
        }
        else return result * PX_MAGNITUDE[6];

        uint32_t frac_rezult = 0;
        auto frac_delta = (rawPx.data() + rawPx.size()) - (status.ptr + 1);
        if(frac_delta > 6) frac_delta = 6;
        auto frac_status = std::from_chars(
            status.ptr + 1, 
            status.ptr + frac_delta + 1, 
            frac_rezult);

        if(frac_status.ptr != rawPx.data() + rawPx.size())
        {
            std::cout << "parseToPX ERROR: frac " << static_cast<int>(frac_status.ec) << '\n';
            return 0;
        }

        if(frac_status.ec != std::errc{})
        {
            std::cout << "parseToPx ERROR: frac" << static_cast<int>(frac_status.ec) << '\n';
            return 0;
        }

        auto delta = frac_status.ptr - (status.ptr + 1);
        result = result * PX_MAGNITUDE[6] + ((rawPx[0] == '-') * -1 + (rawPx[0] != '-')) * ((int64_t) frac_rezult * PX_MAGNITUDE[6 - delta]);

        return result;
    }

    //530000 = 0.53
    // 0 = 0
    inline size_t parseFromPx(int64_t px, char* out, size_t size = 32)
    {
        if(size == 0) return 0;

        char* cursor = out;
        char* const end = out + size - 1;

        if(px < 0)
        {
            if(cursor == end) return 0;
            *cursor++ = '-';            
        }

        int64_t dec = px / PX_MAGNITUDE[6];
        int64_t frac  = px % PX_MAGNITUDE[6];
        if(px < 0) { dec = -dec; frac = -frac; }

        auto [decEnd, decEc] = std::to_chars(cursor, end, dec);
        if(decEc != std::errc{}) return 0;
        cursor = decEnd;

        if(frac != 0)
        {
            auto [fracEnd, fracEc] = std::to_chars(cursor, end, frac + PX_MAGNITUDE[6]);
            if(fracEc != std::errc{}) return 0;
            *cursor = '.';
            cursor = fracEnd;
            while(cursor[-1] == '0') cursor--;
        }

        *cursor = '\0';
        return static_cast<size_t>(cursor - out);
    }
}