//
// tardy - a tar post-processor
// Copyright (C) 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 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. If not, see <http://www.gnu.org/licenses/>.
//

#include <libtardy/endian.h>


const char *
endian_name(endian_t value)
{
    return (value == endian_little ? "little-endian" : "big-endian");
}


unsigned
endian_get2(const void *data, endian_t endian)
{
    if (endian == endian_little)
        return endian_get2le(data);
    else
        return endian_get2be(data);
}


unsigned
endian_get2le(const void *data)
{
    const unsigned char *p = (const unsigned char *)data;
    return (p[0] | (p[1] << 8));
}


unsigned
endian_get2be(const void *data)
{
    const unsigned char *p = (const unsigned char *)data;
    return ((p[0] << 8) | p[1]);
}


void
endian_set2(void *data, unsigned value, endian_t endian)
{
    if (endian == endian_little)
        endian_set2le(data, value);
    else
        endian_set2be(data, value);
}


void
endian_set2le(void *data, unsigned value)
{
    unsigned char *p = (unsigned char *)data;
    p[0] = value;
    p[1] = value >> 8;
}


void
endian_set2be(void *data, unsigned value)
{
    unsigned char *p = (unsigned char *)data;
    p[0] = value >> 8;
    p[1] = value;
}


endian_t
endian_native(void)
{
    union u { char c[2]; short s; } u;
    u.s = 1;
    return (u.c[0] == 1 ? endian_little : endian_big);
}


unsigned long
endian_get4(const void *data, endian_t endian)
{
    if (endian == endian_little)
        return endian_get4le(data);
    else
        return endian_get4be(data);
}


unsigned long
endian_get4le(const void *data)
{
    const unsigned char *p = (const unsigned char *)data;
    return
        (
            (unsigned long)p[0]
        |
            ((unsigned long)p[1] << 8)
        |
            ((unsigned long)p[2] << 16)
        |
            ((unsigned long)p[3] << 24)
        );
}


unsigned long
endian_get4be(const void *data)
{
    const unsigned char *p = (const unsigned char *)data;
    return
        (
            ((unsigned long)p[0] << 24)
        |
            ((unsigned long)p[1] << 16)
        |
            ((unsigned long)p[2] << 8)
        |
            (unsigned long)p[3]
        );
}


void
endian_set4(void *data, unsigned long value, endian_t endian)
{
    if (endian == endian_little)
        endian_set4le(data, value);
    else
        endian_set4be(data, value);
}


void
endian_set4le(void *data, unsigned long value)
{
    unsigned char *p = (unsigned char *)data;
    p[0] = value;
    p[1] = value >> 8;
    p[2] = value >> 16;
    p[3] = value >> 24;
}


void
endian_set4be(void *data, unsigned long value)
{
    unsigned char *p = (unsigned char *)data;
    p[0] = value >> 24;
    p[1] = value >> 16;
    p[2] = value >> 8;
    p[3] = value;
}
