/*
 *  $Id: raw-data.c 28166 2025-06-25 18:11:15Z yeti-dn $
 *  Copyright (C) 2003-2024 David Necas (Yeti), Petr Klapetek.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net.
 *
 *  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 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; if not, write to the
 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <glib/gi18n-lib.h>

#include "libgwyddion/math.h"
#include "libgwyddion/macros.h"
#include "libgwyddion/raw-data.h"

/* NAN which does not depend on fast-math. We actually do not like returning NANs, but it is unclear what should
 * happen when the raw data literally contain NANs. It is fair to pass them to the caller unmolested. */
static const GDoubleIEEE754 simple_nan = {
    .mpn.mantissa_low = 0u,
    .mpn.mantissa_high = 0x8000u,
    .mpn.biased_exponent = 0x7ffu,
    .mpn.sign = 0u,
};

/**
 * gwy_memcpy_byte_swap:
 * @source: Source memory block.
 * @dest: Destination memory location.
 * @item_size: Size of one copied item, it should be a power of two.
 * @nitems: Number of items of size @item_size to copy.
 * @byteswap: Byte swap pattern.
 *
 * Copies a block of memory swapping bytes along the way.
 *
 * The bits in @byteswap correspond to groups of bytes to swap: if j-th bit is set, adjacent groups of 2j bits are
 * swapped. For example, value 3 means items will be divided into couples (bit 1) of bytes and adjacent couples of
 * bytes swapped, and then divided into single bytes (bit 0) and adjacent bytes swapped. The net effect is reversal of
 * byte order in groups of four bytes. More generally, if you want to reverse byte order in groups of size
 * 2<superscript>j</superscript>, use byte swap pattern j-1.
 *
 * When @byteswap is zero, this function reduces to plain memcpy().
 **/
void
gwy_memcpy_byte_swap(const guint8 *source,
                     guint8 *dest,
                     gsize item_size,
                     gsize nitems,
                     gsize byteswap)
{
    gsize i, k;

    if (!byteswap) {
        memcpy(dest, source, item_size*nitems);
        return;
    }

    for (i = 0; i < nitems; i++) {
        guint8 *b = dest + i*item_size;

        for (k = 0; k < item_size; k++)
            b[k ^ byteswap] = *(source++);
    }
}

/* XXX: There are a bunch of cases we do not bother to represent – or even cannot represent – as doubles, for instance
 * denormal values. Non-numbers are are also very approximate. */
static inline gdouble
get_x86_extended_le(const guchar *p)
{
    gdouble x;
    gint i, e;

    e = ((p[9] & 0x7f) << 8) | p[8];
    if (!e)
        return 0.0;
    if (G_UNLIKELY(e == 0x7fff)) {
        if ((p[7] & 0xc0) == 0x80)
            return (p[9] & 0x80) ? -HUGE_VAL : HUGE_VAL;
        return simple_nan.v_double;
    }

    x = p[0];
    for (i = 1; i < 8; i++)
        x = x/256.0 + p[i];
    if (p[9] & 0x80)
        x = -x/128.0;
    else
        x = x/128.0;

    /* XXX: This is finicky. If the compiler decides to do /128.0 after *gwy_powi(), which is allowed with fast math,
     * the intermediate result can overflow to ∞ even though the final number is still representable. There is a
     * similar issue with underflow (which seems to happen with CLANG). */
    return x*gwy_powi(2.0, e - 16383);
}

static inline gdouble
get_x86_extended_be(const guchar *p)
{
    gdouble x;
    gint i, e;

    e = ((p[0] & 0x7f) << 8) | p[1];
    if (!e)
        return 0.0;
    if (G_UNLIKELY(e == 0x7fff)) {
        if ((p[2] & 0xc0) == 0x80)
            return (p[0] & 0x80) ? -HUGE_VAL : HUGE_VAL;
        return simple_nan.v_double;
    }

    x = p[9];
    for (i = 8; i > 1; i--)
        x = x/256.0 + p[i];
    if (p[0] & 0x80)
        x = -x/128.0;
    else
        x = x/128.0;

    /* XXX: This is finicky. If the compiler decides to do /128.0 after *gwy_powi(), which is allowed with fast math,
     * the intermediate result can overflow to ∞ even though the final number is still representable. There is a
     * similar issue with underflow (which seems to happen with CLANG). */
    return x*gwy_powi(2.0, e - 16383);
}

