8 n__tiff_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00

src/n_tiff.c

Reading the tags of a TIFF structured buffer. More...

Functions

Name
uint64_t _n_tiff_u64(const N_TIFF * tiff, const unsigned char * p)
Read a 64 bit value in the buffer's byte order.
uint32_t _n_tiff_u32(const N_TIFF * tiff, const unsigned char * p)
Read a 32 bit value in the buffer's byte order.
uint16_t _n_tiff_u16(const N_TIFF * tiff, const unsigned char * p)
Read a 16 bit value in the buffer's byte order.
size_t _n_tiff_type_size(uint16_t type)
Bytes one value of a field type takes.

Defines

Name
N_TIFF_MAX_ENTRIES
Most entries a directory is read for: a real TIFF directory holds tens, a DCP a few dozen; a count past this is a corrupt or hostile file.

Detailed Description

Reading the tags of a TIFF structured buffer.

Author: Castagnier Mickael

Version: 1.0

Date: 28/09/2026

Functions Documentation

function _n_tiff_u64

static uint64_t _n_tiff_u64(
    const N_TIFF * tiff,
    const unsigned char * p
)

Read a 64 bit value in the buffer's byte order.

Parameters:

  • tiff the parse, for the byte order
  • p where, already checked to be inside the buffer

Return: the value

function _n_tiff_u32

static uint32_t _n_tiff_u32(
    const N_TIFF * tiff,
    const unsigned char * p
)

Read a 32 bit value in the buffer's byte order.

Parameters:

  • tiff the parse, for the byte order
  • p where, already checked to be inside the buffer

Return: the value

function _n_tiff_u16

static uint16_t _n_tiff_u16(
    const N_TIFF * tiff,
    const unsigned char * p
)

Read a 16 bit value in the buffer's byte order.

Parameters:

  • tiff the parse, for the byte order
  • p where, already checked to be inside the buffer

Return: the value

function _n_tiff_type_size

static size_t _n_tiff_type_size(
    uint16_t type
)

Bytes one value of a field type takes.

Parameters:

  • type TIFF field type

Return: its size, 0 for a type this reader does not know

Macros Documentation

define N_TIFF_MAX_ENTRIES

#define N_TIFF_MAX_ENTRIES 4096

Most entries a directory is read for: a real TIFF directory holds tens, a DCP a few dozen; a count past this is a corrupt or hostile file.

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include "nilorea/n_tiff.h"
#include "nilorea/n_common.h"
#include "nilorea/n_log.h"

#include <string.h>

#define N_TIFF_MAX_ENTRIES 4096

static size_t _n_tiff_type_size(uint16_t type) {
    switch (type) {
        case 1: /* BYTE */
        case 2: /* ASCII */
        case 6: /* SBYTE */
        case 7: /* UNDEFINED */
            return 1;
        case 3: /* SHORT */
        case 8: /* SSHORT */
            return 2;
        case 4:  /* LONG */
        case 9:  /* SLONG */
        case 11: /* FLOAT */
            return 4;
        case 5:  /* RATIONAL */
        case 10: /* SRATIONAL */
        case 12: /* DOUBLE */
            return 8;
        default:
            return 0;
    }
}

static uint16_t _n_tiff_u16(const N_TIFF* tiff, const unsigned char* p) {
    if (tiff->big_endian)
        return (uint16_t)((p[0] << 8) | p[1]);
    return (uint16_t)((p[1] << 8) | p[0]);
}

