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/header/header_read_write_utils.h"
18
19#include <cctype>
20#include <cstdio>
21#include <vector>
22
23#include "defines.h"
24#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
25#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
26
27namespace latinime {
28
29const int HeaderReadWriteUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256;
30const int HeaderReadWriteUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256;
31
32const int HeaderReadWriteUtils::HEADER_MAGIC_NUMBER_SIZE = 4;
33const int HeaderReadWriteUtils::HEADER_DICTIONARY_VERSION_SIZE = 2;
34const int HeaderReadWriteUtils::HEADER_FLAG_SIZE = 2;
35const int HeaderReadWriteUtils::HEADER_SIZE_FIELD_SIZE = 4;
36
37const HeaderReadWriteUtils::DictionaryFlags HeaderReadWriteUtils::NO_FLAGS = 0;
38// Flags for special processing
39// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or
40// something very bad (like, the apocalypse) will happen. Please update both at the same time.
41const HeaderReadWriteUtils::DictionaryFlags
42        HeaderReadWriteUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
43const HeaderReadWriteUtils::DictionaryFlags
44        HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2;
45const HeaderReadWriteUtils::DictionaryFlags
46        HeaderReadWriteUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
47
48// Note that these are corresponding definitions in Java side in FormatSpec.FileHeader.
49const char *const HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_KEY = "SUPPORTS_DYNAMIC_UPDATE";
50const char *const HeaderReadWriteUtils::REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY =
51        "REQUIRES_GERMAN_UMLAUT_PROCESSING";
52const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY =
53        "REQUIRES_FRENCH_LIGATURE_PROCESSING";
54
55/* static */ int HeaderReadWriteUtils::getHeaderSize(const uint8_t *const dictBuf) {
56    // See the format of the header in the comment in
57    // BinaryDictionaryFormatUtils::detectFormatVersion()
58    return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE
59            + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE);
60}
61
62/* static */ HeaderReadWriteUtils::DictionaryFlags
63        HeaderReadWriteUtils::getFlags(const uint8_t *const dictBuf) {
64    return ByteArrayUtils::readUint16(dictBuf,
65            HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE);
66}
67
68/* static */ HeaderReadWriteUtils::DictionaryFlags
69        HeaderReadWriteUtils::createAndGetDictionaryFlagsUsingAttributeMap(
70                const HeaderReadWriteUtils::AttributeMap *const attributeMap) {
71    const bool requiresGermanUmlautProcessing = readBoolAttributeValue(attributeMap,
72            REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY, false /* defaultValue */);
73    const bool requiresFrenchLigatureProcessing = readBoolAttributeValue(attributeMap,
74            REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY, false /* defaultValue */);
75    const bool supportsDynamicUpdate = readBoolAttributeValue(attributeMap,
76            SUPPORTS_DYNAMIC_UPDATE_KEY, false /* defaultValue */);
77    DictionaryFlags dictflags = NO_FLAGS;
78    dictflags |= requiresGermanUmlautProcessing ? GERMAN_UMLAUT_PROCESSING_FLAG : 0;
79    dictflags |= requiresFrenchLigatureProcessing ? FRENCH_LIGATURE_PROCESSING_FLAG : 0;
80    dictflags |= supportsDynamicUpdate ? SUPPORTS_DYNAMIC_UPDATE_FLAG : 0;
81    return dictflags;
82}
83
84/* static */ void HeaderReadWriteUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf,
85        AttributeMap *const headerAttributes) {
86    const int headerSize = getHeaderSize(dictBuf);
87    int pos = getHeaderOptionsPosition();
88    if (pos == NOT_A_DICT_POS) {
89        // The header doesn't have header options.
90        return;
91    }
92    int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH];
93    int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH];
94    while (pos < headerSize) {
95        const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
96                MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos);
97        std::vector<int> key;
98        key.insert(key.end(), keyBuffer, keyBuffer + keyLength);
99        const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
100                MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos);
101        std::vector<int> value;
102        value.insert(value.end(), valueBuffer, valueBuffer + valueLength);
103        headerAttributes->insert(AttributeMap::value_type(key, value));
104    }
105}
106
107/* static */ bool HeaderReadWriteUtils::writeDictionaryVersion(
108        BufferWithExtendableBuffer *const buffer, const FormatUtils::FORMAT_VERSION version,
109        int *const writingPos) {
110    if (!buffer->writeUintAndAdvancePosition(FormatUtils::MAGIC_NUMBER, HEADER_MAGIC_NUMBER_SIZE,
111            writingPos)) {
112        return false;
113    }
114    switch (version) {
115        case FormatUtils::VERSION_2:
116            // Version 2 dictionary writing is not supported.
117            return false;
118        case FormatUtils::VERSION_3:
119            return buffer->writeUintAndAdvancePosition(3 /* data */,
120                    HEADER_DICTIONARY_VERSION_SIZE, writingPos);
121        default:
122            return false;
123    }
124}
125
126/* static */ bool HeaderReadWriteUtils::writeDictionaryFlags(
127        BufferWithExtendableBuffer *const buffer, const DictionaryFlags flags,
128        int *const writingPos) {
129    return buffer->writeUintAndAdvancePosition(flags, HEADER_FLAG_SIZE, writingPos);
130}
131
132/* static */ bool HeaderReadWriteUtils::writeDictionaryHeaderSize(
133        BufferWithExtendableBuffer *const buffer, const int size, int *const writingPos) {
134    return buffer->writeUintAndAdvancePosition(size, HEADER_SIZE_FIELD_SIZE, writingPos);
135}
136
137/* static */ bool HeaderReadWriteUtils::writeHeaderAttributes(
138        BufferWithExtendableBuffer *const buffer, const AttributeMap *const headerAttributes,
139        int *const writingPos) {
140    for (AttributeMap::const_iterator it = headerAttributes->begin();
141            it != headerAttributes->end(); ++it) {
142        if (it->first.empty() || it->second.empty()) {
143            continue;
144        }
145        // Write a key.
146        if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(),
147                true /* writesTerminator */, writingPos)) {
148            return false;
149        }
150        // Write a value.
151        if (!buffer->writeCodePointsAndAdvancePosition(&(it->second.at(0)), it->second.size(),
152                true /* writesTerminator */, writingPos)) {
153            return false;
154        }
155    }
156    return true;
157}
158
159/* static */ void HeaderReadWriteUtils::setBoolAttribute(AttributeMap *const headerAttributes,
160        const char *const key, const bool value) {
161    setIntAttribute(headerAttributes, key, value ? 1 : 0);
162}
163
164/* static */ void HeaderReadWriteUtils::setIntAttribute(AttributeMap *const headerAttributes,
165        const char *const key, const int value) {
166    AttributeMap::key_type keyVector;
167    insertCharactersIntoVector(key, &keyVector);
168    setIntAttributeInner(headerAttributes, &keyVector, value);
169}
170
171/* static */ void HeaderReadWriteUtils::setIntAttributeInner(AttributeMap *const headerAttributes,
172        const AttributeMap::key_type *const key, const int value) {
173    AttributeMap::mapped_type valueVector;
174    char charBuf[LARGEST_INT_DIGIT_COUNT + 1];
175    snprintf(charBuf, LARGEST_INT_DIGIT_COUNT + 1, "%d", value);
176    insertCharactersIntoVector(charBuf, &valueVector);
177    (*headerAttributes)[*key] = valueVector;
178}
179
180/* static */ bool HeaderReadWriteUtils::readBoolAttributeValue(
181        const AttributeMap *const headerAttributes, const char *const key,
182        const bool defaultValue) {
183    const int intDefaultValue = defaultValue ? 1 : 0;
184    const int intValue = readIntAttributeValue(headerAttributes, key, intDefaultValue);
185    return intValue != 0;
186}
187
188/* static */ int HeaderReadWriteUtils::readIntAttributeValue(
189        const AttributeMap *const headerAttributes, const char *const key,
190        const int defaultValue) {
191    AttributeMap::key_type keyVector;
192    insertCharactersIntoVector(key, &keyVector);
193    return readIntAttributeValueInner(headerAttributes, &keyVector, defaultValue);
194}
195
196/* static */ int HeaderReadWriteUtils::readIntAttributeValueInner(
197        const AttributeMap *const headerAttributes, const AttributeMap::key_type *const key,
198        const int defaultValue) {
199    AttributeMap::const_iterator it = headerAttributes->find(*key);
200    if (it != headerAttributes->end()) {
201        int value = 0;
202        bool isNegative = false;
203        for (size_t i = 0; i < it->second.size(); ++i) {
204            if (i == 0 && it->second.at(i) == '-') {
205                isNegative = true;
206            } else {
207                if (!isdigit(it->second.at(i))) {
208                    // If not a number.
209                    return defaultValue;
210                }
211                value *= 10;
212                value += it->second.at(i) - '0';
213            }
214        }
215        return isNegative ? -value : value;
216    }
217    return defaultValue;
218}
219
220/* static */ void HeaderReadWriteUtils::insertCharactersIntoVector(const char *const characters,
221        std::vector<int> *const vector) {
222    for (int i = 0; characters[i]; ++i) {
223        vector->push_back(characters[i]);
224    }
225}
226
227} // namespace latinime
228