/*
 *
 * WKTRaster - Raster Types for PostGIS
 * http://trac.osgeo.org/postgis/wiki/WKTRaster
 *
 * Copyright (C) 2011-2013 Regents of the University of California
 *   <bkpark@ucdavis.edu>
 * Copyright (C) 2010-2011 Jorge Arevalo <jorge.arevalo@deimos-space.com>
 * Copyright (C) 2010-2011 David Zwarg <dzwarg@azavea.com>
 * Copyright (C) 2009-2011 Pierre Racine <pierre.racine@sbf.ulaval.ca>
 * Copyright (C) 2009-2011 Mateusz Loskot <mateusz@loskot.net>
 * Copyright (C) 2008-2009 Sandro Santilli <strk@kbt.io>
 *
 * 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 "librtcore.h"
#include "librtcore_internal.h"
#include "rt_serialize.h"

/******************************************************************************
* Debug and Testing Utilities
******************************************************************************/

#if POSTGIS_DEBUG_LEVEL > 2

char*
d_binary_to_hex(const uint8_t * const raw, uint32_t size, uint32_t *hexsize) {
    char* hex = NULL;
    uint32_t i = 0;


    assert(NULL != raw);
    assert(NULL != hexsize);


    *hexsize = size * 2; /* hex is 2 times bytes */
    hex = (char*) rtalloc((*hexsize) + 1);
    if (!hex) {
        rterror("d_binary_to_hex: Out of memory hexifying raw binary");
        return NULL;
    }
    hex[*hexsize] = '\0'; /* Null-terminate */

    for (i = 0; i < size; ++i) {
        deparse_hex(raw[i], &(hex[2 * i]));
    }

    assert(NULL != hex);
    assert(0 == strlen(hex) % 2);
    return hex;
}

void
d_print_binary_hex(const char* msg, const uint8_t * const raw, uint32_t size) {
    char* hex = NULL;
    uint32_t hexsize = 0;


    assert(NULL != msg);
    assert(NULL != raw);


    hex = d_binary_to_hex(raw, size, &hexsize);
    if (NULL != hex) {
        rtinfo("%s\t%s", msg, hex);
        rtdealloc(hex);
    }
}

size_t
d_binptr_to_pos(const uint8_t * const ptr, const uint8_t * const end, size_t size) {
    assert(NULL != ptr && NULL != end);

    return (size - (end - ptr));
}

#endif /* if POSTGIS_DEBUG_LEVEL > 2 */


#ifdef OPTIMIZE_SPACE

/*
 * Set given number of bits of the given byte,
 * starting from given bitOffset (from the first)
 * to the given value.
 *
 * Examples:
 *   char ch;
 *   ch=0; setBits(&ch, 1, 1, 0) -> ch==8
 *   ch=0; setBits(&ch, 3, 2, 1) -> ch==96 (0x60)
 *
 * Note that number of bits set must be <= 8-bitOffset
 *
 */
void
setBits(char* ch, double val, int bits, int bitOffset) {
    char mask = 0xFF >> (8 - bits);
    char ival = val;


		assert(ch != NULL);
    assert(8 - bitOffset >= bits);

    RASTER_DEBUGF(4, "ival:%d bits:%d mask:%hhx bitoffset:%d\n",
            ival, bits, mask, bitOffset);

    /* clear all but significant bits from ival */
    ival &= mask;
#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
    if (ival != val) {
        rtwarn("Pixel value for %d-bits band got truncated"
                " from %g to %hhu", bits, val, ival);
    }
#endif /* POSTGIS_RASTER_WARN_ON_TRUNCATION */

    RASTER_DEBUGF(4, " cleared ival:%hhx\n", ival);


    /* Shift ival so the significant bits start at
     * the first bit */
    ival <<= (8 - bitOffset - bits);

    RASTER_DEBUGF(4, " ival shifted:%hhx\n", ival);
    RASTER_DEBUGF(4, "  ch:%hhx\n", *ch);

    /* clear first bits of target */
    *ch &= ~(mask << (8 - bits - bitOffset));

    RASTER_DEBUGF(4, "  ch cleared:%hhx\n", *ch);

    /* Set the first bit of target */
    *ch |= ival;

    RASTER_DEBUGF(4, "  ch ored:%hhx\n", *ch);

}
#endif /* OPTIMIZE_SPACE */