static uint32_t _n_tiff_u32(const N_TIFF* tiff, const unsigned char* p) {
    if (tiff->big_endian)
        return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
    return ((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[0];
}

static uint64_t _n_tiff_u64(const N_TIFF* tiff, const unsigned char* p) {
    uint64_t hi = _n_tiff_u32(tiff, tiff->big_endian ? p : p + 4);
    uint64_t lo = _n_tiff_u32(tiff, tiff->big_endian ? p + 4 : p);
    return (hi << 32) | lo;
}

N_TIFF* n_tiff_parse_ifd(const unsigned char* data, size_t len, size_t ifd) {
    N_TIFF* tiff = NULL;
    size_t count = 0;
    size_t i = 0;

    __n_assert(data, return NULL);
    if (len < 8)
        return NULL;
    Malloc(tiff, N_TIFF, 1);
    __n_assert(tiff, return NULL);
    tiff->data = data;
    tiff->len = len;
    if (data[0] == 'I' && data[1] == 'I')
        tiff->big_endian = 0;
    else if (data[0] == 'M' && data[1] == 'M')
        tiff->big_endian = 1;
    else {
        Free(tiff);
        return NULL;
    }
    tiff->magic = _n_tiff_u16(tiff, data + 2);
    if (ifd < 8 || ifd > len - 2) {
        Free(tiff);
        return NULL;
    }
    count = _n_tiff_u16(tiff, data + ifd);
    if (count == 0 || count > N_TIFF_MAX_ENTRIES || ifd + 2 + count * 12 > len) {
        Free(tiff);
        return NULL;
    }
    Malloc(tiff->entries, N_TIFF_ENTRY, count);
    if (!tiff->entries) {
        Free(tiff);
        return NULL;
    }
    for (i = 0; i < count; i++) {
        const unsigned char* e = data + ifd + 2 + i * 12;
        N_TIFF_ENTRY entry;
        size_t size = 0;
        size_t bytes = 0;

        entry.tag = _n_tiff_u16(tiff, e);
        entry.type = _n_tiff_u16(tiff, e + 2);
        entry.count = _n_tiff_u32(tiff, e + 4);
        size = _n_tiff_type_size(entry.type);
        if (size == 0 || entry.count == 0)
            continue;
        /* A count so large that its size overflows cannot fit either. */
        if ((size_t)entry.count > len / size)
            continue;
        bytes = size * entry.count;
        /* Four bytes or less sit in the entry itself; more are at an offset. */
        if (bytes <= 4)
            entry.offset = (size_t)(e + 8 - data);
        else {
            entry.offset = _n_tiff_u32(tiff, e + 8);
            if (entry.offset > len || bytes > len - entry.offset)
                continue;
        }
        tiff->entries[tiff->count++] = entry;
    }
    return tiff;
}

N_TIFF* n_tiff_parse(const unsigned char* data, size_t len) {
    __n_assert(data, return NULL);
    if (len < 8)
        return NULL;
    /* IFD0's offset is the header's last four bytes, in the header's order. */
    if (data[0] == 'I' && data[1] == 'I')
        return n_tiff_parse_ifd(data, len, (size_t)data[4] | (size_t)data[5] << 8 | (size_t)data[6] << 16 | (size_t)data[7] << 24);
    if (data[0] == 'M' && data[1] == 'M')
        return n_tiff_parse_ifd(data, len, (size_t)data[7] | (size_t)data[6] << 8 | (size_t)data[5] << 16 | (size_t)data[4] << 24);
    return NULL;
}

void n_tiff_free(N_TIFF** tiff) {
    if (!tiff || !*tiff)
        return;
    FreeNoLog((*tiff)->entries);
    Free((*tiff));
}

const N_TIFF_ENTRY* n_tiff_find(const N_TIFF* tiff, uint16_t tag) {
    size_t i = 0;

    __n_assert(tiff, return NULL);
    for (i = 0; i < tiff->count; i++) {
        if (tiff->entries[i].tag == tag)
            return &tiff->entries[i];
    }
    return NULL;
}

size_t n_tiff_count(const N_TIFF* tiff, uint16_t tag) {
    const N_TIFF_ENTRY* entry = n_tiff_find(tiff, tag);

    return entry ? entry->count : 0;
}

size_t n_tiff_get_doubles(const N_TIFF* tiff, uint16_t tag, double* out, size_t max) {
    const N_TIFF_ENTRY* entry = NULL;
    size_t i = 0;
    size_t n = 0;

    __n_assert(tiff, return 0);
    __n_assert(out, return 0);
    entry = n_tiff_find(tiff, tag);
    if (!entry || entry->type == 2 || entry->type == 7)
        return 0;
    n = entry->count < max ? entry->count : max;
    for (i = 0; i < n; i++) {
        const unsigned char* p = tiff->data + entry->offset + i * _n_tiff_type_size(entry->type);

        switch (entry->type) {
            case 1:
                out[i] = (double)p[0];
                break;
            case 6:
                out[i] = (double)(int8_t)p[0];
                break;
            case 3:
                out[i] = (double)_n_tiff_u16(tiff, p);
                break;
            case 8:
                out[i] = (double)(int16_t)_n_tiff_u16(tiff, p);
                break;
            case 4:
                out[i] = (double)_n_tiff_u32(tiff, p);
                break;
            case 9:
                out[i] = (double)(int32_t)_n_tiff_u32(tiff, p);
                break;
            case 5: {
                uint32_t num = _n_tiff_u32(tiff, p);
                uint32_t den = _n_tiff_u32(tiff, p + 4);

                out[i] = den ? (double)num / (double)den : 0.0;
                break;
            }
            case 10: {
                int32_t num = (int32_t)_n_tiff_u32(tiff, p);
                int32_t den = (int32_t)_n_tiff_u32(tiff, p + 4);

                out[i] = den ? (double)num / (double)den : 0.0;
                break;
            }
            case 11: {
                uint32_t bits = _n_tiff_u32(tiff, p);
                float value = 0.0f;

                memcpy(&value, &bits, sizeof(value));
                out[i] = (double)value;
                break;
            }
            case 12: {
                uint64_t bits = _n_tiff_u64(tiff, p);
                double value = 0.0;

                memcpy(&value, &bits, sizeof(value));
                out[i] = value;
                break;
            }
            default:
                return i;
        }
    }
    return n;
}

int n_tiff_get_ascii(const N_TIFF* tiff, uint16_t tag, char* out, size_t out_len) {
    const N_TIFF_ENTRY* entry = NULL;
    size_t n = 0;
    size_t i = 0;

    __n_assert(out, return -1);
    if (out_len == 0)
        return -1;
    out[0] = '\0';
    __n_assert(tiff, return -1);
    entry = n_tiff_find(tiff, tag);
    if (!entry || entry->type != 2)
        return -1;
    n = entry->count < out_len - 1 ? entry->count : out_len - 1;
    for (i = 0; i < n && tiff->data[entry->offset + i] != '\0'; i++)
        out[i] = (char)tiff->data[entry->offset + i];
    out[i] = '\0';
    return 0;
}