diff --git a/src/io/gpr/IprbReader.cpp b/src/io/gpr/IprbReader.cpp index 180c63d..2f6c76d 100644 --- a/src/io/gpr/IprbReader.cpp +++ b/src/io/gpr/IprbReader.cpp @@ -41,4 +41,52 @@ BScan readIprb(const std::string& path, const IprHeader& h) { 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(f.tellg()); + const std::int64_t samples = static_cast(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(samples * rangeTraces)); + + f.seekg(static_cast(offsetBytes), std::ios::beg); + if (!f) { + throw std::runtime_error("readIprbRange: seek 失败: " + path); + } + if (readBytes > 0) { + f.read(reinterpret_cast(b.data.data()), + static_cast(readBytes)); + if (!f) { + throw std::runtime_error("readIprbRange: 读取数据失败: " + path); + } + } + + return b; +} + } // namespace geopro::io::gpr diff --git a/src/io/gpr/IprbReader.hpp b/src/io/gpr/IprbReader.hpp index d57dd69..84989ae 100644 --- a/src/io/gpr/IprbReader.hpp +++ b/src/io/gpr/IprbReader.hpp @@ -21,6 +21,14 @@ struct BScan { // 注意:h.lastTrace 仅作 header 提示,不决定道数(真实数据规律为道数==lastTrace,非 +1)。 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 #endif // GEOPRO_IO_GPR_IPRBREADER_HPP diff --git a/tests/io/gpr/test_iprb_reader.cpp b/tests/io/gpr/test_iprb_reader.cpp index afe96e3..8662d25 100644 --- a/tests/io/gpr/test_iprb_reader.cpp +++ b/tests/io/gpr/test_iprb_reader.cpp @@ -47,3 +47,23 @@ TEST(IprbReader, ThrowsOnMissingFile) { IprHeader h{}; h.samples = 3; h.lastTrace = 3; 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 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 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()); +}