void
swap_char(uint8_t *a, uint8_t *b) {
    uint8_t c = 0;

    assert(NULL != a && NULL != b);

    c = *a;
    *a = *b;
    *b = c;
}

void
flip_endian_16(uint8_t *d) {
    assert(NULL != d);

    swap_char(d, d + 1);
}

void
flip_endian_32(uint8_t *d) {
    assert(NULL != d);

    swap_char(d, d + 3);
    swap_char(d + 1, d + 2);
}

void
flip_endian_64(uint8_t *d) {
    assert(NULL != d);

    swap_char(d + 7, d);
    swap_char(d + 6, d + 1);
    swap_char(d + 5, d + 2);
    swap_char(d + 4, d + 3);
}

uint8_t
isMachineLittleEndian(void) {
    static int endian_check_int = 1; /* dont modify this!!! */
    /* 0=big endian|xdr --  1=little endian|ndr */
    return *((uint8_t *) & endian_check_int);
}

uint8_t
read_uint8(const uint8_t** from) {
    assert(NULL != from);

    return *(*from)++;
}

/* unused up to now
void
write_uint8(uint8_t** from, uint8_t v) {
    assert(NULL != from);

 *(*from)++ = v;
}
*/

int8_t
read_int8(const uint8_t** from) {
    assert(NULL != from);

    return (int8_t) read_uint8(from);
}

/* unused up to now
void
write_int8(uint8_t** from, int8_t v) {
    assert(NULL != from);

 *(*from)++ = v;
}
*/

uint16_t
read_uint16(const uint8_t** from, uint8_t littleEndian) {
    uint16_t ret = 0;

    assert(NULL != from);

    if (littleEndian) {
        ret = (*from)[0] |
                (*from)[1] << 8;
    } else {
        /* big endian */
        ret = (*from)[0] << 8 |
                (*from)[1];
    }
    *from += 2;
    return ret;
}

void
write_uint16(uint8_t** to, uint8_t littleEndian, uint16_t v) {
    assert(NULL != to);

    if (littleEndian) {
        (*to)[0] = v & 0x00FF;
        (*to)[1] = v >> 8;
    } else {
        (*to)[1] = v & 0x00FF;
        (*to)[0] = v >> 8;
    }
    *to += 2;
}

int16_t
read_int16(const uint8_t** from, uint8_t littleEndian) {
    assert(NULL != from);

    return (int16_t)read_uint16(from, littleEndian);
}

/* unused up to now
void
write_int16(uint8_t** to, uint8_t littleEndian, int16_t v) {
    assert(NULL != to);

    if ( littleEndian )
    {
        (*to)[0] = v & 0x00FF;
        (*to)[1] = v >> 8;
    }
    else
    {
        (*to)[1] = v & 0x00FF;
        (*to)[0] = v >> 8;
    }
 *to += 2;
}
*/

uint32_t
read_uint32(const uint8_t** from, uint8_t littleEndian) {
    uint32_t ret = 0;

    assert(NULL != from);

    if (littleEndian) {
        ret = (uint32_t) ((*from)[0] & 0xff) |
                (uint32_t) ((*from)[1] & 0xff) << 8 |
                (uint32_t) ((*from)[2] & 0xff) << 16 |
                (uint32_t) ((*from)[3] & 0xff) << 24;
    } else {
        /* big endian */
        ret = (uint32_t) ((*from)[3] & 0xff) |
                (uint32_t) ((*from)[2] & 0xff) << 8 |
                (uint32_t) ((*from)[1] & 0xff) << 16 |
                (uint32_t) ((*from)[0] & 0xff) << 24;
    }

    *from += 4;
    return ret;
}