static inline gdouble
get_pascal_real_le(const guchar *p)
{
    gdouble x;

    if (!p[0])
        return 0.0;

    x = 1.0 + ((((p[1]/256.0 + p[2])/256.0 + p[3])/256.0 + p[4])/256.0 + (p[5] & 0x7f))/128.0;
    if (p[5] & 0x80)
        x = -x;

    return x*gwy_powi(2.0, (gint)p[0] - 129);
}

static inline gdouble
get_pascal_real_be(const guchar *p)
{
    gdouble x;

    if (!p[5])
        return 0.0;

    x = 1.0 + ((((p[4]/256.0 + p[3])/256.0 + p[2])/256.0 + p[1])/256.0 + (p[0] & 0x7f))/128.0;
    if (p[0] & 0x80)
        x = -x;

    return x*gwy_powi(2.0, (gint)p[5] - 129);
}

static inline gdouble
get_half_le(const guchar *p)
{
    gdouble x = p[0]/1024.0 + (p[1] & 0x03)/4.0;
    gint exponent = (p[1] >> 2) & 0x1f;

    if (G_UNLIKELY(exponent == 0x1f)) {
        /* XXX: Gwyddion does not work with NaNs.  Should we produce them? */
        if (x)
            return simple_nan.v_double;
        return (p[1] & 0x80) ? -HUGE_VAL : HUGE_VAL;
    }

    if (exponent)
        x = (1.0 + x)*gwy_powi(2.0, exponent - 15);
    else
        x = x/16384.0;

    return (p[1] & 0x80) ? -x : x;
}

static inline gdouble
get_half_be(const guchar *p)
{
    gdouble x = p[1]/1024.0 + (p[0] & 0x03)/4.0;
    gint exponent = (p[0] >> 2) & 0x1f;

    if (G_UNLIKELY(exponent == 0x1f)) {
        /* XXX: Gwyddion does not work with NaNs.  Should we produce them? */
        if (x)
            return simple_nan.v_double;
        return (p[0] & 0x80) ? -HUGE_VAL : HUGE_VAL;
    }

    if (exponent)
        x = (1.0 + x)*gwy_powi(2.0, exponent - 15);
    else
        x = x/16384.0;

    return (p[0] & 0x80) ? -x : x;
}

