Skip to content

Commit

Permalink
tiffsave: ensure large file support (>2GB) on MSVC
Browse files Browse the repository at this point in the history
On MSVC, `off_t` and `lseek()` are limited to signed 32-bit offsets,
which limits their capability to handle files larger than 2GB.

To support saving TIFF files that exceed this size, replace their use
with `gint64` and `vips__seek()`, where the latter uses `_lseeki64()`
internally.

See: conan-io/conan#15578.
  • Loading branch information
kleisauke committed Feb 20, 2024
1 parent 8dc9673 commit 746d875
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 14 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ date-tbd 8.15.2
- ppmload: ensure multi-line comments are skipped [lovell]
- fix arrayjoin with some pipelines [TheEssem]
- fix high Q mono JPEG TIFF write with mozjpeg [cavenel]
- tiffsave: ensure large file support (>2GB) on MSVC [kleisauke]

18/12/23 8.15.1

Expand Down
12 changes: 9 additions & 3 deletions libvips/include/vips/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,11 @@ struct _VipsTarget {
int write_point;

/* Write position in memory_buffer.
*
* off_t can be 32 bits on some platforms, so make sure we have a
* full 64.
*/
off_t position;
gint64 position;

/* Temp targets on the filesystem need deleting, sometimes.
*/
Expand Down Expand Up @@ -466,8 +469,11 @@ typedef struct _VipsTargetClass {
gint64 (*read)(VipsTarget *, void *, size_t);

/* Seek output. Args exactly as lseek(2).
*
* We have to use int64 rather than off_t, since we must work on
* Windows, where off_t can be 32-bits.
*/
off_t (*seek)(VipsTarget *, off_t offset, int whence);
gint64 (*seek)(VipsTarget *, gint64 offset, int whence);

/* Output has been generated, so do any clearing up,
* eg. copy the bytes we saved in memory to the target blob.
Expand All @@ -492,7 +498,7 @@ int vips_target_write(VipsTarget *target, const void *data, size_t length);
VIPS_API
gint64 vips_target_read(VipsTarget *target, void *buffer, size_t length);
VIPS_API
off_t vips_target_seek(VipsTarget *target, off_t offset, int whence);
gint64 vips_target_seek(VipsTarget *target, gint64 offset, int whence);
VIPS_API
int vips_target_end(VipsTarget *target);
VIPS_DEPRECATED_FOR(vips_target_end)
Expand Down
31 changes: 20 additions & 11 deletions libvips/iofuncs/target.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,17 @@ vips_target_write_real(VipsTarget *target, const void *data, size_t length)
return result;
}

static off_t
vips_target_seek_real(VipsTarget *target, off_t offset, int whence)
static gint64
vips_target_seek_real(VipsTarget *target, gint64 offset, int whence)
{
VipsConnection *connection = VIPS_CONNECTION(target);
const char *nick = vips_connection_nick(connection);

off_t new_position;
gint64 new_position;

VIPS_DEBUG_MSG("vips_target_seek_real: offset = %ld, whence = %d\n",
VIPS_DEBUG_MSG(
"vips_target_seek_real: offset = %" G_GINT64_FORMAT
", whence = %d\n",
offset, whence);

if (target->memory_buffer) {
Expand Down Expand Up @@ -212,7 +214,11 @@ vips_target_seek_real(VipsTarget *target, off_t offset, int whence)
target->position = new_position;
}
else
new_position = lseek(connection->descriptor, offset, whence);
/* We need to use the vips__seek() wrapper so we can seek long
* files on Windows.
*/
new_position = vips__seek_no_error(connection->descriptor,
offset, whence);

return new_position;
}
Expand Down Expand Up @@ -539,7 +545,7 @@ vips_target_write(VipsTarget *target, const void *buffer, size_t length)
*
* Arguments exactly as read(2).
*
* Reading froma target sounds weird, but libtiff needs this for
* Reading from a target sounds weird, but libtiff needs this for
* multi-page writes. This method will fail for targets like pipes.
*
* Returns: the number of bytes read, 0 on end of file, -1 on error.
Expand Down Expand Up @@ -570,22 +576,25 @@ vips_target_read(VipsTarget *target, void *buffer, size_t length)
*
* Returns: the new seek position, -1 on error.
*/
off_t
vips_target_seek(VipsTarget *target, off_t position, int whence)
gint64
vips_target_seek(VipsTarget *target, gint64 position, int whence)
{
VipsTargetClass *class = VIPS_TARGET_GET_CLASS(target);

off_t new_position;
gint64 new_position;

VIPS_DEBUG_MSG("vips_target_seek: pos = %ld, whence = %d\n",
VIPS_DEBUG_MSG(
"vips_target_seek: pos = %" G_GINT64_FORMAT
", whence = %d\n",
position, whence);

if (vips_target_flush(target))
return -1;

new_position = class->seek(target, position, whence);

VIPS_DEBUG_MSG("vips_target_seek: new_position = %ld\n",
VIPS_DEBUG_MSG(
"vips_target_seek: new_position = %" G_GINT64_FORMAT "\n",
new_position);

return new_position;
Expand Down

0 comments on commit 746d875

Please sign in to comment.