/* unused up to now
void
write_uint32(uint8_t** to, uint8_t littleEndian, uint32_t v) {
    assert(NULL != to);

    if ( littleEndian )
    {
        (*to)[0] = v & 0x000000FF;
        (*to)[1] = ( v & 0x0000FF00 ) >> 8;
        (*to)[2] = ( v & 0x00FF0000 ) >> 16;
        (*to)[3] = ( v & 0xFF000000 ) >> 24;
    }
    else
    {
        (*to)[3] = v & 0x000000FF;
        (*to)[2] = ( v & 0x0000FF00 ) >> 8;
        (*to)[1] = ( v & 0x00FF0000 ) >> 16;
        (*to)[0] = ( v & 0xFF000000 ) >> 24;
    }
 *to += 4;
}
*/

int32_t
read_int32(const uint8_t** from, uint8_t littleEndian) {
    assert(NULL != from);

    return (int32_t)read_uint32(from, littleEndian);
}

/* unused up to now
void
write_int32(uint8_t** to, uint8_t littleEndian, int32_t v) {
    assert(NULL != to);

    if ( littleEndian )
    {
        (*to)[0] = v & 0x000000FF;
        (*to)[1] = ( v & 0x0000FF00 ) >> 8;
        (*to)[2] = ( v & 0x00FF0000 ) >> 16;
        (*to)[3] = ( v & 0xFF000000 ) >> 24;
    }
    else
    {
        (*to)[3] = v & 0x000000FF;
        (*to)[2] = ( v & 0x0000FF00 ) >> 8;
        (*to)[1] = ( v & 0x00FF0000 ) >> 16;
        (*to)[0] = ( v & 0xFF000000 ) >> 24;
    }
 *to += 4;
}
*/

float
read_float32(const uint8_t** from, uint8_t littleEndian) {

    union {
        float f;
        uint32_t i;
    } ret;

    ret.i = read_uint32(from, littleEndian);

    return ret.f;
}

/* unused up to now
void
write_float32(uint8_t** from, uint8_t littleEndian, float f) {
    union {
        float f;
        uint32_t i;
    } u;

    u.f = f;
    write_uint32(from, littleEndian, u.i);
}
*/

double
read_float64(const uint8_t** from, uint8_t littleEndian) {

    union {
        double d;
        uint64_t i;
    } ret;

    assert(NULL != from);

    if (littleEndian) {
        ret.i = (uint64_t) ((*from)[0] & 0xff) |
                (uint64_t) ((*from)[1] & 0xff) << 8 |
                (uint64_t) ((*from)[2] & 0xff) << 16 |
                (uint64_t) ((*from)[3] & 0xff) << 24 |
                (uint64_t) ((*from)[4] & 0xff) << 32 |
                (uint64_t) ((*from)[5] & 0xff) << 40 |
                (uint64_t) ((*from)[6] & 0xff) << 48 |
                (uint64_t) ((*from)[7] & 0xff) << 56;
    } else {
        /* big endian */
        ret.i = (uint64_t) ((*from)[7] & 0xff) |
                (uint64_t) ((*from)[6] & 0xff) << 8 |
                (uint64_t) ((*from)[5] & 0xff) << 16 |
                (uint64_t) ((*from)[4] & 0xff) << 24 |
                (uint64_t) ((*from)[3] & 0xff) << 32 |
                (uint64_t) ((*from)[2] & 0xff) << 40 |
                (uint64_t) ((*from)[1] & 0xff) << 48 |
                (uint64_t) ((*from)[0] & 0xff) << 56;
    }

    *from += 8;
    return ret.d;
}

