feat(io): IprbReader 新增 readIprbRange 道区间读取
只 seek 并读取 [t0,t1) 道,不载全文件,供流式 slab 装配内存有界。 偏移与读取字节数全 64 位防大文件溢出;越界与文件打不开抛 std::runtime_error。 不改动 readIprb 现有行为。
This commit is contained in:
parent
0537e938b4
commit
c2ec1d34b4
|
|
@ -41,4 +41,52 @@ BScan readIprb(const std::string& path, const IprHeader& h) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BScan readIprbRange(const std::string& path, const IprHeader& h,
|
||||||
|
std::int64_t t0, std::int64_t t1) {
|
||||||
|
std::ifstream f(path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!f) {
|
||||||
|
throw std::runtime_error("readIprbRange: 无法打开文件: " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::int64_t fileBytes = static_cast<std::int64_t>(f.tellg());
|
||||||
|
const std::int64_t samples = static_cast<std::int64_t>(h.samples);
|
||||||
|
if (samples <= 0) {
|
||||||
|
throw std::runtime_error("readIprbRange: samples 非法(<=0): " + path);
|
||||||
|
}
|
||||||
|
const std::int64_t bytesPerTrace = samples * 2; // int16
|
||||||
|
if (fileBytes % bytesPerTrace != 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"readIprbRange: 文件大小不是 samples*2 的整数倍: " + path);
|
||||||
|
}
|
||||||
|
const std::int64_t totalTraces = fileBytes / bytesPerTrace;
|
||||||
|
|
||||||
|
// 区间校验:0<=t0<=t1<=总道数。
|
||||||
|
if (t0 < 0 || t0 > t1 || t1 > totalTraces) {
|
||||||
|
throw std::runtime_error("readIprbRange: 道区间越界 [t0,t1)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::int64_t rangeTraces = t1 - t0;
|
||||||
|
const std::int64_t offsetBytes = t0 * bytesPerTrace; // 64 位防溢出
|
||||||
|
const std::int64_t readBytes = rangeTraces * bytesPerTrace; // 64 位防溢出
|
||||||
|
|
||||||
|
BScan b;
|
||||||
|
b.samples = h.samples;
|
||||||
|
b.traces = rangeTraces;
|
||||||
|
b.data.resize(static_cast<std::size_t>(samples * rangeTraces));
|
||||||
|
|
||||||
|
f.seekg(static_cast<std::streamoff>(offsetBytes), std::ios::beg);
|
||||||
|
if (!f) {
|
||||||
|
throw std::runtime_error("readIprbRange: seek 失败: " + path);
|
||||||
|
}
|
||||||
|
if (readBytes > 0) {
|
||||||
|
f.read(reinterpret_cast<char*>(b.data.data()),
|
||||||
|
static_cast<std::streamsize>(readBytes));
|
||||||
|
if (!f) {
|
||||||
|
throw std::runtime_error("readIprbRange: 读取数据失败: " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace geopro::io::gpr
|
} // namespace geopro::io::gpr
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,14 @@ struct BScan {
|
||||||
// 注意:h.lastTrace 仅作 header 提示,不决定道数(真实数据规律为道数==lastTrace,非 +1)。
|
// 注意:h.lastTrace 仅作 header 提示,不决定道数(真实数据规律为道数==lastTrace,非 +1)。
|
||||||
BScan readIprb(const std::string& path, const IprHeader& h);
|
BScan readIprb(const std::string& path, const IprHeader& h);
|
||||||
|
|
||||||
|
// 只读 [t0, t1) 道(0<=t0<=t1<=总道数)。seek 到 t0*samples*2,读 (t1-t0)*samples*2 字节,
|
||||||
|
// 不载全文件,供流式 slab 装配做到内存有界。
|
||||||
|
// 返回 BScan.samples=h.samples, traces=(t1-t0), data 大小=(t1-t0)*samples,布局同 readIprb。
|
||||||
|
// 总道数 = fileBytes/(samples*2);越界(t1>总道数 或 t0>t1 或 t0<0)抛 std::runtime_error;
|
||||||
|
// 文件打不开抛 std::runtime_error。偏移用 64 位防大文件溢出。
|
||||||
|
BScan readIprbRange(const std::string& path, const IprHeader& h,
|
||||||
|
std::int64_t t0, std::int64_t t1);
|
||||||
|
|
||||||
} // namespace geopro::io::gpr
|
} // namespace geopro::io::gpr
|
||||||
|
|
||||||
#endif // GEOPRO_IO_GPR_IPRBREADER_HPP
|
#endif // GEOPRO_IO_GPR_IPRBREADER_HPP
|
||||||
|
|
|
||||||
|
|
@ -47,3 +47,23 @@ TEST(IprbReader, ThrowsOnMissingFile) {
|
||||||
IprHeader h{}; h.samples = 3; h.lastTrace = 3;
|
IprHeader h{}; h.samples = 3; h.lastTrace = 3;
|
||||||
EXPECT_THROW(readIprb("____no_such_file____.iprb", h), std::runtime_error);
|
EXPECT_THROW(readIprb("____no_such_file____.iprb", h), std::runtime_error);
|
||||||
}
|
}
|
||||||
|
TEST(IprbReader, RangeReadMatchesFullSlice) {
|
||||||
|
// 造 samples=3, traces=5 的文件(15 个 int16,值=trace*10+s)
|
||||||
|
std::vector<int16_t> raw; for(int t=0;t<5;t++)for(int s=0;s<3;s++) raw.push_back((int16_t)(t*10+s));
|
||||||
|
auto path = writeTmp(raw);
|
||||||
|
IprHeader h{}; h.samples=3; h.lastTrace=4; // 总道数=5(由文件大小推)
|
||||||
|
auto full = readIprb(path, h);
|
||||||
|
auto seg = readIprbRange(path, h, 1, 4); // 读道 [1,4)=3 道
|
||||||
|
EXPECT_EQ(seg.traces, 3); EXPECT_EQ(seg.samples, 3);
|
||||||
|
ASSERT_EQ(seg.data.size(), 9u);
|
||||||
|
for (int t=0;t<3;t++) for(int s=0;s<3;s++)
|
||||||
|
EXPECT_EQ(seg.data[t*3+s], full.data[(t+1)*3+s]); // 段==全读对应段
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
TEST(IprbReader, RangeOutOfBoundsThrows) {
|
||||||
|
std::vector<int16_t> raw(15,0); auto path=writeTmp(raw);
|
||||||
|
IprHeader h{}; h.samples=3; h.lastTrace=4;
|
||||||
|
EXPECT_THROW(readIprbRange(path,h,3,99), std::runtime_error); // t1>5
|
||||||
|
EXPECT_THROW(readIprbRange(path,h,4,2), std::runtime_error); // t0>t1
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue