[ledger] Allow getting a part of the object as VMO.
TEST=PageStorageTest
LE-239 #comment the mapping part is done, still need partial download.
Change-Id: I93c5679b864b8da1cd582257177da17d7eccef73
diff --git a/bin/ledger/app/page_utils.cc b/bin/ledger/app/page_utils.cc
index c6fada3..7f0ed50 100644
--- a/bin/ledger/app/page_utils.cc
+++ b/bin/ledger/app/page_utils.cc
@@ -16,22 +16,6 @@
#include "peridot/bin/ledger/storage/public/types.h"
namespace ledger {
-namespace {
-Status ToBuffer(convert::ExtendedStringView value, int64_t offset,
- int64_t max_size, fsl::SizedVmo* buffer) {
- size_t start = value.size();
- // Valid indices are between -N and N-1.
- if (offset >= -static_cast<int64_t>(value.size()) &&
- offset < static_cast<int64_t>(value.size())) {
- start = offset < 0 ? value.size() + offset : offset;
- }
- size_t length = max_size < 0 ? value.size() : max_size;
-
- bool result = fsl::VmoFromString(value.substr(start, length), buffer);
- return result ? Status::OK : Status::UNKNOWN_ERROR;
-}
-
-} // namespace
void PageUtils::ResolveObjectIdentifierAsStringView(
storage::PageStorage* storage, storage::ObjectIdentifier object_identifier,
@@ -85,21 +69,12 @@
int64_t offset, int64_t max_size, storage::PageStorage::Location location,
Status not_found_status,
fit::function<void(Status, fsl::SizedVmo)> callback) {
- ResolveObjectIdentifierAsStringView(
- storage, object_identifier, location, not_found_status,
- [offset, max_size, callback = std::move(callback)](Status status,
- fxl::StringView data) {
- if (status != Status::OK) {
- callback(status, nullptr);
- return;
- }
- fsl::SizedVmo buffer;
- Status buffer_status = ToBuffer(data, offset, max_size, &buffer);
- if (buffer_status != Status::OK) {
- callback(buffer_status, nullptr);
- return;
- }
- callback(Status::OK, std::move(buffer));
+ storage->GetObjectPart(
+ object_identifier, offset, max_size, location,
+ [not_found_status, callback = std::move(callback)](
+ storage::Status status, fsl::SizedVmo object_part) {
+ callback(ConvertStatus(status, not_found_status),
+ std::move(object_part));
});
}
diff --git a/bin/ledger/storage/fake/fake_page_storage.cc b/bin/ledger/storage/fake/fake_page_storage.cc
index e06372a..7fba43e 100644
--- a/bin/ledger/storage/fake/fake_page_storage.cc
+++ b/bin/ledger/storage/fake/fake_page_storage.cc
@@ -12,6 +12,7 @@
#include <lib/async/default.h>
#include <lib/fit/function.h>
#include <lib/fsl/socket/strings.h>
+#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/strings/concatenate.h>
#include <lib/zx/time.h>
@@ -25,6 +26,23 @@
namespace storage {
namespace fake {
+namespace {
+
+Status ToBuffer(convert::ExtendedStringView value, int64_t offset,
+ int64_t max_size, fsl::SizedVmo* buffer) {
+ size_t start = value.size();
+ // Valid indices are between -N and N-1.
+ if (offset >= -static_cast<int64_t>(value.size()) &&
+ offset < static_cast<int64_t>(value.size())) {
+ start = offset < 0 ? value.size() + offset : offset;
+ }
+ size_t length = max_size < 0 ? value.size() : max_size;
+ bool result = fsl::VmoFromString(value.substr(start, length), buffer);
+ return result ? Status::OK : Status::INTERNAL_IO_ERROR;
+}
+
+} // namespace
+
FakePageStorage::FakePageStorage(ledger::Environment* environment,
PageId page_id)
: page_id_(std::move(page_id)),
@@ -175,6 +193,32 @@
GetPiece(object_identifier, std::move(callback));
}
+void FakePageStorage::GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location, fit::function<void(Status, fsl::SizedVmo)> callback) {
+ GetPiece(object_identifier,
+ [offset, max_size, callback = std::move(callback)](
+ Status status, std::unique_ptr<const Object> piece) {
+ if (status != Status::OK) {
+ callback(status, nullptr);
+ return;
+ }
+ fxl::StringView data;
+ Status data_status = piece->GetData(&data);
+ if (data_status != Status::OK) {
+ callback(data_status, nullptr);
+ return;
+ }
+ fsl::SizedVmo buffer;
+ Status buffer_status = ToBuffer(data, offset, max_size, &buffer);
+ if (buffer_status != Status::OK) {
+ callback(buffer_status, nullptr);
+ return;
+ }
+ callback(Status::OK, std::move(buffer));
+ });
+}
+
void FakePageStorage::GetPiece(
ObjectIdentifier object_identifier,
fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
diff --git a/bin/ledger/storage/fake/fake_page_storage.h b/bin/ledger/storage/fake/fake_page_storage.h
index bc6f3a1..836a413 100644
--- a/bin/ledger/storage/fake/fake_page_storage.h
+++ b/bin/ledger/storage/fake/fake_page_storage.h
@@ -57,6 +57,10 @@
void AddObjectFromLocal(
ObjectType object_type, std::unique_ptr<DataSource> data_source,
fit::function<void(Status, ObjectIdentifier)> callback) override;
+ void GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location,
+ fit::function<void(Status, fsl::SizedVmo)> callback) override;
void GetObject(ObjectIdentifier object_identifier, Location location,
fit::function<void(Status, std::unique_ptr<const Object>)>
callback) override;
diff --git a/bin/ledger/storage/impl/page_storage_impl.cc b/bin/ledger/storage/impl/page_storage_impl.cc
index 8809962..fe3d9d7 100644
--- a/bin/ledger/storage/impl/page_storage_impl.cc
+++ b/bin/ledger/storage/impl/page_storage_impl.cc
@@ -19,6 +19,7 @@
#include <lib/callback/trace_callback.h>
#include <lib/callback/waiter.h>
#include <lib/fit/function.h>
+#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/arraysize.h>
#include <lib/fxl/files/directory.h>
#include <lib/fxl/files/file.h>
@@ -71,6 +72,22 @@
}
};
+size_t GetObjectPartStart(int64_t offset, size_t object_size) {
+ size_t start = object_size;
+ // Valid indices are between -N and N-1.
+ if (offset >= -static_cast<int64_t>(object_size) &&
+ offset < static_cast<int64_t>(object_size)) {
+ start = offset < 0 ? object_size + offset : offset;
+ }
+ return start;
+}
+
+size_t GetObjectPartLength(int64_t max_size, size_t object_size, size_t start) {
+ size_t adjusted_max_size = max_size < 0 ? object_size : max_size;
+ return start > object_size ? 0
+ : std::min(adjusted_max_size, object_size - start);
+}
+
} // namespace
PageStorageImpl::PageStorageImpl(
@@ -457,17 +474,19 @@
});
}
-void PageStorageImpl::GetObject(
- ObjectIdentifier object_identifier, Location location,
- fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
+void PageStorageImpl::GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location, fit::function<void(Status, fsl::SizedVmo)> callback) {
FXL_DCHECK(IsDigestValid(object_identifier.object_digest()));
GetPiece(
object_identifier,
- [this, object_identifier, location, callback = std::move(callback)](
+ [this, object_identifier, offset, max_size, location,
+ callback = std::move(callback)](
Status status, std::unique_ptr<const Object> object) mutable {
if (status == Status::NOT_FOUND) {
if (location == Location::NETWORK) {
- GetObjectFromSync(object_identifier, std::move(callback));
+ GetObjectPartFromSync(object_identifier, offset, max_size,
+ std::move(callback));
} else {
callback(Status::NOT_FOUND, nullptr);
}
@@ -482,38 +501,50 @@
FXL_DCHECK(object);
ObjectDigestInfo digest_info =
GetObjectDigestInfo(object_identifier.object_digest());
+ fxl::StringView data;
+ Status get_data_status = object->GetData(&data);
+ if (get_data_status != Status::OK) {
+ callback(get_data_status, nullptr);
+ return;
+ }
if (digest_info.is_inlined() || digest_info.is_chunk()) {
- callback(status, std::move(object));
+ fsl::SizedVmo buffer;
+ size_t start = GetObjectPartStart(offset, data.size());
+ size_t length = GetObjectPartLength(max_size, data.size(), start);
+
+ if (!fsl::VmoFromString(data.substr(start, length), &buffer)) {
+ callback(Status::INTERNAL_IO_ERROR, nullptr);
+ return;
+ }
+ callback(Status::OK, std::move(buffer));
return;
}
FXL_DCHECK(digest_info.piece_type == PieceType::INDEX);
-
- fxl::StringView content;
- status = object->GetData(&content);
- if (status != Status::OK) {
- callback(status, nullptr);
- return;
- }
const FileIndex* file_index;
- status = FileIndexSerialization::ParseFileIndex(content, &file_index);
+ status = FileIndexSerialization::ParseFileIndex(data, &file_index);
if (status != Status::OK) {
callback(Status::FORMAT_ERROR, nullptr);
return;
}
+ size_t start = GetObjectPartStart(offset, file_index->size());
+ size_t length =
+ GetObjectPartLength(max_size, file_index->size(), start);
zx::vmo raw_vmo;
- zx_status_t zx_status =
- zx::vmo::create(file_index->size(), 0, &raw_vmo);
+ zx_status_t zx_status = zx::vmo::create(length, 0, &raw_vmo);
if (zx_status != ZX_OK) {
- FXL_LOG(WARNING) << "Unable to create VMO of size: "
- << file_index->size();
+ FXL_LOG(WARNING) << "Unable to create VMO of size: " << length;
callback(Status::INTERNAL_IO_ERROR, nullptr);
return;
}
+ fsl::SizedVmo vmo(std::move(raw_vmo), length);
- fsl::SizedVmo vmo(std::move(raw_vmo), file_index->size());
+ if (length == 0) {
+ callback(Status::OK, std::move(vmo));
+ }
+
fsl::SizedVmo vmo_copy;
zx_status = vmo.Duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_WRITE, &vmo_copy);
if (zx_status != ZX_OK) {
@@ -522,17 +553,26 @@
return;
}
FillBufferWithObjectContent(
- std::move(object), std::move(vmo_copy), 0, file_index->size(),
- [object_identifier = std::move(object_identifier),
- vmo = std::move(vmo),
- callback = std::move(callback)](Status status) mutable {
- callback(status,
- std::make_unique<VmoObject>(std::move(object_identifier),
- std::move(vmo)));
- });
+ std::move(object), std::move(vmo_copy), start, length, 0,
+ file_index->size(),
+ [vmo = std::move(vmo), callback = std::move(callback)](
+ Status status) mutable { callback(status, std::move(vmo)); });
});
}
+void PageStorageImpl::GetObject(
+ ObjectIdentifier object_identifier, Location location,
+ fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
+ GetObjectPart(object_identifier, 0, -1, location,
+ [object_identifier = std::move(object_identifier),
+ callback = std::move(callback)](Status status,
+ fsl::SizedVmo vmo) mutable {
+ callback(status,
+ std::make_unique<VmoObject>(
+ std::move(object_identifier), std::move(vmo)));
+ });
+}
+
void PageStorageImpl::GetPiece(
ObjectIdentifier object_identifier,
fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
@@ -892,24 +932,26 @@
});
}
-void PageStorageImpl::GetObjectFromSync(
- ObjectIdentifier object_identifier,
- fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
+void PageStorageImpl::GetObjectPartFromSync(
+ ObjectIdentifier object_identifier, size_t offset, size_t size,
+ fit::function<void(Status, fsl::SizedVmo)> callback) {
if (!page_sync_) {
callback(Status::NOT_CONNECTED_ERROR, nullptr);
return;
}
- DownloadFullObject(object_identifier, [this, object_identifier,
- callback = std::move(callback)](
- Status status) mutable {
- if (status != Status::OK) {
- callback(status, nullptr);
- return;
- }
+ // TODO(LE-239): download only the needed part.
+ DownloadFullObject(object_identifier,
+ [this, object_identifier, offset, size,
+ callback = std::move(callback)](Status status) mutable {
+ if (status != Status::OK) {
+ callback(status, nullptr);
+ return;
+ }
- GetObject(object_identifier, Location::LOCAL, std::move(callback));
- });
+ GetObjectPart(object_identifier, offset, size,
+ Location::LOCAL, std::move(callback));
+ });
}
void PageStorageImpl::ObjectIsUntracked(
@@ -934,11 +976,12 @@
}
void PageStorageImpl::FillBufferWithObjectContent(
- ObjectIdentifier object_identifier, fsl::SizedVmo vmo, size_t offset,
- size_t size, fit::function<void(Status)> callback) {
+ ObjectIdentifier object_identifier, fsl::SizedVmo vmo,
+ int64_t global_offset, size_t global_size, int64_t current_position,
+ size_t object_size, fit::function<void(Status)> callback) {
GetPiece(object_identifier,
- [this, vmo = std::move(vmo), offset, size,
- callback = std::move(callback)](
+ [this, vmo = std::move(vmo), global_offset, global_size,
+ current_position, object_size, callback = std::move(callback)](
Status status, std::unique_ptr<const Object> object) mutable {
if (status != Status::OK) {
callback(status);
@@ -946,32 +989,48 @@
}
FXL_DCHECK(object);
- FillBufferWithObjectContent(std::move(object), std::move(vmo),
- offset, size, std::move(callback));
+ FillBufferWithObjectContent(
+ std::move(object), std::move(vmo), global_offset, global_size,
+ current_position, object_size, std::move(callback));
});
}
void PageStorageImpl::FillBufferWithObjectContent(
- std::unique_ptr<const Object> object, fsl::SizedVmo vmo, size_t offset,
- size_t size, fit::function<void(Status)> callback) {
+ std::unique_ptr<const Object> object, fsl::SizedVmo vmo,
+ int64_t global_offset, size_t global_size, int64_t current_position,
+ size_t object_size, fit::function<void(Status)> callback) {
fxl::StringView content;
Status status = object->GetData(&content);
if (status != Status::OK) {
callback(status);
return;
}
-
ObjectDigestInfo digest_info =
GetObjectDigestInfo(object->GetIdentifier().object_digest());
if (digest_info.is_inlined() || digest_info.is_chunk()) {
- if (size != content.size()) {
+ if (object_size != content.size()) {
FXL_LOG(ERROR) << "Error in serialization format. Expecting object: "
- << object->GetIdentifier() << " to have size: " << size
+ << object->GetIdentifier()
+ << " to have size: " << object_size
<< ", but found an object of size: " << content.size();
callback(Status::FORMAT_ERROR);
return;
}
- zx_status_t zx_status = vmo.vmo().write(content.data(), offset, size);
+ // Distance is negative if the offset is ahead and positive if behind.
+ int64_t distance_from_global_offset = current_position - global_offset;
+ // Read offset can be non-zero on first read; in that case, we need to skip
+ // bytes coming before global offset.
+ size_t read_offset = std::max(-distance_from_global_offset, 0L);
+ // Write offset is zero on the first write; otherwise we need to skip number
+ // of bytes corresponding to what we have already written.
+ size_t write_offset = std::max(distance_from_global_offset, 0L);
+ // Read and write until reaching either size of the object, or global size.
+ size_t read_write_size =
+ std::min(content.size() - read_offset, global_size - write_offset);
+ FXL_DCHECK(read_write_size > 0);
+ fxl::StringView read_substr = content.substr(read_offset, read_write_size);
+ zx_status_t zx_status =
+ vmo.vmo().write(read_substr.data(), write_offset, read_write_size);
if (zx_status != ZX_OK) {
FXL_LOG(ERROR) << "Unable to write to vmo. Status: " << zx_status;
callback(Status::INTERNAL_IO_ERROR);
@@ -987,22 +1046,32 @@
callback(Status::FORMAT_ERROR);
return;
}
- if (file_index->size() != size) {
+ if (file_index->size() != object_size) {
FXL_LOG(ERROR) << "Error in serialization format. Expecting object: "
- << object->GetIdentifier() << " to have size: " << size
+ << object->GetIdentifier() << " to have size " << object_size
<< ", but found an index object of size: "
<< file_index->size();
callback(Status::FORMAT_ERROR);
return;
}
- size_t sub_offset = 0;
+ int64_t sub_offset = 0;
auto waiter = fxl::MakeRefCounted<callback::StatusWaiter<Status>>(Status::OK);
for (const auto* child : *file_index->children()) {
if (sub_offset + child->size() > file_index->size()) {
callback(Status::FORMAT_ERROR);
return;
}
+ int64_t child_position = current_position + sub_offset;
+ ObjectIdentifier child_identifier =
+ ToObjectIdentifier(child->object_identifier());
+ if (child_position + static_cast<int64_t>(child->size()) < global_offset) {
+ sub_offset += child->size();
+ continue;
+ }
+ if (global_offset + static_cast<int64_t>(global_size) < child_position) {
+ break;
+ }
fsl::SizedVmo vmo_copy;
zx_status_t zx_status =
vmo.Duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_WRITE, &vmo_copy);
@@ -1011,18 +1080,11 @@
callback(Status::INTERNAL_IO_ERROR);
return;
}
- FillBufferWithObjectContent(ToObjectIdentifier(child->object_identifier()),
- std::move(vmo_copy), offset + sub_offset,
+ FillBufferWithObjectContent(child_identifier, std::move(vmo_copy),
+ global_offset, global_size, child_position,
child->size(), waiter->NewCallback());
sub_offset += child->size();
}
-
- if (sub_offset != file_index->size()) {
- FXL_LOG(ERROR) << "Built file size doesn't add up.";
- callback(Status::FORMAT_ERROR);
- return;
- }
-
waiter->Finalize(std::move(callback));
}
diff --git a/bin/ledger/storage/impl/page_storage_impl.h b/bin/ledger/storage/impl/page_storage_impl.h
index 141288b..1210757 100644
--- a/bin/ledger/storage/impl/page_storage_impl.h
+++ b/bin/ledger/storage/impl/page_storage_impl.h
@@ -98,6 +98,10 @@
void AddObjectFromLocal(
ObjectType object_type, std::unique_ptr<DataSource> data_source,
fit::function<void(Status, ObjectIdentifier)> callback) override;
+ void GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location,
+ fit::function<void(Status, fsl::SizedVmo)> callback) override;
void GetObject(ObjectIdentifier object_identifier, Location location,
fit::function<void(Status, std::unique_ptr<const Object>)>
callback) override;
@@ -172,17 +176,27 @@
void DownloadFullObject(ObjectIdentifier object_identifier,
fit::function<void(Status)> callback);
- void GetObjectFromSync(
- ObjectIdentifier object_identifier,
- fit::function<void(Status, std::unique_ptr<const Object>)> callback);
+ void GetObjectPartFromSync(
+ ObjectIdentifier object_identifier, size_t offset, size_t size,
+ fit::function<void(Status, fsl::SizedVmo)> callback);
+ // Reads the content of an object into a provided VMO. Takes into
+ // account the global offset and size in order to be able to read only the
+ // requested part of an object.
+ // |global_offset| is the offset from the beginning of the full object in
+ // bytes. |global_size| is the maximum size requested to be read into the vmo.
+ // |current_position| is the position of the currently read piece (defined by
+ // |object_identifier|) in the full object. |object_size| is the size of the
+ // currently read piece.
void FillBufferWithObjectContent(ObjectIdentifier object_identifier,
- fsl::SizedVmo vmo, size_t offset,
- size_t size,
+ fsl::SizedVmo vmo, int64_t global_offset,
+ size_t global_size, int64_t current_position,
+ size_t object_size,
fit::function<void(Status)> callback);
void FillBufferWithObjectContent(std::unique_ptr<const Object> object,
- fsl::SizedVmo vmo, size_t offset,
- size_t size,
+ fsl::SizedVmo vmo, int64_t global_offset,
+ size_t global_size, int64_t current_position,
+ size_t object_size,
fit::function<void(Status)> callback);
// Notifies the registered watchers of |new_commits|.
diff --git a/bin/ledger/storage/impl/page_storage_unittest.cc b/bin/ledger/storage/impl/page_storage_unittest.cc
index e5e7731..a4384b2 100644
--- a/bin/ledger/storage/impl/page_storage_unittest.cc
+++ b/bin/ledger/storage/impl/page_storage_unittest.cc
@@ -13,6 +13,7 @@
#include <lib/callback/set_when_called.h>
#include <lib/fit/function.h>
#include <lib/fsl/socket/strings.h>
+#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/arraysize.h>
#include <lib/fxl/files/directory.h>
#include <lib/fxl/files/file.h>
@@ -388,6 +389,22 @@
return object;
}
+ fsl::SizedVmo TryGetObjectPart(const ObjectIdentifier& object_identifier,
+ size_t offset, size_t max_size,
+ PageStorage::Location location,
+ Status expected_status = Status::OK) {
+ bool called;
+ Status status;
+ fsl::SizedVmo vmo;
+ storage_->GetObjectPart(
+ object_identifier, offset, max_size, location,
+ callback::Capture(callback::SetWhenCalled(&called), &status, &vmo));
+ RunLoopUntilIdle();
+ EXPECT_TRUE(called);
+ EXPECT_EQ(expected_status, status);
+ return vmo;
+ }
+
std::unique_ptr<const Object> TryGetPiece(
const ObjectIdentifier& object_identifier,
Status expected_status = Status::OK) {
@@ -1270,6 +1287,117 @@
});
}
+TEST_F(PageStorageTest, GetObjectPart) {
+ RunInCoroutine([this](CoroutineHandler* handler) {
+ ObjectData data("_Some data_");
+ ASSERT_EQ(Status::OK, WriteObject(handler, &data));
+
+ fsl::SizedVmo object_part = TryGetObjectPart(
+ data.object_identifier, 1, data.size - 2, PageStorage::Location::LOCAL);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ EXPECT_EQ(data.value.substr(1, data.size - 2),
+ convert::ToString(object_part_data));
+ });
+}
+
+TEST_F(PageStorageTest, GetObjectPartLargeOffset) {
+ RunInCoroutine([this](CoroutineHandler* handler) {
+ ObjectData data("_Some data_");
+ ASSERT_EQ(Status::OK, WriteObject(handler, &data));
+
+ fsl::SizedVmo object_part =
+ TryGetObjectPart(data.object_identifier, data.size * 2, data.size,
+ PageStorage::Location::LOCAL);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ EXPECT_EQ("", convert::ToString(object_part_data));
+ });
+}
+
+TEST_F(PageStorageTest, GetObjectPartLargeMaxSize) {
+ RunInCoroutine([this](CoroutineHandler* handler) {
+ ObjectData data("_Some data_");
+ ASSERT_EQ(Status::OK, WriteObject(handler, &data));
+
+ fsl::SizedVmo object_part = TryGetObjectPart(
+ data.object_identifier, 0, data.size * 2, PageStorage::Location::LOCAL);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ EXPECT_EQ(data.value, convert::ToString(object_part_data));
+ });
+}
+
+TEST_F(PageStorageTest, GetObjectPartNegativeArgs) {
+ RunInCoroutine([this](CoroutineHandler* handler) {
+ ObjectData data("_Some data_");
+ ASSERT_EQ(Status::OK, WriteObject(handler, &data));
+
+ fsl::SizedVmo object_part =
+ TryGetObjectPart(data.object_identifier, -data.size + 1, -1,
+ PageStorage::Location::LOCAL);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ EXPECT_EQ(data.value.substr(1, data.size - 1),
+ convert::ToString(object_part_data));
+ });
+}
+
+TEST_F(PageStorageTest, GetLargeObjectPart) {
+ std::string data_str = RandomString(environment_.random(), 65536);
+ size_t offset = 6144;
+ size_t size = 49152;
+
+ ObjectData data(std::move(data_str), ObjectType::TREE_NODE,
+ InlineBehavior::PREVENT);
+
+ ASSERT_EQ(
+ PieceType::INDEX,
+ GetObjectDigestInfo(data.object_identifier.object_digest()).piece_type);
+
+ bool called;
+ Status status;
+ ObjectIdentifier object_identifier;
+ storage_->AddObjectFromLocal(
+ ObjectType::TREE_NODE, data.ToDataSource(),
+ callback::Capture(callback::SetWhenCalled(&called), &status,
+ &object_identifier));
+ RunLoopUntilIdle();
+ ASSERT_TRUE(called);
+
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ(data.object_identifier, object_identifier);
+
+ fsl::SizedVmo object_part = TryGetObjectPart(object_identifier, offset, size,
+ PageStorage::Location::LOCAL);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ std::string result_str = convert::ToString(object_part_data);
+ EXPECT_EQ(size, result_str.size());
+ EXPECT_EQ(data.value.substr(offset, size), result_str);
+}
+
+TEST_F(PageStorageTest, GetObjectPartFromSync) {
+ ObjectData data("_Some data_", InlineBehavior::PREVENT);
+ FakeSyncDelegate sync;
+ sync.AddObject(data.object_identifier, data.value);
+ storage_->SetSyncDelegate(&sync);
+
+ fsl::SizedVmo object_part = TryGetObjectPart(
+ data.object_identifier, 1, data.size - 2, PageStorage::Location::NETWORK);
+ std::string object_part_data;
+ ASSERT_TRUE(fsl::StringFromVmo(object_part, &object_part_data));
+ EXPECT_EQ(data.value.substr(1, data.size - 2),
+ convert::ToString(object_part_data));
+
+ storage_->SetSyncDelegate(nullptr);
+ ObjectData other_data("_Some other data_", InlineBehavior::PREVENT);
+ TryGetObjectPart(other_data.object_identifier, 1, other_data.size - 2,
+ PageStorage::Location::LOCAL, Status::NOT_FOUND);
+ TryGetObjectPart(other_data.object_identifier, 1, other_data.size - 2,
+ PageStorage::Location::NETWORK, Status::NOT_CONNECTED_ERROR);
+}
+
TEST_F(PageStorageTest, GetObjectFromSync) {
ObjectData data("Some data", InlineBehavior::PREVENT);
FakeSyncDelegate sync;
diff --git a/bin/ledger/storage/public/page_storage.h b/bin/ledger/storage/public/page_storage.h
index 255688a..cd083d5 100644
--- a/bin/ledger/storage/public/page_storage.h
+++ b/bin/ledger/storage/public/page_storage.h
@@ -157,6 +157,17 @@
virtual void GetObject(
ObjectIdentifier object_identifier, Location location,
fit::function<void(Status, std::unique_ptr<const Object>)> callback) = 0;
+
+ // Retrieve a part of an object starting at |offset| with a maximum size of
+ // |max_size| and map it to a VMO.
+ // If |offset| is less than 0, starts from |-offset| from the end of the
+ // value. If |max_size| is less than 0, retrieves everything untill the end of
+ // an object.
+ virtual void GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location,
+ fit::function<void(Status, fsl::SizedVmo)> callback) = 0;
+
// Finds the piece associated with the given |object_identifier|. The result
// or an error will be returned through the given |callback|. Only local
// storage is checked, and if the object is an index, is it returned as is,
diff --git a/bin/ledger/storage/testing/page_storage_empty_impl.cc b/bin/ledger/storage/testing/page_storage_empty_impl.cc
index 5e61aa6..dfe4ec4 100644
--- a/bin/ledger/storage/testing/page_storage_empty_impl.cc
+++ b/bin/ledger/storage/testing/page_storage_empty_impl.cc
@@ -138,6 +138,13 @@
callback(Status::NOT_IMPLEMENTED, {});
}
+void PageStorageEmptyImpl::GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location, fit::function<void(Status, fsl::SizedVmo)> callback) {
+ FXL_NOTIMPLEMENTED();
+ callback(Status::NOT_IMPLEMENTED, nullptr);
+}
+
void PageStorageEmptyImpl::GetObject(
ObjectIdentifier /*object_identifier*/, Location /*location*/,
fit::function<void(Status, std::unique_ptr<const Object>)> callback) {
diff --git a/bin/ledger/storage/testing/page_storage_empty_impl.h b/bin/ledger/storage/testing/page_storage_empty_impl.h
index cb0695c..5136d11 100644
--- a/bin/ledger/storage/testing/page_storage_empty_impl.h
+++ b/bin/ledger/storage/testing/page_storage_empty_impl.h
@@ -86,6 +86,11 @@
ObjectType object_type, std::unique_ptr<DataSource> data_source,
fit::function<void(Status, ObjectIdentifier)> callback) override;
+ void GetObjectPart(
+ ObjectIdentifier object_identifier, int64_t offset, int64_t max_size,
+ Location location,
+ fit::function<void(Status, fsl::SizedVmo)> callback) override;
+
void GetObject(ObjectIdentifier object_identifier, Location location,
fit::function<void(Status, std::unique_ptr<const Object>)>
callback) override;