/* unused up to now
void
write_float64(uint8_t** to, uint8_t littleEndian, double v) {
    union {
        double d;
        uint64_t i;
    } u;

    assert(NULL != to);

    u.d = v;

    if ( littleEndian )
    {
        (*to)[0] =   u.i & 0x00000000000000FFULL;
        (*to)[1] = ( u.i & 0x000000000000FF00ULL ) >> 8;
        (*to)[2] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
        (*to)[3] = ( u.i & 0x00000000FF000000ULL ) >> 24;
        (*to)[4] = ( u.i & 0x000000FF00000000ULL ) >> 32;
        (*to)[5] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
        (*to)[6] = ( u.i & 0x00FF000000000000ULL ) >> 48;
        (*to)[7] = ( u.i & 0xFF00000000000000ULL ) >> 56;
    }
    else
    {
        (*to)[7] =   u.i & 0x00000000000000FFULL;
        (*to)[6] = ( u.i & 0x000000000000FF00ULL ) >> 8;
        (*to)[5] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
        (*to)[4] = ( u.i & 0x00000000FF000000ULL ) >> 24;
        (*to)[3] = ( u.i & 0x000000FF00000000ULL ) >> 32;
        (*to)[2] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
        (*to)[1] = ( u.i & 0x00FF000000000000ULL ) >> 48;
        (*to)[0] = ( u.i & 0xFF00000000000000ULL ) >> 56;
    }
 *to += 8;
}
*/

static uint32_t
rt_raster_serialized_size(rt_raster raster) {
	uint32_t size = sizeof (struct rt_raster_serialized_t);
	uint16_t i = 0;

	assert(NULL != raster);

	RASTER_DEBUGF(3, "Serialized size with just header:%d - now adding size of %d bands",
		size, raster->numBands);

	for (i = 0; i < raster->numBands; ++i) {
		rt_band band = raster->bands[i];
		rt_pixtype pixtype = band->pixtype;
		int pixbytes = rt_pixtype_size(pixtype);

		if (pixbytes < 1) {
			rterror("rt_raster_serialized_size: Corrupted band: unknown pixtype");
			return 0;
		}

		/* Add space for band type, hasnodata flag and data padding */
		size += pixbytes;

		/* Add space for nodata value */
		size += pixbytes;

		if (band->offline) {
			/* Add space for band number */
			size += 1;

			/* Add space for null-terminated path */
			size += strlen(band->data.offline.path) + 1;
		}
		else {
			/* Add space for raster band data */
			size += pixbytes * raster->width * raster->height;
		}

		RASTER_DEBUGF(3, "Size before alignment is %d", size);

		/* Align size to 8-bytes boundary (trailing padding) */
		/* XXX jorgearevalo: bug here. If the size is actually 8-bytes aligned,
		   this line will add 8 bytes trailing padding, and it's not necessary */
		/*size += 8 - (size % 8);*/
		if (size % 8)
			size += 8 - (size % 8);

		RASTER_DEBUGF(3, "Size after alignment is %d", size);
	}

	return size;
}

/**
 * Return this raster in serialized form.
 * Memory (band data included) is copied from rt_raster.
 *
 * Serialized form is documented in doc/RFC1-SerializedFormat.
 */