/**
 * gwy_convert_raw_data:
 * @data: Pointer to the input raw data to be converted to doubles.  The data type is given by @datatype and
 *        @byteorder.
 * @nitems: Data block length, i.e. the number of consecutive items to convert.
 * @stride: For whole-byte sized data, item stride in the raw data, measured in raw values. For fractional sizes
 *          the interpretation is more complicated, see the description body. Usually pass 1 (contiguous,
 *          non-fractional sized data).
 * @datatype: Type of the raw data items.
 * @byteorder: Byte order of the raw data.  The byte order must be explicit, i.e. %GWY_BYTE_ORDER_IMPLICIT is not
 *             valid. For sub-byte sized data @byteorder instead gives the order of values within one byte.
 * @target: Array of @nitems to store the converted input data to.
 * @scale: Factor to multiply the data with.
 * @offset: Constant to add to the data after multiplying with @scale.
 *
 * Converts a block of raw data items to doubles.
 *
 * Note that conversion from 64bit integral types may lose information as they have more bits than the mantissa of
 * doubles.  All other conversions should be exact.
 *
 * For backward reading, pass -1 (or other negative value) as @stride and point @data to the last raw item instead of
 * the first. More precisely, @data must point to the first byte of the last raw value, not to the very last byte (for
 * most data sizes this is intuitive, but 12bit data may involve some head scracthing). Zero @stride is also allowed
 * and results in @target being filled with a constant value.
 *
 * If the raw data size is not a whole number of bytes, parameter @byteorder gives the order in which the pieces are
 * packed into bytes. However, the usual order is to start from the high bits regardless of the machine endianness, so
 * you normally always pass %GWY_BYTE_ORDER_BIG_ENDIAN for fractional sized data.
 *
 * For 12bit data only the big endian variant is defined at present. It means always storing the highest remaining
 * bits of the 12bit value to the highest available bits of the byte, as TIFF and various cameras normally do.
 *
 * For fractional sized data parameter @stride specifies both the stride (always measured in raw values) and starting
 * position within the first
 * byte:
 * <itemizedlist>
 * <listitem>For 1bit data @stride is equal to 8@S + @R, where @S is the actual stride. @R = 0 means start
 * conversion from the first bit and @R = 1 from the second bit, …, up to @R = 7 meaning starting from the last bit
 * (which one is first depends on @byteorder).</listitem>
 * <listitem>For 4bit data @stride is equal to 2@S + @R, where @S is the actual stride. @R = 0 means start conversion
 * from the first nibble and @R = 1 from the second nibble (which one is first depends on @byteorder).</listitem>
 * <listitem>For 12bit data @stride is equal to 2@S + @R, where @S is the actual stride. @R = 0 means the
 * conversion starts from a value split 8+4 (in forward direction), whereas @R = 1 means the conversion starts from
 * the value split 4+8.</listitem>
 * <listitem>@R is always positive and its meaning does not change for negative @S.</listitem>
 * </itemizedlist>
 *
 * For example, consider conversion of 3 nibbles of 4bit data (in the usual big endian order). This is how they will
 * be read to the output:
 * <itemizedlist>
 * <listitem>[0 1] [2 .] [. .] for @R = 0, @S = 1 (@stride = 2), starting from the first byte.</listitem>
 * <listitem>[. 0] [1 2] [. .] for @R = 1, @S = 1 (@stride = 3), starting from the first byte.</listitem>
 * <listitem>[0 .] [1 .] [2 .] for @R = 0, @S = 2 (@stride = 4), starting from the first byte.</listitem>
 * <listitem>[. .] [. 2] [1 0] for @R = 1, @S = -1 (@stride = -1), starting from the last byte.</listitem>
 * <listitem>[. .] [2 1] [0 .] for @R = 0, @S = -1 (@stride = -2), starting from the last byte.</listitem>
 * </itemizedlist>
 **/
