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 #ifndef ICING_INDEX_MAIN_FLASH_INDEX_STORAGE_HEADER_H_
16 #define ICING_INDEX_MAIN_FLASH_INDEX_STORAGE_HEADER_H_
17 
18 #include <cstdint>
19 #include <memory>
20 
21 #include "icing/text_classifier/lib3/utils/base/statusor.h"
22 #include "icing/absl_ports/canonical_errors.h"
23 #include "icing/file/filesystem.h"
24 
25 namespace icing {
26 namespace lib {
27 
28 // The class used to manage the flash block that contains the header for
29 // FlashIndexStorage. This contains information about the index blocks that
30 // store the posting lists.
31 class HeaderBlock {
32  public:
33   // The class used to access the actual header.
34   struct Header {
35     // A magic used to mark the beginning of a valid header.
36     static constexpr int kMagic = 0x6dfba6ae;
37     int magic;
38     int block_size;
39     int last_indexed_docid;
40     // The size of the index_block_infos array.
41     int num_index_block_infos;
42 
43     struct IndexBlockInfo {
44       // The size of the posting lists that fit on all the index blocks in this
45       // chain. Each block on this posting list will have posting lists of size
46       // posting_list_bytes.
47       int posting_list_bytes;
48       // The block index of the first block in the free list chain.
49       int free_list_block_index;
50     };
51     // Variable-size array, num_index_block_infos long. Can have a max length
52     // of log(block_size). This array is used to maintain a free list for the
53     // available blocks.
54     IndexBlockInfo index_block_infos[0];
55   };
56 
57   // Read HeaderBlock from the specified fd.
58   //
59   // RETURNS:
60   //  - HeaderBlock, on success
61   //  - INTERNAL if unable to read block_size bytes from fd.
Read(const Filesystem * filesystem,int fd,int block_size)62   static libtextclassifier3::StatusOr<HeaderBlock> Read(
63       const Filesystem* filesystem, int fd, int block_size) {
64     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(block_size);
65     if (!filesystem->PRead(fd, buffer.get(), block_size, 0)) {
66       return absl_ports::InternalError("Unable to reader header block!");
67     }
68     return HeaderBlock(filesystem, std::move(buffer), block_size);
69   }
70 
71   // Make a new HeaderBlock with the specified size.
HeaderBlock(const Filesystem * filesystem,int block_size)72   explicit HeaderBlock(const Filesystem* filesystem, int block_size)
73       : HeaderBlock(filesystem, std::make_unique<uint8_t[]>(block_size),
74                     block_size) {
75     std::memset(header_buffer_.get(), 0, block_size);
76   }
77 
header()78   Header* header() const {
79     return reinterpret_cast<Header*>(header_buffer_.get());
80   }
81 
82   // Add another entry to the index_block_infos array and return a pointer to
83   // that entry. Returns a nullptr if the index_block_infos array is already
84   // at a max size.
AddIndexBlockInfo()85   Header::IndexBlockInfo* AddIndexBlockInfo() {
86     if (size() + sizeof(Header::IndexBlockInfo) > block_size_) {
87       return nullptr;
88     }
89     ++header()->num_index_block_infos;
90     return header()->index_block_infos + (header()->num_index_block_infos - 1);
91   }
92 
93   // Returns the size of the header block currently in use.
size()94   int size() const {
95     return sizeof(Header) +
96            header()->num_index_block_infos * sizeof(Header::IndexBlockInfo);
97   }
98 
99   // Writes the header to fd. Returns true on success.
Write(int fd)100   bool Write(int fd) {
101     return filesystem_->PWrite(fd, 0, header_buffer_.get(), block_size_);
102   }
103 
104  private:
HeaderBlock(const Filesystem * filesystem,std::unique_ptr<uint8_t[]> buffer,int block_size)105   explicit HeaderBlock(const Filesystem* filesystem,
106                        std::unique_ptr<uint8_t[]> buffer, int block_size)
107       : filesystem_(filesystem),
108         header_buffer_(std::move(buffer)),
109         block_size_(block_size) {}
110 
111   const Filesystem* filesystem_;  // does NOT own!
112   std::unique_ptr<uint8_t[]> header_buffer_;
113   int block_size_;
114 };
115 static_assert(16 == sizeof(HeaderBlock::Header),
116               "Header has changed size. Consider how this change might affect "
117               "pre-existing indices.");
118 
119 }  // namespace lib
120 }  // namespace icing
121 
122 #endif  // ICING_INDEX_MAIN_FLASH_INDEX_STORAGE_HEADER_H_
123