1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h"
18 
19 #include <climits>
20 
21 #include "defines.h"
22 #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h"
23 #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h"
24 #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h"
25 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
26 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h"
27 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
28 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
29 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h"
30 #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
31 #include "suggest/policyimpl/dictionary/utils/file_utils.h"
32 #include "suggest/policyimpl/dictionary/utils/format_utils.h"
33 #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
34 #include "utils/byte_array_view.h"
35 
36 namespace latinime {
37 
38 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForExistingDictFile(const char * const path,const int bufOffset,const int size,const bool isUpdatable)39         DictionaryStructureWithBufferPolicyFactory::newPolicyForExistingDictFile(
40                 const char *const path, const int bufOffset, const int size,
41                 const bool isUpdatable) {
42     if (FileUtils::existsDir(path)) {
43         // Given path represents a directory.
44         return newPolicyForDirectoryDict(path, isUpdatable);
45     } else {
46         if (isUpdatable) {
47             AKLOGE("One file dictionaries don't support updating. path: %s", path);
48             ASSERT(false);
49             return nullptr;
50         }
51         return newPolicyForFileDict(path, bufOffset, size);
52     }
53 }
54 
55 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForOnMemoryDict(const int formatVersion,const std::vector<int> & locale,const DictionaryHeaderStructurePolicy::AttributeMap * const attributeMap)56         DictionaryStructureWithBufferPolicyFactory:: newPolicyForOnMemoryDict(
57                 const int formatVersion, const std::vector<int> &locale,
58                 const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
59     FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion);
60     switch (dictFormatVersion) {
61         case FormatUtils::VERSION_4: {
62             return newPolicyForOnMemoryV4Dict<backward::v402::Ver4DictConstants,
63                     backward::v402::Ver4DictBuffers,
64                     backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr,
65                     backward::v402::Ver4PatriciaTriePolicy>(
66                             dictFormatVersion, locale, attributeMap);
67         }
68         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
69         case FormatUtils::VERSION_4_DEV: {
70             return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers,
71                     Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
72                             dictFormatVersion, locale, attributeMap);
73         }
74         default:
75             AKLOGE("DICT: dictionary format %d is not supported for on memory dictionary",
76                     formatVersion);
77             break;
78     }
79     return nullptr;
80 }
81 
82 template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
83 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForOnMemoryV4Dict(const FormatUtils::FORMAT_VERSION formatVersion,const std::vector<int> & locale,const DictionaryHeaderStructurePolicy::AttributeMap * const attributeMap)84         DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryV4Dict(
85                 const FormatUtils::FORMAT_VERSION formatVersion,
86                 const std::vector<int> &locale,
87                 const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
88     HeaderPolicy headerPolicy(formatVersion, locale, attributeMap);
89     DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy,
90             DictConstants::MAX_DICT_EXTENDED_REGION_SIZE);
91     if (!DynamicPtWritingUtils::writeEmptyDictionary(
92             dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) {
93         AKLOGE("Empty ver4 dictionary structure cannot be created on memory.");
94         return nullptr;
95     }
96     return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
97             new StructurePolicy(std::move(dictBuffers)));
98 }
99 
100 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForDirectoryDict(const char * const path,const bool isUpdatable)101         DictionaryStructureWithBufferPolicyFactory::newPolicyForDirectoryDict(
102                 const char *const path, const bool isUpdatable) {
103     const int headerFilePathBufSize = PATH_MAX + 1 /* terminator */;
104     char headerFilePath[headerFilePathBufSize];
105     getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath);
106     // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of
107     // MmappedBufferPtr if the instance has the responsibility.
108     MmappedBuffer::MmappedBufferPtr mmappedBuffer =
109             MmappedBuffer::openBuffer(headerFilePath, isUpdatable);
110     if (!mmappedBuffer) {
111         return nullptr;
112     }
113     const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion(
114             mmappedBuffer->getReadOnlyByteArrayView().data(),
115             mmappedBuffer->getReadOnlyByteArrayView().size());
116     switch (formatVersion) {
117         case FormatUtils::VERSION_2:
118             AKLOGE("Given path is a directory but the format is version 2. path: %s", path);
119             break;
120         case FormatUtils::VERSION_4: {
121             return newPolicyForV4Dict<backward::v402::Ver4DictConstants,
122                     backward::v402::Ver4DictBuffers,
123                     backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr,
124                     backward::v402::Ver4PatriciaTriePolicy>(
125                             headerFilePath, formatVersion, std::move(mmappedBuffer));
126         }
127         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
128         case FormatUtils::VERSION_4_DEV: {
129             return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers,
130                     Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
131                             headerFilePath, formatVersion, std::move(mmappedBuffer));
132         }
133         default:
134             AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path);
135             break;
136     }
137     ASSERT(false);
138     return nullptr;
139 }
140 
141 template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
142 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForV4Dict(const char * const headerFilePath,const FormatUtils::FORMAT_VERSION formatVersion,MmappedBuffer::MmappedBufferPtr && mmappedBuffer)143         DictionaryStructureWithBufferPolicyFactory::newPolicyForV4Dict(
144                 const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion,
145                 MmappedBuffer::MmappedBufferPtr &&mmappedBuffer) {
146     const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */;
147     char dictPath[dictDirPathBufSize];
148     if (!FileUtils::getFilePathWithoutSuffix(headerFilePath,
149             DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) {
150         AKLOGE("Dictionary file name is not valid as a ver4 dictionary. header path: %s",
151                 headerFilePath);
152         ASSERT(false);
153         return nullptr;
154     }
155     DictBuffersPtr dictBuffers =
156             DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer), formatVersion);
157     if (!dictBuffers || !dictBuffers->isValid()) {
158         AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s",
159                 dictPath);
160         ASSERT(false);
161         return nullptr;
162     }
163     return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
164             new StructurePolicy(std::move(dictBuffers)));
165 }
166 
167 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
newPolicyForFileDict(const char * const path,const int bufOffset,const int size)168         DictionaryStructureWithBufferPolicyFactory::newPolicyForFileDict(
169                 const char *const path, const int bufOffset, const int size) {
170     // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of
171     // MmappedBufferPtr if the instance has the responsibility.
172     MmappedBuffer::MmappedBufferPtr mmappedBuffer(
173             MmappedBuffer::openBuffer(path, bufOffset, size, false /* isUpdatable */));
174     if (!mmappedBuffer) {
175         return nullptr;
176     }
177     switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView().data(),
178             mmappedBuffer->getReadOnlyByteArrayView().size())) {
179         case FormatUtils::VERSION_2:
180             return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
181                     new PatriciaTriePolicy(std::move(mmappedBuffer)));
182         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
183         case FormatUtils::VERSION_4:
184         case FormatUtils::VERSION_4_DEV:
185             AKLOGE("Given path is a file but the format is version 4. path: %s", path);
186             break;
187         default:
188             AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path);
189             break;
190     }
191     ASSERT(false);
192     return nullptr;
193 }
194 
getHeaderFilePathInDictDir(const char * const dictDirPath,const int outHeaderFileBufSize,char * const outHeaderFilePath)195 /* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir(
196         const char *const dictDirPath, const int outHeaderFileBufSize,
197         char *const outHeaderFilePath) {
198     const int dictNameBufSize = strlen(dictDirPath) + 1 /* terminator */;
199     char dictName[dictNameBufSize];
200     FileUtils::getBasename(dictDirPath, dictNameBufSize, dictName);
201     snprintf(outHeaderFilePath, outHeaderFileBufSize, "%s/%s%s", dictDirPath,
202             dictName, Ver4DictConstants::HEADER_FILE_EXTENSION);
203 }
204 
205 } // namespace latinime
206