Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_tiff.c

n_tiff: build small TIFF structured buffers in both byte orders, read their tags back, and refuse broken ones.

n_tiff: build small TIFF structured buffers in both byte orders, read their tags back, and refuse broken ones. With a file argument, list the tags of that file's first directory instead.

Author
Castagnier Mickael
Version
1.0
Date
28/09/2026
/*
* 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_log.h"
#include "nilorea/n_tiff.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static int failures = 0;
static void check(const char* what, int ok) {
if (!ok) {
fprintf(stderr, "FAIL %s\n", what);
}
}
static void put16(unsigned char* p, uint16_t v, int be) {
p[be ? 0 : 1] = (unsigned char)(v >> 8);
p[be ? 1 : 0] = (unsigned char)(v & 0xff);
}
static void put32(unsigned char* p, uint32_t v, int be) {
int i = 0;
for (i = 0; i < 4; i++)
p[be ? i : 3 - i] = (unsigned char)(v >> (24 - 8 * i));
}
static void put_entry(unsigned char* e, uint16_t tag, uint16_t type, uint32_t count, uint32_t value, int be) {
put16(e, tag, be);
put16(e + 2, type, be);
put32(e + 4, count, be);
put32(e + 8, value, be);
}
static size_t build(unsigned char* buf, int be) {
const float floats[2] = {1.5f, -0.25f};
int i = 0;
memset(buf, 0, 256);
buf[0] = buf[1] = (unsigned char)(be ? 'M' : 'I');
put16(buf + 2, N_TIFF_MAGIC_DCP, be);
put32(buf + 4, 8, be);
put16(buf + 8, 5, be);
/* five entries of 12 bytes from 10, values from 80 */
put_entry(buf + 10, 50708, 2, 12, 80, be); /* ASCII, at 80 */
put_entry(buf + 22, 50778, 3, 1, 0, be); /* SHORT, in the entry */
put16(buf + 22 + 8, 21, be);
put_entry(buf + 34, 50721, 10, 3, 96, be); /* SRATIONAL x3, at 96 */
put_entry(buf + 46, 50938, 11, 2, 120, be); /* FLOAT x2, at 120 */
put_entry(buf + 58, 50939, 11, 64, 200, be); /* past the end */
memcpy(buf + 80, "Test Camera", 12);
put32(buf + 96, (uint32_t)8309, be);
put32(buf + 100, 10000, be);
put32(buf + 104, (uint32_t)-1786, be);
put32(buf + 108, 10000, be);
put32(buf + 112, 5, be);
put32(buf + 116, 0, be);
for (i = 0; i < 2; i++) {
uint32_t bits = 0;
memcpy(&bits, &floats[i], sizeof(bits));
put32(buf + 120 + 4 * i, bits, be);
}
return 256;
}
static void test_order(int be) {
unsigned char buf[256];
size_t len = build(buf, be);
N_TIFF* tiff = n_tiff_parse(buf, len);
double v[4] = {0, 0, 0, 0};
char text[64];
check("the buffer parses", tiff != NULL);
if (!tiff)
return;
check("the byte order is read", tiff->big_endian == be);
check("the magic is reported", tiff->magic == N_TIFF_MAGIC_DCP);
check("the entry past the end is skipped", tiff->count == 4 && !n_tiff_find(tiff, 50939));
check("ASCII reads", n_tiff_get_ascii(tiff, 50708, text, sizeof(text)) == 0 &&
strcmp(text, "Test Camera") == 0);
check("ASCII is cut to the room given",
n_tiff_get_ascii(tiff, 50708, text, 5) == 0 && strcmp(text, "Test") == 0);
check("a short in the entry reads", n_tiff_get_doubles(tiff, 50778, v, 4) == 1 && v[0] == 21.0);
check("signed rationals read", n_tiff_get_doubles(tiff, 50721, v, 4) == 3 &&
fabs(v[0] - 0.8309) < 1e-9 && fabs(v[1] + 0.1786) < 1e-9 &&
v[2] == 0.0);
check("floats read", n_tiff_get_doubles(tiff, 50938, v, 4) == 2 && v[0] == 1.5 && v[1] == -0.25);
check("a count is reported", n_tiff_count(tiff, 50721) == 3 && n_tiff_count(tiff, 1) == 0);
check("ASCII is not a number", n_tiff_get_doubles(tiff, 50708, v, 4) == 0);
check("a number is not ASCII", n_tiff_get_ascii(tiff, 50721, text, sizeof(text)) == -1 &&
text[0] == '\0');
n_tiff_free(&tiff);
check("free clears the pointer", tiff == NULL);
}
static void test_broken(void) {
unsigned char buf[256];
size_t len = build(buf, 0);
check("a short buffer is refused", n_tiff_parse(buf, 6) == NULL);
buf[0] = 'X';
check("a bad byte order is refused", n_tiff_parse(buf, len) == NULL);
build(buf, 0);
put32(buf + 4, 250, 0);
check("a directory past the end is refused", n_tiff_parse(buf, len) == NULL);
build(buf, 0);
put16(buf + 8, 60, 0);
check("a directory longer than the buffer is refused", n_tiff_parse(buf, len) == NULL);
}
static int list_file(const char* path) {
unsigned char* data = NULL;
size_t len = 0;
N_TIFF* tiff = NULL;
size_t i = 0;
if (n_file_read_bounded(path, 64 * 1024 * 1024, &data, &len) != TRUE || !data) {
fprintf(stderr, "cannot read %s\n", path);
return 1;
}
tiff = n_tiff_parse(data, len);
if (!tiff) {
fprintf(stderr, "%s is not TIFF structured\n", path);
free(data);
return 1;
}
printf("%s: %s endian, magic 0x%04x, %zu entries\n", path, tiff->big_endian ? "big" : "little",
tiff->magic, tiff->count);
for (i = 0; i < tiff->count; i++)
printf(" tag %5u type %2u count %u\n", tiff->entries[i].tag, tiff->entries[i].type,
tiff->entries[i].count);
n_tiff_free(&tiff);
free(data);
return 0;
}
int main(int argc, char** argv) {
int opt = 0, log_level = LOG_ERR;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt != 'V') {
fprintf(stderr, "Usage: %s [-V LOG_LEVEL] [file]\n", argv[0]);
return 2;
}
log_level = atoi(optarg);
}
if (optind < argc)
return list_file(argv[optind]);
printf("%s: %d failure(s)\n", failures ? "FAILED" : "ok", failures);
return failures ? 1 : 0;
}
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static int log_level
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static void check(const char *what, int ok)
Count and print a failed condition.
Definition ex_icc.c:46
static size_t build(unsigned char *buf, int be)
Build a DCP shaped buffer: a name, a short, three signed rationals, two floats, and one entry pointin...
Definition ex_tiff.c:93
static void put16(unsigned char *p, uint16_t v, int be)
Write a 16 bit value in a byte order.
Definition ex_tiff.c:58
static void put32(unsigned char *p, uint32_t v, int be)
Write a 32 bit value in a byte order.
Definition ex_tiff.c:67
static void test_order(int be)
Parse a built buffer and check every value.
Definition ex_tiff.c:127
static void test_broken(void)
Broken buffers are refused, never read out of bounds.
Definition ex_tiff.c:158
static int list_file(const char *path)
List the tags of a file's first directory.
Definition ex_tiff.c:176
static void put_entry(unsigned char *e, uint16_t tag, uint16_t type, uint32_t count, uint32_t value, int be)
Write one directory entry.
Definition ex_tiff.c:81
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
int n_file_read_bounded(const char *path, size_t max_size, unsigned char **out, size_t *out_len)
read a whole file into a freshly allocated, NUL-terminated buffer, refusing a file larger than max_si...
Definition n_files.c:270
uint16_t tag
tag number
Definition n_tiff.h:68
N_TIFF_ENTRY * entries
the entries whose values lie inside the buffer, in file order
Definition n_tiff.h:88
uint16_t type
TIFF field type, 1 BYTE to 12 DOUBLE.
Definition n_tiff.h:70
uint32_t count
number of values
Definition n_tiff.h:72
int big_endian
1 for "MM" (Motorola, big endian), 0 for "II"
Definition n_tiff.h:84
size_t count
how many
Definition n_tiff.h:90
uint16_t magic
the 16 bit value after the byte order mark
Definition n_tiff.h:86
N_TIFF * n_tiff_parse(const unsigned char *data, size_t len)
Parse the first directory of a TIFF structured buffer.
Definition n_tiff.c:94
size_t n_tiff_count(const N_TIFF *tiff, uint16_t tag)
How many values a tag has.
Definition n_tiff.c:178
size_t n_tiff_get_doubles(const N_TIFF *tiff, uint16_t tag, double *out, size_t max)
Read a numeric tag as doubles.
Definition n_tiff.c:184
#define N_TIFF_MAGIC_DCP
The magic of an Adobe DCP camera profile, "RC" read little endian.
Definition n_tiff.h:63
const N_TIFF_ENTRY * n_tiff_find(const N_TIFF *tiff, uint16_t tag)
Find an entry by tag.
Definition n_tiff.c:167
int n_tiff_get_ascii(const N_TIFF *tiff, uint16_t tag, char *out, size_t out_len)
Read an ASCII tag.
Definition n_tiff.c:254
void n_tiff_free(N_TIFF **tiff)
Release a parse, and set the pointer to NULL.
Definition n_tiff.c:160
The first directory of a TIFF structured buffer.
Definition n_tiff.h:78
Common headers and low-level functions & define.
Files configuration header.
Generic log system.
Reading the tags of a TIFF structured buffer.