Merge changes from topic "liblp-blocksize"
* changes: liblp: Require block-aligned partition sizes. liblp: Simplify GrowPartition().
This commit is contained in:
commit
d0e5bcc13f
|
@ -48,10 +48,20 @@ bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device
|
|||
PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
|
||||
return false;
|
||||
}
|
||||
if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
|
||||
|
||||
int alignment_offset;
|
||||
if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
|
||||
return false;
|
||||
}
|
||||
int logical_block_size;
|
||||
if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
|
||||
device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
|
||||
return true;
|
||||
#else
|
||||
(void)block_device;
|
||||
|
@ -178,6 +188,7 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) {
|
|||
|
||||
device_info_.alignment = geometry_.alignment;
|
||||
device_info_.alignment_offset = geometry_.alignment_offset;
|
||||
device_info_.logical_block_size = geometry_.logical_block_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -201,6 +212,10 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
|
|||
LERROR << "Block device size must be a multiple of 512.";
|
||||
return false;
|
||||
}
|
||||
if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
|
||||
LERROR << "Logical block size must be a multiple of 512.";
|
||||
return false;
|
||||
}
|
||||
if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
|
||||
LERROR << "Alignment offset is not sector-aligned.";
|
||||
return false;
|
||||
|
@ -244,6 +259,18 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
|
|||
return false;
|
||||
}
|
||||
|
||||
// Finally, the size of the allocatable space must be a multiple of the
|
||||
// logical block size. If we have no more free space after this
|
||||
// computation, then we abort. Note that the last sector is inclusive,
|
||||
// so we have to account for that.
|
||||
uint64_t num_free_sectors = last_sector - first_sector + 1;
|
||||
uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
|
||||
if (num_free_sectors < sectors_per_block) {
|
||||
LERROR << "Not enough space to allocate any partition tables.";
|
||||
return false;
|
||||
}
|
||||
last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
|
||||
|
||||
geometry_.first_logical_sector = first_sector;
|
||||
geometry_.last_logical_sector = last_sector;
|
||||
geometry_.metadata_max_size = metadata_max_size;
|
||||
|
@ -251,6 +278,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
|
|||
geometry_.alignment = device_info_.alignment;
|
||||
geometry_.alignment_offset = device_info_.alignment_offset;
|
||||
geometry_.block_device_size = device_info_.size;
|
||||
geometry_.logical_block_size = device_info.logical_block_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -297,87 +325,93 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size)
|
|||
uint64_t end;
|
||||
|
||||
Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
|
||||
uint64_t length() const { return end - start; }
|
||||
bool operator<(const Interval& other) const { return start < other.start; }
|
||||
};
|
||||
std::vector<Interval> intervals;
|
||||
|
||||
// Collect all extents in the partition table.
|
||||
// Collect all extents in the partition table, then sort them by starting
|
||||
// sector.
|
||||
std::vector<Interval> extents;
|
||||
for (const auto& partition : partitions_) {
|
||||
for (const auto& extent : partition->extents()) {
|
||||
LinearExtent* linear = extent->AsLinearExtent();
|
||||
if (!linear) {
|
||||
continue;
|
||||
}
|
||||
intervals.emplace_back(linear->physical_sector(),
|
||||
linear->physical_sector() + extent->num_sectors());
|
||||
extents.emplace_back(linear->physical_sector(),
|
||||
linear->physical_sector() + extent->num_sectors());
|
||||
}
|
||||
}
|
||||
std::sort(extents.begin(), extents.end());
|
||||
|
||||
// Sort extents by starting sector.
|
||||
std::sort(intervals.begin(), intervals.end());
|
||||
// Convert the extent list into a list of gaps between the extents; i.e.,
|
||||
// the list of ranges that are free on the disk.
|
||||
std::vector<Interval> free_regions;
|
||||
for (size_t i = 1; i < extents.size(); i++) {
|
||||
const Interval& previous = extents[i - 1];
|
||||
const Interval& current = extents[i];
|
||||
|
||||
uint64_t aligned = AlignSector(previous.end);
|
||||
if (aligned >= current.start) {
|
||||
// There is no gap between these two extents, try the next one.
|
||||
// Note that we check with >= instead of >, since alignment may
|
||||
// bump the ending sector past the beginning of the next extent.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The new interval represents the free space starting at the end of
|
||||
// the previous interval, and ending at the start of the next interval.
|
||||
free_regions.emplace_back(aligned, current.start);
|
||||
}
|
||||
|
||||
// Add a final interval representing the remainder of the free space.
|
||||
uint64_t last_free_extent_start =
|
||||
extents.empty() ? geometry_.first_logical_sector : extents.back().end;
|
||||
last_free_extent_start = AlignSector(last_free_extent_start);
|
||||
if (last_free_extent_start <= geometry_.last_logical_sector) {
|
||||
free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
|
||||
}
|
||||
|
||||
const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
|
||||
CHECK(sectors_needed % sectors_per_block == 0);
|
||||
|
||||
// Find gaps that we can use for new extents. Note we store new extents in a
|
||||
// temporary vector, and only commit them if we are guaranteed enough free
|
||||
// space.
|
||||
std::vector<std::unique_ptr<LinearExtent>> new_extents;
|
||||
for (size_t i = 1; i < intervals.size(); i++) {
|
||||
const Interval& previous = intervals[i - 1];
|
||||
const Interval& current = intervals[i];
|
||||
for (auto& region : free_regions) {
|
||||
if (region.length() % sectors_per_block != 0) {
|
||||
// This should never happen, because it would imply that we
|
||||
// once allocated an extent that was not a multiple of the
|
||||
// block size. That extent would be rejected by DM_TABLE_LOAD.
|
||||
LERROR << "Region " << region.start << ".." << region.end
|
||||
<< " is not a multiple of the block size, " << sectors_per_block;
|
||||
|
||||
if (previous.end >= current.start) {
|
||||
// There is no gap between these two extents, try the next one. Note that
|
||||
// extents may never overlap, but just for safety, we ignore them if they
|
||||
// do.
|
||||
DCHECK(previous.end == current.start);
|
||||
continue;
|
||||
// If for some reason the final region is mis-sized we still want
|
||||
// to be able to grow partitions. So just to be safe, round the
|
||||
// region down to the nearest block.
|
||||
region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
|
||||
if (!region.length()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t aligned = AlignSector(previous.end);
|
||||
if (aligned >= current.start) {
|
||||
// After alignment, this extent is not usable.
|
||||
continue;
|
||||
}
|
||||
uint64_t sectors = std::min(sectors_needed, region.length());
|
||||
CHECK(sectors % sectors_per_block == 0);
|
||||
|
||||
// This gap is enough to hold the remainder of the space requested, so we
|
||||
// can allocate what we need and return.
|
||||
if (current.start - aligned >= sectors_needed) {
|
||||
auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
|
||||
sectors_needed -= extent->num_sectors();
|
||||
new_extents.push_back(std::move(extent));
|
||||
auto extent = std::make_unique<LinearExtent>(sectors, region.start);
|
||||
new_extents.push_back(std::move(extent));
|
||||
sectors_needed -= sectors;
|
||||
if (!sectors_needed) {
|
||||
break;
|
||||
}
|
||||
|
||||
// This gap is not big enough to fit the remainder of the space requested,
|
||||
// so consume the whole thing and keep looking for more.
|
||||
auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
|
||||
sectors_needed -= extent->num_sectors();
|
||||
new_extents.push_back(std::move(extent));
|
||||
}
|
||||
|
||||
// If we still have more to allocate, take it from the remaining free space
|
||||
// in the allocatable region.
|
||||
if (sectors_needed) {
|
||||
uint64_t first_sector;
|
||||
if (intervals.empty()) {
|
||||
first_sector = geometry_.first_logical_sector;
|
||||
} else {
|
||||
first_sector = intervals.back().end;
|
||||
}
|
||||
DCHECK(first_sector <= geometry_.last_logical_sector);
|
||||
|
||||
// Note: After alignment, |first_sector| may be > the last usable sector.
|
||||
first_sector = AlignSector(first_sector);
|
||||
|
||||
// Note: the last usable sector is inclusive.
|
||||
if (first_sector > geometry_.last_logical_sector ||
|
||||
geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
|
||||
LERROR << "Not enough free space to expand partition: " << partition->name();
|
||||
return false;
|
||||
}
|
||||
auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
|
||||
new_extents.push_back(std::move(extent));
|
||||
LERROR << "Not enough free space to expand partition: " << partition->name();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything succeeded, so commit the new extents.
|
||||
for (auto& extent : new_extents) {
|
||||
partition->AddExtent(std::move(extent));
|
||||
}
|
||||
|
@ -445,6 +479,12 @@ uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
|
|||
void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
|
||||
device_info_.size = device_info.size;
|
||||
|
||||
// Note that if the logical block size changes, we're probably in trouble:
|
||||
// we could have already built extents that will only work on the previous
|
||||
// size.
|
||||
DCHECK(partitions_.empty() ||
|
||||
device_info_.logical_block_size == device_info.logical_block_size);
|
||||
|
||||
// The kernel does not guarantee these values are present, so we only
|
||||
// replace existing values if the new values are non-zero.
|
||||
if (device_info.alignment) {
|
||||
|
@ -457,7 +497,7 @@ void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info)
|
|||
|
||||
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
|
||||
// Align the space needed up to the nearest sector.
|
||||
uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
|
||||
uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
|
||||
|
||||
if (aligned_size > partition->size()) {
|
||||
return GrowPartition(partition, aligned_size);
|
||||
|
|
|
@ -92,11 +92,11 @@ TEST(liblp, PartitionAlignment) {
|
|||
Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
|
||||
ASSERT_NE(system, nullptr);
|
||||
EXPECT_EQ(builder->ResizePartition(system, 10000), true);
|
||||
EXPECT_EQ(system->size(), 10240);
|
||||
EXPECT_EQ(system->size(), 12288);
|
||||
EXPECT_EQ(system->extents().size(), 1);
|
||||
|
||||
builder->ResizePartition(system, 9000);
|
||||
EXPECT_EQ(system->size(), 9216);
|
||||
builder->ResizePartition(system, 7000);
|
||||
EXPECT_EQ(system->size(), 8192);
|
||||
EXPECT_EQ(system->extents().size(), 1);
|
||||
}
|
||||
|
||||
|
@ -120,13 +120,13 @@ TEST(liblp, MetadataAlignment) {
|
|||
|
||||
TEST(liblp, InternalAlignment) {
|
||||
// Test the metadata fitting within alignment.
|
||||
BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
|
||||
BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
|
||||
|
||||
// Test a large alignment offset thrown in.
|
||||
device_info.alignment_offset = 753664;
|
||||
|
@ -135,7 +135,7 @@ TEST(liblp, InternalAlignment) {
|
|||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
|
||||
|
||||
// Alignment offset without alignment doesn't mean anything.
|
||||
device_info.alignment = 0;
|
||||
|
@ -150,7 +150,7 @@ TEST(liblp, InternalAlignment) {
|
|||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 78);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
|
||||
|
||||
// Test a small alignment with no alignment offset.
|
||||
device_info.alignment = 11 * 1024;
|
||||
|
@ -163,7 +163,7 @@ TEST(liblp, InternalAlignment) {
|
|||
}
|
||||
|
||||
TEST(liblp, InternalPartitionAlignment) {
|
||||
BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
|
||||
BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
|
||||
|
||||
Partition* a = builder->AddPartition("a", TEST_GUID, 0);
|
||||
|
@ -381,7 +381,7 @@ TEST(liblp, MetadataTooLarge) {
|
|||
static const size_t kMetadataSize = 64 * 1024;
|
||||
|
||||
// No space to store metadata + geometry.
|
||||
BlockDeviceInfo device_info(kDiskSize, 0, 0);
|
||||
BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
|
@ -390,8 +390,8 @@ TEST(liblp, MetadataTooLarge) {
|
|||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
// Space for metadata + geometry + one free sector.
|
||||
device_info.size += LP_SECTOR_SIZE;
|
||||
// Space for metadata + geometry + one free block.
|
||||
device_info.size += device_info.logical_block_size;
|
||||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_NE(builder, nullptr);
|
||||
|
||||
|
@ -424,19 +424,21 @@ TEST(liblp, block_device_info) {
|
|||
ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
|
||||
ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
|
||||
ASSERT_LE(device_info.alignment_offset, INT_MAX);
|
||||
ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
|
||||
|
||||
// Having an alignment offset > alignment doesn't really make sense.
|
||||
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
|
||||
}
|
||||
|
||||
TEST(liblp, UpdateBlockDeviceInfo) {
|
||||
BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
|
||||
BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
|
||||
EXPECT_EQ(builder->block_device_info().size, device_info.size);
|
||||
EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
|
||||
EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
|
||||
EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
|
||||
|
||||
device_info.alignment = 0;
|
||||
device_info.alignment_offset = 2048;
|
||||
|
@ -450,3 +452,27 @@ TEST(liblp, UpdateBlockDeviceInfo) {
|
|||
EXPECT_EQ(builder->block_device_info().alignment, 8192);
|
||||
EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
|
||||
}
|
||||
|
||||
TEST(liblp, InvalidBlockSize) {
|
||||
BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
}
|
||||
|
||||
TEST(liblp, AlignedExtentSize) {
|
||||
BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
|
||||
Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
|
||||
ASSERT_NE(partition, nullptr);
|
||||
ASSERT_TRUE(builder->ResizePartition(partition, 512));
|
||||
EXPECT_EQ(partition->size(), 4096);
|
||||
}
|
||||
|
||||
TEST(liblp, AlignedFreeSpace) {
|
||||
// Only one sector free - at least one block is required.
|
||||
BlockDeviceInfo device_info(10240, 0, 0, 4096);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
|
||||
ASSERT_EQ(builder, nullptr);
|
||||
}
|
||||
|
|
|
@ -32,11 +32,16 @@ class LinearExtent;
|
|||
|
||||
// By default, partitions are aligned on a 1MiB boundary.
|
||||
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
|
||||
static const uint32_t kDefaultBlockSize = 4096;
|
||||
|
||||
struct BlockDeviceInfo {
|
||||
BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
|
||||
BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
|
||||
: size(size), alignment(alignment), alignment_offset(alignment_offset) {}
|
||||
BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
|
||||
BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
|
||||
uint32_t logical_block_size)
|
||||
: size(size),
|
||||
alignment(alignment),
|
||||
alignment_offset(alignment_offset),
|
||||
logical_block_size(logical_block_size) {}
|
||||
// Size of the block device, in bytes.
|
||||
uint64_t size;
|
||||
// Optimal target alignment, in bytes. Partition extents will be aligned to
|
||||
|
@ -46,6 +51,8 @@ struct BlockDeviceInfo {
|
|||
// |alignment_offset| on the target device is correctly aligned on its
|
||||
// parent device. This value must be 0 or a multiple of 512.
|
||||
uint32_t alignment_offset;
|
||||
// Block size, for aligning extent sizes and partition sizes.
|
||||
uint32_t logical_block_size;
|
||||
};
|
||||
|
||||
// Abstraction around dm-targets that can be encoded into logical partition tables.
|
||||
|
@ -88,6 +95,8 @@ class ZeroExtent final : public Extent {
|
|||
};
|
||||
|
||||
class Partition final {
|
||||
friend class MetadataBuilder;
|
||||
|
||||
public:
|
||||
Partition(const std::string& name, const std::string& guid, uint32_t attributes);
|
||||
|
||||
|
@ -97,10 +106,6 @@ class Partition final {
|
|||
// Remove all extents from this partition.
|
||||
void RemoveExtents();
|
||||
|
||||
// Remove and/or shrink extents until the partition is the requested size.
|
||||
// See MetadataBuilder::ShrinkPartition for more information.
|
||||
void ShrinkTo(uint64_t requested_size);
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
uint32_t attributes() const { return attributes_; }
|
||||
const std::string& guid() const { return guid_; }
|
||||
|
@ -108,6 +113,8 @@ class Partition final {
|
|||
uint64_t size() const { return size_; }
|
||||
|
||||
private:
|
||||
void ShrinkTo(uint64_t aligned_size);
|
||||
|
||||
std::string name_;
|
||||
std::string guid_;
|
||||
std::vector<std::unique_ptr<Extent>> extents_;
|
||||
|
@ -144,7 +151,7 @@ class MetadataBuilder {
|
|||
// size. This is a convenience method for tests.
|
||||
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
|
||||
uint32_t metadata_slot_count) {
|
||||
BlockDeviceInfo device_info(blockdev_size, 0, 0);
|
||||
BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
|
||||
return New(device_info, metadata_max_size, metadata_slot_count);
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,12 @@ typedef struct LpMetadataGeometry {
|
|||
* can be used to verify the geometry against a target device.
|
||||
*/
|
||||
uint64_t block_device_size;
|
||||
|
||||
/* 76: Logical block size of the super partition block device. This is the
|
||||
* minimal alignment for partition and extent sizes, and it must be a
|
||||
* multiple of LP_SECTOR_SIZE.
|
||||
*/
|
||||
uint32_t logical_block_size;
|
||||
} __attribute__((packed)) LpMetadataGeometry;
|
||||
|
||||
/* The logical partition metadata has a number of tables; they are described
|
||||
|
|
|
@ -40,7 +40,8 @@ TEST(liblp, GetMetadataOffset) {
|
|||
80000,
|
||||
0,
|
||||
0,
|
||||
1024 * 1024};
|
||||
1024 * 1024,
|
||||
4096};
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
|
||||
|
|
Loading…
Reference in New Issue