void
gwy_convert_raw_data(gconstpointer data,
                     gsize nitems,
                     gssize stride,
                     GwyRawDataType datatype,
                     GwyByteOrder byteorder,
                     gdouble *target,
                     gdouble scale,
                     gdouble offset)
{
    gboolean littleendian, byteswap;
    gdouble *tgt;
    gsize i;

    g_return_if_fail(byteorder == GWY_BYTE_ORDER_LITTLE_ENDIAN
                     || byteorder == GWY_BYTE_ORDER_BIG_ENDIAN
                     || byteorder == GWY_BYTE_ORDER_NATIVE);
    g_return_if_fail(data && target);

    littleendian = (byteorder == GWY_BYTE_ORDER_LITTLE_ENDIAN
                    || (G_BYTE_ORDER == G_LITTLE_ENDIAN && byteorder == GWY_BYTE_ORDER_NATIVE));
    byteswap = (byteorder && byteorder != G_BYTE_ORDER);

    tgt = target;
    if (datatype == GWY_RAW_DATA_SINT8) {
        const gint8 *s8 = (const gint8*)data;
        for (i = nitems; i; i--, s8 += stride, tgt++)
            *tgt = *s8;
    }
    else if (datatype == GWY_RAW_DATA_UINT8) {
        const guint8 *u8 = (const guint8*)data;
        for (i = nitems; i; i--, u8 += stride, tgt++)
            *tgt = *u8;
    }
    else if (datatype == GWY_RAW_DATA_SINT16) {
        const gint16 *s16 = (const gint16*)data;
        if (byteswap) {
            for (i = nitems; i; i--, s16 += stride, tgt++)
                *tgt = (gint16)GUINT16_SWAP_LE_BE(*s16);
        }
        else {
            for (i = nitems; i; i--, s16 += stride, tgt++)
                *tgt = *s16;
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT16) {
        const guint16 *u16 = (const guint16*)data;
        if (byteswap) {
            for (i = nitems; i; i--, u16 += stride, tgt++)
                *tgt = GUINT16_SWAP_LE_BE(*u16);
        }
        else {
            for (i = nitems; i; i--, u16 += stride, tgt++)
                *tgt = *u16;
        }
    }
    else if (datatype == GWY_RAW_DATA_SINT32) {
        const gint32 *s32 = (const gint32*)data;
        if (byteswap) {
            for (i = nitems; i; i--, s32 += stride, tgt++)
                *tgt = (gint32)GUINT32_SWAP_LE_BE(*s32);
        }
        else {
            for (i = nitems; i; i--, s32 += stride, tgt++)
                *tgt = *s32;
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT32) {
        const guint32 *u32 = (const guint32*)data;
        if (byteswap) {
            for (i = nitems; i; i--, u32 += stride, tgt++)
                *tgt = GUINT32_SWAP_LE_BE(*u32);
        }
        else {
            for (i = nitems; i; i--, u32 += stride, tgt++)
                *tgt = *u32;
        }
    }
    else if (datatype == GWY_RAW_DATA_SINT64) {
        const gint64 *s64 = (const gint64*)data;
        if (byteswap) {
            for (i = nitems; i; i--, s64 += stride, tgt++)
                *tgt = (gint64)GUINT64_SWAP_LE_BE(*s64);
        }
        else {
            for (i = nitems; i; i--, s64 += stride, tgt++)
                *tgt = *s64;
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT64) {
        const guint64 *u64 = (const guint64*)data;
        if (byteswap) {
            for (i = nitems; i; i--, u64 += stride, tgt++)
                *tgt = GUINT64_SWAP_LE_BE(*u64);
        }
        else {
            for (i = nitems; i; i--, u64 += stride, tgt++)
                *tgt = *u64;
        }
    }
    else if (datatype == GWY_RAW_DATA_HALF) {
        const guchar *p = (const guchar*)data;
        if (littleendian) {
            for (i = nitems; i; i--, p += 2*stride, tgt++)
                *tgt = get_half_le(p);
        }
        else {
            for (i = nitems; i; i--, p += 2*stride, tgt++)
                *tgt = get_half_be(p);
        }
    }
    else if (datatype == GWY_RAW_DATA_FLOAT) {
        const guint32 *u32 = (const guint32*)data;
        const gfloat *f32 = (const gfloat*)data;
        union { guint32 u; gfloat f; } v;
        if (byteswap) {
            for (i = nitems; i; i--, u32 += stride, tgt++) {
                v.u = GUINT32_SWAP_LE_BE(*u32);
                *tgt = v.f;
            }
        }
        else {
            for (i = nitems; i; i--, f32 += stride, tgt++)
                *tgt = *f32;
        }
    }
    else if (datatype == GWY_RAW_DATA_REAL) {
        const guchar *p = (const guchar*)data;
        if (littleendian) {
            for (i = nitems; i; i--, p += 6*stride, tgt++)
                *tgt = get_pascal_real_le(p);
        }
        else {
            for (i = nitems; i; i--, p += 6*stride, tgt++)
                *tgt = get_pascal_real_be(p);
        }
    }
    else if (datatype == GWY_RAW_DATA_EXTENDED) {
        const guchar *p = (const guchar*)data;
        if (littleendian) {
            for (i = nitems; i; i--, p += 10*stride, tgt++)
                *tgt = get_x86_extended_le(p);
        }
        else {
            for (i = nitems; i; i--, p += 10*stride, tgt++)
                *tgt = get_x86_extended_be(p);
        }
    }
    else if (datatype == GWY_RAW_DATA_DOUBLE) {
        const guint64 *u64 = (const guint64*)data;
        const gdouble *d64 = (const gdouble*)data;
        union { guint64 u; double d; } v;
        if (byteswap) {
            for (i = nitems; i; i--, u64 += stride, tgt++) {
                v.u = GUINT64_SWAP_LE_BE(*u64);
                *tgt = v.d;
            }
        }
        else {
            for (i = nitems; i; i--, d64 += stride, tgt++)
                *tgt = *d64;
        }
    }
    else if (datatype == GWY_RAW_DATA_BIT) {
        const guint8 *u8 = (const guint8*)data;
        gssize pos, ntotal, p, q;
        gint target_incr;

        if (stride < 0) {
            /* Transform negative stride to positive because otherwise we are constantly fighting with C integer
             * division rounding towards zero, whereas we would need towards minus infinity to proceed smoothly. */
            pos = stride % 8;
            if (pos < 0)
                pos += 8;
            stride = (stride - pos)/8;  /* Correct negative stride in bits. */
            stride = -stride;

            /* The total length of the sequence we access (in items, i.e. bits). */
            ntotal = nitems + (stride - 1)*(nitems - 1);

            /* Here rounding towards zero is what we want. The big parentheses being negative mean we do not
             * even leave the single byte. */
            u8 -= (ntotal - (pos + 1) + 7)/8;
            pos = (pos - (ntotal - 1)) % 8;
            pos = (pos + 8) % 8;
            tgt += nitems-1;
            target_incr = -1;
        }
        else {
            pos = stride % 8;
            stride /= 8;
            target_incr = 1;
        }

        p = 7*(1 - littleendian);
        q = 2*littleendian - 1;
        for (i = nitems; i; i--, tgt += target_incr) {
            *tgt = ((*u8 >> (p + q*pos)) & 1);
            pos += stride;
            u8 += pos/8;
            pos = pos % 8;
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT4 || datatype == GWY_RAW_DATA_SINT4) {
        const guint8 *u8 = (const guint8*)data;
        union { guint8 u; gint8 s; } v;
        gssize pos, ntotal, p, q;
        gint target_incr;

        if (stride < 0) {
            /* Transform negative stride to positive because otherwise we are constantly fighting with C integer
             * division rounding towards zero, whereas we would need towards minus infinity to proceed smoothly. */
            pos = stride % 2;
            if (pos < 0)
                pos += 2;
            stride = (stride - pos)/2;  /* Correct negative stride in nibbles. */
            stride = -stride;

            /* The total length of the sequence we access (in items, i.e. nibbles). */
            ntotal = nitems + (stride - 1)*(nitems - 1);

            /* Here rounding towards zero is what we want. The big parentheses being negative mean we do not
             * even leave the single byte. */
            u8 -= (ntotal - (pos + 1) + 1)/2;
            pos = (pos - (ntotal - 1)) % 2;
            pos = (pos + 2) % 2;
            tgt += nitems-1;
            target_incr = -1;
        }
        else {
            pos = stride % 2;
            stride /= 2;
            target_incr = 1;
        }

        p = 4*(1 - littleendian);
        q = 4*(2*littleendian - 1);
        if (datatype == GWY_RAW_DATA_UINT4) {
            for (i = nitems; i; i--, tgt += target_incr) {
                *tgt = ((*u8 >> (p + q*pos)) & 0xfu);
                pos += stride;
                u8 += pos/2;
                pos = pos % 2;
            }
        }
        else {
            for (i = nitems; i; i--, tgt += target_incr) {
                v.u = ((*u8 >> (p + q*pos)) & 0xfu);
                v.u |= (v.u & 0x08u)*0x1eu;  /* Sign-extend to a byte, leave the rest to the compiler. */
                *tgt = v.s;
                pos += stride;
                u8 += pos/2;
                pos = pos % 2;
            }
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT12 || datatype == GWY_RAW_DATA_SINT12) {
        const guint8 *u8 = (const guint8*)data;
        union { guint16 u; gint16 s; } v;
        gssize pos, ntotal;
        gint target_incr;

        if (byteorder == G_LITTLE_ENDIAN) {
            g_warning("No little endian 12bit format is currently defined. Converting as big endian.");
        }

        if (stride < 0) {
            /* Transform negative stride to positive because otherwise we are constantly fighting with C integer
             * division rounding towards zero, whereas we would need towards minus infinity to proceed smoothly. */
            pos = stride % 2;
            if (pos < 0)
                pos += 2;
            stride = (stride - pos)/2;  /* Correct negative stride in nibbles. */
            stride = -stride;

            /* The total length of the sequence we access (in 12bit items). */
            ntotal = nitems + (stride - 1)*(nitems - 1);

            /* We need to move about 3n/2 bytes and 1-pos gives the right correction for the integer division. */
            u8 -= (3*(ntotal - 1) + (1 - pos))/2;
            pos = (pos - (3*ntotal - 1)) % 2;
            pos = (pos + 2) % 2;
            tgt += nitems-1;
            target_incr = -1;
        }
        else {
            pos = stride % 2;
            stride /= 2;
            target_incr = 1;
        }

        if (datatype == GWY_RAW_DATA_UINT12) {
            for (i = nitems; i; i--, tgt += target_incr) {
                if (pos)
                    *tgt = ((guint)(u8[0] & 0xf) << 8) | u8[1];
                else
                    *tgt = ((guint)u8[0] << 4) | (u8[1] >> 4);
                /* Count in nibbles. */
                pos += 3*stride;
                u8 += pos/2;
                pos = pos % 2;
            }
        }
        else {
            for (i = nitems; i; i--, tgt += target_incr) {
                /* Sign-extend to a 16bit integer, leave the rest to the compiler. */
                if (pos)
                    v.u = ((u8[0] & 0x08u)*0x1e00u) | ((guint)(u8[0] & 0x0f) << 8) | u8[1];
                else
                    v.u = ((u8[0] & 0x80u)*0x1e0u) | ((guint)u8[0] << 4) | (u8[1] >> 4);
                *tgt = v.s;
                /* Count in nibbles. */
                pos += 3*stride;
                u8 += pos/2;
                pos = pos % 2;
            }
        }
    }
    else if (datatype == GWY_RAW_DATA_UINT24) {
        const guint8 *u8 = (const guint8*)data;

        if (littleendian) {
            for (i = nitems; i; i--, u8 += 3*stride, tgt++)
                *tgt = ((guint)u8[2] << 16) | ((guint)u8[1] << 8) | u8[0];
        }
        else {
            for (i = nitems; i; i--, u8 += 3*stride, tgt++)
                *tgt = ((guint)u8[0] << 16) | ((guint)u8[1] << 8) | u8[2];
        }
    }
    else if (datatype == GWY_RAW_DATA_SINT24) {
        const guint8 *u8 = (const guint8*)data;
        union { guint32 u; gint32 s; } v;

        if (littleendian) {
            for (i = nitems; i; i--, u8 += 3*stride, tgt++) {
                /* Sign-extend to 32 bits. */
                v.u = ((u8[2] & 0x80u)*0x1fe0000u) | ((guint)u8[2] << 16) | ((guint)u8[1] << 8) | u8[0];
                *tgt = v.s;
            }
        }
        else {
            for (i = nitems; i; i--, u8 += 3*stride, tgt++) {
                /* Sign-extend to 32 bits. */
                v.u = ((u8[0] & 0x80u)*0x1fe0000u) | ((guint)u8[0] << 16) | ((guint)u8[1] << 8) | u8[2];
                *tgt = v.s;
            }
        }
    }
    else {
        g_assert_not_reached();
    }

    if (scale == 1.0 && offset == 0.0)
        return;

    if (offset == 0.0) {
        for (i = 0; i < nitems; i++)
            target[i] *= scale;
    }
    else if (scale == 1.0) {
        for (i = 0; i < nitems; i++)
            target[i] += offset;
    }
    else {
        for (i = 0; i < nitems; i++)
            target[i] = target[i]*scale + offset;
    }
}

/**
 * gwy_raw_data_size:
 * @datatype: Raw data type.
 *
 * Reports the size of a single raw data item in bytes.
 *
 * For raw data types which are not whole bytes (whether smaller or larger, like 12bit), zero is returned. Use
 * gwy_raw_data_size_bits() to get the size in bits.
 *
 * Returns: The size (in bytes) of a single raw data item of type @datatype.
 **/
guint
gwy_raw_data_size(GwyRawDataType datatype)
{
    static const guint sizes[GWY_RAW_DATA_EXTENDED+1] = {
        1, 1, 2, 2, 4, 4, 8, 8, 2, 4, 6, 8, 0, 0, 0, 0, 0, 3, 3, 10,
    };

    g_return_val_if_fail(datatype <= GWY_RAW_DATA_EXTENDED, 0);
    return sizes[datatype];
}

/**
 * gwy_raw_data_size_bits:
 * @datatype: Raw data type.
 *
 * Reports the size of a single raw data item in bits.
 *
 * If the data type is known to be whole bytes, gwy_raw_data_size() is usually simpler to use.
 *
 * Returns: The size (in bits) of a single raw data item of type @datatype.
 **/
guint
gwy_raw_data_size_bits(GwyRawDataType datatype)
{
    static const guint sizes[GWY_RAW_DATA_EXTENDED+1] = {
        8, 8, 16, 16, 32, 32, 64, 64, 16, 32, 48, 64, 1, 4, 4, 12, 12, 24, 24, 80,
    };

    g_return_val_if_fail(datatype <= GWY_RAW_DATA_EXTENDED, 0);
    return sizes[datatype];
}

/**
 * SECTION: raw-data
 * @title: Raw data
 * @short_description: Raw data conversion
 **/

/**
 * GwyRawDataType:
 * @GWY_RAW_DATA_SINT8: Signed 8bit integer (one byte).
 * @GWY_RAW_DATA_UINT8: Unsigned 8bit integer (one byte).
 * @GWY_RAW_DATA_SINT16: Signed 16bit integer (two bytes).
 * @GWY_RAW_DATA_UINT16: Unsigned 16bit integer (two bytes).
 * @GWY_RAW_DATA_SINT32: Signed 32bit integer (four bytes).
 * @GWY_RAW_DATA_UINT32: Unsigned 32bit integer (four bytes).
 * @GWY_RAW_DATA_SINT64: Signed 64bit integer (eight bytes).
 * @GWY_RAW_DATA_UINT64: Unsigned 64bit integer (eight bytes).
 * @GWY_RAW_DATA_HALF: Half-precision floating point number (two bytes).
 * @GWY_RAW_DATA_FLOAT: Single-precision floating point number (four bytes).
 * @GWY_RAW_DATA_REAL: Pascal ‘real’ floating point number (six bytes).
 * @GWY_RAW_DATA_DOUBLE: Double-precision floating point number (eight bytes).
 * @GWY_RAW_DATA_BIT: Unsigned 1bit integer (one bit).
 * @GWY_RAW_DATA_SINT4: Signed 4bit integer (one nibble).
 * @GWY_RAW_DATA_UINT4: Unsigned 4bit integer (one nibble).
 * @GWY_RAW_DATA_SINT12: Signed 12bit integer (byte and half).
 * @GWY_RAW_DATA_UINT12: Unsigned 12bit integer (byte and half).
 * @GWY_RAW_DATA_SINT24: Signed 24bit integer (three bytes).
 * @GWY_RAW_DATA_UINT24: Unsigned 24bit integer (three bytes).
 * @GWY_RAW_DATA_EXTENDED: x86 ‘extended’ floating point number (ten bytes). The values are assumed closely packed,
 *                         which should be the case for old files, even though they would be usually aligned to 4 or 8
 *                         byte boundaries nowadays.
 *
 * Types of raw data.
 *
 * They are used with gwy_convert_raw_data(). Multibyte types usually need to be complemented with #GwyByteOrder to
 * get a full type specification.
 **/

/**
 * GwyByteOrder:
 * @GWY_BYTE_ORDER_NATIVE: Native byte order for the system the code is running on.
 * @GWY_BYTE_ORDER_LITTLE_ENDIAN: Little endian byte order (the same as %G_LITTLE_ENDIAN).
 * @GWY_BYTE_ORDER_BIG_ENDIAN: Big endian byte order (the same as %G_BIG_ENDIAN).
 * @GWY_BYTE_ORDER_IMPLICIT: Byte order implied by data, for instance a byte-order-mark (Since 2.60).
 *
 * Type of byte order.
 *
 * Note all types are valid for all functions.
 **/

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
