FreeBSD的fseeko()
和ftello()
被记录为符合POSIX.1-2001标准,这意味着off_t
是一个有符号的整数类型。
在FreeBSD 7上,你可以安全地执行:
off_t actual_offset;
unsigned long stored_offset;
if (actual_offset >= (off_t)0 && actual_offset < (off_t)4294967296.0)
stored_offset = (unsigned long)actual_offset;
else
some_fatal_error("Unsupportable file offset!");
(在 LP64 架构中,上述代码将是愚蠢的,因为 off_t
和 long
都将是 64 位有符号整数。即使这样也是安全的;只是愚蠢的,因为支持所有可能的文件偏移量。)
人们经常会被这个东西咬伤,原因是偏移量的计算必须使用 off_t
。也就是说,仅仅将结果转换为 off_t
是不够的,你必须将参与运算的 values
转换为 off_t
。(从技术上讲,你只需要确保每个算术操作都在 off_t
精度下进行,但我发现如果我只是按惯例和习惯性地将所有操作数都强制转换为 off_t
更容易记住规则。)例如:
off_t offset;
unsigned long some, values, used;
offset = (off_t)some * (off_t)value + (off_t)used;
fseeko(file, offset, SEEK_SET);
通常偏移量计算用于查找特定记录中的字段;算法往往保持不变。如果可能的话,我真的建议您将查找操作移到辅助函数中:
int fseek_to(FILE *const file,
const unsigned long some,
const unsigned long values,
const unsigned long used)
{
const off_t offset = (off_t)some * (off_t)value + (off_t)used;
if (offset < (off_t)0 || offset >= (off_t)4294967296.0)
fatal_error("Offset exceeds 4GB; I must abort!");
return fseeko(file, offset, SEEK_SET);
}
现在,如果你恰好处于一个幸运的位置,你知道所有的偏移都对齐了(比如,对齐到整数 4),那么你可以使用上述方法的扩展来为自己争取几年时间来重写应用程序:
#define BIG_N 4
int fseek_to(FILE *const file,
const unsigned long some,
const unsigned long values,
const unsigned long used)
{
const off_t offset = (off_t)some * (off_t)value + (off_t)used;
if (offset < (off_t)0)
fatal_error("Offset is negative; I must abort!");
if (offset >= (off_t)(BIG_N * 2147483648.0))
fatal_error("Offset is too large; I must abort!");
if ((offset % BIG_N) && (offset >= (off_t)2147483648.0))
fatal_error("Offset is not a multiple of BIG_N; I must abort!");
return fseeko(file, offset, SEEK_SET);
}
int fseek_big(FILE *const file, const unsigned long position)
{
off_t offset;
if (position >= 2147483648UL)
offset = (off_t)2147483648UL
+ (off_t)BIG_N * (off_t)(position - 2147483648UL);
else
offset = (off_t)position;
return fseeko(file, offset, SEEK_SET);
}
unsigned long ftell_big(FILE *const file)
{
off_t offset;
offset = ftello(file);
if (offset < (off_t)0)
fatal_error("Offset is negative; I must abort!");
if (offset < (off_t)2147483648UL)
return (unsigned long)offset;
if (offset % BIG_N)
fatal_error("Offset is not a multiple of BIG_N; I must abort!");
if (offset >= (off_t)(BIG_N * 2147483648.0))
fatal_error("Offset is too large; I must abort!");
return (unsigned long)2147483648UL
+ (unsigned long)((offset - (off_t)2147483648UL) / (off_t)BIG_N);
}
逻辑很简单:如果
offset小于2的31次方,它将按原样使用。否则,它将由值2的31次方+
BIG_N×(
offset-2的31次方)来表示。
BIG_N必须满足offset 2的31次方及以上始终是
BIG_N的倍数。
显然,你只能使用上面三个函数以及所需的fseek_to()变体,只要它们进行相同的检查,并使用不同的参数和公式进行offset计算,你就可以支持高达2147483648 + BIG_N × 2147483647的文件大小。对于BIG_N==4,这是10 GiB(减去4字节;确切地说是10,737,418,236字节)。
有问题吗?
编辑以澄清:
首先,用调用fseek_pos(file, position)
替换fseek(file, position, SEEK_SET)
。
static inline void fseek_pos(FILE *const file, const unsigned long position)
{
if (fseeko(file, (off_t)position, SEEK_SET))
fatal_error("Cannot set file position!");
}
替换 fseek(file, position, SEEK_END)
为调用 fseek_end(file, position)
(为了对称性——我假设此处的位置通常为字面整数),
static inline void fseek_end(FILE *const file, const off_t relative)
{
if (fseeko(file, relative, SEEK_END))
fatal_error("Cannot set file position!");
}
最后,使用 ftell_pos(file)
调用 ftell(file)
:
static inline unsigned long ftell_pos(FILE *const file)
{
off_t position;
position = ftello(file);
if (position == (off_t)-1)
fatal_error("Lost file position!");
if (position < (off_t)0 || position >= (off_t)4294967296.0)
fatal_error("File position outside the 4GB range!");
return (unsigned long)position;
}
由于您的架构和操作系统中,unsigned long
是32位无符号整型,而off_t
是64位有符号整型,因此可以获得完整的4GB范围。
对于偏移量计算,请定义一个或多个类似于以下函数的函数:
static inline void fseek_to(FILE *const file, const off_t term1,
const off_t term2,
const off_t term3)
{
const off_t position = term1 * term2 + term3;
if (position < (off_t)0 || position >= (off_t)4294967296.0)
fatal_error("File position outside the 4GB range!");
if (fseeko(file, position, SEEK_SET))
fatal_error("Cannot set file position!");
}
对于每个偏移量计算算法,定义一个
fseek_to
变体。命名参数使算术合理。如上所述,将参数设为
const off_t
,这样在算术运算中就不需要额外的强制转换。只有参数和定义计算算法的
const off_t position =
行会因变异函数而有所不同。
有问题吗?