void*
rt_raster_serialize(rt_raster raster) {
	uint32_t size = 0;
	uint8_t* ret = NULL;
	uint8_t* ptr = NULL;
	uint16_t i = 0;

	assert(NULL != raster);

	size = rt_raster_serialized_size(raster);
	ret = (uint8_t*) rtalloc(size);
	if (!ret) {
		rterror("rt_raster_serialize: Out of memory allocating %d bytes for serializing a raster", size);
		return NULL;
	}
	memset(ret, '-', size);
	ptr = ret;

	RASTER_DEBUGF(3, "sizeof(struct rt_raster_serialized_t):%u",
		sizeof (struct rt_raster_serialized_t));
	RASTER_DEBUGF(3, "sizeof(struct rt_raster_t):%u",
		sizeof (struct rt_raster_t));
	RASTER_DEBUGF(3, "serialized size:%lu", (long unsigned) size);

	/* Set size */
	/* NOTE: Value of rt_raster.size may be updated in
	 * returned object, for instance, by rt_pg layer to
	 * store value calculated by SET_VARSIZE.
	 */
	raster->size = size;

	/* Set version */
	raster->version = 0;

	/* Copy header */
	memcpy(ptr, raster, sizeof (struct rt_raster_serialized_t));

	RASTER_DEBUG(3, "Start hex dump of raster being serialized using 0x2D to mark non-written bytes");

#if POSTGIS_DEBUG_LEVEL > 2
	uint8_t* dbg_ptr = ptr;
	d_print_binary_hex("HEADER", dbg_ptr, size);
#endif

	ptr += sizeof (struct rt_raster_serialized_t);

	/* Serialize bands now */
	for (i = 0; i < raster->numBands; ++i) {
		rt_band band = raster->bands[i];
		assert(NULL != band);

		rt_pixtype pixtype = band->pixtype;
		int pixbytes = rt_pixtype_size(pixtype);
		if (pixbytes < 1) {
			rterror("rt_raster_serialize: Corrupted band: unknown pixtype");
			rtdealloc(ret);
			return NULL;
		}

		/* Add band type */
		*ptr = band->pixtype;
		if (band->offline) {
#ifdef POSTGIS_RASTER_DISABLE_OFFLINE
      rterror("rt_raster_serialize: offdb raster support disabled at compile-time");
      return NULL;
#endif
			*ptr |= BANDTYPE_FLAG_OFFDB;
		}
		if (band->hasnodata) {
			*ptr |= BANDTYPE_FLAG_HASNODATA;
		}

		if (band->isnodata) {
			*ptr |= BANDTYPE_FLAG_ISNODATA;
		}

#if POSTGIS_DEBUG_LEVEL > 2
		d_print_binary_hex("PIXTYPE", dbg_ptr, size);
#endif

		ptr += 1;

		/* Add padding (if needed) */
		if (pixbytes > 1) {
			memset(ptr, '\0', pixbytes - 1);
			ptr += pixbytes - 1;
		}

#if POSTGIS_DEBUG_LEVEL > 2
		d_print_binary_hex("PADDING", dbg_ptr, size);
#endif

		/* Consistency checking (ptr is pixbytes-aligned) */
		assert(!((ptr - ret) % pixbytes));

		/* Add nodata value */
		switch (pixtype) {
			case PT_1BB:
			case PT_2BUI:
			case PT_4BUI:
			case PT_8BUI: {
				uint8_t v = band->nodataval;
				*ptr = v;
				ptr += 1;
				break;
			}
			case PT_8BSI: {
				int8_t v = band->nodataval;
				*ptr = (uint8_t)v;
				ptr += 1;
				break;
			}
			case PT_16BSI: {
				int16_t v = band->nodataval;
				memcpy(ptr, &v, 2);
				ptr += 2;
				break;
			}
			case PT_16BUI: {
				uint16_t v = band->nodataval;
				memcpy(ptr, &v, 2);
				ptr += 2;
				break;
			}
			case PT_32BSI: {
				int32_t v = band->nodataval;
				memcpy(ptr, &v, 4);
				ptr += 4;
				break;
			}
			case PT_32BUI: {
				uint32_t v = band->nodataval;
				memcpy(ptr, &v, 4);
				ptr += 4;
				break;
			}
			case PT_32BF: {
				float v = band->nodataval;
				memcpy(ptr, &v, 4);
				ptr += 4;
				break;
			}
			case PT_64BF: {
				memcpy(ptr, &band->nodataval, 8);
				ptr += 8;
				break;
			}
			default:
				rterror("rt_raster_serialize: Fatal error caused by unknown pixel type. Aborting.");
				rtdealloc(ret);
				return NULL;
		}

		/* Consistency checking (ptr is pixbytes-aligned) */
		assert(!((ptr - ret) % pixbytes));

#if POSTGIS_DEBUG_LEVEL > 2
		d_print_binary_hex("nodata", dbg_ptr, size);
#endif

		if (band->offline) {
			/* Write band number */
			*ptr = band->data.offline.bandNum;
			ptr += 1;

			/* Write path */
			strcpy((char*) ptr, band->data.offline.path);
			ptr += strlen(band->data.offline.path) + 1;
		}
		else {
			/* Write data */
			uint32_t datasize = raster->width * raster->height * pixbytes;
			memcpy(ptr, band->data.mem, datasize);
			ptr += datasize;
		}

#if POSTGIS_DEBUG_LEVEL > 2
		d_print_binary_hex("BAND", dbg_ptr, size);
#endif

		/* Pad up to 8-bytes boundary */
		while ((ptr-ret) % 8) {
			*ptr = 0;
			++ptr;
		}

		/* Consistency checking (ptr is pixbytes-aligned) */
		assert(!((ptr - ret) % pixbytes));
	} /* for-loop over bands */

#if POSTGIS_DEBUG_LEVEL > 2
		d_print_binary_hex("SERIALIZED RASTER", dbg_ptr, size);
#endif
	return ret;
}

