@@ -156,9 +156,53 @@ void InsertObjectVectorVectors(
156156}
157157
158158#if GOOGLE_CLOUD_CPP_HAVE_COROUTINES
159- void OpenObject (google::cloud::storage_experimental::AsyncClient& client,
160- std::vector<std::string> const & argv) {
161- // ! [open-object]
159+ void OpenObjectSingleRangedRead (
160+ google::cloud::storage_experimental::AsyncClient& client,
161+ std::vector<std::string> const & argv) {
162+ // ! [open-object-single-ranged-read]
163+ namespace gcs_ex = google::cloud::storage_experimental;
164+
165+ // Helper coroutine to count newlines returned by an AsyncReader.
166+ // This helps consume the data from the read operation.
167+ auto count_newlines =
168+ [](gcs_ex::AsyncReader reader,
169+ gcs_ex::AsyncToken token) -> google::cloud::future<std::uint64_t > {
170+ std::uint64_t count = 0 ;
171+ while (token.valid ()) {
172+ auto [payload, t] = (co_await reader.Read (std::move (token))).value ();
173+ token = std::move (t);
174+ for (auto const & buffer : payload.contents ()) {
175+ count += std::count (buffer.begin (), buffer.end (), ' \n ' );
176+ }
177+ }
178+ co_return count;
179+ };
180+
181+ auto coro =
182+ [&count_newlines](
183+ gcs_ex::AsyncClient& client, std::string bucket_name,
184+ std::string object_name) -> google::cloud::future<std::uint64_t > {
185+ auto descriptor =
186+ (co_await client.Open (gcs_ex::BucketName (std::move (bucket_name)),
187+ std::move (object_name)))
188+ .value ();
189+
190+ auto [reader, token] = descriptor.Read (0 , 1024 );
191+
192+ co_return co_await count_newlines (std::move (reader), std::move (token));
193+ };
194+ // ! [open-object-single-ranged-read]
195+
196+ // The example is easier to test if we call the coroutine and block
197+ // until it completes.
198+ auto const count = coro (client, argv.at (0 ), argv.at (1 )).get ();
199+ std::cout << " The range contains " << count << " newlines\n " ;
200+ }
201+
202+ void OpenObjectMultipleRangedRead (
203+ google::cloud::storage_experimental::AsyncClient& client,
204+ std::vector<std::string> const & argv) {
205+ // ! [open-object-multiple-ranged-read]
162206 namespace gcs_ex = google::cloud::storage_experimental;
163207 // Helper coroutine, count lines returned by a AsyncReader
164208 auto count_newlines =
@@ -191,13 +235,56 @@ void OpenObject(google::cloud::storage_experimental::AsyncClient& client,
191235 auto c2 = count_newlines (std::move (r2), std::move (t2));
192236 co_return (co_await std::move (c1)) + (co_await std::move (c2));
193237 };
194- // ! [open-object]
238+ // ! [open-object-multiple-ranged-read ]
195239 // The example is easier to test and run if we call the coroutine and block
196240 // until it completes.
197241 auto const count = coro (client, argv.at (0 ), argv.at (1 )).get ();
198242 std::cout << " The ranges contain " << count << " newlines\n " ;
199243}
200244
245+ void OpenObjectReadFullObject (
246+ google::cloud::storage_experimental::AsyncClient& client,
247+ std::vector<std::string> const & argv) {
248+ // ! [open-object-read-full-object]
249+ namespace gcs_ex = google::cloud::storage_experimental;
250+
251+ // Helper coroutine to count newlines returned by an AsyncReader.
252+ // This helps consume the data from the read operation.
253+ auto count_newlines =
254+ [](gcs_ex::AsyncReader reader,
255+ gcs_ex::AsyncToken token) -> google::cloud::future<std::uint64_t > {
256+ std::uint64_t count = 0 ;
257+ while (token.valid ()) {
258+ auto [payload, t] = (co_await reader.Read (std::move (token))).value ();
259+ token = std::move (t);
260+ for (auto const & buffer : payload.contents ()) {
261+ count += std::count (buffer.begin (), buffer.end (), ' \n ' );
262+ }
263+ }
264+ co_return count;
265+ };
266+
267+ auto coro =
268+ [&count_newlines](
269+ gcs_ex::AsyncClient& client, std::string bucket_name,
270+ std::string object_name) -> google::cloud::future<std::uint64_t > {
271+ auto descriptor =
272+ (co_await client.Open (gcs_ex::BucketName (std::move (bucket_name)),
273+ std::move (object_name)))
274+ .value ();
275+
276+ auto [reader, token] = descriptor.ReadFromOffset (0 );
277+
278+ co_return co_await count_newlines (std::move (reader), std::move (token));
279+ };
280+ // ! [open-object-read-full-object]
281+
282+ // The example is easier to test if we call the coroutine and block
283+ // until it completes.
284+ auto const count = coro (client, argv.at (0 ), argv.at (1 )).get ();
285+ std::cout << " The range contains " << count << " newlines\n " ;
286+ }
287+
201288void ReadObject (google::cloud::storage_experimental::AsyncClient& client,
202289 std::vector<std::string> const & argv) {
203290 // ! [read-object]
@@ -564,6 +651,69 @@ void StartAppendableObjectUpload(
564651 std::cout << " File successfully uploaded " << object.DebugString () << " \n " ;
565652}
566653
654+ void ResumeAppendableObjectUpload (
655+ google::cloud::storage_experimental::AsyncClient& client,
656+ std::vector<std::string> const & argv) {
657+ // ! [resume-appendable-object-upload]
658+ namespace gcs = google::cloud::storage;
659+ namespace gcs_ex = google::cloud::storage_experimental;
660+ auto coro = [](gcs_ex::AsyncClient& client, std::string bucket_name,
661+ std::string object_name)
662+ -> google::cloud::future<google::storage::v2::Object> {
663+ // Start an appendable upload and write some data.
664+ auto [writer, token] = (co_await client.StartAppendableObjectUpload (
665+ gcs_ex::BucketName (bucket_name), object_name))
666+ .value ();
667+ for (int i = 0 ; i != 5 ; ++i) {
668+ auto line = gcs_ex::WritePayload (std::vector<std::string>{
669+ std::string (" line number " ), std::to_string (i), std::string (" \n " )});
670+ token =
671+ (co_await writer.Write (std::move (token), std::move (line))).value ();
672+ }
673+ // The writer is closed, but the upload is not finalized. The object remains
674+ // appendable.
675+ auto close_status = co_await writer.Close ();
676+ if (!close_status.ok ()) {
677+ throw std::runtime_error (close_status.message ());
678+ }
679+
680+ // To resume the upload we need the object's generation. We can use the
681+ // regular GCS client to get the latest metadata.
682+ auto regular_client = gcs::Client ();
683+ auto metadata =
684+ regular_client.GetObjectMetadata (bucket_name, object_name).value ();
685+
686+ // Now resume the upload from the beginning.
687+ std::tie (writer, token) = (co_await client.ResumeAppendableObjectUpload (
688+ gcs_ex::BucketName (bucket_name), object_name,
689+ metadata.generation ()))
690+ .value ();
691+
692+ // The writer returns the persisted size, which can be used to understand
693+ // where to resume from.
694+ auto persisted_size = absl::get<std::int64_t >(writer.PersistedState ());
695+ std::cout << " Upload resumed at offset " << persisted_size << " \n " ;
696+
697+ // Append the rest of the data.
698+ for (int i = 5 ; i != 10 ; ++i) {
699+ auto line = gcs_ex::WritePayload (std::vector<std::string>{
700+ std::string (" line number " ), std::to_string (i), std::string (" \n " )});
701+ token =
702+ (co_await writer.Write (std::move (token), std::move (line))).value ();
703+ }
704+
705+ // Finalize the upload and return the object metadata.
706+ co_return (co_await writer.Finalize (std::move (token))).value ();
707+ };
708+ // ! [resume-appendable-object-upload]
709+
710+ // The example is easier to test and run if we call the coroutine and block
711+ // until it completes.
712+ auto const object = coro (client, argv.at (0 ), argv.at (1 )).get ();
713+ std::cout << " File successfully uploaded and finalized "
714+ << object.DebugString () << " \n " ;
715+ }
716+
567717void RewriteObject (google::cloud::storage_experimental::AsyncClient& client,
568718 std::vector<std::string> const & argv) {
569719 // ! [rewrite-object]
@@ -653,8 +803,20 @@ void ResumeRewrite(google::cloud::storage_experimental::AsyncClient& client,
653803}
654804
655805#else
656- void OpenObject (google::cloud::storage_experimental::AsyncClient&,
657- std::vector<std::string> const &) {
806+ void OpenObjectSingleRangedRead (
807+ google::cloud::storage_experimental::AsyncClient&,
808+ std::vector<std::string> const &) {
809+ std::cerr << " AsyncClient::Open() example requires coroutines\n " ;
810+ }
811+
812+ void OpenObjectMultipleRangedRead (
813+ google::cloud::storage_experimental::AsyncClient&,
814+ std::vector<std::string> const &) {
815+ std::cerr << " AsyncClient::Open() example requires coroutines\n " ;
816+ }
817+
818+ void OpenObjectReadFullObject (google::cloud::storage_experimental::AsyncClient&,
819+ std::vector<std::string> const &) {
658820 std::cerr << " AsyncClient::Open() example requires coroutines\n " ;
659821}
660822
@@ -726,6 +888,13 @@ void StartAppendableObjectUpload(
726888 " coroutines\n " ;
727889}
728890
891+ void ResumeAppendableObjectUpload (
892+ google::cloud::storage_experimental::AsyncClient&,
893+ std::vector<std::string> const &) {
894+ std::cerr << " AsyncClient::ResumeAppendableObjectUpload() example requires "
895+ " coroutines\n " ;
896+ }
897+
729898void RewriteObject (google::cloud::storage_experimental::AsyncClient&,
730899 std::vector<std::string> const &) {
731900 std::cerr << " AsyncClient::RewriteObject() example requires coroutines\n " ;
@@ -901,8 +1070,15 @@ void AutoRun(std::vector<std::string> const& argv) {
9011070 scheduled_for_delete.push_back (std::move (object_name));
9021071 object_name = examples::MakeRandomObjectName (generator, " object-" );
9031072
904- std::cout << " Running the OpenObject() example" << std::endl;
905- OpenObject (client, {bucket_name, composed_name});
1073+ std::cout << " Running the OpenObjectSingleRangedRead() example" << std::endl;
1074+ OpenObjectSingleRangedRead (client, {bucket_name, composed_name});
1075+
1076+ std::cout << " Running the OpenObjectMultipleRangedRead() example"
1077+ << std::endl;
1078+ OpenObjectMultipleRangedRead (client, {bucket_name, composed_name});
1079+
1080+ std::cout << " Running the OpenObjectReadFullObject() example" << std::endl;
1081+ OpenObjectReadFullObject (client, {bucket_name, composed_name});
9061082
9071083 std::cout << " Running the ReadObject() example" << std::endl;
9081084 ReadObject (client, {bucket_name, composed_name});
@@ -1064,7 +1240,11 @@ int main(int argc, char* argv[]) try {
10641240 make_entry (" insert-object-vector" , {}, InsertObjectVector),
10651241 make_entry (" insert-object-vector-strings" , {}, InsertObjectVectorStrings),
10661242 make_entry (" insert-object-vector-vectors" , {}, InsertObjectVectorVectors),
1067- make_entry (" open-object" , {}, OpenObject),
1243+ make_entry (" open-object-single-ranged-read" , {},
1244+ OpenObjectSingleRangedRead),
1245+ make_entry (" open-object-multiple-ranged-read" , {},
1246+ OpenObjectMultipleRangedRead),
1247+ make_entry (" open-object-read-full-object" , {}, OpenObjectReadFullObject),
10681248 make_entry (" read-object" , {}, ReadObject),
10691249 make_entry (" read-all" , {}, ReadAll),
10701250 make_entry (" read-object-range" , {}, ReadObjectRange),
@@ -1086,6 +1266,8 @@ int main(int argc, char* argv[]) try {
10861266
10871267 make_entry (" start-appendable-object-upload" , {},
10881268 StartAppendableObjectUpload),
1269+ make_entry (" resume-appendable-object-upload" , {},
1270+ ResumeAppendableObjectUpload),
10891271
10901272 make_entry (" rewrite-object" , {" <destination>" }, RewriteObject),
10911273 make_entry (" resume-rewrite-object" , {" <destination>" }, ResumeRewrite),
0 commit comments