xref: /aosp_15_r20/external/icing/icing/file/posting_list/posting-list-accessor.cc (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "icing/file/posting_list/posting-list-accessor.h"
16 
17 #include <cstdint>
18 #include <memory>
19 #include <utility>
20 
21 #include "icing/text_classifier/lib3/utils/base/status.h"
22 #include "icing/text_classifier/lib3/utils/base/statusor.h"
23 #include "icing/absl_ports/canonical_errors.h"
24 #include "icing/file/posting_list/flash-index-storage.h"
25 #include "icing/file/posting_list/posting-list-identifier.h"
26 #include "icing/file/posting_list/posting-list-used.h"
27 #include "icing/util/status-macros.h"
28 
29 namespace icing {
30 namespace lib {
31 
FlushPreexistingPostingList()32 libtextclassifier3::Status PostingListAccessor::FlushPreexistingPostingList() {
33   if (preexisting_posting_list_->posting_list.size_in_bytes() ==
34       storage_->max_posting_list_bytes()) {
35     // If this is a max-sized posting list, then sync to disk and keep track of
36     // the id.
37     ICING_RETURN_IF_ERROR(
38         storage_->WritePostingListToDisk(*preexisting_posting_list_));
39     prev_block_identifier_ = preexisting_posting_list_->id;
40   } else {
41     // If this is NOT a max-sized posting list, then our data have outgrown this
42     // particular posting list. Move the data into the in-memory posting list
43     // and free this posting list.
44     //
45     // Move will always succeed since in_memory_posting_list_ is max_pl_bytes.
46     ICING_RETURN_IF_ERROR(GetSerializer()->MoveFrom(
47         /*dst=*/&in_memory_posting_list_,
48         /*src=*/&preexisting_posting_list_->posting_list));
49 
50     // Now that all the contents of this posting list have been copied, there's
51     // no more use for it. Make it available to be used for another posting
52     // list.
53     ICING_RETURN_IF_ERROR(
54         storage_->FreePostingList(std::move(*preexisting_posting_list_)));
55   }
56   preexisting_posting_list_.reset();
57   return libtextclassifier3::Status::OK;
58 }
59 
FlushInMemoryPostingList()60 libtextclassifier3::Status PostingListAccessor::FlushInMemoryPostingList() {
61   // We exceeded max_pl_bytes(). Need to flush in_memory_posting_list_ and
62   // update the chain.
63   ICING_ASSIGN_OR_RETURN(PostingListHolder holder,
64                          storage_->AllocateAndChainMaxSizePostingList(
65                              prev_block_identifier_.block_index()));
66   ICING_RETURN_IF_ERROR(
67       GetSerializer()->MoveFrom(/*dst=*/&holder.posting_list,
68                                 /*src=*/&in_memory_posting_list_));
69   ICING_RETURN_IF_ERROR(storage_->WritePostingListToDisk(holder));
70 
71   // Set prev block id only if persist to disk succeeded.
72   prev_block_identifier_ = holder.id;
73   return libtextclassifier3::Status::OK;
74 }
75 
Finalize()76 PostingListAccessor::FinalizeResult PostingListAccessor::Finalize() && {
77   if (preexisting_posting_list_ != nullptr) {
78     // Sync to disk.
79     return FinalizeResult(
80         storage_->WritePostingListToDisk(*preexisting_posting_list_),
81         preexisting_posting_list_->id);
82   }
83 
84   if (GetSerializer()->GetBytesUsed(&in_memory_posting_list_) <= 0) {
85     return FinalizeResult(absl_ports::InvalidArgumentError(
86                               "Can't finalize an empty PostingListAccessor. "
87                               "There's nothing to Finalize!"),
88                           PostingListIdentifier::kInvalid);
89   }
90 
91   libtextclassifier3::StatusOr<PostingListHolder> holder_or;
92   if (prev_block_identifier_.is_valid()) {
93     // If prev_block_identifier_ is valid, then it means there was a max-sized
94     // posting list, so we have to allocate another new max size posting list
95     // and chain them together.
96     holder_or = storage_->AllocateAndChainMaxSizePostingList(
97         prev_block_identifier_.block_index());
98   } else {
99     // Otherwise, it is the first posting list, and we can use smaller size pl.
100     // Note that even if it needs a max-sized posting list here, it is ok to
101     // call AllocatePostingList without setting next block index since we don't
102     // have any previous posting list to chain and AllocatePostingList will set
103     // next block index to kInvalidBlockIndex.
104     uint32_t posting_list_bytes =
105         GetSerializer()->GetMinPostingListSizeToFit(&in_memory_posting_list_);
106     holder_or = storage_->AllocatePostingList(posting_list_bytes);
107   }
108 
109   if (!holder_or.ok()) {
110     return FinalizeResult(std::move(holder_or).status(),
111                           prev_block_identifier_);
112   }
113   PostingListHolder holder = std::move(holder_or).ValueOrDie();
114 
115   // Move to allocated area. This should never actually return an error. We know
116   // that editor.posting_list() is valid because it wouldn't have successfully
117   // returned by AllocatePostingList if it wasn't. We know
118   // in_memory_posting_list_ is valid because we created it in-memory. And
119   // finally, we know that the data from in_memory_posting_list_ will fit in
120   // editor.posting_list() because we requested it be at at least
121   // posting_list_bytes large.
122   auto status = GetSerializer()->MoveFrom(/*dst=*/&holder.posting_list,
123                                           /*src=*/&in_memory_posting_list_);
124   if (!status.ok()) {
125     return FinalizeResult(std::move(status), prev_block_identifier_);
126   }
127 
128   status = storage_->WritePostingListToDisk(holder);
129   if (!status.ok()) {
130     return FinalizeResult(std::move(status), prev_block_identifier_);
131   }
132   return FinalizeResult(libtextclassifier3::Status::OK, holder.id);
133 }
134 
135 }  // namespace lib
136 }  // namespace icing
137