/**
 * Return a raster from a serialized form.
 *
 * Serialized form is documented in doc/RFC1-SerializedFormat.
 *
 * NOTE: the raster will contain pointer to the serialized
 * form (including band data), which must be kept alive.
 */
rt_raster
rt_raster_deserialize(void* serialized, int header_only) {
	rt_raster rast = NULL;
	const uint8_t *ptr = NULL;
	const uint8_t *beg = NULL;
	uint16_t i = 0;
	uint16_t j = 0;
#ifdef WORDS_BIGENDIAN
	uint8_t littleEndian = LW_FALSE;
#else
	uint8_t littleEndian = LW_TRUE;
#endif

	assert(NULL != serialized);

	RASTER_DEBUG(2, "rt_raster_deserialize: Entering...");

	/* NOTE: Value of rt_raster.size may be different
	 * than actual size of raster data being read.
	 * See note on SET_VARSIZE in rt_raster_serialize function above.
	 */

	/* Allocate memory for deserialized raster header */
	RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for deserialized raster header");
	rast = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
	if (!rast) {
		rterror("rt_raster_deserialize: Out of memory allocating raster for deserialization");
		return NULL;
	}

	/* Deserialize raster header */
	RASTER_DEBUG(3, "rt_raster_deserialize: Deserialize raster header");
	memcpy(rast, serialized, sizeof (struct rt_raster_serialized_t));

	if (0 == rast->numBands || header_only) {
		rast->bands = 0;
		return rast;
	}

	beg = (const uint8_t*) serialized;

	/* Allocate registry of raster bands */
	RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for bands");
	rast->bands = rtalloc(rast->numBands * sizeof (rt_band));
	if (rast->bands == NULL) {
		rterror("rt_raster_deserialize: Out of memory allocating bands");
		rtdealloc(rast);
		return NULL;
	}

	RASTER_DEBUGF(3, "rt_raster_deserialize: %d bands", rast->numBands);

	/* Move to the beginning of first band */
	ptr = beg;
	ptr += sizeof (struct rt_raster_serialized_t);

	/* Deserialize bands now */
	for (i = 0; i < rast->numBands; ++i) {
		rt_band band = NULL;
		uint8_t type = 0;
		int pixbytes = 0;

		band = rtalloc(sizeof(struct rt_band_t));
		if (!band) {
			rterror("rt_raster_deserialize: Out of memory allocating rt_band during deserialization");
			for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
			rt_raster_destroy(rast);
			return NULL;
		}

		rast->bands[i] = band;

		type = *ptr;
		ptr++;
		band->pixtype = type & BANDTYPE_PIXTYPE_MASK;

		RASTER_DEBUGF(3, "rt_raster_deserialize: band %d with pixel type %s", i, rt_pixtype_name(band->pixtype));

		band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
		band->hasnodata = BANDTYPE_HAS_NODATA(type) ? 1 : 0;
		band->isnodata = band->hasnodata ? (BANDTYPE_IS_NODATA(type) ? 1 : 0) : 0;
		band->width = rast->width;
		band->height = rast->height;
		band->ownsdata = 0; /* we do NOT own this data!!! */
		band->raster = rast;

		/* Advance by data padding */
		pixbytes = rt_pixtype_size(band->pixtype);
		ptr += pixbytes - 1;

		/* Read nodata value */
		switch (band->pixtype) {
			case PT_1BB: {
				band->nodataval = ((int) read_uint8(&ptr)) & 0x01;
				break;
			}
			case PT_2BUI: {
				band->nodataval = ((int) read_uint8(&ptr)) & 0x03;
				break;
			}
			case PT_4BUI: {
				band->nodataval = ((int) read_uint8(&ptr)) & 0x0F;
				break;
			}
			case PT_8BSI: {
				band->nodataval = read_int8(&ptr);
				break;
			}
			case PT_8BUI: {
				band->nodataval = read_uint8(&ptr);
				break;
			}
			case PT_16BSI: {
				band->nodataval = read_int16(&ptr, littleEndian);
				break;
			}
			case PT_16BUI: {
				band->nodataval = read_uint16(&ptr, littleEndian);
				break;
			}
			case PT_32BSI: {
				band->nodataval = read_int32(&ptr, littleEndian);
				break;
			}
			case PT_32BUI: {
				band->nodataval = read_uint32(&ptr, littleEndian);
				break;
			}
			case PT_32BF: {
				band->nodataval = read_float32(&ptr, littleEndian);
				break;
			}
			case PT_64BF: {
				band->nodataval = read_float64(&ptr, littleEndian);
				break;
			}
			default: {
				rterror("rt_raster_deserialize: Unknown pixeltype %d", band->pixtype);
				for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
				rt_raster_destroy(rast);
				return NULL;
			}
		}

		RASTER_DEBUGF(3, "rt_raster_deserialize: has nodata flag %d", band->hasnodata);
		RASTER_DEBUGF(3, "rt_raster_deserialize: nodata value %g", band->nodataval);

		/* Consistency checking (ptr is pixbytes-aligned) */
		assert(!((ptr - beg) % pixbytes));

		if (band->offline) {
			int pathlen = 0;

			/* Read band number */
			band->data.offline.bandNum = *ptr;
			ptr += 1;

			/* Register path */
			pathlen = strlen((char*) ptr);
			band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
			if (band->data.offline.path == NULL) {
				rterror("rt_raster_deserialize: Could not allocate memory for offline band path");
				for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
				rt_raster_destroy(rast);
				return NULL;
			}

			memcpy(band->data.offline.path, ptr, pathlen);
			band->data.offline.path[pathlen] = '\0';
			ptr += pathlen + 1;

			band->data.offline.mem = NULL;
		}
		else {
			/* Register data */
			const uint32_t datasize = rast->width * rast->height * pixbytes;
			band->data.mem = (uint8_t*) ptr;
			ptr += datasize;
		}

		/* Skip bytes of padding up to 8-bytes boundary */
#if POSTGIS_DEBUG_LEVEL > 0
		const uint8_t *padbeg = ptr;
#endif
		while (0 != ((ptr - beg) % 8)) {
			++ptr;
		}

		RASTER_DEBUGF(3, "rt_raster_deserialize: skip %d bytes of 8-bytes boundary padding", ptr - padbeg);

		/* Consistency checking (ptr is pixbytes-aligned) */
		assert(!((ptr - beg) % pixbytes));
	}

	return rast;
}
