1 // Copyright (C) 2019 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/index/main/posting-list-hit-accessor.h"
16
17 #include <cstdint>
18 #include <memory>
19 #include <vector>
20
21 #include "icing/absl_ports/canonical_errors.h"
22 #include "icing/file/posting_list/flash-index-storage.h"
23 #include "icing/file/posting_list/posting-list-identifier.h"
24 #include "icing/file/posting_list/posting-list-used.h"
25 #include "icing/index/main/posting-list-hit-serializer.h"
26 #include "icing/util/status-macros.h"
27
28 namespace icing {
29 namespace lib {
30
31 libtextclassifier3::StatusOr<std::unique_ptr<PostingListHitAccessor>>
Create(FlashIndexStorage * storage,PostingListHitSerializer * serializer)32 PostingListHitAccessor::Create(FlashIndexStorage *storage,
33 PostingListHitSerializer *serializer) {
34 uint32_t max_posting_list_bytes = storage->max_posting_list_bytes();
35 ICING_ASSIGN_OR_RETURN(PostingListUsed in_memory_posting_list,
36 PostingListUsed::CreateFromUnitializedRegion(
37 serializer, max_posting_list_bytes));
38 return std::unique_ptr<PostingListHitAccessor>(new PostingListHitAccessor(
39 storage, serializer, std::move(in_memory_posting_list)));
40 }
41
42 libtextclassifier3::StatusOr<std::unique_ptr<PostingListHitAccessor>>
CreateFromExisting(FlashIndexStorage * storage,PostingListHitSerializer * serializer,PostingListIdentifier existing_posting_list_id)43 PostingListHitAccessor::CreateFromExisting(
44 FlashIndexStorage *storage, PostingListHitSerializer *serializer,
45 PostingListIdentifier existing_posting_list_id) {
46 // Our in_memory_posting_list_ will start as empty.
47 ICING_ASSIGN_OR_RETURN(std::unique_ptr<PostingListHitAccessor> pl_accessor,
48 Create(storage, serializer));
49 ICING_ASSIGN_OR_RETURN(PostingListHolder holder,
50 storage->GetPostingList(existing_posting_list_id));
51 pl_accessor->preexisting_posting_list_ =
52 std::make_unique<PostingListHolder>(std::move(holder));
53 return pl_accessor;
54 }
55
56 // Returns the next batch of hits for the provided posting list.
57 libtextclassifier3::StatusOr<std::vector<Hit>>
GetNextHitsBatch()58 PostingListHitAccessor::GetNextHitsBatch() {
59 if (preexisting_posting_list_ == nullptr) {
60 if (has_reached_posting_list_chain_end_) {
61 return std::vector<Hit>();
62 }
63 return absl_ports::FailedPreconditionError(
64 "Cannot retrieve hits from a PostingListHitAccessor that was not "
65 "created from a preexisting posting list.");
66 }
67 ICING_ASSIGN_OR_RETURN(
68 std::vector<Hit> batch,
69 serializer_->GetHits(&preexisting_posting_list_->posting_list));
70 uint32_t next_block_index = kInvalidBlockIndex;
71 // Posting lists will only be chained when they are max-sized, in which case
72 // next_block_index will point to the next block for the next posting list.
73 // Otherwise, next_block_index can be kInvalidBlockIndex or be used to point
74 // to the next free list block, which is not relevant here.
75 if (preexisting_posting_list_->posting_list.size_in_bytes() ==
76 storage_->max_posting_list_bytes()) {
77 next_block_index = preexisting_posting_list_->next_block_index;
78 }
79
80 if (next_block_index != kInvalidBlockIndex) {
81 // Since we only have to deal with next block for max-sized posting list
82 // block, max_num_posting_lists is 1 and posting_list_index_bits is
83 // BitsToStore(1).
84 PostingListIdentifier next_posting_list_id(
85 next_block_index, /*posting_list_index=*/0,
86 /*posting_list_index_bits=*/BitsToStore(1));
87 ICING_ASSIGN_OR_RETURN(PostingListHolder holder,
88 storage_->GetPostingList(next_posting_list_id));
89 preexisting_posting_list_ =
90 std::make_unique<PostingListHolder>(std::move(holder));
91 } else {
92 has_reached_posting_list_chain_end_ = true;
93 preexisting_posting_list_.reset();
94 }
95 return batch;
96 }
97
PrependHit(const Hit & hit)98 libtextclassifier3::Status PostingListHitAccessor::PrependHit(const Hit &hit) {
99 PostingListUsed &active_pl = (preexisting_posting_list_ != nullptr)
100 ? preexisting_posting_list_->posting_list
101 : in_memory_posting_list_;
102 libtextclassifier3::Status status = serializer_->PrependHit(&active_pl, hit);
103 if (!absl_ports::IsResourceExhausted(status)) {
104 return status;
105 }
106 // There is no more room to add hits to this current posting list! Therefore,
107 // we need to either move those hits to a larger posting list or flush this
108 // posting list and create another max-sized posting list in the chain.
109 if (preexisting_posting_list_ != nullptr) {
110 ICING_RETURN_IF_ERROR(FlushPreexistingPostingList());
111 } else {
112 ICING_RETURN_IF_ERROR(FlushInMemoryPostingList());
113 }
114
115 // Re-add hit. Should always fit since we just cleared
116 // in_memory_posting_list_. It's fine to explicitly reference
117 // in_memory_posting_list_ here because there's no way of reaching this line
118 // while preexisting_posting_list_ is still in use.
119 return serializer_->PrependHit(&in_memory_posting_list_, hit);
120 }
121
122 } // namespace lib
123 } // namespace icing
124